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 return true;
1640}
1641
1642int Quilt::AdjustRefSelection(const ViewPort &vp_in) {
1643 // Starting from the currently selected Ref chart,
1644 // choose a ref chart that meets the required under/overzoom limits
1645 // It might be the same, so no change required
1646
1647 if (!ChartData) return false;
1648
1649 if (ChartData
1650 ->IsBusy()) // This prevent recursion on chart loads that Yeild()
1651 return false;
1652
1653 ViewPort vp_local = vp_in; // need a non-const copy
1654
1655 // As ChartdB data is always in rectilinear space, region calculations need
1656 // to be done with no VP rotation
1657 vp_local.SetRotationAngle(0.);
1658
1659 BuildExtendedChartStackAndCandidateArray(m_refchart_dbIndex, vp_local);
1660
1661 ChartFamilyEnum family = CHART_FAMILY_RASTER;
1662 ChartTypeEnum type = CHART_TYPE_KAP;
1663
1664 // Get the desired family/type
1665 if (m_refchart_dbIndex >= 0) {
1666 const ChartTableEntry &cte_ref =
1667 ChartData->GetChartTableEntry(m_refchart_dbIndex);
1668 type = (ChartTypeEnum)cte_ref.GetChartType();
1669 family = (ChartFamilyEnum)cte_ref.GetChartFamily();
1670 }
1671
1672 int ret_index = AdjustRefOnZoom(true, family, type, vp_in.chart_scale);
1673
1674 return ret_index;
1675}
1676
1677double Quilt::GetBestStartScale(int dbi_ref_hint, const ViewPort &vp_in) {
1678 if (!ChartData) return vp_in.view_scale_ppm;
1679
1680 if (ChartData
1681 ->IsBusy()) // This prevent recursion on chart loads that Yeild()
1682 return vp_in.view_scale_ppm;
1683
1684 ViewPort vp_local = vp_in; // need a non-const copy
1685
1686 // Validate Reference Chart hint
1687 int tentative_ref_index = dbi_ref_hint;
1688 if (dbi_ref_hint < 0) {
1689 // arbitrarily select reference chart as largest scale on current stack
1690 // if( !pCurrentStack ) {
1691 // pCurrentStack = new ChartStack;
1692 // ChartData->BuildChartStack( pCurrentStack, vp_local.clat,
1693 // vp_local.clon );
1694 // }
1695 tentative_ref_index = m_parent->GetpCurrentStack()->GetDBIndex(0);
1696 }
1697
1698 // As ChartdB data is always in rectilinear space, region calculations need
1699 // to be done with no VP rotation
1700 // double saved_vp_rotation = vp_local.rotation; // save
1701 // a copy
1702 vp_local.SetRotationAngle(0.);
1703
1704 BuildExtendedChartStackAndCandidateArray(tentative_ref_index, vp_local);
1705
1706 // tentative choice might not be in the extended stack....
1707 bool bf = false;
1708 for (unsigned int i = 0; i < m_pcandidate_array->GetCount(); i++) {
1709 QuiltCandidate *qc = m_pcandidate_array->Item(i);
1710 if (qc->dbIndex == tentative_ref_index) {
1711 bf = true;
1712 break;
1713 }
1714 }
1715
1716 if (!bf && m_pcandidate_array->GetCount()) {
1717 tentative_ref_index = GetNewRefChart();
1718 BuildExtendedChartStackAndCandidateArray(tentative_ref_index, vp_local);
1719 }
1720
1721 double proposed_scale_onscreen = vp_in.chart_scale;
1722
1723 if (m_pcandidate_array->GetCount()) {
1724 SetReferenceChart(tentative_ref_index);
1725 } else {
1726 // Need to choose some chart, find a quiltable candidate
1727 bool bfq = false;
1728 for (unsigned int i = 0; i < m_pcandidate_array->GetCount(); i++) {
1729 QuiltCandidate *qc = m_pcandidate_array->Item(i);
1730 if (IsChartQuiltableRef(qc->dbIndex)) {
1731 SetReferenceChart(qc->dbIndex);
1732 bfq = true;
1733 break;
1734 }
1735 }
1736
1737 if (!bfq) // fallback to first chart in stack
1738 SetReferenceChart(m_parent->GetpCurrentStack()->GetDBIndex(0));
1739 }
1740
1741 if (m_refchart_dbIndex >= 0) {
1742 // Suggest a scale so that the largest scale candidate is "nominally"
1743 // scaled, meaning not overzoomed, and not underzoomed
1744 ChartBase *pc = ChartData->OpenChartFromDB(m_refchart_dbIndex, FULL_INIT);
1745 if (pc) {
1746 double min_ref_scale =
1747 pc->GetNormalScaleMin(m_parent->GetCanvasScaleFactor(), false);
1748 double max_ref_scale = pc->GetNormalScaleMax(
1749 m_parent->GetCanvasScaleFactor(), m_canvas_width);
1750 if ((proposed_scale_onscreen >= min_ref_scale) &&
1751 (proposed_scale_onscreen <= max_ref_scale))
1752 return vp_in.view_scale_ppm;
1753 else {
1754 proposed_scale_onscreen = wxMin(proposed_scale_onscreen, max_ref_scale);
1755 proposed_scale_onscreen = wxMax(proposed_scale_onscreen, min_ref_scale);
1756 }
1757 }
1758 }
1759 return m_parent->GetCanvasScaleFactor() / proposed_scale_onscreen;
1760}
1761
1762ChartBase *Quilt::GetRefChart() {
1763 if (m_refchart_dbIndex >= 0 && ChartData)
1764 return ChartData->OpenChartFromDB(m_refchart_dbIndex, FULL_INIT);
1765 return nullptr;
1766}
1767
1768void Quilt::UnlockQuilt() {
1769 wxASSERT(m_bbusy == false);
1770 ChartData->UnLockCache();
1771 ChartData->UnLockAllCacheCharts();
1772 // unlocked only charts owned by the Quilt
1773 for (unsigned int ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
1774 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
1775 // ChartData->UnLockCacheChart(pqc->dbIndex);
1776 }
1777}
1778
1779bool Quilt::Compose(const ViewPort &vp_in) {
1780 if (!ChartData) return false;
1781 if (ChartData
1782 ->IsBusy()) // This prevent recursion on chart loads that Yeild()
1783 return false;
1784
1785 if (!m_parent->GetpCurrentStack()) return false;
1786
1787 if (m_bbusy) return false;
1788
1789 // XXX call before setting m_bbusy for wxASSERT in UnlockQuilt
1790 UnlockQuilt();
1791 m_bbusy = true;
1792
1793 ViewPort vp_local = vp_in; // need a non-const copy
1794
1795 // Get Reference Chart parameters
1796 if (m_refchart_dbIndex >= 0) {
1797 const ChartTableEntry &cte_ref =
1798 ChartData->GetChartTableEntry(m_refchart_dbIndex);
1799 m_reference_scale = cte_ref.GetScale();
1800 m_reference_type = cte_ref.GetChartType();
1801 if (!m_bquiltanyproj)
1802 m_quilt_proj = ChartData->GetDBChartProj(m_refchart_dbIndex);
1803 m_reference_family = cte_ref.GetChartFamily();
1804 }
1805
1806 // Set up the viewport projection type
1807 if (!m_bquiltanyproj) vp_local.SetProjectionType(m_quilt_proj);
1808
1809 // As ChartdB data is always in rectilinear space, region calculations need
1810 // to be done with no VP rotation
1811 // double saved_vp_rotation = vp_local.rotation; //
1812 // save a copy vp_local.SetRotationAngle( 0. );
1813
1814 BuildExtendedChartStackAndCandidateArray(m_refchart_dbIndex, vp_local);
1815
1816 // It can happen (in groups switch, or single->quilt mode) that there
1817 // is no refchart known, but there are charts available in the piano.
1818 // Detect this case, and build the quilt based on the smallest scale chart
1819 // anywhere on screen.
1820
1821 // if ((m_refchart_dbIndex < 0) && m_extended_stack_array.size()){
1822 // // Take the smallest scale chart in the array.
1823 // int tentative_dbIndex = m_extended_stack_array.back();
1824 //
1825 // // Verify that the zoom scale is acceptable.
1826 // const ChartTableEntry &cte =
1827 // ChartData->GetChartTableEntry(tentative_dbIndex); int
1828 // candidate_chart_scale = cte.GetScale(); double chart_native_ppm =
1829 // m_canvas_scale_factor / (double)candidate_chart_scale;
1830 // double zoom_factor = vp_local.view_scale_ppm / chart_native_ppm;
1831 // if (zoom_factor > 0.1){
1832 // m_refchart_dbIndex = tentative_dbIndex;
1833 // BuildExtendedChartStackAndCandidateArray(m_refchart_dbIndex,
1834 // vp_local);
1835 // }
1836 // }
1837
1838 // It is possible that the reference chart is not really part of the
1839 // visible quilt This can happen when the reference chart is panned
1840 // off-screen in full screen quilt mode
1841 // If this situation occurs, we need to immediately select a new reference
1842 // chart And rebuild the Candidate Array
1843 //
1844 // We also save the dbIndex of the "lost" chart, and try to recover it
1845 // on subsequent quilts, typically as the user pans the "lost" chart back
1846 // on-screen. The "lost" chart logic is reset on any zoom operations. See
1847 // FS#1221
1848 //
1849 // A special case occurs with cm93 composite chart set as the reference
1850 // chart: It is not at this point a candidate, so won't be found by the
1851 // search This case is indicated if the candidate count is zero. If so, do
1852 // not invalidate the ref chart
1853 bool bf = false;
1854 for (unsigned int i = 0; i < m_pcandidate_array->GetCount(); i++) {
1855 QuiltCandidate *qc = m_pcandidate_array->Item(i);
1856 if (qc->dbIndex == m_refchart_dbIndex) {
1857 bf = true;
1858 break;
1859 }
1860 }
1861
1862 // Process "lost chart" logic by default
1863 // However, if the chart family has been directly set
1864 // by SelectRefChartByFamily(), then honor the selection always.
1865 if (m_chart_familyFix == CHART_FAMILY_UNKNOWN) {
1866 if (!bf && m_pcandidate_array->GetCount() &&
1867 (m_reference_type != CHART_TYPE_CM93COMP)) {
1868 m_lost_refchart_dbIndex = m_refchart_dbIndex; // save for later
1869 int candidate_ref_index = GetNewRefChart();
1870 if (m_refchart_dbIndex != candidate_ref_index) {
1871 m_refchart_dbIndex = candidate_ref_index;
1872 BuildExtendedChartStackAndCandidateArray(m_refchart_dbIndex, vp_local);
1873 }
1874 // There was no viable candidate of smaller scale than the "lost
1875 // chart", so choose the smallest scale chart in the candidate list.
1876 // Exception: if the smallest scale chart in the candidate list
1877 // is a different family, then do not change the reference chart
1878 else {
1879 BuildExtendedChartStackAndCandidateArray(m_refchart_dbIndex, vp_local);
1880 if (m_pcandidate_array->GetCount()) {
1881 int alternate_index =
1882 m_pcandidate_array->Item(m_pcandidate_array->GetCount() - 1)
1883 ->dbIndex;
1884 const ChartTableEntry &cte_alt =
1885 ChartData->GetChartTableEntry(alternate_index);
1886
1887 if (GetRefFamily() == cte_alt.GetChartFamily()) {
1888 m_refchart_dbIndex = alternate_index;
1889 BuildExtendedChartStackAndCandidateArray(m_refchart_dbIndex,
1890 vp_local);
1891 }
1892 }
1893 }
1894 }
1895 }
1896
1897 if ((-1 != m_lost_refchart_dbIndex) &&
1898 (m_lost_refchart_dbIndex != m_refchart_dbIndex)) {
1899 // Is the lost chart in the extended stack ?
1900 // If so, build a new Cnadidate array based upon the lost chart
1901 for (unsigned int ir = 0; ir < m_extended_stack_array.size(); ir++) {
1902 if (m_lost_refchart_dbIndex == m_extended_stack_array[ir]) {
1903 m_refchart_dbIndex = m_lost_refchart_dbIndex;
1904 BuildExtendedChartStackAndCandidateArray(m_refchart_dbIndex, vp_local);
1905 m_lost_refchart_dbIndex = -1;
1906 break;
1907 }
1908 }
1909 }
1910
1911 bool b_has_overlays = false;
1912
1913 // If this is an S57 quilt, we need to know if there are overlays in it
1914 if (CHART_FAMILY_VECTOR == m_reference_family) {
1915 for (unsigned int ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
1916 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
1917 const ChartTableEntry &cte = ChartData->GetChartTableEntry(pqc->dbIndex);
1918
1919 if (s57chart::IsCellOverlayType(cte.GetFullSystemPath())) {
1920 b_has_overlays = true;
1921 break;
1922 }
1923 }
1924 }
1925
1926 // Using Region logic, and starting from the largest scale chart
1927 // figuratively "draw" charts until the ViewPort window is completely
1928 // quilted over Add only those charts whose scale is smaller than the
1929 // "reference scale"
1930 // const LLRegion cvp_region = vp_local.GetLLRegion(
1931 // wxRect(0, 0, vp_local.pix_width, vp_local.pix_height));
1932 const LLRegion cvp_region = vp_local.GetLLRegion(vp_local.rv_rect);
1933 LLRegion vp_region = cvp_region;
1934 unsigned int ir;
1935
1936 // "Draw" the reference chart first, since it is special in that it
1937 // controls the fine vpscale setting
1938 QuiltCandidate *pqc_ref = NULL;
1939 for (ir = 0; ir < m_pcandidate_array->GetCount();
1940 ir++) // find ref chart entry
1941 {
1942 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
1943 if (pqc->dbIndex == m_refchart_dbIndex) {
1944 pqc_ref = pqc;
1945 break;
1946 }
1947 }
1948
1949 // Quilted regions can be simplified to reduce the cost of region operations,
1950 // in this case allow a maximum error of 8 pixels (the rendered display is
1951 // much better, this is only for composing the quilt)
1952 const double z = 111274.96299695622;
1954 double factor = 8.0 / (vp_local.view_scale_ppm * z);
1955
1956 if (pqc_ref) {
1957 const ChartTableEntry &cte_ref =
1958 ChartData->GetChartTableEntry(m_refchart_dbIndex);
1959
1960 LLRegion vpu_region(cvp_region);
1961
1962 // LLRegion chart_region = pqc_ref->GetCandidateRegion();
1963 LLRegion &chart_region = pqc_ref->GetReducedCandidateRegion(factor);
1964
1965 if (cte_ref.GetChartType() != CHART_TYPE_MBTILES) {
1966 if (!chart_region.Empty()) {
1967 vpu_region.Intersect(chart_region);
1968
1969 if (vpu_region.Empty())
1970 pqc_ref->b_include = false; // skip this chart, no true overlap
1971 else {
1972 pqc_ref->b_include = true;
1973 vp_region.Subtract(chart_region); // adding this chart
1974 }
1975 } else
1976 pqc_ref->b_include = false; // skip this chart, empty region
1977 } else {
1978 pqc_ref->b_include = false; // skip this chart, mbtiles
1979 }
1980 }
1981
1982 // Now the rest of the candidates
1983 if (!vp_region.Empty()) {
1984 for (ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
1985 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
1986
1987 if (pqc->dbIndex == m_refchart_dbIndex) continue; // already did this one
1988
1989 const ChartTableEntry &cte = ChartData->GetChartTableEntry(pqc->dbIndex);
1990
1991 // Skip overlays on this pass, so that they do not subtract from quilt
1992 // and thus displace a geographical cell with the same extents. Overlays
1993 // will be picked up in the next pass, if any are found
1994 if (CHART_FAMILY_VECTOR == m_reference_family) {
1995 if (s57chart::IsCellOverlayType(cte.GetFullSystemPath())) {
1996 continue;
1997 }
1998 }
1999
2000 // Skip MBTiles
2001 if (CHART_TYPE_MBTILES == cte.GetChartType()) {
2002 pqc->b_include = false; // skip this chart, mbtiles
2003 continue;
2004 }
2005
2006 if (cte.Scale_ge(m_reference_scale)) {
2007 // If this chart appears in the no-show array, then simply include it,
2008 // but don't subtract its region when determining the smaller scale
2009 // charts to include.....
2010 bool b_in_noshow = false;
2011 for (unsigned int ins = 0;
2012 ins < m_parent->GetQuiltNoshowIindexArray().size(); ins++) {
2013 if (m_parent->GetQuiltNoshowIindexArray()[ins] ==
2014 pqc->dbIndex) // chart is in the noshow list
2015 {
2016 b_in_noshow = true;
2017 break;
2018 }
2019 }
2020
2021 if (!b_in_noshow) {
2022 // Check intersection
2023 LLRegion vpu_region(cvp_region);
2024
2025 // LLRegion chart_region = pqc->GetCandidateRegion( ); //quilt_region;
2026 LLRegion &chart_region = pqc->GetReducedCandidateRegion(factor);
2027
2028 if (!chart_region.Empty()) {
2029 vpu_region.Intersect(chart_region);
2030
2031 if (vpu_region.Empty())
2032 pqc->b_include = false; // skip this chart, no true overlap
2033 else {
2034 pqc->b_include = true;
2035 vp_region.Subtract(chart_region); // adding this chart
2036 }
2037 } else
2038 pqc->b_include = false; // skip this chart, empty region
2039 } else {
2040 pqc->b_include = true;
2041 }
2042
2043 } else {
2044 pqc->b_include = false; // skip this chart, scale is too large
2045 }
2046
2047 if (vp_region.Empty()) // normal stop condition, quilt is full
2048 break;
2049 }
2050 }
2051
2052 // For S57 quilts, walk the list again to identify overlay cells found
2053 // previously, and make sure they are always included and not eclipsed
2054 if (b_has_overlays && (CHART_FAMILY_VECTOR == m_reference_family)) {
2055 for (ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
2056 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
2057
2058 if (pqc->dbIndex == m_refchart_dbIndex) continue; // already did this one
2059
2060 const ChartTableEntry &cte = ChartData->GetChartTableEntry(pqc->dbIndex);
2061
2062 if (cte.Scale_ge(m_reference_scale)) {
2063 bool b_in_noshow = false;
2064 for (unsigned int ins = 0;
2065 ins < m_parent->GetQuiltNoshowIindexArray().size(); ins++) {
2066 if (m_parent->GetQuiltNoshowIindexArray()[ins] ==
2067 pqc->dbIndex) // chart is in the noshow list
2068 {
2069 b_in_noshow = true;
2070 break;
2071 }
2072 }
2073
2074 if (!b_in_noshow) {
2075 // Check intersection
2076 LLRegion vpu_region(cvp_region);
2077
2078 // LLRegion chart_region = pqc->GetCandidateRegion( );
2079 LLRegion &chart_region = pqc->GetReducedCandidateRegion(factor);
2080
2081 if (!chart_region.Empty()) vpu_region.Intersect(chart_region);
2082
2083 if (vpu_region.Empty())
2084 pqc->b_include = false; // skip this chart, no true overlap
2085 else {
2086 bool b_overlay =
2087 s57chart::IsCellOverlayType(cte.GetFullSystemPath());
2088 if (b_overlay) pqc->b_include = true;
2089 }
2090
2091 // If the reference chart happens to be an overlay (e.g.
2092 // 3UABUOYS.000), we dont want it to eclipse any smaller scale
2093 // standard useage charts.
2094 const ChartTableEntry &cte_ref =
2095 ChartData->GetChartTableEntry(m_refchart_dbIndex);
2096 if (s57chart::IsCellOverlayType(cte_ref.GetFullSystemPath())) {
2097 pqc->b_include = true;
2098 }
2099 }
2100 }
2101 }
2102 }
2103
2104 // Walk the candidate list again, marking "eclipsed" charts
2105 // which at this point are the ones with b_include == false .AND. whose
2106 // scale is strictly smaller than the ref scale Also, maintain the member
2107 // list of same
2108
2109 m_eclipsed_stack_array.clear();
2110
2111 for (ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
2112 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
2113
2114 if (!pqc->b_include) {
2115 const ChartTableEntry &cte = ChartData->GetChartTableEntry(pqc->dbIndex);
2116 if (cte.Scale_ge(m_reference_scale) &&
2117 (cte.GetChartType() != CHART_TYPE_MBTILES)) {
2118 m_eclipsed_stack_array.push_back(pqc->dbIndex);
2119 pqc->b_eclipsed = true;
2120 }
2121 }
2122 }
2123
2124 // Potentially add cm93 to the candidate array if the region is not yet
2125 // fully covered
2126 if (((m_bquiltanyproj || m_quilt_proj == PROJECTION_MERCATOR)) &&
2127 !vp_region.Empty()) {
2128 bool b_must_add_cm93 = true;
2129#if 0
2130 // Check the remaining unpainted region.
2131 // It may contain very small "slivers" of empty space, due to mixing of very small scale charts
2132 // with the quilt. If this is the case, do not waste time loading cm93....
2133
2134 OCPNRegionIterator updd( vp_region );
2135 while( updd .HaveRects()) {
2136 wxRect rect = updd.GetRect();
2137 if( ( rect.width > 2 ) && ( rect.height > 2 ) ) {
2138 b_must_add_cm93 = true;
2139 break;
2140 }
2141 updd.NextRect();
2142 }
2143#endif
2144
2145 if (b_must_add_cm93) {
2146 for (int ics = 0; ics < m_parent->GetpCurrentStack()->nEntry; ics++) {
2147 int i = m_parent->GetpCurrentStack()->GetDBIndex(ics);
2148 if (CHART_TYPE_CM93COMP == ChartData->GetDBChartType(i)) {
2149 QuiltCandidate *qcnew = new QuiltCandidate;
2150 qcnew->dbIndex = i;
2151 qcnew->SetScale(ChartData->GetDBChartScale(i));
2152
2153 m_pcandidate_array->Add(qcnew);
2154 }
2155 }
2156 }
2157 }
2158
2159 // Check the list...if no charts are visible due to all being smaller than
2160 // reference_scale, then make sure the smallest scale chart which has any
2161 // true region intersection is visible anyway Also enable any other charts
2162 // which are the same scale as the first one added
2163 bool b_vis = false;
2164 for (unsigned int i = 0; i < m_pcandidate_array->GetCount(); i++) {
2165 QuiltCandidate *pqc = m_pcandidate_array->Item(i);
2166 if (pqc->b_include) {
2167 b_vis = true;
2168 break;
2169 }
2170 }
2171
2172 if (!b_vis && m_pcandidate_array->GetCount()) {
2173 int add_scale = 0;
2174
2175 for (int i = m_pcandidate_array->GetCount() - 1; i >= 0; i--) {
2176 QuiltCandidate *pqc = m_pcandidate_array->Item(i);
2177 const ChartTableEntry &cte = ChartData->GetChartTableEntry(pqc->dbIndex);
2178
2179 // Don't add cm93 yet, it is always covering the quilt...
2180 if (cte.GetChartType() == CHART_TYPE_CM93COMP) continue;
2181
2182 // Don't add MBTiles
2183 if (cte.GetChartType() == CHART_TYPE_MBTILES) continue;
2184
2185 // Check intersection
2186 LLRegion vpck_region(vp_local.GetBBox());
2187
2188 // LLRegion chart_region = pqc->GetCandidateRegion();
2189 LLRegion &chart_region = pqc->GetReducedCandidateRegion(factor);
2190
2191 if (!chart_region.Empty()) vpck_region.Intersect(chart_region);
2192
2193 if (!vpck_region.Empty()) {
2194 if (add_scale) {
2195 if (cte.Scale_eq(add_scale)) {
2196 pqc->b_include = true;
2197 }
2198 } else {
2199 pqc->b_include = true;
2200 add_scale = cte.GetScale();
2201 }
2202 }
2203 }
2204 }
2205
2206 // Finally, build a list of "patches" for the quilt.
2207 // Smallest scale first, as this will be the natural drawing order
2208
2209 m_PatchList.DeleteContents(true);
2210 m_PatchList.Clear();
2211
2212 if (m_pcandidate_array->GetCount()) {
2213 for (int i = m_pcandidate_array->GetCount() - 1; i >= 0; i--) {
2214 QuiltCandidate *pqc = m_pcandidate_array->Item(i);
2215
2216 // cm93 add has been deferred until here
2217 // so that it would not displace possible raster or ENCs of larger
2218 // scale
2219 const ChartTableEntry &m = ChartData->GetChartTableEntry(pqc->dbIndex);
2220
2221 if (m.GetChartType() == CHART_TYPE_CM93COMP)
2222 pqc->b_include = true; // force acceptance of this chart in quilt
2223 // would not be in candidate array if not elected
2224
2225 if (pqc->b_include) {
2226 QuiltPatch *pqp = new QuiltPatch;
2227 pqp->dbIndex = pqc->dbIndex;
2228 pqp->ProjType = m.GetChartProjectionType();
2229 // this is the region used for drawing, don't reduce it
2230 // it's visible
2231 pqp->quilt_region = pqc->GetCandidateRegion();
2232 // pqp->quilt_region = pqc->GetReducedCandidateRegion(factor);
2233
2234 pqp->b_Valid = true;
2235
2236 m_PatchList.Append(pqp);
2237 }
2238 }
2239 }
2240 // From here on out, the PatchList is usable...
2241
2242#ifdef QUILT_TYPE_1
2243 if (!m_bquiltanyproj) {
2244 // Establish the quilt projection type
2245 m_quilt_proj = PROJECTION_MERCATOR; // default
2246 ChartBase *ppc = GetLargestScaleChart();
2247 if (ppc) m_quilt_proj = ppc->GetChartProjectionType();
2248 }
2249#endif
2250
2251 if (!m_bquiltanyproj) {
2252 // Walk the PatchList, marking any entries whose projection does not
2253 // match the determined quilt projection
2254 for (unsigned int i = 0; i < m_PatchList.GetCount(); i++) {
2255 wxPatchListNode *pcinode = m_PatchList.Item(i);
2256 QuiltPatch *piqp = pcinode->GetData();
2257 if ((piqp->ProjType != m_quilt_proj) &&
2258 (piqp->ProjType != PROJECTION_UNKNOWN))
2259 piqp->b_Valid = false;
2260 }
2261 }
2262
2263 // Walk the PatchList, marking any entries which appear in the noshow array
2264 for (unsigned int i = 0; i < m_PatchList.GetCount(); i++) {
2265 wxPatchListNode *pcinode = m_PatchList.Item(i);
2266 QuiltPatch *piqp = pcinode->GetData();
2267 for (unsigned int ins = 0;
2268 ins < m_parent->GetQuiltNoshowIindexArray().size(); ins++) {
2269 if (m_parent->GetQuiltNoshowIindexArray()[ins] ==
2270 piqp->dbIndex) // chart is in the noshow list
2271 {
2272 piqp->b_Valid = false;
2273 break;
2274 }
2275 }
2276 }
2277
2278 // Generate the final render regions for the patches, one by one
2279
2280 m_covered_region.Clear();
2281#if 1 // this does the same as before with a lot less operations if there are
2282 // many charts
2283
2284 // If the reference chart is cm93, we need to render it first.
2285 bool b_skipCM93 = false;
2286 if (m_reference_type == CHART_TYPE_CM93COMP) {
2287 // find cm93 in the list
2288 for (int i = m_PatchList.GetCount() - 1; i >= 0; i--) {
2289 wxPatchListNode *pcinode = m_PatchList.Item(i);
2290 QuiltPatch *piqp = pcinode->GetData();
2291 if (!piqp->b_Valid) // skip invalid entries
2292 continue;
2293
2294 const ChartTableEntry &m = ChartData->GetChartTableEntry(piqp->dbIndex);
2295
2296 if (m.GetChartType() == CHART_TYPE_CM93COMP) {
2297 // Start with the chart's full region coverage.
2298 piqp->ActiveRegion = piqp->quilt_region;
2299 piqp->ActiveRegion.Intersect(cvp_region);
2300
2301 // Update the next pass full region to remove the region just
2302 // allocated
2303 m_covered_region.Union(piqp->quilt_region);
2304
2305 b_skipCM93 = true; // did this already...
2306 break;
2307 }
2308 }
2309 }
2310
2311 // Proceeding from largest scale to smallest....
2312
2313 for (int i = m_PatchList.GetCount() - 1; i >= 0; i--) {
2314 wxPatchListNode *pcinode = m_PatchList.Item(i);
2315 QuiltPatch *piqp = pcinode->GetData();
2316 if (!piqp->b_Valid) // skip invalid entries
2317 continue;
2318
2319 const ChartTableEntry &cte = ChartData->GetChartTableEntry(piqp->dbIndex);
2320
2321 if (b_skipCM93) {
2322 if (cte.GetChartType() == CHART_TYPE_CM93COMP) continue;
2323 }
2324
2325 // Start with the chart's full region coverage.
2326 piqp->ActiveRegion = piqp->quilt_region;
2327
2328 // this operation becomes expensive with lots of charts
2329 if (!b_has_overlays && m_PatchList.GetCount() < 25)
2330 piqp->ActiveRegion.Subtract(m_covered_region);
2331
2332 piqp->ActiveRegion.Intersect(cvp_region);
2333
2334 // Could happen that a larger scale chart covers completely a smaller
2335 // scale chart
2336 if (piqp->ActiveRegion.Empty() && (piqp->dbIndex != m_refchart_dbIndex))
2337 piqp->b_eclipsed = true;
2338
2339 // Maintain the present full quilt coverage region
2340 piqp->b_overlay = false;
2341 if (cte.GetChartFamily() == CHART_FAMILY_VECTOR) {
2342 piqp->b_overlay = s57chart::IsCellOverlayType(cte.GetFullSystemPath());
2343 }
2344
2345 if (!piqp->b_overlay) m_covered_region.Union(piqp->quilt_region);
2346 }
2347#else
2348 // this is the old algorithm does the same thing in n^2/2 operations instead
2349 // of 2*n-1
2350 for (unsigned int i = 0; i < m_PatchList.GetCount(); i++) {
2351 wxPatchListNode *pcinode = m_PatchList.Item(i);
2352 QuiltPatch *piqp = pcinode->GetData();
2353
2354 if (!piqp->b_Valid) // skip invalid entries
2355 continue;
2356
2357 const ChartTableEntry &ctei = ChartData->GetChartTableEntry(piqp->dbIndex);
2358
2359 // Start with the chart's full region coverage.
2360 LLRegion vpr_region = piqp->quilt_region;
2361
2362 // This clause should be moved into the rendering routine for quilts so that
2363 // the actual region logic need only be applied to the render region
2364#if 1 // This clause went away with full-screen quilting
2365 // ...and came back with OpenGL....
2366
2367 // fetch and subtract regions for all larger scale charts
2368 for (unsigned int k = i + 1; k < m_PatchList.GetCount(); k++) {
2369 wxPatchListNode *pnode = m_PatchList.Item(k);
2370 QuiltPatch *pqp = pnode->GetData();
2371
2372 if (!pqp->b_Valid) // skip invalid entries
2373 continue;
2374
2383
2385
2389 // if( ( CHART_TYPE_S57 != ctei.GetChartType() ))
2390 if (!b_has_overlays) {
2391 if (!vpr_region.Empty()) {
2392 const ChartTableEntry &cte =
2393 ChartData->GetChartTableEntry(pqp->dbIndex);
2394 LLRegion larger_scale_chart_region =
2395 pqp->quilt_region; // GetChartQuiltRegion( cte, vp_local );
2396
2397 vpr_region.Subtract(larger_scale_chart_region);
2398 }
2399 }
2400 }
2401#endif
2402
2403 // Whatever is left in the vpr region and has not been yet rendered must
2404 // belong to the current target chart
2405
2406 wxPatchListNode *pinode = m_PatchList.Item(i);
2407 QuiltPatch *pqpi = pinode->GetData();
2408 pqpi->ActiveRegion = vpr_region;
2409
2410 // Move the active region so that upper left is 0,0 in final render
2411 // region
2412 // pqpi->ActiveRegion.Offset( -vp_local.rv_rect.x,
2413 // -vp_local.rv_rect.y );
2414
2415 // Could happen that a larger scale chart covers completely a smaller
2416 // scale chart
2417 if (pqpi->ActiveRegion.Empty()) pqpi->b_eclipsed = true;
2418
2419 // Update the next pass full region to remove the region just allocated
2420 // if( !vpr_region.Empty() )
2421 // unrendered_region.Subtract( vpr_region );
2422
2423 // Maintain the present full quilt coverage region
2424 // if( !pqpi->ActiveRegion.Empty() )
2425 m_covered_region.Union(pqpi->ActiveRegion);
2426 }
2427#endif
2428 // Restore temporary VP Rotation
2429 // vp_local.SetRotationAngle( saved_vp_rotation );
2430
2431 // Walk the list again, removing any entries marked as eclipsed....
2432 unsigned int il = 0;
2433 while (il < m_PatchList.GetCount()) {
2434 wxPatchListNode *pcinode = m_PatchList.Item(il);
2435 QuiltPatch *piqp = pcinode->GetData();
2436 if (piqp->b_eclipsed) {
2437 // Make sure that this chart appears in the eclipsed list...
2438 // This can happen when....
2439 bool b_noadd = false;
2440 for (unsigned int ir = 0; ir < m_eclipsed_stack_array.size(); ir++) {
2441 if (piqp->dbIndex == m_eclipsed_stack_array[ir]) {
2442 b_noadd = true;
2443 break;
2444 }
2445 }
2446 if (!b_noadd) m_eclipsed_stack_array.push_back(piqp->dbIndex);
2447
2448 m_PatchList.DeleteNode(pcinode);
2449 il = 0; // restart the list walk
2450 }
2451
2452 else
2453 il++;
2454 }
2455 // Mark the quilt to indicate need for background clear if the region is
2456 // not fully covered
2457 // m_bneed_clear = !unrendered_region.Empty();
2458 // m_back_region = unrendered_region;
2459
2460 // Finally, iterate thru the quilt and preload all of the required charts.
2461 // For dynamic S57 SENC creation, this is where SENC creation happens
2462 // first.....
2463
2464 // Stop (temporarily) canvas paint events, since some chart loads mught
2465 // Yield(), thus causing performance loss on recursion We will (always??) get
2466 // a refresh on the new Quilt anyway...
2467 m_parent->EnablePaint(false);
2468
2469 // Load and lock all required charts
2470 // First lock required charts already in the cache
2471 // otherwise under memory pressure if chart1 and chart2
2472 // are in the quilt loading chart1 could evict chart2
2473 //
2474 for (ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
2475 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
2476 if ((pqc->b_include) && (!pqc->b_eclipsed)) {
2477 if (!ChartData->IsChartLocked(pqc->dbIndex))
2478 ChartData->LockCacheChart(pqc->dbIndex);
2479 }
2480 }
2481
2482 // Now load and lock any new charts required by the quilt
2483 for (ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
2484 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
2485 if ((pqc->b_include) && (!pqc->b_eclipsed)) {
2486 if (!ChartData->IsChartLocked(pqc->dbIndex)) // Not locked, or not loaded
2487 ChartData->OpenChartFromDBAndLock(pqc->dbIndex, FULL_INIT, true);
2488 }
2489 }
2490
2491#if 0
2492 // first lock charts already in the cache
2493 // otherwise under memory pressure if chart1 and chart2
2494 // are in the quilt loading chart1 could evict chart2
2495 //
2496 for (ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
2497 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
2498 if ((pqc->b_include) && (!pqc->b_eclipsed)) {
2499 if (ChartData->IsChartLocked(pqc->dbIndex)) // already locked
2500 pqc->b_locked = true;
2501 else
2502 pqc->b_locked = ChartData->LockCacheChart(pqc->dbIndex);
2503 }
2504 }
2505
2506 // open charts not in the cache
2507 for (ir = 0; ir < m_pcandidate_array->GetCount(); ir++) {
2508 QuiltCandidate *pqc = m_pcandidate_array->Item(ir);
2509 if ((pqc->b_include) && (!pqc->b_eclipsed)) {
2510 // I am fairly certain this test can now be removed
2511 // with improved smooth movement logic
2512 // if( !ChartData->IsChartInCache( pqc->dbIndex ) )
2513 // b_stop_movement = true;
2514 // only lock chart if not already locked
2515 if (ChartData->OpenChartFromDBAndLock(pqc->dbIndex, FULL_INIT,
2516 !pqc->b_locked))
2517 pqc->b_locked = true;
2518 }
2519 }
2520#endif
2521
2522 m_parent->EnablePaint(true);
2523 // Build and maintain the array of indexes in this quilt
2524
2525 m_last_index_array = m_index_array; // save the last one for delta checks
2526
2527 m_index_array.clear();
2528
2529 // The index array is to be built in reverse, largest scale first
2530 unsigned int kl = m_PatchList.GetCount();
2531 for (unsigned int k = 0; k < kl; k++) {
2532 wxPatchListNode *cnode = m_PatchList.Item((kl - k) - 1);
2533 m_index_array.push_back(cnode->GetData()->dbIndex);
2534 cnode = cnode->GetNext();
2535 }
2536
2537 // Walk the patch list again, checking the depth units
2538 // If they are all the same, then the value is usable
2539
2540 m_quilt_depth_unit = "";
2541 ChartBase *pc = ChartData->OpenChartFromDB(m_refchart_dbIndex, FULL_INIT);
2542 if (pc) {
2543 m_quilt_depth_unit = pc->GetDepthUnits();
2544
2545 if (pc->GetChartFamily() == CHART_FAMILY_VECTOR) {
2546 int units = ps52plib->m_nDepthUnitDisplay;
2547 switch (units) {
2548 case 0:
2549 m_quilt_depth_unit = "Feet";
2550 break;
2551 case 1:
2552 m_quilt_depth_unit = "Meters";
2553 break;
2554 case 2:
2555 m_quilt_depth_unit = "Fathoms";
2556 break;
2557 }
2558 }
2559 }
2560
2561 for (unsigned int k = 0; k < m_PatchList.GetCount(); k++) {
2562 wxPatchListNode *pnode = m_PatchList.Item(k);
2563 QuiltPatch *pqp = pnode->GetData();
2564
2565 if (!pqp->b_Valid) // skip invalid entries
2566 continue;
2567
2568 ChartBase *pc = ChartData->OpenChartFromDB(pqp->dbIndex, FULL_INIT);
2569 if (pc) {
2570 wxString du = pc->GetDepthUnits();
2571 if (pc->GetChartFamily() == CHART_FAMILY_VECTOR) {
2572 int units = ps52plib->m_nDepthUnitDisplay;
2573 switch (units) {
2574 case 0:
2575 du = "Feet";
2576 break;
2577 case 1:
2578 du = "Meters";
2579 break;
2580 case 2:
2581 du = "Fathoms";
2582 break;
2583 }
2584 }
2585 wxString dul = du.Lower();
2586 wxString ml = m_quilt_depth_unit.Lower();
2587
2588 if (dul != ml) {
2589 // Try all the odd cases
2590 if (dul.StartsWith("meters") && ml.StartsWith("meters"))
2591 continue;
2592 else if (dul.StartsWith("metres") && ml.StartsWith("metres"))
2593 continue;
2594 else if (dul.StartsWith("fathoms") && ml.StartsWith("fathoms"))
2595 continue;
2596 else if (dul.StartsWith("met") && ml.StartsWith("met"))
2597 continue;
2598
2599 // They really are different
2600 m_quilt_depth_unit = "";
2601 break;
2602 }
2603 }
2604 }
2605
2606 // And try to prove that all required charts are in the cache
2607 // If one is missing, try to load it
2608 // If still missing, remove its patch from the quilt
2609 // This will probably leave a "black hole" in the quilt...
2610 for (unsigned int k = 0; k < m_PatchList.GetCount(); k++) {
2611 wxPatchListNode *pnode = m_PatchList.Item(k);
2612 QuiltPatch *pqp = pnode->GetData();
2613
2614 if (pqp->b_Valid) {
2615 if (!ChartData->IsChartInCache(pqp->dbIndex)) {
2616 wxLogMessage(" Quilt Compose cache miss...");
2617 ChartData->OpenChartFromDB(pqp->dbIndex, FULL_INIT);
2618 if (!ChartData->IsChartInCache(pqp->dbIndex)) {
2619 wxLogMessage(" Oops, removing from quilt...");
2620 pqp->b_Valid = false;
2621 }
2622 }
2623 }
2624 }
2625
2626 // Make sure the reference chart is in the cache
2627 if (!ChartData->IsChartInCache(m_refchart_dbIndex))
2628 ChartData->OpenChartFromDB(m_refchart_dbIndex, FULL_INIT);
2629
2630 // Walk the patch list again, checking the error factor
2631 // Also, directly mark the patch to indicate if it should be treated as an
2632 // overlay as seen in Austrian Inland series
2633
2634 m_bquilt_has_overlays = false;
2635 m_max_error_factor = 0.;
2636 for (unsigned int k = 0; k < m_PatchList.GetCount(); k++) {
2637 wxPatchListNode *pnode = m_PatchList.Item(k);
2638 QuiltPatch *pqp = pnode->GetData();
2639
2640 if (!pqp->b_Valid) // skip invalid entries
2641 continue;
2642
2643 ChartBase *pc = ChartData->OpenChartFromDB(pqp->dbIndex, FULL_INIT);
2644 if (pc) {
2645 m_max_error_factor =
2646 wxMax(m_max_error_factor, pc->GetChart_Error_Factor());
2647 if (pc->GetChartFamily() == CHART_FAMILY_VECTOR) {
2648 bool isOverlay = IsChartS57Overlay(pqp->dbIndex);
2649 pqp->b_overlay = isOverlay;
2650 if (isOverlay) m_bquilt_has_overlays = true;
2651 }
2652 }
2653 }
2654
2655 m_bcomposed = true;
2656
2657 m_vp_quilt = vp_in; // save the corresponding ViewPort locally
2658
2659 ChartData->LockCache();
2660
2661 // Create and store a hash value representing the contents of the
2662 // m_extended_stack_array
2663 unsigned long xa_hash = 5381;
2664 for (unsigned int im = 0; im < m_extended_stack_array.size(); im++) {
2665 int dbindex = m_extended_stack_array[im];
2666 xa_hash = ((xa_hash << 5) + xa_hash) + dbindex; /* hash * 33 + dbindex */
2667 }
2668
2669 m_xa_hash = xa_hash;
2670 m_bbusy = false;
2671 return true;
2672}
2673
2674// Compute and update the member quilt render region, considering all scale
2675// factors, group exclusions, etc.
2676void Quilt::ComputeRenderRegion(ViewPort &vp, OCPNRegion &chart_region) {
2677 if (!m_bcomposed) return;
2678
2679 OCPNRegion rendered_region;
2680
2681 if (GetnCharts() && !m_bbusy && !chart_region.Empty()) {
2682 // Walk the quilt, considering each chart from smallest scale to largest
2683
2684 ChartBase *chart = GetFirstChart();
2685
2686 while (chart) {
2687 if (!(chart->GetChartProjectionType() != PROJECTION_MERCATOR &&
2688 vp.b_MercatorProjectionOverride)) {
2689 QuiltPatch *pqp = GetCurrentPatch();
2690 if (pqp->b_Valid) {
2691 OCPNRegion get_screen_region = vp.GetVPRegionIntersect(
2692 chart_region, pqp->ActiveRegion, chart->GetNativeScale());
2693 if (!get_screen_region.Empty())
2694 rendered_region.Union(get_screen_region);
2695 }
2696 }
2697 chart = GetNextChart();
2698 }
2699 }
2700 // Record the region actually rendered
2701 m_rendered_region = rendered_region;
2702}
2703
2704int g_render;
2705
2706bool Quilt::RenderQuiltRegionViewOnDCNoText(wxMemoryDC &dc, ViewPort &vp,
2707 OCPNRegion &chart_region) {
2708 return DoRenderQuiltRegionViewOnDC(dc, vp, chart_region);
2709}
2710
2711bool Quilt::RenderQuiltRegionViewOnDCTextOnly(wxMemoryDC &dc, ViewPort &vp,
2712 OCPNRegion &chart_region) {
2713 return DoRenderQuiltRegionViewOnDCTextOnly(dc, vp, chart_region);
2714}
2715
2716bool Quilt::DoRenderQuiltRegionViewOnDC(wxMemoryDC &dc, ViewPort &vp,
2717 OCPNRegion &chart_region) {
2718#ifdef ocpnUSE_DIBSECTION
2719 ocpnMemDC tmp_dc;
2720#else
2721 wxMemoryDC tmp_dc;
2722#endif
2723
2724 if (!m_bcomposed) return false;
2725
2726 OCPNRegion rendered_region;
2727
2728 if (GetnCharts() && !m_bbusy) {
2729 OCPNRegion screen_region = chart_region;
2730
2731 // Walk the quilt, drawing each chart from smallest scale to largest
2732 // Render the quilt's charts onto a temp dc
2733 // and blit the active region rectangles to to target dc, one-by-one
2734
2735 ChartBase *chart = GetFirstChart();
2736 int chartsDrawn = 0;
2737
2738 if (!chart_region.Empty()) {
2739 while (chart) {
2740 bool okToRender = true;
2741
2742 if (chart->GetChartProjectionType() != PROJECTION_MERCATOR &&
2743 vp.b_MercatorProjectionOverride)
2744 okToRender = false;
2745
2746 if (!okToRender) {
2747 chart = GetNextChart();
2748 continue;
2749 }
2750 QuiltPatch *pqp = GetCurrentPatch();
2751 if (pqp->b_Valid) {
2752 bool b_chart_rendered = false;
2753 LLRegion get_region = pqp->ActiveRegion;
2754
2755 OCPNRegion get_screen_region = vp.GetVPRegionIntersect(
2756 chart_region, get_region, chart->GetNativeScale());
2757 if (!get_screen_region.Empty()) {
2758 if (!pqp->b_overlay) {
2759 if (chart->GetChartType() == CHART_TYPE_CM93COMP) {
2760 b_chart_rendered =
2761 chart->RenderRegionViewOnDC(tmp_dc, vp, get_screen_region);
2762 } else {
2763 s57chart *Chs57 = dynamic_cast<s57chart *>(chart);
2764 if (Chs57) {
2765 if (Chs57->m_RAZBuilt) {
2766 b_chart_rendered = Chs57->RenderRegionViewOnDCNoText(
2767 tmp_dc, vp, get_screen_region);
2768 }
2769 } else {
2770 ChartPlugInWrapper *ChPI =
2771 dynamic_cast<ChartPlugInWrapper *>(chart);
2772 if (ChPI) {
2773 b_chart_rendered = ChPI->RenderRegionViewOnDCNoText(
2774 tmp_dc, vp, get_screen_region);
2775 } else
2776 b_chart_rendered = chart->RenderRegionViewOnDC(
2777 tmp_dc, vp, get_screen_region);
2778
2779 b_chart_rendered = true;
2780 }
2781 }
2782
2783 // if( chart->GetChartType() !=
2784 // CHART_TYPE_CM93COMP )
2785 // b_chart_rendered = true;
2786 screen_region.Subtract(get_screen_region);
2787 }
2788 }
2789
2790 OCPNRegionIterator upd(get_screen_region);
2791 while (upd.HaveRects()) {
2792 wxRect rect = upd.GetRect();
2793 dc.Blit(rect.x, rect.y, rect.width, rect.height, &tmp_dc, rect.x,
2794 rect.y, wxCOPY, true);
2795 upd.NextRect();
2796 }
2797
2798 tmp_dc.SelectObject(wxNullBitmap);
2799
2800 if (b_chart_rendered) rendered_region.Union(get_screen_region);
2801 }
2802
2803 chartsDrawn++;
2804 chart = GetNextChart();
2805 }
2806 }
2807
2808 if (!chartsDrawn) m_parent->GetVP().SetProjectionType(PROJECTION_MERCATOR);
2809
2810 // Render any Overlay patches for s57 charts(cells)
2811 if (m_bquilt_has_overlays && !chart_region.Empty()) {
2812 chart = GetFirstChart();
2813 while (chart) {
2814 QuiltPatch *pqp = GetCurrentPatch();
2815 if (pqp->b_Valid) {
2816 if (pqp->b_overlay) {
2817 LLRegion get_region = pqp->ActiveRegion;
2818 OCPNRegion get_screen_region = vp.GetVPRegionIntersect(
2819 chart_region, get_region, chart->GetNativeScale());
2820 if (!get_region.Empty()) {
2821 s57chart *Chs57 = dynamic_cast<s57chart *>(chart);
2822 if (Chs57) {
2823 Chs57->RenderOverlayRegionViewOnDC(tmp_dc, vp,
2824 get_screen_region);
2825 } else {
2826 ChartPlugInWrapper *ChPI =
2827 dynamic_cast<ChartPlugInWrapper *>(chart);
2828 if (ChPI) {
2829 ChPI->RenderRegionViewOnDC(tmp_dc, vp, get_screen_region);
2830 }
2831 }
2832
2833 OCPNRegionIterator upd(get_screen_region);
2834 while (upd.HaveRects()) {
2835 wxRect rect = upd.GetRect();
2836 dc.Blit(rect.x, rect.y, rect.width, rect.height, &tmp_dc,
2837 rect.x, rect.y, wxCOPY, true);
2838 upd.NextRect();
2839 }
2840 tmp_dc.SelectObject(wxNullBitmap);
2841 }
2842 }
2843 }
2844
2845 chart = GetNextChart();
2846 }
2847 }
2848
2849 // Any part of the chart region that was not rendered in the loop needs
2850 // to be cleared
2851 OCPNRegionIterator clrit(screen_region);
2852 while (clrit.HaveRects()) {
2853 wxRect rect = clrit.GetRect();
2854#ifdef __WXOSX__
2855 dc.SetPen(*wxBLACK_PEN);
2856 dc.SetBrush(*wxBLACK_BRUSH);
2857 dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height);
2858#else
2859 dc.Blit(rect.x, rect.y, rect.width, rect.height, &dc, rect.x, rect.y,
2860 wxCLEAR);
2861#endif
2862 clrit.NextRect();
2863 }
2864
2865 // Highlighting....
2866 if (m_nHiLiteIndex >= 0) {
2867 OCPNRegion hiregion =
2868 vp.GetVPRegionIntersect(chart_region, GetHiliteRegion(), 1);
2869 wxRect box = hiregion.GetBox();
2870
2871 if (!box.IsEmpty()) {
2872 // Is scratch member bitmap OK?
2873 if (m_pBM) {
2874 if ((m_pBM->GetWidth() != vp.rv_rect.width) ||
2875 (m_pBM->GetHeight() != vp.rv_rect.height)) {
2876 delete m_pBM;
2877 m_pBM = NULL;
2878 }
2879 }
2880
2881 if (NULL == m_pBM)
2882 m_pBM = new wxBitmap(vp.rv_rect.width, vp.rv_rect.height);
2883
2884 // Copy the entire quilt to my scratch bm
2885 wxMemoryDC q_dc;
2886 q_dc.SelectObject(*m_pBM);
2887 q_dc.Blit(0, 0, vp.rv_rect.width, vp.rv_rect.height, &dc, 0, 0);
2888 q_dc.SelectObject(wxNullBitmap);
2889
2890 // Create a "mask" bitmap from the chart's region
2891 // WxGTK has an error in this method....Creates a color bitmap, not
2892 // usable for mask creation So, I clone with correction
2893 wxBitmap hl_mask_bm(vp.rv_rect.width, vp.rv_rect.height, 1);
2894 wxMemoryDC mdc;
2895 mdc.SelectObject(hl_mask_bm);
2896 mdc.SetBackground(*wxBLACK_BRUSH);
2897 mdc.Clear();
2898 mdc.SetClippingRegion(box);
2899 mdc.SetBackground(*wxWHITE_BRUSH);
2900 mdc.Clear();
2901 mdc.SelectObject(wxNullBitmap);
2902
2903 if (hl_mask_bm.IsOk()) {
2904 wxMask *phl_mask = new wxMask(hl_mask_bm);
2905 m_pBM->SetMask(phl_mask);
2906 q_dc.SelectObject(*m_pBM);
2907
2908 // Create another mask, dc and bitmap for red-out
2909 wxBitmap rbm(vp.rv_rect.width, vp.rv_rect.height);
2910 wxMask *pr_mask = new wxMask(hl_mask_bm);
2911 wxMemoryDC rdc;
2912 rbm.SetMask(pr_mask);
2913 rdc.SelectObject(rbm);
2914 unsigned char hlcolor = 255;
2915 switch (global_color_scheme) {
2916 case GLOBAL_COLOR_SCHEME_DAY:
2917 hlcolor = 255;
2918 break;
2919 case GLOBAL_COLOR_SCHEME_DUSK:
2920 hlcolor = 64;
2921 break;
2922 case GLOBAL_COLOR_SCHEME_NIGHT:
2923 hlcolor = 16;
2924 break;
2925 default:
2926 hlcolor = 255;
2927 break;
2928 }
2929
2930 rdc.SetBackground(wxBrush(wxColour(hlcolor, 0, 0)));
2931 rdc.Clear();
2932
2933 OCPNRegionIterator upd(hiregion);
2934 while (upd.HaveRects()) {
2935 wxRect rect = upd.GetRect();
2936 rdc.Blit(rect.x, rect.y, rect.width, rect.height, &q_dc, rect.x,
2937 rect.y, wxOR, true);
2938 upd.NextRect();
2939 }
2940
2941 OCPNRegionIterator updq(hiregion);
2942 while (updq.HaveRects()) {
2943 wxRect rect = updq.GetRect();
2944 q_dc.Blit(rect.x, rect.y, rect.width, rect.height, &rdc, rect.x,
2945 rect.y, wxCOPY, true);
2946 updq.NextRect();
2947 }
2948
2949 q_dc.SelectObject(wxNullBitmap);
2950 m_pBM->SetMask(NULL);
2951
2952 // Select the scratch BM as the return dc contents
2953 dc.SelectObject(*m_pBM);
2954
2955 // Clear the rdc
2956 rdc.SelectObject(wxNullBitmap);
2957 }
2958 } // box not empty
2959 } // m_nHiLiteIndex
2960
2961 // Fogging....
2962 if (g_fog_overzoom) {
2963 double scale_factor = vp.ref_scale / vp.chart_scale;
2964
2965 if (scale_factor > g_overzoom_emphasis_base) {
2966 float fog = ((scale_factor - g_overzoom_emphasis_base) * 255.) / 20.;
2967 fog = wxMin(fog, 200.); // Don't fog out completely
2968
2969 // Is scratch member bitmap OK?
2970 if (m_pBM) {
2971 if ((m_pBM->GetWidth() != vp.rv_rect.width) ||
2972 (m_pBM->GetHeight() != vp.rv_rect.height)) {
2973 delete m_pBM;
2974 m_pBM = NULL;
2975 }
2976 }
2977
2978 if (NULL == m_pBM)
2979 m_pBM = new wxBitmap(vp.rv_rect.width, vp.rv_rect.height);
2980
2981 // Copy the entire quilt to my scratch bm
2982 wxMemoryDC q_dc;
2983 q_dc.SelectObject(*m_pBM);
2984 q_dc.Blit(0, 0, vp.rv_rect.width, vp.rv_rect.height, &dc, 0, 0);
2985 q_dc.SelectObject(wxNullBitmap);
2986
2987 wxImage src = m_pBM->ConvertToImage();
2988#if 1
2989 int blur_factor =
2990 wxMin((scale_factor - g_overzoom_emphasis_base) / 4, 4);
2991 if (src.IsOk()) {
2992 wxImage dest = src.Blur(blur_factor);
2993#endif
2994
2995#if 0 // this is fogging effect
2996 unsigned char *bg = src.GetData();
2997 wxColour color = m_parent->GetFogColor();
2998
2999 float transparency = fog;
3000
3001 // destination image
3002 wxImage dest(vp.rv_rect.width, vp.rv_rect.height);
3003 unsigned char *dest_data = (unsigned char *) malloc( vp.rv_rect.width * vp.rv_rect.height * 3 * sizeof(unsigned char) );
3004 unsigned char *d = dest_data;
3005
3006 float alpha = 1.0 - (float)transparency / 255.0;
3007 int sb = vp.rv_rect.width * vp.rv_rect.height;
3008 for( int i = 0; i < sb; i++ ) {
3009 float a = alpha;
3010
3011 int r = ( ( *bg++ ) * a ) + (1.0-a) * color.Red();
3012 *d++ = r;
3013 int g = ( ( *bg++ ) * a ) + (1.0-a) * color.Green();
3014 *d++ = g;
3015 int b = ( ( *bg++ ) * a ) + (1.0-a) * color.Blue();
3016 *d++ = b;
3017 }
3018
3019 dest.SetData( dest_data );
3020#endif
3021
3022 wxBitmap dim(dest);
3023 wxMemoryDC ddc;
3024 ddc.SelectObject(dim);
3025
3026 q_dc.SelectObject(*m_pBM);
3027 OCPNRegionIterator upd(rendered_region);
3028 while (upd.HaveRects()) {
3029 wxRect rect = upd.GetRect();
3030 q_dc.Blit(rect.x, rect.y, rect.width, rect.height, &ddc, rect.x,
3031 rect.y);
3032 upd.NextRect();
3033 }
3034
3035 ddc.SelectObject(wxNullBitmap);
3036 q_dc.SelectObject(wxNullBitmap);
3037
3038 // Select the scratch BM as the return dc contents
3039 dc.SelectObject(*m_pBM);
3040 }
3041 }
3042 } // overzoom
3043
3044 if (!dc.IsOk()) // some error, probably bad charts, to be disabled on next
3045 // compose
3046 {
3047 SubstituteClearDC(dc, vp);
3048 }
3049
3050 } else { // no charts yet, or busy....
3051 SubstituteClearDC(dc, vp);
3052 }
3053
3054 // Record the region actually rendered
3055 m_rendered_region = rendered_region;
3056
3057 m_vp_rendered = vp;
3058 return true;
3059}
3060
3061void Quilt::SubstituteClearDC(wxMemoryDC &dc, ViewPort &vp) {
3062 if (m_pBM) {
3063 if ((m_pBM->GetWidth() != vp.rv_rect.width) ||
3064 (m_pBM->GetHeight() != vp.rv_rect.height)) {
3065 delete m_pBM;
3066 m_pBM = NULL;
3067 }
3068 }
3069
3070 if (NULL == m_pBM) {
3071 m_pBM = new wxBitmap(vp.rv_rect.width, vp.rv_rect.height);
3072 }
3073
3074 dc.SelectObject(wxNullBitmap);
3075 dc.SelectObject(*m_pBM);
3076 dc.SetBackground(*wxBLACK_BRUSH);
3077 dc.Clear();
3078 m_covered_region.Clear();
3079}
3080
3081bool Quilt::DoRenderQuiltRegionViewOnDCTextOnly(wxMemoryDC &dc, ViewPort &vp,
3082 OCPNRegion &chart_region) {
3083 if (!m_bcomposed) return false;
3084
3085 OCPNRegion rendered_region;
3086
3087 if (GetnCharts() && !m_bbusy) {
3088 OCPNRegion screen_region = chart_region;
3089
3090 // Walk the quilt, drawing each chart from largest scale to smallest
3091
3092 ChartBase *chart = GetLargestScaleChart();
3093
3094 while (chart) {
3095 QuiltPatch *pqp = GetCurrentPatch();
3096 if (pqp->b_Valid) {
3097 s57chart *Chs57 = dynamic_cast<s57chart *>(chart);
3098 if (Chs57)
3099 Chs57->RenderRegionViewOnDCTextOnly(dc, vp, chart_region);
3100 else {
3101 ChartPlugInWrapper *ChPI = dynamic_cast<ChartPlugInWrapper *>(chart);
3102 if (ChPI) {
3103 ChPI->RenderRegionViewOnDCTextOnly(dc, vp, chart_region);
3104 }
3105 }
3106 }
3107
3108 chart = GetNextSmallerScaleChart();
3109 }
3110
3111 } else { // no charts yet, or busy....
3112 SubstituteClearDC(dc, vp);
3113 }
3114
3115 return true;
3116}
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:1779
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