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