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