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