OpenCPN Partial API docs
Loading...
Searching...
No Matches
quilt.cpp
Go to the documentation of this file.
1/**************************************************************************
2 * Copyright (C) 2013 by David S. Register *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
16 **************************************************************************/
17
24#include <algorithm>
25
26#include <wx/wxprec.h>
27#include <wx/list.h>
28#include <wx/listimpl.cpp>
29
30#include "model/config_vars.h"
31#include "model/ocpn_utils.h"
32
33#include "chartdb.h"
34#include "chartimg.h"
35#include "chcanv.h"
36#include "config.h"
37#include "ocpn_pixel.h" // for ocpnUSE_DIBSECTION
38#include "quilt.h"
39#include "s52plib.h"
40#include "s57chart.h"
41
42#ifdef __ANDROID__
43#include "androidUTIL.h"
44#endif
45
46// We define and use this one Macro in this module
47// Reason: some compilers refuse to inline "GetChartTableEntry()"
48// and so this leads to a push/call sequence for this heavily utilized but
49// short function Note also that in the macor expansion there is no bounds
50// checking on the parameter (i), So it is probably better to confine the
51// macro use to one module, and scrub carefully. Anyway, makes a
52// significant difference with Windows MSVC compiler builds.
53
54#ifndef __ANDROID__
55// #define GetChartTableEntry(i) GetChartTable()[i]
56#endif
57
58// Calculating the chart coverage region with extremely complicated shape is
59// very expensive, put a limit on the complefity of "not covered" areas to
60// prevent the application from slowing down to total unusability. On US ENC
61// charts, the number of NOCOVR PLYs seems to always be under 300, but on the
62// SCS ENCs it can get as high as 10000 and make the application totally
63// unusable with chart quilting enabled while bringing little real effect.
64#define NOCOVR_PLY_PERF_LIMIT 500
65#define AUX_PLY_PERF_LIMIT 500
66
67WX_DEFINE_LIST(PatchList);
68
69#if 0
70static int CompareScales(int i1, int i2) {
71 if (!ChartData) return 0;
72
73 const ChartTableEntry &cte1 = ChartData->GetChartTableEntry(i1);
74 const ChartTableEntry &cte2 = ChartData->GetChartTableEntry(i2);
75
76 if (cte1.Scale_eq(cte2.GetScale())) { // same scales, so sort on dbIndex
77 float lat1, lat2;
78 lat1 = cte1.GetLatMax();
79 lat2 = cte2.GetLatMax();
80 if (roundf(lat1 * 100.) == roundf(lat2 * 100.)) {
81 float lon1, lon2;
82 lon1 = cte1.GetLonMin();
83 lon2 = cte2.GetLonMin();
84 if (lon1 == lon2) {
85 return i1 - i2;
86 } else
87 return (lon1 < lon2) ? -1 : 1;
88 } else
89 return (lat1 < lat2) ? 1 : -1;
90 } else
91 return cte1.GetScale() - cte2.GetScale();
92}
93#endif
94
95// Compare chart Z stack based on scale
96// Equal scale charts will be stacked indiscriminately
97static int CompareScales(const int i1, const int i2) {
98 if (!ChartData) return 0;
99
100 const ChartTableEntry &cte1 = ChartData->GetChartTableEntry(i1);
101 const ChartTableEntry &cte2 = ChartData->GetChartTableEntry(i2);
102
103 // Primary: scale (smaller scale value means larger scale chart)
104 return cte1.GetScale() - cte2.GetScale();
105}
106
107static bool CompareScalesStd(int i1, int i2) {
108 return CompareScales(i1, i2) < 0;
109}
110
111static bool CompareScalesStdFULLSCREEN(int i1, int i2) {
112 return CompareScales(i1, i2) < 0;
113}
114
115static int CompareQuiltCandidateScales(QuiltCandidate *qc1,
116 QuiltCandidate *qc2) {
117 if (!ChartData) return 0;
118 return CompareScales(qc1->dbIndex, qc2->dbIndex);
119}
120
121const LLRegion &QuiltCandidate::GetCandidateRegion() {
122 const ChartTableEntry &cte = ChartData->GetChartTableEntry(dbIndex);
123 LLRegion &candidate_region =
124 const_cast<LLRegion &>(cte.quilt_candidate_region);
125
126 if (!candidate_region.Empty()) return candidate_region;
127
128 LLRegion world_region(-90, -180, 90, 180);
129
130 // for cm93 charts use their valid canvas region (should this apply to all
131 // vector charts?)
132 if (ChartData->GetDBChartType(dbIndex) == CHART_TYPE_CM93COMP) {
133 double cm93_ll_bounds[8] = {-80, -180, -80, 180, 80, 180, 80, -180};
134 candidate_region = LLRegion(4, cm93_ll_bounds);
135 return candidate_region;
136 }
137
138 // If the chart has an aux ply table, use it for finer region precision
139 int nAuxPlyEntries = cte.GetnAuxPlyEntries();
140 if (nAuxPlyEntries >= 1) {
141 candidate_region.Clear();
142 for (int ip = 0; ip < nAuxPlyEntries; ip++) {
143 float *pfp = cte.GetpAuxPlyTableEntry(ip);
144 int nAuxPly = cte.GetAuxCntTableEntry(ip);
145
146 candidate_region.Union(LLRegion(nAuxPly, pfp));
147 }
148 } else {
149 // int n_ply_entries = cte.GetnPlyEntries();
150 // float *pfp = cte.GetpPlyTable();
151 //
152 //
153 // if( n_ply_entries >= 3 ) // could happen with old database and
154 // some charts, e.g. SHOM 2381.kap
155 // candidate_region = LLRegion( n_ply_entries, pfp );
156 // else
157 // candidate_region = world_region;
158
159 std::vector<float> vec = ChartData->GetReducedPlyPoints(dbIndex);
160
161 std::vector<float> vecr;
162 for (size_t i = 0; i < vec.size() / 2; i++) {
163 float a = vec[i * 2 + 1];
164 vecr.push_back(a);
165 a = vec[i * 2];
166 vecr.push_back(a);
167 }
168
169 std::vector<float>::iterator it = vecr.begin();
170
171 if (vecr.size() / 2 >= 3) { // could happen with old database and some
172 // charts, e.g. SHOM 2381.kap
173
174 candidate_region = LLRegion(vecr.size() / 2, (float *)&(*it));
175 } else
176 candidate_region = world_region;
177 }
178
179 // Remove the NoCovr regions
180 if (!candidate_region
181 .Empty()) { // don't bother if the region is already empty
182 int nNoCovrPlyEntries = cte.GetnNoCovrPlyEntries();
183 if (nNoCovrPlyEntries) {
184 for (int ip = 0; ip < nNoCovrPlyEntries; ip++) {
185 float *pfp = cte.GetpNoCovrPlyTableEntry(ip);
186 int nNoCovrPly = cte.GetNoCovrCntTableEntry(ip);
187
188 LLRegion t_region = LLRegion(nNoCovrPly, pfp);
189
190 // For performance reasons, we do not support
191 // no-cover regions with "holes" in them.
192 // This leads to the possibility that a cover region may be fully
193 // embedded within a larger no-cover region.
194 // Handle this case by assuming that such cells will contain more than
195 // one cover region. To create a valid no-cover region we may simply
196 // subtract each cover region from the larger no-cover region.
197 if (nAuxPlyEntries > 1) {
198 for (int ipr = 0; ipr < nAuxPlyEntries; ipr++) {
199 float *pfpr = cte.GetpAuxPlyTableEntry(ipr);
200 int nAuxPly = cte.GetAuxCntTableEntry(ipr);
201 t_region.Subtract(LLRegion(nAuxPly, pfpr));
202 }
203 }
204
205 // We do a test removal of the NoCovr region.
206 // If the result iz empty, it must be that the NoCovr region is
207 // the full extent M_COVR(CATCOV=2) feature found in NOAA ENCs.
208 // We ignore it.
209
210 if (!t_region.Empty()) {
211 LLRegion test_region = candidate_region;
212 test_region.Subtract(t_region);
213 if (!test_region.Empty()) candidate_region = test_region;
214 }
215 }
216 }
217 }
218
219 // Another superbad hack....
220 // Super small scale raster charts like bluemarble.kap usually cross the
221 // prime meridian and Plypoints georef is problematic...... So, force full
222 // screen coverage in the quilt
223 if ((cte.GetScale() > 90000000) &&
224 (cte.GetChartFamily() == CHART_FAMILY_RASTER))
225 candidate_region = world_region;
226
227 return candidate_region;
228}
229
230LLRegion &QuiltCandidate::GetReducedCandidateRegion(double factor) {
231 if (factor != last_factor) {
232 reduced_candidate_region = GetCandidateRegion();
233 reduced_candidate_region.Reduce(factor);
234 last_factor = factor;
235 }
236
237 return reduced_candidate_region;
238}
239
240void QuiltCandidate::SetScale(int scale) {
241 ChartScale = scale;
242 rounding = 0;
243 // XXX find the right rounding
244 if (scale >= 1000) rounding = 5 * pow(10, log10(scale) - 2);
245}
246
247Quilt::Quilt(ChartCanvas *parent) {
248 // m_bEnableRaster = true;
249 // m_bEnableVector = false;;
250 // m_bEnableCM93 = false;
251
252 m_parent = parent;
253 m_reference_scale = 1;
254 m_refchart_dbIndex = -1;
255 m_reference_type = CHART_TYPE_UNKNOWN;
256 m_reference_family = CHART_FAMILY_UNKNOWN;
257 m_quilt_proj = PROJECTION_UNKNOWN;
258 m_chart_familyFix = CHART_FAMILY_UNKNOWN;
259 m_lost_refchart_dbIndex = -1;
260
261 cnode = NULL;
262
263 m_pBM = NULL;
264 m_bcomposed = false;
265 m_bbusy = false;
266 m_b_hidef = false;
267
268 m_pcandidate_array =
269 new ArrayOfSortedQuiltCandidates(CompareQuiltCandidateScales);
270 m_nHiLiteIndex = -1;
271
272 m_zout_family = -1;
273 m_zout_type = -1;
274 m_preferred_family = CHART_FAMILY_RASTER;
275
276 // Quilting of skewed raster charts is allowed for OpenGL only
277 m_bquiltskew = g_bopengl;
278 // Quilting of different projections is allowed for OpenGL only
279 m_bquiltanyproj = g_bopengl;
280}
281
282Quilt::~Quilt() {
283 m_PatchList.DeleteContents(true);
284 m_PatchList.Clear();
285
286 EmptyCandidateArray();
287 delete m_pcandidate_array;
288
289 m_extended_stack_array.clear();
290
291 delete m_pBM;
292}
293
294void Quilt::SetReferenceChart(int dbIndex) {
295 // if (dbIndex >= 0) {
296 // const ChartTableEntry &cte = ChartData->GetChartTableEntry(dbIndex);
297 // if (cte.GetChartFamily() != m_reference_family) {
298 // printf("************family switch\n");
299 // }
300 // }
301
302 m_refchart_dbIndex = dbIndex;
303 if (dbIndex >= 0) {
304 m_zout_family = -1;
305 }
306}
307
308bool Quilt::IsVPBlittable(ViewPort &VPoint, int dx, int dy,
309 bool b_allow_vector) {
310 if (!m_vp_rendered.IsValid()) return false;
311
312 wxPoint2DDouble p1 =
313 VPoint.GetDoublePixFromLL(m_vp_rendered.clat, m_vp_rendered.clon);
314 wxPoint2DDouble p2 = VPoint.GetDoublePixFromLL(VPoint.clat, VPoint.clon);
315 double deltax = p2.m_x - p1.m_x;
316 double deltay = p2.m_y - p1.m_y;
317
318 if ((fabs(deltax - dx) > 1e-2) || (fabs(deltay - dy) > 1e-2)) return false;
319
320 return true;
321}
322
323bool Quilt::IsChartS57Overlay(int db_index) {
324 if (db_index < 0) return false;
325
326 const ChartTableEntry &cte = ChartData->GetChartTableEntry(db_index);
327 if (CHART_FAMILY_VECTOR == cte.GetChartFamily()) {
328 return s57chart::IsCellOverlayType(cte.GetFullSystemPath());
329 } else
330 return false;
331}
332
333bool Quilt::IsChartQuiltableRef(int db_index) {
334 if (db_index < 0 || db_index > ChartData->GetChartTableEntries() - 1)
335 return false;
336
337 // Is the chart targeted by db_index useable as a quilt reference chart?
338 const ChartTableEntry &ctei = ChartData->GetChartTableEntry(db_index);
339
340 bool bproj_match = true; // Accept all projections
341
342 double skew_norm = ctei.GetChartSkew();
343 if (skew_norm > 180.) skew_norm -= 360.;
344
345 bool skew_match =
346 fabs(skew_norm) < 1.; // Only un-skewed charts are acceptable for quilt
347 if (m_bquiltskew) skew_match = true;
348
349 // In noshow array?
350 bool b_noshow = false;
351 for (unsigned int i = 0; i < m_parent->GetQuiltNoshowIindexArray().size();
352 i++) {
353 if (m_parent->GetQuiltNoshowIindexArray()[i] ==
354 db_index) // chart is in the noshow list
355 {
356 b_noshow = true;
357 break;
358 }
359 }
360
361 return (bproj_match & skew_match & !b_noshow);
362}
363
364bool Quilt::IsChartInQuilt(ChartBase *pc) {
365 // Iterate thru the quilt
366 for (unsigned int ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
367 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
368 if ((pqc->b_include) && (!pqc->b_eclipsed)) {
369 if (ChartData->OpenChartFromDB(pqc->dbIndex, FULL_INIT) == pc)
370 return true;
371 }
372 }
373 return false;
374}
375
376bool Quilt::IsChartInQuilt(wxString &full_path) {
377 // Iterate thru the quilt
378 for (unsigned int ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
379 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
380 if ((pqc->b_include) && (!pqc->b_eclipsed)) {
381 auto &cte = ChartData->GetChartTableEntry(pqc->dbIndex);
382 if (cte.GetpsFullPath()->IsSameAs(full_path)) return true;
383 }
384 }
385 return false;
386}
387
388std::vector<int> Quilt::GetCandidatedbIndexArray(bool from_ref_chart,
389 bool exclude_user_hidden) {
390 std::vector<int> ret;
391 for (unsigned int ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
392 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
393 if (from_ref_chart) // only add entries of smaller scale than ref scale
394 {
395 if (pqc->Scale_ge(m_reference_scale)) {
396 // Search the no-show array
397 if (exclude_user_hidden) {
398 bool b_noshow = false;
399 for (unsigned int i = 0;
400 i < m_parent->GetQuiltNoshowIindexArray().size(); i++) {
401 if (m_parent->GetQuiltNoshowIindexArray()[i] ==
402 pqc->dbIndex) // chart is in the noshow list
403 {
404 b_noshow = true;
405 break;
406 }
407 }
408 if (!b_noshow) ret.push_back(pqc->dbIndex);
409 } else {
410 ret.push_back(pqc->dbIndex);
411 }
412 }
413 } else
414 ret.push_back(pqc->dbIndex);
415 }
416 return ret;
417}
418
419QuiltPatch *Quilt::GetCurrentPatch() {
420 if (cnode)
421 return (cnode->GetData());
422 else
423 return NULL;
424}
425
426void Quilt::EmptyCandidateArray() {
427 for (unsigned int i = 0; i < m_pcandidate_array->GetCount(); i++) {
428 delete m_pcandidate_array->Item(i);
429 }
430
431 m_pcandidate_array->Clear();
432}
433
434ChartBase *Quilt::GetFirstChart() {
435 if (!ChartData) return NULL;
436
437 if (!ChartData->IsValid()) // This could happen during yield recursion from
438 // progress dialog during databse update
439 return NULL;
440
441 if (!m_bcomposed) return NULL;
442
443 if (m_bbusy) return NULL;
444
445 m_bbusy = true;
446 ChartBase *pret = NULL;
447 cnode = m_PatchList.GetFirst();
448 while (cnode && !cnode->GetData()->b_Valid) cnode = cnode->GetNext();
449 if (cnode && cnode->GetData()->b_Valid)
450 pret = ChartData->OpenChartFromDB(cnode->GetData()->dbIndex, FULL_INIT);
451
452 m_bbusy = false;
453 return pret;
454}
455
456ChartBase *Quilt::GetNextChart() {
457 if (!ChartData) return NULL;
458
459 if (!ChartData->IsValid()) return NULL;
460
461 if (m_bbusy) return NULL;
462
463 m_bbusy = true;
464 ChartBase *pret = NULL;
465 if (cnode) {
466 cnode = cnode->GetNext();
467 while (cnode && !cnode->GetData()->b_Valid) cnode = cnode->GetNext();
468 if (cnode && cnode->GetData()->b_Valid)
469 pret = ChartData->OpenChartFromDB(cnode->GetData()->dbIndex, FULL_INIT);
470 }
471
472 m_bbusy = false;
473 return pret;
474}
475
476ChartBase *Quilt::GetNextSmallerScaleChart() {
477 if (!ChartData) return NULL;
478
479 if (!ChartData->IsValid()) return NULL;
480
481 if (m_bbusy) return NULL;
482
483 m_bbusy = true;
484 ChartBase *pret = NULL;
485 if (cnode) {
486 cnode = cnode->GetPrevious();
487 while (cnode && !cnode->GetData()->b_Valid) cnode = cnode->GetPrevious();
488 if (cnode && cnode->GetData()->b_Valid)
489 pret = ChartData->OpenChartFromDB(cnode->GetData()->dbIndex, FULL_INIT);
490 }
491
492 m_bbusy = false;
493 return pret;
494}
495
496ChartBase *Quilt::GetLargestScaleChart() {
497 if (!ChartData) return NULL;
498
499 if (m_bbusy) return NULL;
500
501 m_bbusy = true;
502 ChartBase *pret = NULL;
503 cnode = m_PatchList.GetLast();
504 if (cnode)
505 pret = ChartData->OpenChartFromDB(cnode->GetData()->dbIndex, FULL_INIT);
506
507 m_bbusy = false;
508 return pret;
509}
510
511LLRegion Quilt::GetChartQuiltRegion(const ChartTableEntry &cte, ViewPort &vp) {
512 LLRegion chart_region;
513 LLRegion screen_region(vp.GetBBox());
514
515 // Special case for charts which extend around the world, or near to it
516 // Mostly this means cm93....
517 // Take the whole screen, clipped at +/- 80 degrees lat
518 if (fabs(cte.GetLonMax() - cte.GetLonMin()) > 180.) {
519 /*
520 int n_ply_entries = 4;
521 float ply[8];
522 ply[0] = 80.;
523 ply[1] = vp.GetBBox().GetMinX();
524 ply[2] = 80.;
525 ply[3] = vp.GetBBox().GetMaxX();
526 ply[4] = -80.;
527 ply[5] = vp.GetBBox().GetMaxX();
528 ply[6] = -80.;
529 ply[7] = vp.GetBBox().GetMinX();
530
531
532 OCPNRegion t_region = vp.GetVPRegionIntersect( screen_region, 4,
533 &ply[0], cte.GetScale() ); return t_region;
534 */
535 return LLRegion(-80, vp.GetBBox().GetMinLon(), 80,
536 vp.GetBBox().GetMaxLon());
537 }
538
539 // If the chart has an aux ply table, use it for finer region precision
540 int nAuxPlyEntries = cte.GetnAuxPlyEntries();
541 bool aux_ply_skipped = false;
542 if (nAuxPlyEntries >= 1) {
543 for (int ip = 0; ip < nAuxPlyEntries; ip++) {
544 int nAuxPly = cte.GetAuxCntTableEntry(ip);
545 if (nAuxPly > AUX_PLY_PERF_LIMIT) {
546 // wxLogMessage("PLY calculation skipped for %s, nAuxPly: %d",
547 // cte.GetpFullPath(), nAuxPly);
548 aux_ply_skipped = true;
549 break;
550 }
551 float *pfp = cte.GetpAuxPlyTableEntry(ip);
552 LLRegion t_region(nAuxPly, pfp);
553 t_region.Intersect(screen_region);
554 // OCPNRegion t_region = vp.GetVPRegionIntersect(
555 // screen_region, nAuxPly, pfp,
556 // cte.GetScale() );
557 if (!t_region.Empty()) chart_region.Union(t_region);
558 }
559 }
560
561 if (aux_ply_skipped || nAuxPlyEntries == 0) {
562 int n_ply_entries = cte.GetnPlyEntries();
563 float *pfp = cte.GetpPlyTable();
564
565 if (n_ply_entries >= 3) // could happen with old database and some charts,
566 // e.g. SHOM 2381.kap
567 {
568 LLRegion t_region(n_ply_entries, pfp);
569 t_region.Intersect(screen_region);
570 // const OCPNRegion t_region = vp.GetVPRegionIntersect(
571 // screen_region, n_ply_entries, pfp,
572 // cte.GetScale() );
573 if (!t_region.Empty()) chart_region.Union(t_region);
574
575 } else
576 chart_region = screen_region;
577 }
578
579 // Remove the NoCovr regions
580 int nNoCovrPlyEntries = cte.GetnNoCovrPlyEntries();
581 if (nNoCovrPlyEntries) {
582 for (int ip = 0; ip < nNoCovrPlyEntries; ip++) {
583 int nNoCovrPly = cte.GetNoCovrCntTableEntry(ip);
584 if (nNoCovrPly > NOCOVR_PLY_PERF_LIMIT) {
585 // wxLogMessage("NOCOVR calculation skipped for %s, nNoCovrPly: %d",
586 // cte.GetpFullPath(), nNoCovrPly);
587 continue;
588 }
589 float *pfp = cte.GetpNoCovrPlyTableEntry(ip);
590
591 LLRegion t_region(nNoCovrPly, pfp);
592 t_region.Intersect(screen_region);
593 // OCPNRegion t_region = vp.GetVPRegionIntersect(
594 // screen_region, nNoCovrPly, pfp,
595 // cte.GetScale()
596 // );
597
598 // We do a test removal of the NoCovr region.
599 // If the result iz empty, it must be that the NoCovr region is
600 // the full extent M_COVR(CATCOV=2) feature found in NOAA ENCs.
601 // We ignore it.
602
603 if (!t_region.Empty()) {
604 LLRegion test_region = chart_region;
605 test_region.Subtract(t_region);
606
607 if (!test_region.Empty()) chart_region = test_region;
608 }
609 }
610 }
611
612 // Another superbad hack....
613 // Super small scale raster charts like bluemarble.kap usually cross the
614 // prime meridian and Plypoints georef is problematic...... So, force full
615 // screen coverage in the quilt
616 if ((cte.GetScale() > 90000000) &&
617 (cte.GetChartFamily() == CHART_FAMILY_RASTER))
618 chart_region = screen_region;
619
620 // Clip the region to the current viewport
621 // chart_region.Intersect( vp.rv_rect ); already done
622
623 return chart_region;
624}
625
626bool Quilt::IsQuiltVector() {
627 if (m_bbusy) return false;
628
629 m_bbusy = true;
630
631 bool ret = false;
632
633 wxPatchListNode *cnode = m_PatchList.GetFirst();
634 while (cnode) {
635 if (cnode->GetData()) {
636 QuiltPatch *pqp = cnode->GetData();
637
638 if ((pqp->b_Valid) && (!pqp->b_eclipsed) &&
639 (pqp->dbIndex < ChartData->GetChartTableEntries())) {
640 const ChartTableEntry &ctei =
641 ChartData->GetChartTableEntry(pqp->dbIndex);
642
643 if (ctei.GetChartFamily() == CHART_FAMILY_VECTOR) {
644 ret = true;
645 break;
646 }
647 }
648 }
649 cnode = cnode->GetNext();
650 }
651
652 m_bbusy = false;
653 return ret;
654}
655
656bool Quilt::DoesQuiltContainPlugins() {
657 if (m_bbusy) return false;
658
659 m_bbusy = true;
660
661 bool ret = false;
662
663 wxPatchListNode *cnode = m_PatchList.GetFirst();
664 while (cnode) {
665 if (cnode->GetData()) {
666 QuiltPatch *pqp = cnode->GetData();
667
668 if ((pqp->b_Valid) && (!pqp->b_eclipsed)) {
669 const ChartTableEntry &ctei =
670 ChartData->GetChartTableEntry(pqp->dbIndex);
671
672 if (ctei.GetChartType() == CHART_TYPE_PLUGIN) {
673 ret = true;
674 break;
675 }
676 }
677 }
678 cnode = cnode->GetNext();
679 }
680
681 m_bbusy = false;
682 return ret;
683}
684
685int Quilt::GetChartdbIndexAtPix(ViewPort &VPoint, wxPoint p) {
686 if (m_bbusy) return -1;
687
688 m_bbusy = true;
689
690 double lat, lon;
691 VPoint.GetLLFromPix(p, &lat, &lon);
692
693 int ret = -1;
694
695 wxPatchListNode *cnode = m_PatchList.GetFirst();
696 while (cnode) {
697 if (cnode->GetData()->ActiveRegion.Contains(lat, lon)) {
698 ret = cnode->GetData()->dbIndex;
699 break;
700 } else
701 cnode = cnode->GetNext();
702 }
703
704 m_bbusy = false;
705 return ret;
706}
707
708ChartBase *Quilt::GetChartAtPix(ViewPort &VPoint, wxPoint p) {
709 if (m_bbusy) return NULL;
710
711 m_bbusy = true;
712
713 double lat, lon;
714 VPoint.GetLLFromPix(p, &lat, &lon);
715
716 // The patchlist is organized from small to large scale.
717 // We generally will want the largest scale chart at this point, so
718 // walk the whole list. The result will be the last one found, i.e. the
719 // largest scale chart.
720 ChartBase *pret = NULL;
721 wxPatchListNode *cnode = m_PatchList.GetFirst();
722 while (cnode) {
723 QuiltPatch *pqp = cnode->GetData();
724 if (!pqp->b_overlay && (pqp->ActiveRegion.Contains(lat, lon)))
725 if (ChartData->IsChartInCache(pqp->dbIndex)) {
726 pret = ChartData->OpenChartFromDB(pqp->dbIndex, FULL_INIT);
727 }
728 cnode = cnode->GetNext();
729 }
730
731 m_bbusy = false;
732 return pret;
733}
734
735ChartBase *Quilt::GetOverlayChartAtPix(ViewPort &VPoint, wxPoint p) {
736 if (m_bbusy) return NULL;
737
738 m_bbusy = true;
739
740 double lat, lon;
741 VPoint.GetLLFromPix(p, &lat, &lon);
742
743 // The patchlist is organized from small to large scale.
744 // We generally will want the largest scale chart at this point, so
745 // walk the whole list. The result will be the last one found, i.e. the
746 // largest scale chart.
747 ChartBase *pret = NULL;
748 wxPatchListNode *cnode = m_PatchList.GetFirst();
749 while (cnode) {
750 QuiltPatch *pqp = cnode->GetData();
751 if (pqp->b_overlay && (pqp->ActiveRegion.Contains(lat, lon)))
752 pret = ChartData->OpenChartFromDB(pqp->dbIndex, FULL_INIT);
753 cnode = cnode->GetNext();
754 }
755
756 m_bbusy = false;
757 return pret;
758}
759
760void Quilt::InvalidateAllQuiltPatchs() {
761 /*
762 if( m_bbusy )
763 return;
764
765 m_bbusy = true;
766 m_bbusy = false;
767 */
768 return;
769}
770
771std::vector<int> Quilt::GetQuiltIndexArray() {
772 return m_index_array;
773
774 std::vector<int> ret;
775
776 if (m_bbusy) return ret;
777
778 m_bbusy = true;
779
780 wxPatchListNode *cnode = m_PatchList.GetFirst();
781 while (cnode) {
782 ret.push_back(cnode->GetData()->dbIndex);
783 cnode = cnode->GetNext();
784 }
785
786 m_bbusy = false;
787
788 return ret;
789}
790
791bool Quilt::IsQuiltDelta(ViewPort &vp) {
792 if (!m_vp_quilt.IsValid() || !m_bcomposed) return true;
793
794 if (m_vp_quilt.view_scale_ppm != vp.view_scale_ppm) return true;
795
796 if (m_vp_quilt.m_projection_type != vp.m_projection_type) return true;
797
798 if (m_vp_quilt.rotation != vp.rotation) return true;
799
800 // Has the quilt shifted by more than one pixel in any direction?
801 wxPoint cp_last, cp_this;
802
803 cp_last = m_vp_quilt.GetPixFromLL(vp.clat, vp.clon);
804 cp_this = vp.GetPixFromLL(vp.clat, vp.clon);
805
806 return (cp_last != cp_this);
807}
808
809void Quilt::AdjustQuiltVP(ViewPort &vp_last, ViewPort &vp_proposed) {
810 if (m_bbusy) return;
811
812 // ChartBase *pRefChart = GetLargestScaleChart();
813 ChartBase *pRefChart =
814 ChartData->OpenChartFromDB(m_refchart_dbIndex, FULL_INIT);
815
816 if (pRefChart) pRefChart->AdjustVP(vp_last, vp_proposed);
817}
818
819double Quilt::GetRefNativeScale() {
820 double ret_val = 1.0;
821 if (ChartData) {
822 ChartBase *pc = ChartData->OpenChartFromDB(m_refchart_dbIndex, FULL_INIT);
823 if (pc) ret_val = pc->GetNativeScale();
824 }
825
826 return ret_val;
827}
828
829int Quilt::GetNewRefChart() {
830 // Using the current quilt, select a useable reference chart
831 // Said chart will be in the extended (possibly full-screen) stack,
832 // And will have a scale equal to or just greater than the current quilt
833 // reference scale, And will match current quilt projection type, and will
834 // have Skew=0, so as to be fully quiltable
835 int new_ref_dbIndex = m_refchart_dbIndex;
836 unsigned int im = m_extended_stack_array.size();
837 if (im > 0) {
838 for (unsigned int is = 0; is < im; is++) {
839 const ChartTableEntry &m =
840 ChartData->GetChartTableEntry(m_extended_stack_array[is]);
841
842 double skew_norm = m.GetChartSkew();
843 if (skew_norm > 180.) skew_norm -= 360.;
844
845 if ((m.Scale_ge(m_reference_scale)) &&
846 (m_reference_family == m.GetChartFamily()) &&
847 (m_bquiltanyproj || m_quilt_proj == m.GetChartProjectionType()) &&
848 (m_bquiltskew || (fabs(skew_norm) < 1.0))) {
849 new_ref_dbIndex = m_extended_stack_array[is];
850 break;
851 }
852 }
853 }
854 return new_ref_dbIndex;
855}
856
857int Quilt::GetNomScaleMax(int scale, ChartTypeEnum type,
858 ChartFamilyEnum family) {
859 switch (family) {
860 case CHART_FAMILY_RASTER: {
861 return scale / 4;
862 }
863
864 case CHART_FAMILY_VECTOR: {
865 return scale / 4;
866 }
867
868 default: {
869 return scale / 2;
870 }
871 }
872}
873
874int Quilt::GetNomScaleMin(int scale, ChartTypeEnum type,
875 ChartFamilyEnum family) {
876 double zoom_mod = (double)g_chart_zoom_modifier_raster;
877
878 if (family == CHART_FAMILY_VECTOR)
879 zoom_mod = (double)g_chart_zoom_modifier_vector;
880
881 double modf = zoom_mod / 5.; // -1->1
882 double mod = pow(16., modf);
883 mod = wxMax(mod, .2);
884 mod = wxMin(mod, 16.0);
885
886 // Apply zoom scale modifier according to chart family.
887 switch (family) {
888 case CHART_FAMILY_RASTER: {
889 if (CHART_TYPE_MBTILES == type)
890 // Here we don't apply the zoom modifier because MBTILES renderer
891 // changes the zoom layer accordingly so that the minimum scale does not
892 // change.
893 return scale * 4; // MBTiles are fast enough
894 else
895 return scale * 1 * mod;
896 }
897
898 case CHART_FAMILY_VECTOR: {
899 return scale * 4 * mod;
900 }
901
902 default: {
903 mod = wxMin(mod, 2.0);
904 return scale * 2 * mod;
905 }
906 }
907}
908
909struct scale {
910 int index, nom, min, max;
911};
912
913int Quilt::AdjustRefOnZoom(bool b_zin, ChartFamilyEnum family,
914 ChartTypeEnum type, double proposed_scale_onscreen) {
915 std::vector<scale> scales;
916 std::vector<scale> scales_mbtiles;
917
918 // For Vector charts, or for ZoomIN operations, we can switch to any chart
919 // that is on screen. Otherwise, we can only switch to charts contining the
920 // VP center point
921
922 // Since this rule is mainly for preservation of performance,
923 // we can also allow fullscreen reference chart selection if opengl is active
924
925 bool b_allow_fullscreen_ref =
926 (family == CHART_FAMILY_VECTOR) || b_zin || g_bopengl;
927
928 // Get the scale of the smallest scale
929 // chart, of the current type, in the quilt
930 int smallest_scale = 1;
931 for (size_t i = 0; i < m_extended_stack_array.size(); i++) {
932 int index = m_extended_stack_array[i];
933 if (ChartData->GetDBChartType(index) == type)
934 smallest_scale = wxMax(smallest_scale, ChartData->GetDBChartScale(index));
935 }
936
937 // Walk the extended chart array, capturing data
938 int i_first = 0;
939 for (size_t i = 0; i < m_extended_stack_array.size(); i++) {
940 int test_db_index = m_extended_stack_array[i];
941
942 if (b_allow_fullscreen_ref ||
943 m_parent->GetpCurrentStack()->DoesStackContaindbIndex(test_db_index)) {
944 if ((family == ChartData->GetDBChartFamily(test_db_index)) &&
945 IsChartQuiltableRef(test_db_index)
946 /*&& !IsChartS57Overlay( test_db_index )*/) {
947 int nscale = ChartData->GetDBChartScale(test_db_index);
948
949 int nmax_scale = GetNomScaleMax(nscale, type, family);
950
951 // For the largest scale chart, allow essentially infinite overzoom.
952 // The range will be clipped later
953 if (0 == i_first) nmax_scale = 1;
954
955 int nmin_scale = GetNomScaleMin(nscale, type, family);
956
957 // Allow RNC quilt to zoom far out and still show smallest scale chart.
958 if ((type == CHART_TYPE_KAP) && (nscale == smallest_scale))
959 nmin_scale *= 24;
960
961 // Allow MBTiles quilt to zoom far out and still show smallest scale
962 // chart.
963 if ((type == CHART_TYPE_MBTILES) && (nscale == smallest_scale))
964 nmin_scale *= 24;
965
966 if (CHART_TYPE_MBTILES == ChartData->GetDBChartType(test_db_index))
967 scales_mbtiles.push_back(
968 scale{test_db_index, nscale, nmin_scale, nmax_scale});
969 else
970 scales.push_back(
971 scale{test_db_index, nscale, nmin_scale, nmax_scale});
972
973 i_first++;
974 }
975 }
976 }
977 // mbtiles charts only set
978 if (scales.empty()) scales = scales_mbtiles;
979 // If showing Vector charts,
980 // Find the smallest scale chart of the target type (i.e. skipping cm93)
981 // and make sure that its min scale is at least
982 // small enough to allow reasonable zoomout.
983 // But this will be calculated without regard to zoom scale factor, so that
984 // the piano does not grow excessively
985 if (CHART_FAMILY_VECTOR == family) {
986 for (size_t i = scales.size(); i; i--) {
987 int test_db_index = scales[i - 1].index;
988 if (type == ChartData->GetDBChartType(test_db_index)) {
989 scales[i - 1].min = scales[i - 1].nom * 80;
990 break;
991 }
992 }
993 }
994
995 // Traverse the list, making sure that the allowable scale ranges overlap so
996 // as to make no "holes" in the coverage. We do this by extending upward the
997 // range of larger scale charts, so that they overlap the next smaller scale
998 // chart. Makes a nicer image... However, we don't want excessive underzoom,
999 // for performance reasons. So make sure any adjusted min_scale is not more
1000 // than twice the already established value
1001 if (scales.size() > 1) {
1002 for (unsigned i = 0; i < scales.size() - 1; i++) {
1003 int min_scale_test = wxMax(scales[i].min, scales[i + 1].max + 1);
1004 min_scale_test = wxMin(min_scale_test, scales[i].min * 2);
1005 scales[i].min = min_scale_test;
1006 // min_scale[i] = wxMax(min_scale[i], max_scale.Item(i+1) +
1007 // 1);
1008 }
1009 }
1010
1011 // There may still be holes...
1012 // Traverse the list again, from smaller to larger scale, filling in any holes
1013 // by increasing the max_scale of smaller scale charts. Skip cm93 if present
1014 if (scales.size() > 2) {
1015 for (size_t i = scales.size() - 2; i >= 1; i--) {
1016 scales[i].max = wxMin(scales[i].max, scales[i - 1].min - 1);
1017 }
1018 }
1019
1020 int new_ref_dbIndex = -1;
1021
1022 // Search for the largest scale chart whose scale limits contain the requested
1023 // scale.
1024 for (size_t i = 0; i < scales.size(); i++) {
1025 if ((proposed_scale_onscreen <
1026 scales[i].min *
1027 1.05) && // 5 percent leeway to allow for roundoff errors
1028 (proposed_scale_onscreen > scales[i].max)) {
1029 new_ref_dbIndex = scales[i].index;
1030 break;
1031 }
1032 }
1033
1034 return new_ref_dbIndex;
1035}
1036
1037int Quilt::AdjustRefOnZoomOut(double proposed_scale_onscreen) {
1038 // Reset "lost" chart logic
1039 m_lost_refchart_dbIndex = -1;
1040
1041 int current_db_index = m_refchart_dbIndex;
1042 int current_family = m_reference_family;
1043 ChartTypeEnum current_type = (ChartTypeEnum)m_reference_type;
1044
1045 if (m_refchart_dbIndex >= 0) {
1046 const ChartTableEntry &cte =
1047 ChartData->GetChartTableEntry(m_refchart_dbIndex);
1048 current_family = cte.GetChartFamily();
1049 current_type = (ChartTypeEnum)cte.GetChartType();
1050 }
1051
1052 if (current_type == CHART_TYPE_CM93COMP) return current_db_index;
1053
1054 int proposed_ref_index =
1055 AdjustRefOnZoom(false, (ChartFamilyEnum)current_family, current_type,
1056 proposed_scale_onscreen);
1057
1058 m_zout_family = -1;
1059 if (proposed_ref_index < 0) {
1060 m_zout_family = current_family; // save it
1061 m_zout_type = current_type;
1062 m_zout_dbindex = current_db_index;
1063 }
1064
1065 if (proposed_ref_index < 0) {
1066 return m_refchart_dbIndex;
1067 } else {
1068 SetReferenceChart(proposed_ref_index);
1069 return proposed_ref_index;
1070 }
1071}
1072
1073int Quilt::AdjustRefOnZoomIn(double proposed_scale_onscreen) {
1074 // Reset "lost" chart logic
1075 m_lost_refchart_dbIndex = -1;
1076
1077 int current_db_index = m_refchart_dbIndex;
1078 int current_family = m_reference_family;
1079 ChartTypeEnum current_type = (ChartTypeEnum)m_reference_type;
1080
1081 if (m_zout_family >= 0) {
1082 current_type = (ChartTypeEnum)m_zout_type;
1083 current_family = m_zout_family;
1084 }
1085
1086 // If the current reference chart is cm93, and it became so due to a zout
1087 // from another family, detect this case and allow switch to save chart index
1088 // family
1089 if (current_type == CHART_TYPE_CM93COMP) {
1090 if (m_zout_family >= 0) {
1091 current_family = ChartData->GetDBChartFamily(m_zout_dbindex);
1092 } else // cm93 (selected) does not shift charts
1093 return current_db_index;
1094 }
1095
1096 if ((-1 == m_refchart_dbIndex) && (m_zout_dbindex >= 0))
1097 BuildExtendedChartStackAndCandidateArray(m_zout_dbindex, m_vp_quilt);
1098 else
1099 BuildExtendedChartStackAndCandidateArray(m_refchart_dbIndex, m_vp_quilt);
1100
1101 int proposed_ref_index =
1102 AdjustRefOnZoom(true, (ChartFamilyEnum)current_family, current_type,
1103 proposed_scale_onscreen);
1104
1105 if (current_db_index == -1) {
1106 SetReferenceChart(proposed_ref_index);
1107 return proposed_ref_index;
1108 }
1109
1110 if (proposed_ref_index != -1) {
1111 if (ChartData->GetDBChartScale(current_db_index) >=
1112 ChartData->GetDBChartScale(proposed_ref_index)) {
1113 SetReferenceChart(proposed_ref_index);
1114 return proposed_ref_index;
1115 }
1116 }
1117 proposed_ref_index = current_db_index;
1118
1119 SetReferenceChart(proposed_ref_index);
1120
1121 return proposed_ref_index;
1122}
1123
1124bool Quilt::IsChartSmallestScale(int dbIndex) {
1125 if (!ChartData) return false;
1126
1127 // find the smallest scale chart of the specified type on the extended stack
1128 // array
1129 int specified_type = ChartData->GetDBChartType(dbIndex);
1130 int target_dbindex = -1;
1131
1132 unsigned int target_stack_index = 0;
1133 if (m_extended_stack_array.size()) {
1134 while ((target_stack_index <= (m_extended_stack_array.size() - 1))) {
1135 int test_db_index = m_extended_stack_array[target_stack_index];
1136
1137 if (specified_type == ChartData->GetDBChartType(test_db_index))
1138 target_dbindex = test_db_index;
1139
1140 target_stack_index++;
1141 }
1142 }
1143 return (dbIndex == target_dbindex);
1144}
1145
1146LLRegion Quilt::GetHiliteRegion() {
1147 LLRegion r;
1148
1149 // TODO Idea: convert this to an array of smaller regions. Should be faster
1150 // to compose...
1151
1152 for (auto &index : m_HiLiteIndexArray) {
1153 const ChartTableEntry &cte = ChartData->GetChartTableEntry(index);
1154 LLRegion cell_region = GetChartQuiltRegion(cte, m_vp_quilt);
1155 r.Union(cell_region);
1156#if 0
1157 xxx
1158 // Walk the PatchList, looking for the target hilite index
1159 for (unsigned int i = 0; i < m_PatchList.GetCount(); i++) {
1160 wxPatchListNode *pcinode = m_PatchList.Item(i);
1161 QuiltPatch *piqp = pcinode->GetData();
1162 if ((index == piqp->dbIndex) && (piqp->b_Valid)) // found it
1163 {
1164 r.Union(piqp->ActiveRegion);
1165 }
1166 }
1167#endif
1168 }
1169 return r;
1170}
1171
1172#if 0
1173LLRegion Quilt::GetHiliteRegion() {
1174 LLRegion r;
1175 if (m_nHiLiteIndex >= 0) {
1176 // Walk the PatchList, looking for the target hilite index
1177 for (unsigned int i = 0; i < m_PatchList.GetCount(); i++) {
1178 wxPatchListNode *pcinode = m_PatchList.Item(i);
1179 QuiltPatch *piqp = pcinode->GetData();
1180 if ((m_nHiLiteIndex == piqp->dbIndex) && (piqp->b_Valid)) // found it
1181 {
1182 r = piqp->ActiveRegion;
1183 break;
1184 }
1185 }
1186
1187 // If not in the patchlist, look in the full chartbar
1188 if (r.Empty()) {
1189 for (unsigned int ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
1190 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
1191 if (m_nHiLiteIndex == pqc->dbIndex) {
1192 LLRegion chart_region = pqc->GetCandidateRegion();
1193 if (!chart_region.Empty()) {
1194 // Do not highlite fully eclipsed charts
1195 bool b_eclipsed = false;
1196 for (unsigned int ir = 0; ir < m_eclipsed_stack_array.size();
1197 ir++) {
1198 if (m_nHiLiteIndex == m_eclipsed_stack_array[ir]) {
1199 b_eclipsed = true;
1200 break;
1201 }
1202 }
1203
1204 if (!b_eclipsed) r = chart_region;
1205 break;
1206 }
1207 }
1208 }
1209 }
1210
1211 // Might be MBTiles....
1212 if (r.Empty()) {
1213 const ChartTableEntry &cte =
1214 ChartData->GetChartTableEntry(m_nHiLiteIndex);
1215 if (cte.GetChartType() == CHART_TYPE_MBTILES) {
1216 r = GetTilesetRegion(m_nHiLiteIndex);
1217 }
1218 }
1219 }
1220 return r;
1221}
1222#endif
1223
1224const LLRegion &Quilt::GetTilesetRegion(int dbIndex) {
1225 LLRegion world_region(-90, -180, 90, 180);
1226
1227 const ChartTableEntry &cte = ChartData->GetChartTableEntry(dbIndex);
1228 LLRegion &target_region = const_cast<LLRegion &>(cte.quilt_candidate_region);
1229
1230 if (!target_region.Empty()) return target_region;
1231
1232 // If the chart has an aux ply table, use it for finer region precision
1233 int nAuxPlyEntries = cte.GetnAuxPlyEntries();
1234 if (nAuxPlyEntries >= 1) {
1235 target_region.Clear();
1236 for (int ip = 0; ip < nAuxPlyEntries; ip++) {
1237 float *pfp = cte.GetpAuxPlyTableEntry(ip);
1238 int nAuxPly = cte.GetAuxCntTableEntry(ip);
1239
1240 target_region.Union(LLRegion(nAuxPly, pfp));
1241 }
1242 } else {
1243 std::vector<float> vec = ChartData->GetReducedPlyPoints(dbIndex);
1244
1245 std::vector<float> vecr;
1246 for (size_t i = 0; i < vec.size() / 2; i++) {
1247 float a = vec[i * 2 + 1];
1248 vecr.push_back(a);
1249 a = vec[i * 2];
1250 vecr.push_back(a);
1251 }
1252
1253 std::vector<float>::iterator it = vecr.begin();
1254
1255 if (vecr.size() / 2 >= 3) { // could happen with old database and some
1256 // charts, e.g. SHOM 2381.kap
1257
1258 target_region = LLRegion(vecr.size() / 2, (float *)&(*it));
1259 } else
1260 target_region = world_region;
1261 }
1262
1263 // Remove the NoCovr regions
1264 if (!target_region.Empty()) { // don't bother if the region is already empty
1265 int nNoCovrPlyEntries = cte.GetnNoCovrPlyEntries();
1266 if (nNoCovrPlyEntries) {
1267 for (int ip = 0; ip < nNoCovrPlyEntries; ip++) {
1268 float *pfp = cte.GetpNoCovrPlyTableEntry(ip);
1269 int nNoCovrPly = cte.GetNoCovrCntTableEntry(ip);
1270
1271 LLRegion t_region = LLRegion(nNoCovrPly, pfp);
1272
1273 // We do a test removal of the NoCovr region.
1274 // If the result iz empty, it must be that the NoCovr region is
1275 // the full extent M_COVR(CATCOV=2) feature found in NOAA ENCs.
1276 // We ignore it.
1277
1278 if (!t_region.Empty()) {
1279 LLRegion test_region = target_region;
1280 test_region.Subtract(t_region);
1281
1282 if (!test_region.Empty()) target_region = test_region;
1283 }
1284 }
1285 }
1286 }
1287
1288 // Another superbad hack....
1289 // Super small scale raster charts like bluemarble.kap usually cross the
1290 // prime meridian and Plypoints georef is problematic...... So, force full
1291 // screen coverage in the quilt
1292 // if( (cte.GetScale() > 90000000) && (cte.GetChartFamily() ==
1293 // CHART_FAMILY_RASTER) )
1294 // target_region = world_region;
1295
1296 return target_region;
1297}
1298
1299int Quilt::SelectRefChartByFamily(ChartFamilyEnum family) {
1300 // Get the full screen chart index array, and sort it
1301 auto array = GetFullscreenIndexArray();
1302 // Sort bu largest scale first.
1303 std::sort(array.begin(), array.end(), CompareScalesStd);
1304
1305 // Walk the array, largest to smallest scale
1306 int selIndex = -1;
1307 for (int dbIndex : array) {
1308 if (dbIndex >= 0) {
1309 const ChartTableEntry &cte_candidate =
1310 ChartData->GetChartTableEntry(dbIndex);
1311
1312 // Skip basemaps
1313 wxFileName fn(cte_candidate.GetFullPath());
1314 if (fn.GetPath().Lower().Contains("basemap")) continue;
1315
1316 // Choose the appropriate reference chart index
1317 if (cte_candidate.GetChartFamily() == family) {
1318 selIndex = dbIndex;
1319 m_chart_familyFix = family; // record the switch
1320 break;
1321 }
1322 }
1323 }
1324 m_lost_refchart_dbIndex = -1; // force clear lost chart logic
1325
1326 return selIndex;
1327}
1328
1329bool Quilt::BuildExtendedChartStackAndCandidateArray(int ref_db_index,
1330 ViewPort &vp_in) {
1331 double zoom_test_val = .002;
1332 zoom_test_val *= 2;
1333
1334 EmptyCandidateArray();
1335 m_extended_stack_array.clear();
1336 m_fullscreen_index_array.clear();
1337
1338 int reference_scale = 1e8;
1339 int reference_type = -1;
1340 int reference_family = -1;
1341 int quilt_proj =
1342 m_bquiltanyproj ? vp_in.m_projection_type : PROJECTION_UNKNOWN;
1343
1344 if (ref_db_index >= 0) {
1345 const ChartTableEntry &cte_ref =
1346 ChartData->GetChartTableEntry(ref_db_index);
1347 reference_scale = cte_ref.GetScale();
1348 reference_type = cte_ref.GetChartType();
1349 if (!m_bquiltanyproj) quilt_proj = ChartData->GetDBChartProj(ref_db_index);
1350 reference_family = cte_ref.GetChartFamily();
1351 }
1352
1353 bool b_need_resort = false;
1354
1355 ViewPort vp_local = vp_in; // non-const copy
1356
1357 // if( !pCurrentStack ) {
1358 // pCurrentStack = new ChartStack;
1359 // ChartData->BuildChartStack( pCurrentStack, vp_local.clat,
1360 // vp_local.clon );
1361 // }
1362
1363 int n_charts = m_parent->GetpCurrentStack()->nEntry;
1364
1365 // Walk the current ChartStack...
1366 // Building the quilt candidate array
1367 for (int ics = 0; ics < n_charts; ics++) {
1368 int istack = m_parent->GetpCurrentStack()->GetDBIndex(ics);
1369 if (istack < 0) continue;
1370 m_extended_stack_array.push_back(istack);
1371
1372 // If the reference chart is cm93, we need not add any charts to the
1373 // candidate array from the vp center. All required charts will be added
1374 // (by family) as we consider the entire database (group) and full screen
1375 // later
1376 if (reference_type == CHART_TYPE_CM93COMP) continue;
1377
1378 const ChartTableEntry &cte = ChartData->GetChartTableEntry(istack);
1379
1380 // only charts of the proper projection and type may be quilted....
1381 // Also, only unskewed charts if so directed
1382 // and we avoid adding CM93 Composite until later
1383
1384 // If any PlugIn charts are involved, we make the inclusion test on chart
1385 // family, instead of chart type.
1386 if ((cte.GetChartType() == CHART_TYPE_PLUGIN) ||
1387 (reference_type == CHART_TYPE_PLUGIN)) {
1388 if (reference_family != cte.GetChartFamily()) {
1389 continue;
1390 }
1391 } else {
1392 if (reference_type != cte.GetChartType()) {
1393 // If the chart identifies as a "basemap",
1394 // then include its candidate region.
1395 // The chart is a possible basemap if the chart path name
1396 // contains the string "basemap", not case-sensitive
1397
1398 wxFileName fn(cte.GetFullPath());
1399 if (!fn.GetPath().Lower().Contains("basemap")) continue;
1400 }
1401 }
1402
1403 if (cte.GetChartType() == CHART_TYPE_CM93COMP) continue;
1404
1405 // Also, check the scale of the proposed chart. If too small, skip it.
1406 int candidate_chart_scale = cte.GetScale();
1407 double chart_native_ppm =
1408 m_canvas_scale_factor / (double)candidate_chart_scale;
1409 double zoom_factor = vp_local.view_scale_ppm / chart_native_ppm;
1410 if ((zoom_factor < zoom_test_val) &&
1411 // MBTILES charts report the scale of their smallest layer (i.e. most
1412 // detailed) as native chart scale, even if they are embedding many more
1413 // layers. Since we don't know their maximum scale at this stage, we
1414 // don't skip the chart if this native scale is apparently too small.
1415 (cte.GetChartType() != CHART_TYPE_MBTILES)) {
1416 m_extended_stack_array.pop_back();
1417 continue;
1418 }
1419
1420 double skew_norm = cte.GetChartSkew();
1421 if (skew_norm > 180.) skew_norm -= 360.;
1422
1423 if ((m_bquiltskew ? 1 : fabs(skew_norm) < 1.0) &&
1424 (m_bquiltanyproj || cte.GetChartProjectionType() == quilt_proj)) {
1425 QuiltCandidate *qcnew = new QuiltCandidate;
1426 qcnew->dbIndex = istack;
1427 qcnew->SetScale(cte.GetScale());
1428 m_pcandidate_array->push_back(qcnew); // auto-sorted on scale
1429 }
1430
1431 // if( ( reference_type == cte.GetChartType() ) ||
1432 // ( (cte.GetChartType() == CHART_TYPE_PLUGIN ) &&
1433 // (reference_family == cte.GetChartFamily() )) || (
1434 // (reference_type == CHART_TYPE_PLUGIN ) &&
1435 // (reference_family == cte.GetChartFamily() )) ){
1436 //
1437 // if( ( m_bquiltskew ? 1: fabs( skew_norm ) < 1.0 )
1438 // && ( m_bquiltanyproj || cte.GetChartProjectionType()
1439 // == quilt_proj )
1440 // && ( cte.GetChartType() != CHART_TYPE_CM93COMP ) ) {
1441 // QuiltCandidate *qcnew = new QuiltCandidate;
1442 // qcnew->dbIndex = i;
1443 // qcnew->ChartScale = cte.GetScale();
1444 //
1445 // m_pcandidate_array->push_back( qcnew ); //
1446 // auto-sorted on scale
1447 // }
1448 // }
1449 }
1450
1451 // Search the entire database, potentially adding all charts
1452 // which intersect the ViewPort in any way
1453 // .AND. other requirements.
1454 // Again, skipping cm93 for now
1455 int n_all_charts = ChartData->GetChartTableEntries();
1456
1457 LLBBox viewbox = vp_local.GetBBox();
1458 int sure_index = -1;
1459 int sure_index_scale = 0;
1460 int sure_index_type = -1;
1461
1462 for (int i = 0; i < n_all_charts; i++) {
1463 // We can eliminate some charts immediately
1464 // Try to make these tests in some sensible order....
1465
1466 int groupIndex = m_parent->m_groupIndex;
1467 if ((groupIndex > 0) && (!ChartData->IsChartInGroup(i, groupIndex)))
1468 continue;
1469
1470 const ChartTableEntry &cte = ChartData->GetChartTableEntry(i);
1471
1472 // Skip any charts in Exclude array
1473 if (ChartData->IsChartDirectoryExcluded(cte.GetFullPath())) continue;
1474
1475 if (cte.GetChartType() == CHART_TYPE_CM93COMP)
1476 m_fullscreen_index_array.push_back(i);
1477
1478 // On android, SDK > 29, we require that the directory of charts be
1479 // "writable" as determined by Android Java file system
1480#ifdef __ANDROID__
1481 wxFileName fn(cte.GetFullSystemPath());
1482 if (!androidIsDirWritable(fn.GetPath())) continue;
1483#endif
1484 if (cte.GetChartType() == CHART_TYPE_CM93COMP) continue;
1485
1486 const LLBBox &chart_box = cte.GetBBox();
1487 if ((viewbox.IntersectOut(chart_box))) continue;
1488
1489 if (cte.GetChartType() == CHART_TYPE_PLUGIN) {
1490 if (!ChartData->IsChartAvailable(i)) continue;
1491 }
1492
1493 m_fullscreen_index_array.push_back(i);
1494
1495 // If the chart identifies as a "basemap",
1496 // then include its candidate region.
1497 // The chart is a possible basemap if the chart path name
1498 // contains the string "basemap", not case-sensitive
1499
1500 bool guest_family_include = false;
1501 if (reference_family != cte.GetChartFamily()) {
1502 wxFileName fn(cte.GetFullPath());
1503 if (fn.GetPath().Lower().Contains("basemap")) {
1504 guest_family_include = true;
1505 }
1506
1507 if (m_lost_refchart_dbIndex == i) guest_family_include = true;
1508
1509 if ((cte.GetChartType() != CHART_TYPE_MBTILES) && !guest_family_include)
1510 continue;
1511 }
1512
1513 if (!m_bquiltanyproj && quilt_proj != cte.GetChartProjectionType())
1514 continue;
1515
1516 double skew_norm = cte.GetChartSkew();
1517 if (skew_norm > 180.) skew_norm -= 360.;
1518
1519 if (!m_bquiltskew && fabs(skew_norm) > 1.0) continue;
1520
1521 // Special case for S57 ENC
1522 // Add the chart only if the chart's fractional area exceeds n%
1523#if 0
1524 if( CHART_TYPE_S57 == cte.GetChartType() ) {
1525 //Get the fractional area of this candidate
1526 double chart_area = (cte.GetLonMax() - cte.GetLonMin()) *
1527 (cte.GetLatMax() - cte.GetLatMin());
1528 double quilt_area = viewbox.GetLonRange() * viewbox.GetLatRange();
1529 if ((chart_area / quilt_area) < .01) continue;
1530 }
1531#endif
1532 int candidate_chart_scale = cte.GetScale();
1533
1534 // Try to guarantee that there is one chart added with scale larger than
1535 // reference scale
1536 // Take note here, and keep track of the smallest scale chart that is
1537 // larger scale than reference....
1538 if (!cte.Scale_ge(reference_scale)) {
1539 if (cte.Scale_gt(sure_index_scale)) {
1540 sure_index = i;
1541 sure_index_scale = candidate_chart_scale;
1542 sure_index_type = cte.GetChartType();
1543 }
1544 }
1545
1546 // At this point, the candidate is the right type, skew, and projection,
1547 // and is on-screen somewhere.... Now add the candidate if its scale is
1548 // smaller than the reference scale, or is not excessively underzoomed.
1549
1550 // Calculate zoom factor for this chart
1551 double chart_native_ppm =
1552 m_canvas_scale_factor / (double)candidate_chart_scale;
1553 double zoom_factor = vp_in.view_scale_ppm / chart_native_ppm;
1554 double zoom_factor_test_extra = 0.2;
1555
1556 // For some quilts (e.g.cm93 + MBTiles), the reference scale is not known
1557 // or is default 1e8 value. This would exclude large scale MBtiles.
1558 // Adjust the reference scale test value so as to include the MBTiles,
1559 // if their zoom factor is suitable
1560 double ref_scale_test = reference_scale;
1561 if (cte.GetChartType() == CHART_TYPE_MBTILES)
1562 ref_scale_test = candidate_chart_scale;
1563
1564 if ((cte.Scale_ge(ref_scale_test) && (zoom_factor > zoom_test_val)) ||
1565 (zoom_factor > zoom_factor_test_extra)) {
1566 LLRegion cell_region = GetChartQuiltRegion(cte, vp_local);
1567
1568 // this is false if the chart has no actual overlap on screen
1569 // or lots of NoCovr regions. US3EC04.000 is a good example
1570 // i.e the full bboxes overlap, but the actual vp intersect is null.
1571 if (!cell_region.Empty()) {
1572 // Check to see if this chart is already in the stack array
1573 // by virtue of being under the Viewport center point....
1574 bool b_exists = false;
1575 for (unsigned int ir = 0; ir < m_extended_stack_array.size(); ir++) {
1576 if (i == m_extended_stack_array[ir]) {
1577 b_exists = true;
1578 break;
1579 }
1580 }
1581
1582 if (!b_exists) {
1583 // Check to be sure that this chart has not already been added
1584 // i.e. charts that have exactly the same file name and nearly the
1585 // same mod time These charts can be in the database due to having
1586 // the exact same chart in different directories, as may be desired
1587 // for some grouping schemes
1588 // Extended to also check for "identical" charts, having exact same
1589 // EditionDate
1590 bool b_noadd = false;
1591 auto &cte_n = ChartData->GetChartTableEntry(i);
1592 for (unsigned int id = 0; id < m_extended_stack_array.size(); id++) {
1593 if (m_extended_stack_array[id] != -1) {
1594 auto &cte_m =
1595 ChartData->GetChartTableEntry(m_extended_stack_array[id]);
1596 bool bsameTime = false;
1597 if (cte_m.GetFileTime() && cte_n.GetFileTime()) {
1598 if (labs(cte_m.GetFileTime() - cte_n.GetFileTime()) < 60)
1599 bsameTime = true;
1600 }
1601 if (cte_m.GetChartEditionDate() == cte_n.GetChartEditionDate())
1602 bsameTime = true;
1603
1604 if (bsameTime) {
1605 if (cte_n.GetpFileName()->IsSameAs(*(cte_m.GetpFileName())))
1606 b_noadd = true;
1607 }
1608 }
1609 }
1610
1611 if (!b_noadd) {
1612 m_extended_stack_array.push_back(i);
1613
1614 QuiltCandidate *qcnew = new QuiltCandidate;
1615 qcnew->dbIndex = i;
1616 qcnew->SetScale(
1617 candidate_chart_scale); // ChartData->GetDBChartScale( i );
1618
1619 m_pcandidate_array->push_back(qcnew); // auto-sorted on scale
1620
1621 b_need_resort = true;
1622 }
1623 }
1624 }
1625 }
1626 } // for all charts
1627
1628 // Check to be sure that at least one chart was added that is larger scale
1629 // than reference scale
1630 if (-1 != sure_index) {
1631 // check to see if it is already in
1632 bool sure_exists = false;
1633 for (unsigned int ir = 0; ir < m_extended_stack_array.size(); ir++) {
1634 if (sure_index == m_extended_stack_array[ir]) {
1635 sure_exists = true;
1636 break;
1637 }
1638 }
1639
1640 // If not already added, do so now
1641 if (!sure_exists && (sure_index_type != CHART_TYPE_MBTILES)) {
1642 m_extended_stack_array.push_back(sure_index);
1643
1644 QuiltCandidate *qcnew = new QuiltCandidate;
1645 qcnew->dbIndex = sure_index;
1646 qcnew->SetScale(ChartData->GetDBChartScale(sure_index));
1647 m_pcandidate_array->push_back(qcnew); // auto-sorted on scale
1648
1649 b_need_resort = true;
1650 }
1651 }
1652
1653 // Re sort the extended stack array on scale
1654 if (b_need_resort && m_extended_stack_array.size() > 1) {
1655 std::sort(m_extended_stack_array.begin(), m_extended_stack_array.end(),
1656 CompareScalesStd);
1657 }
1658 // Sort the full screen array too
1659 std::sort(m_fullscreen_index_array.begin(), m_fullscreen_index_array.end(),
1660 CompareScalesStdFULLSCREEN);
1661
1662 return true;
1663}
1664
1665int Quilt::AdjustRefSelection(const ViewPort &vp_in) {
1666 // Starting from the currently selected Ref chart,
1667 // choose a ref chart that meets the required under/overzoom limits
1668 // It might be the same, so no change required
1669
1670 if (!ChartData) return false;
1671
1672 if (ChartData
1673 ->IsBusy()) // This prevent recursion on chart loads that Yeild()
1674 return false;
1675
1676 ViewPort vp_local = vp_in; // need a non-const copy
1677
1678 // As ChartdB data is always in rectilinear space, region calculations need
1679 // to be done with no VP rotation
1680 vp_local.SetRotationAngle(0.);
1681
1682 BuildExtendedChartStackAndCandidateArray(m_refchart_dbIndex, vp_local);
1683
1684 ChartFamilyEnum family = CHART_FAMILY_RASTER;
1685 ChartTypeEnum type = CHART_TYPE_KAP;
1686
1687 // Get the desired family/type
1688 if (m_refchart_dbIndex >= 0) {
1689 const ChartTableEntry &cte_ref =
1690 ChartData->GetChartTableEntry(m_refchart_dbIndex);
1691 type = (ChartTypeEnum)cte_ref.GetChartType();
1692 family = (ChartFamilyEnum)cte_ref.GetChartFamily();
1693 }
1694
1695 int ret_index = AdjustRefOnZoom(true, family, type, vp_in.chart_scale);
1696
1697 return ret_index;
1698}
1699
1700double Quilt::GetBestStartScale(int dbi_ref_hint, const ViewPort &vp_in) {
1701 if (!ChartData) return vp_in.view_scale_ppm;
1702
1703 if (ChartData
1704 ->IsBusy()) // This prevent recursion on chart loads that Yeild()
1705 return vp_in.view_scale_ppm;
1706
1707 ViewPort vp_local = vp_in; // need a non-const copy
1708
1709 // Validate Reference Chart hint
1710 int tentative_ref_index = dbi_ref_hint;
1711 if (dbi_ref_hint < 0) {
1712 // arbitrarily select reference chart as largest scale on current stack
1713 // if( !pCurrentStack ) {
1714 // pCurrentStack = new ChartStack;
1715 // ChartData->BuildChartStack( pCurrentStack, vp_local.clat,
1716 // vp_local.clon );
1717 // }
1718 tentative_ref_index = m_parent->GetpCurrentStack()->GetDBIndex(0);
1719 }
1720
1721 // As ChartdB data is always in rectilinear space, region calculations need
1722 // to be done with no VP rotation
1723 // double saved_vp_rotation = vp_local.rotation; // save
1724 // a copy
1725 vp_local.SetRotationAngle(0.);
1726
1727 BuildExtendedChartStackAndCandidateArray(tentative_ref_index, vp_local);
1728
1729 // tentative choice might not be in the extended stack....
1730 bool bf = false;
1731 for (unsigned int i = 0; i < m_pcandidate_array->GetCount(); i++) {
1732 QuiltCandidate *qc = m_pcandidate_array->Item(i);
1733 if (qc->dbIndex == tentative_ref_index) {
1734 bf = true;
1735 break;
1736 }
1737 }
1738
1739 if (!bf && m_pcandidate_array->GetCount()) {
1740 tentative_ref_index = GetNewRefChart();
1741 BuildExtendedChartStackAndCandidateArray(tentative_ref_index, vp_local);
1742 }
1743
1744 double proposed_scale_onscreen = vp_in.chart_scale;
1745
1746 if (m_pcandidate_array->GetCount()) {
1747 SetReferenceChart(tentative_ref_index);
1748 } else {
1749 // Need to choose some chart, find a quiltable candidate
1750 bool bfq = false;
1751 for (unsigned int i = 0; i < m_pcandidate_array->GetCount(); i++) {
1752 QuiltCandidate *qc = m_pcandidate_array->Item(i);
1753 if (IsChartQuiltableRef(qc->dbIndex)) {
1754 SetReferenceChart(qc->dbIndex);
1755 bfq = true;
1756 break;
1757 }
1758 }
1759
1760 if (!bfq) // fallback to first chart in stack
1761 SetReferenceChart(m_parent->GetpCurrentStack()->GetDBIndex(0));
1762 }
1763
1764 if (m_refchart_dbIndex >= 0) {
1765 // Suggest a scale so that the largest scale candidate is "nominally"
1766 // scaled, meaning not overzoomed, and not underzoomed
1767 ChartBase *pc = ChartData->OpenChartFromDB(m_refchart_dbIndex, FULL_INIT);
1768 if (pc) {
1769 double min_ref_scale =
1770 pc->GetNormalScaleMin(m_parent->GetCanvasScaleFactor(), false);
1771 double max_ref_scale = pc->GetNormalScaleMax(
1772 m_parent->GetCanvasScaleFactor(), m_canvas_width);
1773 if ((proposed_scale_onscreen >= min_ref_scale) &&
1774 (proposed_scale_onscreen <= max_ref_scale))
1775 return vp_in.view_scale_ppm;
1776 else {
1777 proposed_scale_onscreen = wxMin(proposed_scale_onscreen, max_ref_scale);
1778 proposed_scale_onscreen = wxMax(proposed_scale_onscreen, min_ref_scale);
1779 }
1780 }
1781 }
1782 return m_parent->GetCanvasScaleFactor() / proposed_scale_onscreen;
1783}
1784
1785ChartBase *Quilt::GetRefChart() {
1786 if (m_refchart_dbIndex >= 0 && ChartData)
1787 return ChartData->OpenChartFromDB(m_refchart_dbIndex, FULL_INIT);
1788 return nullptr;
1789}
1790
1791void Quilt::UnlockQuilt() {
1792 wxASSERT(m_bbusy == false);
1793 ChartData->UnLockCache();
1794 // unlocked only charts owned by the Quilt
1795 for (unsigned int ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
1796 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
1797 ChartData->UnLockCacheChart(pqc->dbIndex);
1798 }
1799}
1800
1801bool Quilt::Compose(const ViewPort &vp_in) {
1802 if (!ChartData) return false;
1803 if (ChartData
1804 ->IsBusy()) // This prevent recursion on chart loads that Yeild()
1805 return false;
1806
1807 if (!m_parent->GetpCurrentStack()) return false;
1808
1809 if (m_bbusy) return false;
1810
1811 // XXX call before setting m_bbusy for wxASSERT in UnlockQuilt
1812 UnlockQuilt();
1813 m_bbusy = true;
1814
1815 ViewPort vp_local = vp_in; // need a non-const copy
1816
1817 // Get Reference Chart parameters
1818 if (m_refchart_dbIndex >= 0) {
1819 const ChartTableEntry &cte_ref =
1820 ChartData->GetChartTableEntry(m_refchart_dbIndex);
1821 m_reference_scale = cte_ref.GetScale();
1822 m_reference_type = cte_ref.GetChartType();
1823 if (!m_bquiltanyproj)
1824 m_quilt_proj = ChartData->GetDBChartProj(m_refchart_dbIndex);
1825 m_reference_family = cte_ref.GetChartFamily();
1826 }
1827
1828 // Set up the viewport projection type
1829 if (!m_bquiltanyproj) vp_local.SetProjectionType(m_quilt_proj);
1830
1831 // As ChartdB data is always in rectilinear space, region calculations need
1832 // to be done with no VP rotation
1833 // double saved_vp_rotation = vp_local.rotation; //
1834 // save a copy vp_local.SetRotationAngle( 0. );
1835
1836 BuildExtendedChartStackAndCandidateArray(m_refchart_dbIndex, vp_local);
1837
1838 // It can happen (in groups switch, or single->quilt mode) that there
1839 // is no refchart known, but there are charts available in the piano.
1840 // Detect this case, and build the quilt based on the smallest scale chart
1841 // anywhere on screen.
1842
1843 // if ((m_refchart_dbIndex < 0) && m_extended_stack_array.size()){
1844 // // Take the smallest scale chart in the array.
1845 // int tentative_dbIndex = m_extended_stack_array.back();
1846 //
1847 // // Verify that the zoom scale is acceptable.
1848 // const ChartTableEntry &cte =
1849 // ChartData->GetChartTableEntry(tentative_dbIndex); int
1850 // candidate_chart_scale = cte.GetScale(); double chart_native_ppm =
1851 // m_canvas_scale_factor / (double)candidate_chart_scale;
1852 // double zoom_factor = vp_local.view_scale_ppm / chart_native_ppm;
1853 // if (zoom_factor > 0.1){
1854 // m_refchart_dbIndex = tentative_dbIndex;
1855 // BuildExtendedChartStackAndCandidateArray(m_refchart_dbIndex,
1856 // vp_local);
1857 // }
1858 // }
1859
1860 // It is possible that the reference chart is not really part of the
1861 // visible quilt This can happen when the reference chart is panned
1862 // off-screen in full screen quilt mode
1863 // If this situation occurs, we need to immediately select a new reference
1864 // chart And rebuild the Candidate Array
1865 //
1866 // We also save the dbIndex of the "lost" chart, and try to recover it
1867 // on subsequent quilts, typically as the user pans the "lost" chart back
1868 // on-screen. The "lost" chart logic is reset on any zoom operations. See
1869 // FS#1221
1870 //
1871 // A special case occurs with cm93 composite chart set as the reference
1872 // chart: It is not at this point a candidate, so won't be found by the
1873 // search This case is indicated if the candidate count is zero. If so, do
1874 // not invalidate the ref chart
1875 bool bf = false;
1876 for (unsigned int i = 0; i < m_pcandidate_array->GetCount(); i++) {
1877 QuiltCandidate *qc = m_pcandidate_array->Item(i);
1878 if (qc->dbIndex == m_refchart_dbIndex) {
1879 bf = true;
1880 break;
1881 }
1882 }
1883
1884 // Process "lost chart" logic by default
1885 // However, if the chart family has been directly set
1886 // by SelectRefChartByFamily(), then honor the selection always.
1887 if (m_chart_familyFix == CHART_FAMILY_UNKNOWN) {
1888 if (!bf && m_pcandidate_array->GetCount() &&
1889 (m_reference_type != CHART_TYPE_CM93COMP)) {
1890 m_lost_refchart_dbIndex = m_refchart_dbIndex; // save for later
1891 int candidate_ref_index = GetNewRefChart();
1892 if (m_refchart_dbIndex != candidate_ref_index) {
1893 m_refchart_dbIndex = candidate_ref_index;
1894 BuildExtendedChartStackAndCandidateArray(m_refchart_dbIndex, vp_local);
1895 }
1896 // There was no viable candidate of smaller scale than the "lost
1897 // chart", so choose the smallest scale chart in the candidate list.
1898 // Exception: if the smallest scale chart in the candidate list
1899 // is a different family, then do not change the reference chart
1900 else {
1901 BuildExtendedChartStackAndCandidateArray(m_refchart_dbIndex, vp_local);
1902 if (m_pcandidate_array->GetCount()) {
1903 int alternate_index =
1904 m_pcandidate_array->Item(m_pcandidate_array->GetCount() - 1)
1905 ->dbIndex;
1906 const ChartTableEntry &cte_alt =
1907 ChartData->GetChartTableEntry(alternate_index);
1908
1909 if (GetRefFamily() == cte_alt.GetChartFamily()) {
1910 m_refchart_dbIndex = alternate_index;
1911 BuildExtendedChartStackAndCandidateArray(m_refchart_dbIndex,
1912 vp_local);
1913 }
1914 }
1915 }
1916 }
1917 }
1918
1919 if ((-1 != m_lost_refchart_dbIndex) &&
1920 (m_lost_refchart_dbIndex != m_refchart_dbIndex)) {
1921 // Is the lost chart in the extended stack ?
1922 // If so, build a new Cnadidate array based upon the lost chart
1923 for (unsigned int ir = 0; ir < m_extended_stack_array.size(); ir++) {
1924 if (m_lost_refchart_dbIndex == m_extended_stack_array[ir]) {
1925 m_refchart_dbIndex = m_lost_refchart_dbIndex;
1926 BuildExtendedChartStackAndCandidateArray(m_refchart_dbIndex, vp_local);
1927 m_lost_refchart_dbIndex = -1;
1928 break;
1929 }
1930 }
1931 }
1932
1933 bool b_has_overlays = false;
1934
1935 // If this is an S57 quilt, we need to know if there are overlays in it
1936 if (CHART_FAMILY_VECTOR == m_reference_family) {
1937 for (unsigned int ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
1938 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
1939 const ChartTableEntry &cte = ChartData->GetChartTableEntry(pqc->dbIndex);
1940
1941 if (s57chart::IsCellOverlayType(cte.GetFullSystemPath())) {
1942 b_has_overlays = true;
1943 break;
1944 }
1945 }
1946 }
1947
1948 // Using Region logic, and starting from the largest scale chart
1949 // figuratively "draw" charts until the ViewPort window is completely
1950 // quilted over Add only those charts whose scale is smaller than the
1951 // "reference scale"
1952 // const LLRegion cvp_region = vp_local.GetLLRegion(
1953 // wxRect(0, 0, vp_local.pix_width, vp_local.pix_height));
1954 const LLRegion cvp_region = vp_local.GetLLRegion(vp_local.rv_rect);
1955 LLRegion vp_region = cvp_region;
1956 unsigned int ir;
1957
1958 // "Draw" the reference chart first, since it is special in that it
1959 // controls the fine vpscale setting
1960 QuiltCandidate *pqc_ref = NULL;
1961 for (ir = 0; ir < m_pcandidate_array->GetCount();
1962 ir++) // find ref chart entry
1963 {
1964 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
1965 if (pqc->dbIndex == m_refchart_dbIndex) {
1966 pqc_ref = pqc;
1967 break;
1968 }
1969 }
1970
1971 // Quilted regions can be simplified to reduce the cost of region operations,
1972 // in this case allow a maximum error of 8 pixels (the rendered display is
1973 // much better, this is only for composing the quilt)
1974 const double z = 111274.96299695622;
1976 double factor = 8.0 / (vp_local.view_scale_ppm * z);
1977
1978 if (pqc_ref) {
1979 const ChartTableEntry &cte_ref =
1980 ChartData->GetChartTableEntry(m_refchart_dbIndex);
1981
1982 LLRegion vpu_region(cvp_region);
1983
1984 // LLRegion chart_region = pqc_ref->GetCandidateRegion();
1985 LLRegion &chart_region = pqc_ref->GetReducedCandidateRegion(factor);
1986
1987 if (cte_ref.GetChartType() != CHART_TYPE_MBTILES) {
1988 if (!chart_region.Empty()) {
1989 vpu_region.Intersect(chart_region);
1990
1991 if (vpu_region.Empty())
1992 pqc_ref->b_include = false; // skip this chart, no true overlap
1993 else {
1994 pqc_ref->b_include = true;
1995 vp_region.Subtract(chart_region); // adding this chart
1996 }
1997 } else
1998 pqc_ref->b_include = false; // skip this chart, empty region
1999 } else {
2000 pqc_ref->b_include = false; // skip this chart, mbtiles
2001 }
2002 }
2003
2004 // Now the rest of the candidates
2005 if (!vp_region.Empty()) {
2006 for (ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
2007 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
2008
2009 if (pqc->dbIndex == m_refchart_dbIndex) continue; // already did this one
2010
2011 const ChartTableEntry &cte = ChartData->GetChartTableEntry(pqc->dbIndex);
2012
2013 // Skip overlays on this pass, so that they do not subtract from quilt
2014 // and thus displace a geographical cell with the same extents. Overlays
2015 // will be picked up in the next pass, if any are found
2016 if (CHART_FAMILY_VECTOR == m_reference_family) {
2017 if (s57chart::IsCellOverlayType(cte.GetFullSystemPath())) {
2018 continue;
2019 }
2020 }
2021
2022 // Skip MBTiles
2023 if (CHART_TYPE_MBTILES == cte.GetChartType()) {
2024 pqc->b_include = false; // skip this chart, mbtiles
2025 continue;
2026 }
2027
2028 if (cte.Scale_ge(m_reference_scale)) {
2029 // If this chart appears in the no-show array, then simply include it,
2030 // but don't subtract its region when determining the smaller scale
2031 // charts to include.....
2032 bool b_in_noshow = false;
2033 for (unsigned int ins = 0;
2034 ins < m_parent->GetQuiltNoshowIindexArray().size(); ins++) {
2035 if (m_parent->GetQuiltNoshowIindexArray()[ins] ==
2036 pqc->dbIndex) // chart is in the noshow list
2037 {
2038 b_in_noshow = true;
2039 break;
2040 }
2041 }
2042
2043 if (!b_in_noshow) {
2044 // Check intersection
2045 LLRegion vpu_region(cvp_region);
2046
2047 // LLRegion chart_region = pqc->GetCandidateRegion( ); //quilt_region;
2048 LLRegion &chart_region = pqc->GetReducedCandidateRegion(factor);
2049
2050 if (!chart_region.Empty()) {
2051 vpu_region.Intersect(chart_region);
2052
2053 if (vpu_region.Empty())
2054 pqc->b_include = false; // skip this chart, no true overlap
2055 else {
2056 pqc->b_include = true;
2057 vp_region.Subtract(chart_region); // adding this chart
2058 }
2059 } else
2060 pqc->b_include = false; // skip this chart, empty region
2061 } else {
2062 pqc->b_include = true;
2063 }
2064
2065 } else {
2066 pqc->b_include = false; // skip this chart, scale is too large
2067 }
2068
2069 if (vp_region.Empty()) // normal stop condition, quilt is full
2070 break;
2071 }
2072 }
2073
2074 // For S57 quilts, walk the list again to identify overlay cells found
2075 // previously, and make sure they are always included and not eclipsed
2076 if (b_has_overlays && (CHART_FAMILY_VECTOR == m_reference_family)) {
2077 for (ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
2078 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
2079
2080 if (pqc->dbIndex == m_refchart_dbIndex) continue; // already did this one
2081
2082 const ChartTableEntry &cte = ChartData->GetChartTableEntry(pqc->dbIndex);
2083
2084 if (cte.Scale_ge(m_reference_scale)) {
2085 bool b_in_noshow = false;
2086 for (unsigned int ins = 0;
2087 ins < m_parent->GetQuiltNoshowIindexArray().size(); ins++) {
2088 if (m_parent->GetQuiltNoshowIindexArray()[ins] ==
2089 pqc->dbIndex) // chart is in the noshow list
2090 {
2091 b_in_noshow = true;
2092 break;
2093 }
2094 }
2095
2096 if (!b_in_noshow) {
2097 // Check intersection
2098 LLRegion vpu_region(cvp_region);
2099
2100 // LLRegion chart_region = pqc->GetCandidateRegion( );
2101 LLRegion &chart_region = pqc->GetReducedCandidateRegion(factor);
2102
2103 if (!chart_region.Empty()) vpu_region.Intersect(chart_region);
2104
2105 if (vpu_region.Empty())
2106 pqc->b_include = false; // skip this chart, no true overlap
2107 else {
2108 bool b_overlay =
2109 s57chart::IsCellOverlayType(cte.GetFullSystemPath());
2110 if (b_overlay) pqc->b_include = true;
2111 }
2112
2113 // If the reference chart happens to be an overlay (e.g.
2114 // 3UABUOYS.000), we dont want it to eclipse any smaller scale
2115 // standard useage charts.
2116 const ChartTableEntry &cte_ref =
2117 ChartData->GetChartTableEntry(m_refchart_dbIndex);
2118 if (s57chart::IsCellOverlayType(cte_ref.GetFullSystemPath())) {
2119 pqc->b_include = true;
2120 }
2121 }
2122 }
2123 }
2124 }
2125
2126 // Walk the candidate list again, marking "eclipsed" charts
2127 // which at this point are the ones with b_include == false .AND. whose
2128 // scale is strictly smaller than the ref scale Also, maintain the member
2129 // list of same
2130
2131 m_eclipsed_stack_array.clear();
2132
2133 for (ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
2134 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
2135
2136 if (!pqc->b_include) {
2137 const ChartTableEntry &cte = ChartData->GetChartTableEntry(pqc->dbIndex);
2138 if (cte.Scale_ge(m_reference_scale) &&
2139 (cte.GetChartType() != CHART_TYPE_MBTILES)) {
2140 m_eclipsed_stack_array.push_back(pqc->dbIndex);
2141 pqc->b_eclipsed = true;
2142 }
2143 }
2144 }
2145
2146 // Potentially add cm93 to the candidate array if the region is not yet
2147 // fully covered
2148 if (((m_bquiltanyproj || m_quilt_proj == PROJECTION_MERCATOR)) &&
2149 !vp_region.Empty()) {
2150 bool b_must_add_cm93 = true;
2151#if 0
2152 // Check the remaining unpainted region.
2153 // It may contain very small "slivers" of empty space, due to mixing of very small scale charts
2154 // with the quilt. If this is the case, do not waste time loading cm93....
2155
2156 OCPNRegionIterator updd( vp_region );
2157 while( updd .HaveRects()) {
2158 wxRect rect = updd.GetRect();
2159 if( ( rect.width > 2 ) && ( rect.height > 2 ) ) {
2160 b_must_add_cm93 = true;
2161 break;
2162 }
2163 updd.NextRect();
2164 }
2165#endif
2166
2167 if (b_must_add_cm93) {
2168 for (int ics = 0; ics < m_parent->GetpCurrentStack()->nEntry; ics++) {
2169 int i = m_parent->GetpCurrentStack()->GetDBIndex(ics);
2170 if (CHART_TYPE_CM93COMP == ChartData->GetDBChartType(i)) {
2171 QuiltCandidate *qcnew = new QuiltCandidate;
2172 qcnew->dbIndex = i;
2173 qcnew->SetScale(ChartData->GetDBChartScale(i));
2174
2175 m_pcandidate_array->Add(qcnew);
2176 }
2177 }
2178 }
2179 }
2180
2181 // Check the list...if no charts are visible due to all being smaller than
2182 // reference_scale, then make sure the smallest scale chart which has any
2183 // true region intersection is visible anyway Also enable any other charts
2184 // which are the same scale as the first one added
2185 bool b_vis = false;
2186 for (unsigned int i = 0; i < m_pcandidate_array->GetCount(); i++) {
2187 QuiltCandidate *pqc = m_pcandidate_array->Item(i);
2188 if (pqc->b_include) {
2189 b_vis = true;
2190 break;
2191 }
2192 }
2193
2194 if (!b_vis && m_pcandidate_array->GetCount()) {
2195 int add_scale = 0;
2196
2197 for (int i = m_pcandidate_array->GetCount() - 1; i >= 0; i--) {
2198 QuiltCandidate *pqc = m_pcandidate_array->Item(i);
2199 const ChartTableEntry &cte = ChartData->GetChartTableEntry(pqc->dbIndex);
2200
2201 // Don't add cm93 yet, it is always covering the quilt...
2202 if (cte.GetChartType() == CHART_TYPE_CM93COMP) continue;
2203
2204 // Don't add MBTiles
2205 if (cte.GetChartType() == CHART_TYPE_MBTILES) continue;
2206
2207 // Check intersection
2208 LLRegion vpck_region(vp_local.GetBBox());
2209
2210 // LLRegion chart_region = pqc->GetCandidateRegion();
2211 LLRegion &chart_region = pqc->GetReducedCandidateRegion(factor);
2212
2213 if (!chart_region.Empty()) vpck_region.Intersect(chart_region);
2214
2215 if (!vpck_region.Empty()) {
2216 if (add_scale) {
2217 if (cte.Scale_eq(add_scale)) {
2218 pqc->b_include = true;
2219 }
2220 } else {
2221 pqc->b_include = true;
2222 add_scale = cte.GetScale();
2223 }
2224 }
2225 }
2226 }
2227
2228 // Finally, build a list of "patches" for the quilt.
2229 // Smallest scale first, as this will be the natural drawing order
2230
2231 m_PatchList.DeleteContents(true);
2232 m_PatchList.Clear();
2233
2234 if (m_pcandidate_array->GetCount()) {
2235 for (int i = m_pcandidate_array->GetCount() - 1; i >= 0; i--) {
2236 QuiltCandidate *pqc = m_pcandidate_array->Item(i);
2237
2238 // cm93 add has been deferred until here
2239 // so that it would not displace possible raster or ENCs of larger
2240 // scale
2241 const ChartTableEntry &m = ChartData->GetChartTableEntry(pqc->dbIndex);
2242
2243 if (m.GetChartType() == CHART_TYPE_CM93COMP)
2244 pqc->b_include = true; // force acceptance of this chart in quilt
2245 // would not be in candidate array if not elected
2246
2247 if (pqc->b_include) {
2248 QuiltPatch *pqp = new QuiltPatch;
2249 pqp->dbIndex = pqc->dbIndex;
2250 pqp->ProjType = m.GetChartProjectionType();
2251 // this is the region used for drawing, don't reduce it
2252 // it's visible
2253 pqp->quilt_region = pqc->GetCandidateRegion();
2254 // pqp->quilt_region = pqc->GetReducedCandidateRegion(factor);
2255
2256 pqp->b_Valid = true;
2257
2258 m_PatchList.Append(pqp);
2259 }
2260 }
2261 }
2262 // From here on out, the PatchList is usable...
2263
2264#ifdef QUILT_TYPE_1
2265 if (!m_bquiltanyproj) {
2266 // Establish the quilt projection type
2267 m_quilt_proj = PROJECTION_MERCATOR; // default
2268 ChartBase *ppc = GetLargestScaleChart();
2269 if (ppc) m_quilt_proj = ppc->GetChartProjectionType();
2270 }
2271#endif
2272
2273 if (!m_bquiltanyproj) {
2274 // Walk the PatchList, marking any entries whose projection does not
2275 // match the determined quilt projection
2276 for (unsigned int i = 0; i < m_PatchList.GetCount(); i++) {
2277 wxPatchListNode *pcinode = m_PatchList.Item(i);
2278 QuiltPatch *piqp = pcinode->GetData();
2279 if ((piqp->ProjType != m_quilt_proj) &&
2280 (piqp->ProjType != PROJECTION_UNKNOWN))
2281 piqp->b_Valid = false;
2282 }
2283 }
2284
2285 // Walk the PatchList, marking any entries which appear in the noshow array
2286 for (unsigned int i = 0; i < m_PatchList.GetCount(); i++) {
2287 wxPatchListNode *pcinode = m_PatchList.Item(i);
2288 QuiltPatch *piqp = pcinode->GetData();
2289 for (unsigned int ins = 0;
2290 ins < m_parent->GetQuiltNoshowIindexArray().size(); ins++) {
2291 if (m_parent->GetQuiltNoshowIindexArray()[ins] ==
2292 piqp->dbIndex) // chart is in the noshow list
2293 {
2294 piqp->b_Valid = false;
2295 break;
2296 }
2297 }
2298 }
2299
2300 // Generate the final render regions for the patches, one by one
2301
2302 m_covered_region.Clear();
2303#if 1 // this does the same as before with a lot less operations if there are
2304 // many charts
2305
2306 // If the reference chart is cm93, we need to render it first.
2307 bool b_skipCM93 = false;
2308 if (m_reference_type == CHART_TYPE_CM93COMP) {
2309 // find cm93 in the list
2310 for (int i = m_PatchList.GetCount() - 1; i >= 0; i--) {
2311 wxPatchListNode *pcinode = m_PatchList.Item(i);
2312 QuiltPatch *piqp = pcinode->GetData();
2313 if (!piqp->b_Valid) // skip invalid entries
2314 continue;
2315
2316 const ChartTableEntry &m = ChartData->GetChartTableEntry(piqp->dbIndex);
2317
2318 if (m.GetChartType() == CHART_TYPE_CM93COMP) {
2319 // Start with the chart's full region coverage.
2320 piqp->ActiveRegion = piqp->quilt_region;
2321 piqp->ActiveRegion.Intersect(cvp_region);
2322
2323 // Update the next pass full region to remove the region just
2324 // allocated
2325 m_covered_region.Union(piqp->quilt_region);
2326
2327 b_skipCM93 = true; // did this already...
2328 break;
2329 }
2330 }
2331 }
2332
2333 // Proceeding from largest scale to smallest....
2334
2335 for (int i = m_PatchList.GetCount() - 1; i >= 0; i--) {
2336 wxPatchListNode *pcinode = m_PatchList.Item(i);
2337 QuiltPatch *piqp = pcinode->GetData();
2338 if (!piqp->b_Valid) // skip invalid entries
2339 continue;
2340
2341 const ChartTableEntry &cte = ChartData->GetChartTableEntry(piqp->dbIndex);
2342
2343 if (b_skipCM93) {
2344 if (cte.GetChartType() == CHART_TYPE_CM93COMP) continue;
2345 }
2346
2347 // Start with the chart's full region coverage.
2348 piqp->ActiveRegion = piqp->quilt_region;
2349
2350 // this operation becomes expensive with lots of charts
2351 if (!b_has_overlays && m_PatchList.GetCount() < 25)
2352 piqp->ActiveRegion.Subtract(m_covered_region);
2353
2354 piqp->ActiveRegion.Intersect(cvp_region);
2355
2356 // Could happen that a larger scale chart covers completely a smaller
2357 // scale chart
2358 if (piqp->ActiveRegion.Empty() && (piqp->dbIndex != m_refchart_dbIndex))
2359 piqp->b_eclipsed = true;
2360
2361 // Maintain the present full quilt coverage region
2362 piqp->b_overlay = false;
2363 if (cte.GetChartFamily() == CHART_FAMILY_VECTOR) {
2364 piqp->b_overlay = s57chart::IsCellOverlayType(cte.GetFullSystemPath());
2365 }
2366
2367 if (!piqp->b_overlay) m_covered_region.Union(piqp->quilt_region);
2368 }
2369#else
2370 // this is the old algorithm does the same thing in n^2/2 operations instead
2371 // of 2*n-1
2372 for (unsigned int i = 0; i < m_PatchList.GetCount(); i++) {
2373 wxPatchListNode *pcinode = m_PatchList.Item(i);
2374 QuiltPatch *piqp = pcinode->GetData();
2375
2376 if (!piqp->b_Valid) // skip invalid entries
2377 continue;
2378
2379 const ChartTableEntry &ctei = ChartData->GetChartTableEntry(piqp->dbIndex);
2380
2381 // Start with the chart's full region coverage.
2382 LLRegion vpr_region = piqp->quilt_region;
2383
2384 // This clause should be moved into the rendering routine for quilts so that
2385 // the actual region logic need only be applied to the render region
2386#if 1 // This clause went away with full-screen quilting
2387 // ...and came back with OpenGL....
2388
2389 // fetch and subtract regions for all larger scale charts
2390 for (unsigned int k = i + 1; k < m_PatchList.GetCount(); k++) {
2391 wxPatchListNode *pnode = m_PatchList.Item(k);
2392 QuiltPatch *pqp = pnode->GetData();
2393
2394 if (!pqp->b_Valid) // skip invalid entries
2395 continue;
2396
2405
2407
2411 // if( ( CHART_TYPE_S57 != ctei.GetChartType() ))
2412 if (!b_has_overlays) {
2413 if (!vpr_region.Empty()) {
2414 const ChartTableEntry &cte =
2415 ChartData->GetChartTableEntry(pqp->dbIndex);
2416 LLRegion larger_scale_chart_region =
2417 pqp->quilt_region; // GetChartQuiltRegion( cte, vp_local );
2418
2419 vpr_region.Subtract(larger_scale_chart_region);
2420 }
2421 }
2422 }
2423#endif
2424
2425 // Whatever is left in the vpr region and has not been yet rendered must
2426 // belong to the current target chart
2427
2428 wxPatchListNode *pinode = m_PatchList.Item(i);
2429 QuiltPatch *pqpi = pinode->GetData();
2430 pqpi->ActiveRegion = vpr_region;
2431
2432 // Move the active region so that upper left is 0,0 in final render
2433 // region
2434 // pqpi->ActiveRegion.Offset( -vp_local.rv_rect.x,
2435 // -vp_local.rv_rect.y );
2436
2437 // Could happen that a larger scale chart covers completely a smaller
2438 // scale chart
2439 if (pqpi->ActiveRegion.Empty()) pqpi->b_eclipsed = true;
2440
2441 // Update the next pass full region to remove the region just allocated
2442 // if( !vpr_region.Empty() )
2443 // unrendered_region.Subtract( vpr_region );
2444
2445 // Maintain the present full quilt coverage region
2446 // if( !pqpi->ActiveRegion.Empty() )
2447 m_covered_region.Union(pqpi->ActiveRegion);
2448 }
2449#endif
2450 // Restore temporary VP Rotation
2451 // vp_local.SetRotationAngle( saved_vp_rotation );
2452
2453 // Walk the list again, removing any entries marked as eclipsed....
2454 unsigned int il = 0;
2455 while (il < m_PatchList.GetCount()) {
2456 wxPatchListNode *pcinode = m_PatchList.Item(il);
2457 QuiltPatch *piqp = pcinode->GetData();
2458 if (piqp->b_eclipsed) {
2459 // Make sure that this chart appears in the eclipsed list...
2460 // This can happen when....
2461 bool b_noadd = false;
2462 for (unsigned int ir = 0; ir < m_eclipsed_stack_array.size(); ir++) {
2463 if (piqp->dbIndex == m_eclipsed_stack_array[ir]) {
2464 b_noadd = true;
2465 break;
2466 }
2467 }
2468 if (!b_noadd) m_eclipsed_stack_array.push_back(piqp->dbIndex);
2469
2470 m_PatchList.DeleteNode(pcinode);
2471 il = 0; // restart the list walk
2472 }
2473
2474 else
2475 il++;
2476 }
2477 // Mark the quilt to indicate need for background clear if the region is
2478 // not fully covered
2479 // m_bneed_clear = !unrendered_region.Empty();
2480 // m_back_region = unrendered_region;
2481
2482 // Finally, iterate thru the quilt and preload all of the required charts.
2483 // For dynamic S57 SENC creation, this is where SENC creation happens
2484 // first.....
2485
2486 // Stop (temporarily) canvas paint events, since some chart loads mught
2487 // Yield(), thus causing performance loss on recursion We will (always??) get
2488 // a refresh on the new Quilt anyway...
2489 m_parent->EnablePaint(false);
2490
2491 // Load and lock all required charts
2492 // First lock required charts already in the cache
2493 // otherwise under memory pressure if chart1 and chart2
2494 // are in the quilt loading chart1 could evict chart2
2495 //
2496 for (ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
2497 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
2498 if ((pqc->b_include) && (!pqc->b_eclipsed)) {
2499 if (!ChartData->IsChartLocked(pqc->dbIndex))
2500 ChartData->LockCacheChart(pqc->dbIndex);
2501 }
2502 }
2503
2504 // Now load and lock any new charts required by the quilt
2505 for (ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
2506 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
2507 if ((pqc->b_include) && (!pqc->b_eclipsed)) {
2508 if (!ChartData->IsChartLocked(pqc->dbIndex)) // Not locked, or not loaded
2509 ChartData->OpenChartFromDBAndLock(pqc->dbIndex, FULL_INIT, true);
2510 }
2511 }
2512
2513#if 0
2514 // first lock charts already in the cache
2515 // otherwise under memory pressure if chart1 and chart2
2516 // are in the quilt loading chart1 could evict chart2
2517 //
2518 for (ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
2519 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
2520 if ((pqc->b_include) && (!pqc->b_eclipsed)) {
2521 if (ChartData->IsChartLocked(pqc->dbIndex)) // already locked
2522 pqc->b_locked = true;
2523 else
2524 pqc->b_locked = ChartData->LockCacheChart(pqc->dbIndex);
2525 }
2526 }
2527
2528 // open charts not in the cache
2529 for (ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
2530 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
2531 if ((pqc->b_include) && (!pqc->b_eclipsed)) {
2532 // I am fairly certain this test can now be removed
2533 // with improved smooth movement logic
2534 // if( !ChartData->IsChartInCache( pqc->dbIndex ) )
2535 // b_stop_movement = true;
2536 // only lock chart if not already locked
2537 if (ChartData->OpenChartFromDBAndLock(pqc->dbIndex, FULL_INIT,
2538 !pqc->b_locked))
2539 pqc->b_locked = true;
2540 }
2541 }
2542#endif
2543
2544 m_parent->EnablePaint(true);
2545 // Build and maintain the array of indexes in this quilt
2546
2547 m_last_index_array = m_index_array; // save the last one for delta checks
2548
2549 m_index_array.clear();
2550
2551 // The index array is to be built in reverse, largest scale first
2552 unsigned int kl = m_PatchList.GetCount();
2553 for (unsigned int k = 0; k < kl; k++) {
2554 wxPatchListNode *cnode = m_PatchList.Item((kl - k) - 1);
2555 m_index_array.push_back(cnode->GetData()->dbIndex);
2556 cnode = cnode->GetNext();
2557 }
2558
2559 // Walk the patch list again, checking the depth units
2560 // If they are all the same, then the value is usable
2561
2562 m_quilt_depth_unit = "";
2563 ChartBase *pc = ChartData->OpenChartFromDB(m_refchart_dbIndex, FULL_INIT);
2564 if (pc) {
2565 m_quilt_depth_unit = pc->GetDepthUnits();
2566
2567 if (pc->GetChartFamily() == CHART_FAMILY_VECTOR) {
2568 int units = ps52plib->m_nDepthUnitDisplay;
2569 switch (units) {
2570 case 0:
2571 m_quilt_depth_unit = "Feet";
2572 break;
2573 case 1:
2574 m_quilt_depth_unit = "Meters";
2575 break;
2576 case 2:
2577 m_quilt_depth_unit = "Fathoms";
2578 break;
2579 }
2580 }
2581 }
2582
2583 for (unsigned int k = 0; k < m_PatchList.GetCount(); k++) {
2584 wxPatchListNode *pnode = m_PatchList.Item(k);
2585 QuiltPatch *pqp = pnode->GetData();
2586
2587 if (!pqp->b_Valid) // skip invalid entries
2588 continue;
2589
2590 ChartBase *pc = ChartData->OpenChartFromDB(pqp->dbIndex, FULL_INIT);
2591 if (pc) {
2592 wxString du = pc->GetDepthUnits();
2593 if (pc->GetChartFamily() == CHART_FAMILY_VECTOR) {
2594 int units = ps52plib->m_nDepthUnitDisplay;
2595 switch (units) {
2596 case 0:
2597 du = "Feet";
2598 break;
2599 case 1:
2600 du = "Meters";
2601 break;
2602 case 2:
2603 du = "Fathoms";
2604 break;
2605 }
2606 }
2607 wxString dul = du.Lower();
2608 wxString ml = m_quilt_depth_unit.Lower();
2609
2610 if (dul != ml) {
2611 // Try all the odd cases
2612 if (dul.StartsWith("meters") && ml.StartsWith("meters"))
2613 continue;
2614 else if (dul.StartsWith("metres") && ml.StartsWith("metres"))
2615 continue;
2616 else if (dul.StartsWith("fathoms") && ml.StartsWith("fathoms"))
2617 continue;
2618 else if (dul.StartsWith("met") && ml.StartsWith("met"))
2619 continue;
2620
2621 // They really are different
2622 m_quilt_depth_unit = "";
2623 break;
2624 }
2625 }
2626 }
2627
2628 // And try to prove that all required charts are in the cache
2629 // If one is missing, try to load it
2630 // If still missing, remove its patch from the quilt
2631 // This will probably leave a "black hole" in the quilt...
2632 for (unsigned int k = 0; k < m_PatchList.GetCount(); k++) {
2633 wxPatchListNode *pnode = m_PatchList.Item(k);
2634 QuiltPatch *pqp = pnode->GetData();
2635
2636 if (pqp->b_Valid) {
2637 if (!ChartData->IsChartInCache(pqp->dbIndex)) {
2638 wxLogMessage(" Quilt Compose cache miss...");
2639 ChartData->OpenChartFromDB(pqp->dbIndex, FULL_INIT);
2640 if (!ChartData->IsChartInCache(pqp->dbIndex)) {
2641 wxLogMessage(" Oops, removing from quilt...");
2642 pqp->b_Valid = false;
2643 }
2644 }
2645 }
2646 }
2647
2648 // Make sure the reference chart is in the cache
2649 if (!ChartData->IsChartInCache(m_refchart_dbIndex))
2650 ChartData->OpenChartFromDB(m_refchart_dbIndex, FULL_INIT);
2651
2652 // Walk the patch list again, checking the error factor
2653 // Also, directly mark the patch to indicate if it should be treated as an
2654 // overlay as seen in Austrian Inland series
2655
2656 m_bquilt_has_overlays = false;
2657 m_max_error_factor = 0.;
2658 for (unsigned int k = 0; k < m_PatchList.GetCount(); k++) {
2659 wxPatchListNode *pnode = m_PatchList.Item(k);
2660 QuiltPatch *pqp = pnode->GetData();
2661
2662 if (!pqp->b_Valid) // skip invalid entries
2663 continue;
2664
2665 ChartBase *pc = ChartData->OpenChartFromDB(pqp->dbIndex, FULL_INIT);
2666 if (pc) {
2667 m_max_error_factor =
2668 wxMax(m_max_error_factor, pc->GetChart_Error_Factor());
2669 if (pc->GetChartFamily() == CHART_FAMILY_VECTOR) {
2670 bool isOverlay = IsChartS57Overlay(pqp->dbIndex);
2671 pqp->b_overlay = isOverlay;
2672 if (isOverlay) m_bquilt_has_overlays = true;
2673 }
2674 }
2675 }
2676
2677 m_bcomposed = true;
2678
2679 m_vp_quilt = vp_in; // save the corresponding ViewPort locally
2680
2681 ChartData->LockCache();
2682
2683 // Create and store a hash value representing the contents of the
2684 // m_extended_stack_array
2685 unsigned long xa_hash = 5381;
2686 for (unsigned int im = 0; im < m_extended_stack_array.size(); im++) {
2687 int dbindex = m_extended_stack_array[im];
2688 xa_hash = ((xa_hash << 5) + xa_hash) + dbindex; /* hash * 33 + dbindex */
2689 }
2690
2691 m_xa_hash = xa_hash;
2692 m_bbusy = false;
2693 return true;
2694}
2695
2696// Compute and update the member quilt render region, considering all scale
2697// factors, group exclusions, etc.
2698void Quilt::ComputeRenderRegion(ViewPort &vp, OCPNRegion &chart_region) {
2699 if (!m_bcomposed) return;
2700
2701 OCPNRegion rendered_region;
2702
2703 if (GetnCharts() && !m_bbusy && !chart_region.Empty()) {
2704 // Walk the quilt, considering each chart from smallest scale to largest
2705
2706 ChartBase *chart = GetFirstChart();
2707
2708 while (chart) {
2709 if (!(chart->GetChartProjectionType() != PROJECTION_MERCATOR &&
2710 vp.b_MercatorProjectionOverride)) {
2711 QuiltPatch *pqp = GetCurrentPatch();
2712 if (pqp->b_Valid) {
2713 OCPNRegion get_screen_region = vp.GetVPRegionIntersect(
2714 chart_region, pqp->ActiveRegion, chart->GetNativeScale());
2715 if (!get_screen_region.Empty())
2716 rendered_region.Union(get_screen_region);
2717 }
2718 }
2719 chart = GetNextChart();
2720 }
2721 }
2722 // Record the region actually rendered
2723 m_rendered_region = rendered_region;
2724}
2725
2726int g_render;
2727
2728bool Quilt::RenderQuiltRegionViewOnDCNoText(wxMemoryDC &dc, ViewPort &vp,
2729 OCPNRegion &chart_region) {
2730 return DoRenderQuiltRegionViewOnDC(dc, vp, chart_region);
2731}
2732
2733bool Quilt::RenderQuiltRegionViewOnDCTextOnly(wxMemoryDC &dc, ViewPort &vp,
2734 OCPNRegion &chart_region) {
2735 return DoRenderQuiltRegionViewOnDCTextOnly(dc, vp, chart_region);
2736}
2737
2738bool Quilt::DoRenderQuiltRegionViewOnDC(wxMemoryDC &dc, ViewPort &vp,
2739 OCPNRegion &chart_region) {
2740#ifdef ocpnUSE_DIBSECTION
2741 ocpnMemDC tmp_dc;
2742#else
2743 wxMemoryDC tmp_dc;
2744#endif
2745
2746 if (!m_bcomposed) return false;
2747
2748 OCPNRegion rendered_region;
2749
2750 if (GetnCharts() && !m_bbusy) {
2751 OCPNRegion screen_region = chart_region;
2752
2753 // Walk the quilt, drawing each chart from smallest scale to largest
2754 // Render the quilt's charts onto a temp dc
2755 // and blit the active region rectangles to to target dc, one-by-one
2756
2757 ChartBase *chart = GetFirstChart();
2758 int chartsDrawn = 0;
2759
2760 if (!chart_region.Empty()) {
2761 while (chart) {
2762 bool okToRender = true;
2763
2764 if (chart->GetChartProjectionType() != PROJECTION_MERCATOR &&
2765 vp.b_MercatorProjectionOverride)
2766 okToRender = false;
2767
2768 if (!okToRender) {
2769 chart = GetNextChart();
2770 continue;
2771 }
2772 QuiltPatch *pqp = GetCurrentPatch();
2773 if (pqp->b_Valid) {
2774 bool b_chart_rendered = false;
2775 LLRegion get_region = pqp->ActiveRegion;
2776
2777 OCPNRegion get_screen_region = vp.GetVPRegionIntersect(
2778 chart_region, get_region, chart->GetNativeScale());
2779 if (!get_screen_region.Empty()) {
2780 if (!pqp->b_overlay) {
2781 if (chart->GetChartType() == CHART_TYPE_CM93COMP) {
2782 b_chart_rendered =
2783 chart->RenderRegionViewOnDC(tmp_dc, vp, get_screen_region);
2784 } else {
2785 s57chart *Chs57 = dynamic_cast<s57chart *>(chart);
2786 if (Chs57) {
2787 if (Chs57->m_RAZBuilt) {
2788 b_chart_rendered = Chs57->RenderRegionViewOnDCNoText(
2789 tmp_dc, vp, get_screen_region);
2790 }
2791 } else {
2792 ChartPlugInWrapper *ChPI =
2793 dynamic_cast<ChartPlugInWrapper *>(chart);
2794 if (ChPI) {
2795 b_chart_rendered = ChPI->RenderRegionViewOnDCNoText(
2796 tmp_dc, vp, get_screen_region);
2797 } else
2798 b_chart_rendered = chart->RenderRegionViewOnDC(
2799 tmp_dc, vp, get_screen_region);
2800
2801 b_chart_rendered = true;
2802 }
2803 }
2804
2805 // if( chart->GetChartType() !=
2806 // CHART_TYPE_CM93COMP )
2807 // b_chart_rendered = true;
2808 screen_region.Subtract(get_screen_region);
2809 }
2810 }
2811
2812 OCPNRegionIterator upd(get_screen_region);
2813 while (upd.HaveRects()) {
2814 wxRect rect = upd.GetRect();
2815 dc.Blit(rect.x, rect.y, rect.width, rect.height, &tmp_dc, rect.x,
2816 rect.y, wxCOPY, true);
2817 upd.NextRect();
2818 }
2819
2820 tmp_dc.SelectObject(wxNullBitmap);
2821
2822 if (b_chart_rendered) rendered_region.Union(get_screen_region);
2823 }
2824
2825 chartsDrawn++;
2826 chart = GetNextChart();
2827 }
2828 }
2829
2830 if (!chartsDrawn) m_parent->GetVP().SetProjectionType(PROJECTION_MERCATOR);
2831
2832 // Render any Overlay patches for s57 charts(cells)
2833 if (m_bquilt_has_overlays && !chart_region.Empty()) {
2834 chart = GetFirstChart();
2835 while (chart) {
2836 QuiltPatch *pqp = GetCurrentPatch();
2837 if (pqp->b_Valid) {
2838 if (pqp->b_overlay) {
2839 LLRegion get_region = pqp->ActiveRegion;
2840 OCPNRegion get_screen_region = vp.GetVPRegionIntersect(
2841 chart_region, get_region, chart->GetNativeScale());
2842 if (!get_region.Empty()) {
2843 s57chart *Chs57 = dynamic_cast<s57chart *>(chart);
2844 if (Chs57) {
2845 Chs57->RenderOverlayRegionViewOnDC(tmp_dc, vp,
2846 get_screen_region);
2847 } else {
2848 ChartPlugInWrapper *ChPI =
2849 dynamic_cast<ChartPlugInWrapper *>(chart);
2850 if (ChPI) {
2851 ChPI->RenderRegionViewOnDC(tmp_dc, vp, get_screen_region);
2852 }
2853 }
2854
2855 OCPNRegionIterator upd(get_screen_region);
2856 while (upd.HaveRects()) {
2857 wxRect rect = upd.GetRect();
2858 dc.Blit(rect.x, rect.y, rect.width, rect.height, &tmp_dc,
2859 rect.x, rect.y, wxCOPY, true);
2860 upd.NextRect();
2861 }
2862 tmp_dc.SelectObject(wxNullBitmap);
2863 }
2864 }
2865 }
2866
2867 chart = GetNextChart();
2868 }
2869 }
2870
2871 // Any part of the chart region that was not rendered in the loop needs
2872 // to be cleared
2873 OCPNRegionIterator clrit(screen_region);
2874 while (clrit.HaveRects()) {
2875 wxRect rect = clrit.GetRect();
2876#ifdef __WXOSX__
2877 dc.SetPen(*wxBLACK_PEN);
2878 dc.SetBrush(*wxBLACK_BRUSH);
2879 dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height);
2880#else
2881 dc.Blit(rect.x, rect.y, rect.width, rect.height, &dc, rect.x, rect.y,
2882 wxCLEAR);
2883#endif
2884 clrit.NextRect();
2885 }
2886
2887 // Highlighting....
2888 if (m_nHiLiteIndex >= 0) {
2889 OCPNRegion hiregion =
2890 vp.GetVPRegionIntersect(chart_region, GetHiliteRegion(), 1);
2891 wxRect box = hiregion.GetBox();
2892
2893 if (!box.IsEmpty()) {
2894 // Is scratch member bitmap OK?
2895 if (m_pBM) {
2896 if ((m_pBM->GetWidth() != vp.rv_rect.width) ||
2897 (m_pBM->GetHeight() != vp.rv_rect.height)) {
2898 delete m_pBM;
2899 m_pBM = NULL;
2900 }
2901 }
2902
2903 if (NULL == m_pBM)
2904 m_pBM = new wxBitmap(vp.rv_rect.width, vp.rv_rect.height);
2905
2906 // Copy the entire quilt to my scratch bm
2907 wxMemoryDC q_dc;
2908 q_dc.SelectObject(*m_pBM);
2909 q_dc.Blit(0, 0, vp.rv_rect.width, vp.rv_rect.height, &dc, 0, 0);
2910 q_dc.SelectObject(wxNullBitmap);
2911
2912 // Create a "mask" bitmap from the chart's region
2913 // WxGTK has an error in this method....Creates a color bitmap, not
2914 // usable for mask creation So, I clone with correction
2915 wxBitmap hl_mask_bm(vp.rv_rect.width, vp.rv_rect.height, 1);
2916 wxMemoryDC mdc;
2917 mdc.SelectObject(hl_mask_bm);
2918 mdc.SetBackground(*wxBLACK_BRUSH);
2919 mdc.Clear();
2920 mdc.SetClippingRegion(box);
2921 mdc.SetBackground(*wxWHITE_BRUSH);
2922 mdc.Clear();
2923 mdc.SelectObject(wxNullBitmap);
2924
2925 if (hl_mask_bm.IsOk()) {
2926 wxMask *phl_mask = new wxMask(hl_mask_bm);
2927 m_pBM->SetMask(phl_mask);
2928 q_dc.SelectObject(*m_pBM);
2929
2930 // Create another mask, dc and bitmap for red-out
2931 wxBitmap rbm(vp.rv_rect.width, vp.rv_rect.height);
2932 wxMask *pr_mask = new wxMask(hl_mask_bm);
2933 wxMemoryDC rdc;
2934 rbm.SetMask(pr_mask);
2935 rdc.SelectObject(rbm);
2936 unsigned char hlcolor = 255;
2937 switch (global_color_scheme) {
2938 case GLOBAL_COLOR_SCHEME_DAY:
2939 hlcolor = 255;
2940 break;
2941 case GLOBAL_COLOR_SCHEME_DUSK:
2942 hlcolor = 64;
2943 break;
2944 case GLOBAL_COLOR_SCHEME_NIGHT:
2945 hlcolor = 16;
2946 break;
2947 default:
2948 hlcolor = 255;
2949 break;
2950 }
2951
2952 rdc.SetBackground(wxBrush(wxColour(hlcolor, 0, 0)));
2953 rdc.Clear();
2954
2955 OCPNRegionIterator upd(hiregion);
2956 while (upd.HaveRects()) {
2957 wxRect rect = upd.GetRect();
2958 rdc.Blit(rect.x, rect.y, rect.width, rect.height, &q_dc, rect.x,
2959 rect.y, wxOR, true);
2960 upd.NextRect();
2961 }
2962
2963 OCPNRegionIterator updq(hiregion);
2964 while (updq.HaveRects()) {
2965 wxRect rect = updq.GetRect();
2966 q_dc.Blit(rect.x, rect.y, rect.width, rect.height, &rdc, rect.x,
2967 rect.y, wxCOPY, true);
2968 updq.NextRect();
2969 }
2970
2971 q_dc.SelectObject(wxNullBitmap);
2972 m_pBM->SetMask(NULL);
2973
2974 // Select the scratch BM as the return dc contents
2975 dc.SelectObject(*m_pBM);
2976
2977 // Clear the rdc
2978 rdc.SelectObject(wxNullBitmap);
2979 }
2980 } // box not empty
2981 } // m_nHiLiteIndex
2982
2983 // Fogging....
2984 if (g_fog_overzoom) {
2985 double scale_factor = vp.ref_scale / vp.chart_scale;
2986
2987 if (scale_factor > g_overzoom_emphasis_base) {
2988 float fog = ((scale_factor - g_overzoom_emphasis_base) * 255.) / 20.;
2989 fog = wxMin(fog, 200.); // Don't fog out completely
2990
2991 // Is scratch member bitmap OK?
2992 if (m_pBM) {
2993 if ((m_pBM->GetWidth() != vp.rv_rect.width) ||
2994 (m_pBM->GetHeight() != vp.rv_rect.height)) {
2995 delete m_pBM;
2996 m_pBM = NULL;
2997 }
2998 }
2999
3000 if (NULL == m_pBM)
3001 m_pBM = new wxBitmap(vp.rv_rect.width, vp.rv_rect.height);
3002
3003 // Copy the entire quilt to my scratch bm
3004 wxMemoryDC q_dc;
3005 q_dc.SelectObject(*m_pBM);
3006 q_dc.Blit(0, 0, vp.rv_rect.width, vp.rv_rect.height, &dc, 0, 0);
3007 q_dc.SelectObject(wxNullBitmap);
3008
3009 wxImage src = m_pBM->ConvertToImage();
3010#if 1
3011 int blur_factor =
3012 wxMin((scale_factor - g_overzoom_emphasis_base) / 4, 4);
3013 if (src.IsOk()) {
3014 wxImage dest = src.Blur(blur_factor);
3015#endif
3016
3017#if 0 // this is fogging effect
3018 unsigned char *bg = src.GetData();
3019 wxColour color = m_parent->GetFogColor();
3020
3021 float transparency = fog;
3022
3023 // destination image
3024 wxImage dest(vp.rv_rect.width, vp.rv_rect.height);
3025 unsigned char *dest_data = (unsigned char *) malloc( vp.rv_rect.width * vp.rv_rect.height * 3 * sizeof(unsigned char) );
3026 unsigned char *d = dest_data;
3027
3028 float alpha = 1.0 - (float)transparency / 255.0;
3029 int sb = vp.rv_rect.width * vp.rv_rect.height;
3030 for( int i = 0; i < sb; i++ ) {
3031 float a = alpha;
3032
3033 int r = ( ( *bg++ ) * a ) + (1.0-a) * color.Red();
3034 *d++ = r;
3035 int g = ( ( *bg++ ) * a ) + (1.0-a) * color.Green();
3036 *d++ = g;
3037 int b = ( ( *bg++ ) * a ) + (1.0-a) * color.Blue();
3038 *d++ = b;
3039 }
3040
3041 dest.SetData( dest_data );
3042#endif
3043
3044 wxBitmap dim(dest);
3045 wxMemoryDC ddc;
3046 ddc.SelectObject(dim);
3047
3048 q_dc.SelectObject(*m_pBM);
3049 OCPNRegionIterator upd(rendered_region);
3050 while (upd.HaveRects()) {
3051 wxRect rect = upd.GetRect();
3052 q_dc.Blit(rect.x, rect.y, rect.width, rect.height, &ddc, rect.x,
3053 rect.y);
3054 upd.NextRect();
3055 }
3056
3057 ddc.SelectObject(wxNullBitmap);
3058 q_dc.SelectObject(wxNullBitmap);
3059
3060 // Select the scratch BM as the return dc contents
3061 dc.SelectObject(*m_pBM);
3062 }
3063 }
3064 } // overzoom
3065
3066 if (!dc.IsOk()) // some error, probably bad charts, to be disabled on next
3067 // compose
3068 {
3069 SubstituteClearDC(dc, vp);
3070 }
3071
3072 } else { // no charts yet, or busy....
3073 SubstituteClearDC(dc, vp);
3074 }
3075
3076 // Record the region actually rendered
3077 m_rendered_region = rendered_region;
3078
3079 m_vp_rendered = vp;
3080 return true;
3081}
3082
3083void Quilt::SubstituteClearDC(wxMemoryDC &dc, ViewPort &vp) {
3084 if (m_pBM) {
3085 if ((m_pBM->GetWidth() != vp.rv_rect.width) ||
3086 (m_pBM->GetHeight() != vp.rv_rect.height)) {
3087 delete m_pBM;
3088 m_pBM = NULL;
3089 }
3090 }
3091
3092 if (NULL == m_pBM) {
3093 m_pBM = new wxBitmap(vp.rv_rect.width, vp.rv_rect.height);
3094 }
3095
3096 dc.SelectObject(wxNullBitmap);
3097 dc.SelectObject(*m_pBM);
3098 dc.SetBackground(*wxBLACK_BRUSH);
3099 dc.Clear();
3100 m_covered_region.Clear();
3101}
3102
3103bool Quilt::DoRenderQuiltRegionViewOnDCTextOnly(wxMemoryDC &dc, ViewPort &vp,
3104 OCPNRegion &chart_region) {
3105 if (!m_bcomposed) return false;
3106
3107 OCPNRegion rendered_region;
3108
3109 if (GetnCharts() && !m_bbusy) {
3110 OCPNRegion screen_region = chart_region;
3111
3112 // Walk the quilt, drawing each chart from largest scale to smallest
3113
3114 ChartBase *chart = GetLargestScaleChart();
3115
3116 while (chart) {
3117 QuiltPatch *pqp = GetCurrentPatch();
3118 if (pqp->b_Valid) {
3119 s57chart *Chs57 = dynamic_cast<s57chart *>(chart);
3120 if (Chs57)
3121 Chs57->RenderRegionViewOnDCTextOnly(dc, vp, chart_region);
3122 else {
3123 ChartPlugInWrapper *ChPI = dynamic_cast<ChartPlugInWrapper *>(chart);
3124 if (ChPI) {
3125 ChPI->RenderRegionViewOnDCTextOnly(dc, vp, chart_region);
3126 }
3127 }
3128 }
3129
3130 chart = GetNextSmallerScaleChart();
3131 }
3132
3133 } else { // no charts yet, or busy....
3134 SubstituteClearDC(dc, vp);
3135 }
3136
3137 return true;
3138}
ChartDB * ChartData
Global instance.
Definition chartdb.cpp:71
Charts database management
BSB chart management.
Generic Chart canvas base.
Base class for all chart types.
Definition chartbase.h:126
ChartCanvas - Main chart display and interaction component.
Definition chcanv.h:173
double GetCanvasScaleFactor()
Return the number of logical pixels per meter for the screen.
Definition chcanv.h:511
Wrapper class for plugin-based charts.
Definition chartimg.h:389
An iterator class for OCPNRegion.
A wrapper class for wxRegion with additional functionality.
Definition ocpn_region.h:37
bool Compose(const ViewPort &vp)
Definition quilt.cpp:1801
ViewPort - Core geographic projection and coordinate transformation engine.
Definition viewport.h:56
double view_scale_ppm
Requested view scale in physical pixels per meter (ppm), before applying projections.
Definition viewport.h:204
double ref_scale
The nominal scale of the "reference chart" for this view.
Definition viewport.h:221
double rotation
Rotation angle of the viewport in radians.
Definition viewport.h:214
wxPoint2DDouble GetDoublePixFromLL(double lat, double lon)
Convert latitude and longitude on the ViewPort to physical pixel coordinates with double precision.
Definition viewport.cpp:133
void GetLLFromPix(const wxPoint &p, double *lat, double *lon)
Convert physical pixel coordinates on the ViewPort to latitude and longitude.
Definition viewport.h:80
OCPNRegion GetVPRegionIntersect(const OCPNRegion &region, const LLRegion &llregion, int chart_native_scale)
Get the intersection of the viewport with a given region.
Definition viewport.cpp:409
double clon
Center longitude of the viewport in degrees.
Definition viewport.h:199
double clat
Center latitude of the viewport in degrees.
Definition viewport.h:197
wxPoint GetPixFromLL(double lat, double lon)
Convert latitude and longitude on the ViewPort to physical pixel coordinates.
Definition viewport.cpp:124
double chart_scale
Chart scale denominator (e.g., 50000 for a 1:50000 scale).
Definition viewport.h:219
Represents an S57 format electronic navigational chart in OpenCPN.
Definition s57chart.h:90
Global variables stored in configuration file.
Optimized wxBitmap Object.
Miscellaneous utilities, many of which string related.
Chart quilt support.
S57 Chart Object.
Represents an entry in the chart table, containing information about a single chart.
Definition chartdbs.h:187