OpenCPN Partial API docs
Loading...
Searching...
No Matches
chartdb.cpp
1/***************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose: Chart Database Object
5 * Author: David Register, Mark A Sikes
6 *
7 ***************************************************************************
8 * Copyright (C) 2010 by David S. Register *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the *
22 * Free Software Foundation, Inc., *
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24 **************************************************************************/
25
26// For compilers that support precompilation, includes "wx.h".
27#include <wx/wxprec.h>
28
29#ifndef WX_PRECOMP
30#include <wx/wx.h>
31#endif // precompiled headers
32
33#include <wx/stopwatch.h>
34#include <wx/regex.h>
35#include <wx/tokenzr.h>
36#include <wx/dir.h>
37
38#include "dychart.h"
39
40#include "config.h"
41#include "chartdb.h"
42#include "chartimg.h"
43#include "thumbwin.h"
44#include "mbtiles.h"
45#include "CanvasConfig.h"
46#include "ocpn_frame.h" //FIXME (dave) LoadS57
47#ifdef __OCPN__ANDROID__
48 #include "androidUTIL.h"
49#endif
50
51#ifdef ocpnUSE_GL
52#include "glChartCanvas.h"
53#endif
54
55#include <stdio.h>
56#include <math.h>
57
58#include <wx/progdlg.h>
59
60#include "chcanv.h"
61
62#include "s57chart.h"
63#include "cm93.h"
64
65extern ColorScheme GetColorScheme();
66
67class s52plib;
68
69extern ThumbWin *pthumbwin;
70extern int g_nCacheLimit;
71extern int g_memCacheLimit;
72extern s52plib *ps52plib;
73extern ChartDB *ChartData;
74extern unsigned int g_canvasConfig;
75extern arrayofCanvasConfigPtr g_canvasConfigArray;
76
77bool G_FloatPtInPolygon(MyFlPoint *rgpts, int wnumpts, float x, float y);
78bool GetMemoryStatus(int *mem_total, int *mem_used);
79
80// ============================================================================
81// ChartStack implementation
82// ============================================================================
83
84int ChartStack::GetCurrentEntrydbIndex(void) {
85 if (nEntry && (CurrentStackEntry >= 0) /*&& b_valid*/)
86 return DBIndex[CurrentStackEntry];
87 else
88 return -1;
89}
90
91void ChartStack::SetCurrentEntryFromdbIndex(int current_db_index) {
92 for (int i = 0; i < nEntry; i++) {
93 if (current_db_index == DBIndex[i]) CurrentStackEntry = i;
94 }
95}
96
97int ChartStack::GetDBIndex(int stack_index) {
98 if ((stack_index >= 0) && (stack_index < nEntry) && (stack_index < MAXSTACK))
99 return DBIndex[stack_index];
100 else
101 return -1;
102}
103
104void ChartStack::SetDBIndex(int stack_index, int db_index) {
105 if ((stack_index >= 0) && (stack_index < nEntry) && (stack_index < MAXSTACK))
106 DBIndex[stack_index] = db_index;
107}
108
109bool ChartStack::DoesStackContaindbIndex(int db_index) {
110 for (int i = 0; i < nEntry; i++) {
111 if (db_index == DBIndex[i]) return true;
112 }
113
114 return false;
115}
116
117void ChartStack::AddChart(int db_add) {
118 if (!ChartData) return;
119
120 if (!ChartData->IsValid()) return;
121
122 int db_index = db_add;
123
124 int j = nEntry;
125
126 if (db_index >= 0) {
127 j++;
128 nEntry = j;
129 SetDBIndex(j - 1, db_index);
130 }
131 // Remove exact duplicates, i.e. charts that have exactly the same file
132 // name and
133 // nearly the same mod time.
134 // These charts can be in the database due to having the exact same chart
135 // in different directories, as may be desired for some grouping schemes
136 // Note that if the target name is actually a directory, then windows fails
137 // to produce a valid file modification time. Detect GetFileTime() == 0,
138 // and skip the test in this case
139 for (int id = 0; id < j - 1; id++) {
140 if (GetDBIndex(id) != -1) {
141 ChartTableEntry *pm = ChartData->GetpChartTableEntry(GetDBIndex(id));
142
143 for (int jd = id + 1; jd < j; jd++) {
144 if (GetDBIndex(jd) != -1) {
145 ChartTableEntry *pn = ChartData->GetpChartTableEntry(GetDBIndex(jd));
146 if (pm->GetFileTime() && pn->GetFileTime()) {
147 if (labs(pm->GetFileTime() - pn->GetFileTime()) <
148 60) { // simple test
149 if (pn->GetpFileName()->IsSameAs(*(pm->GetpFileName())))
150 SetDBIndex(jd, -1); // mark to remove
151 }
152 }
153 }
154 }
155 }
156 }
157
158 int id = 0;
159 while ((id < j)) {
160 if (GetDBIndex(id) == -1) {
161 int jd = id + 1;
162 while (jd < j) {
163 int db_index = GetDBIndex(jd);
164 SetDBIndex(jd - 1, db_index);
165 jd++;
166 }
167
168 j--;
169 nEntry = j;
170
171 id = 0;
172 } else
173 id++;
174 }
175
176 // Sort the stack on scale
177 int swap = 1;
178 int ti;
179 while (swap == 1) {
180 swap = 0;
181 for (int i = 0; i < j - 1; i++) {
182 const ChartTableEntry &m = ChartData->GetChartTableEntry(GetDBIndex(i));
183 const ChartTableEntry &n =
184 ChartData->GetChartTableEntry(GetDBIndex(i + 1));
185
186 if (n.GetScale() < m.GetScale()) {
187 ti = GetDBIndex(i);
188 SetDBIndex(i, GetDBIndex(i + 1));
189 SetDBIndex(i + 1, ti);
190 swap = 1;
191 }
192 }
193 }
194}
195
196// ============================================================================
197// ChartDB implementation
198// ============================================================================
199
200ChartDB::ChartDB() {
201 pChartCache = new wxArrayPtrVoid;
202
203 SetValid(false); // until loaded or created
204 UnLockCache();
205
206 m_b_busy = false;
207 m_ticks = 0;
208
209 // Report cache policy
210 if (g_memCacheLimit) {
211 wxString msg;
212 msg.Printf(_T("ChartDB Cache policy: Application target is %d MBytes"),
213 g_memCacheLimit / 1024);
214 wxLogMessage(msg);
215 } else {
216 wxString msg;
217 msg.Printf(_T("ChartDB Cache policy: Max open chart limit is %d."),
218 g_nCacheLimit);
219 wxLogMessage(msg);
220 }
221
222 m_checkGroupIndex[0] = m_checkGroupIndex[1] = -1;
223 m_checkedTileOnly[0] = m_checkedTileOnly[1] = false;
224}
225
226ChartDB::~ChartDB() {
227 // Empty the cache
228 PurgeCache();
229
230 delete pChartCache;
231}
232
233bool ChartDB::LoadBinary(const wxString &filename,
234 ArrayOfCDI &dir_array_check) {
235 m_dir_array = dir_array_check;
236 return ChartDatabase::Read(filename);
237
238 // Check chartDirs against dir_array_check
239}
240
241void ChartDB::DeleteCacheEntry(CacheEntry *pce, bool bDelTexture,
242 const wxString &msg) {
243 ChartBase *ch = (ChartBase *)pce->pChart;
244
245 if (msg != wxEmptyString) {
246 wxLogMessage(_T("%s%s"), msg.c_str(), ch->GetFullPath().c_str());
247 }
248
249 // If this chart should happen to be in the thumbnail window....
250 if (pthumbwin) {
251 if (pthumbwin->pThumbChart == ch) pthumbwin->pThumbChart = NULL;
252 }
253
254#ifdef ocpnUSE_GL
255 // The glCanvas may be cacheing some information for this chart
256 if (g_glTextureManager)
257 g_glTextureManager->PurgeChartTextures(ch, bDelTexture);
258#endif
259
260 pChartCache->Remove(pce);
261 delete ch;
262 delete pce;
263}
264
265void ChartDB::DeleteCacheEntry(int i, bool bDelTexture, const wxString &msg) {
266 CacheEntry *pce = (CacheEntry *)(pChartCache->Item(i));
267 if (pce) DeleteCacheEntry(pce, bDelTexture, msg);
268}
269
270void ChartDB::PurgeCache() {
271 // Empty the cache
272 // wxLogMessage(_T("Chart cache purge"));
273
274 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
275 unsigned int nCache = pChartCache->GetCount();
276 for (unsigned int i = 0; i < nCache; i++) {
277 DeleteCacheEntry(0, true);
278 }
279 pChartCache->Clear();
280
281 m_cache_mutex.Unlock();
282 }
283}
284
285void ChartDB::PurgeCachePlugins() {
286 // Empty the cache
287 wxLogMessage(_T("Chart cache PlugIn purge"));
288
289 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
290 unsigned int nCache = pChartCache->GetCount();
291 unsigned int i = 0;
292 while (i < nCache) {
293 CacheEntry *pce = (CacheEntry *)(pChartCache->Item(i));
294 ChartBase *Ch = (ChartBase *)pce->pChart;
295
296 if (CHART_TYPE_PLUGIN == Ch->GetChartType()) {
297 DeleteCacheEntry(pce, true);
298
299 nCache = pChartCache->GetCount(); // restart the while loop
300 i = 0;
301
302 } else
303 i++;
304 }
305
306 m_cache_mutex.Unlock();
307 }
308}
309
310void ChartDB::ClearCacheInUseFlags(void) {
311 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
312 unsigned int nCache = pChartCache->GetCount();
313 for (unsigned int i = 0; i < nCache; i++) {
314 CacheEntry *pce = (CacheEntry *)(pChartCache->Item(i));
315 pce->b_in_use = false;
316 }
317 m_cache_mutex.Unlock();
318 }
319}
320
321// Try to purge and delete charts from the cache until the application
322// memory used is until the application memory used is less than {factor *
323// Limit} Purge charts on LRU policy
324void ChartDB::PurgeCacheUnusedCharts(double factor) {
325 // Use memory limited cache policy, if defined....
326 if (g_memCacheLimit) {
327 if (wxMUTEX_NO_ERROR == m_cache_mutex.TryLock()) {
328 // Check memory status to see if above limit
329 int mem_used;
330 GetMemoryStatus(0, &mem_used);
331 int mem_limit = g_memCacheLimit * factor;
332
333 int nl = pChartCache->GetCount(); // max loop count, by definition
334
335 wxString msg(_T("Purging unused chart from cache: "));
336 // printf("Try Purge count: %d\n", nl);
337 while ((mem_used > mem_limit) && (nl > 0)) {
338 if (pChartCache->GetCount() < 2) {
339 nl = 0;
340 break;
341 }
342
343 CacheEntry *pce = FindOldestDeleteCandidate(false);
344 if (pce) {
345 // don't purge background spooler
346 DeleteCacheEntry(pce, false /*true*/, msg);
347 // printf("DCE, new count is: %d\n", pChartCache->GetCount());
348 } else {
349 break;
350 }
351
352 GetMemoryStatus(0, &mem_used);
353
354 nl--;
355 }
356 }
357 m_cache_mutex.Unlock();
358 }
359
360 // Else use chart count cache policy, if defined....
361 else if (g_nCacheLimit) {
362 if (wxMUTEX_NO_ERROR == m_cache_mutex.TryLock()) {
363 // Check chart count to see if above limit
364 double fac10 = factor * 10;
365 int chart_limit = g_nCacheLimit * fac10 / 10;
366
367 int nl = pChartCache->GetCount(); // max loop count, by definition
368
369 wxString msg(_T("Purging unused chart from cache: "));
370 while ((nl > chart_limit) && (nl > 0)) {
371 if (pChartCache->GetCount() < 2) {
372 nl = 0;
373 break;
374 }
375
376 CacheEntry *pce = FindOldestDeleteCandidate(false);
377 if (pce) {
378 // don't purge background spooler
379 DeleteCacheEntry(pce, false /*true*/, msg);
380 } else {
381 break;
382 }
383
384 nl = pChartCache->GetCount();
385 }
386 }
387 m_cache_mutex.Unlock();
388 }
389}
390
391//-------------------------------------------------------------------------------------------------------
392// Create a Chart
393// This version creates a fully functional UI-capable chart.
394//-------------------------------------------------------------------------------------------------------
395
396ChartBase *ChartDB::GetChart(const wxChar *theFilePath,
397 ChartClassDescriptor &chart_desc) const {
398 wxFileName fn(theFilePath);
399
400 if (!fn.FileExists()) {
401 // Might be a directory
402 if (!wxDir::Exists(theFilePath)) {
403 wxLogMessage(wxT(" ...file does not exist: %s"), theFilePath);
404 return NULL;
405 }
406 }
407 ChartBase *pch = NULL;
408
409 wxString chartExt = fn.GetExt().Upper();
410
411 if (chartExt == wxT("XZ")) {
412 wxString npath = theFilePath;
413 npath = npath.Left(npath.length() - 3);
414 wxFileName fn(npath);
415 chartExt = fn.GetExt().Upper();
416 }
417
418 if (chartExt == wxT("KAP")) {
419 pch = new ChartKAP;
420 } else if (chartExt == wxT("GEO")) {
421 pch = new ChartGEO;
422 } else if (chartExt == wxT("MBTILES")) {
423 pch = new ChartMBTiles;
424 } else if (chartExt == wxT("000") || chartExt == wxT("S57")) {
425 LoadS57();
426 pch = new s57chart;
427 } else if (chart_desc.m_descriptor_type == PLUGIN_DESCRIPTOR) {
428 LoadS57();
429 ChartPlugInWrapper *cpiw = new ChartPlugInWrapper(chart_desc.m_class_name);
430 pch = (ChartBase *)cpiw;
431 }
432
433 else {
434 wxRegEx rxName(wxT("[0-9]+"));
435 wxRegEx rxExt(wxT("[A-G]"));
436 if (rxName.Matches(fn.GetName()) && rxExt.Matches(chartExt))
437 pch = new cm93compchart;
438 else {
439 // Might be a directory
440 if (wxDir::Exists(theFilePath)) pch = new cm93compchart;
441 }
442 }
443
444 return pch;
445}
446
447// Build a Chart Stack, and add the indicated chart to the stack, even if
448// the chart does not cover the lat/lon specification
449
450int ChartDB::BuildChartStack(ChartStack *cstk, float lat, float lon, int db_add,
451 int groupIndex) {
452 BuildChartStack(cstk, lat, lon, groupIndex);
453
454 if (db_add >= 0) cstk->AddChart(db_add);
455
456 return cstk->nEntry;
457}
458
459int ChartDB::BuildChartStack(ChartStack *cstk, float lat, float lon,
460 int groupIndex) {
461 int i = 0;
462 int j = 0;
463
464 if (!IsValid()) return 0; // Database is not properly initialized
465
466 if (!cstk) return 0; // Chartstack not ready yet
467
468 int nEntry = GetChartTableEntries();
469
470 for (int db_index = 0; db_index < nEntry; db_index++) {
471 const ChartTableEntry &cte = GetChartTableEntry(db_index);
472
473 // Check to see if the candidate chart is in the currently active group
474 bool b_group_add = false;
475 if (groupIndex > 0) {
476 const int ng = cte.GetGroupArray().size();
477 for (int ig = 0; ig < ng; ig++) {
478 if (groupIndex == cte.GetGroupArray()[ig]) {
479 b_group_add = true;
480 break;
481 }
482 }
483 } else
484 b_group_add = true;
485
486 bool b_writable_add = true;
487 // On android, SDK > 29, we require that the directory of charts be "writable"
488 // as determined by Android Java file system
489#ifdef __OCPN__ANDROID__
490 wxFileName fn(cte.GetFullSystemPath());
491 if (!androidIsDirWritable( fn.GetPath()))
492 b_writable_add = false;
493#endif
494
495 bool b_pos_add = false;
496 if (b_group_add && b_writable_add) {
497 // Plugin loading is deferred, so the chart may have been disabled
498 // elsewhere. Tentatively reenable the chart so that it appears in the
499 // piano. It will get disabled later if really not useable
500 if (cte.GetChartType() == CHART_TYPE_PLUGIN) {
501 ChartTableEntry *pcte = (ChartTableEntry *)&cte;
502 pcte->ReEnable();
503 }
504
505 if (CheckPositionWithinChart(db_index, lat, lon) && (j < MAXSTACK))
506 b_pos_add = true;
507
508 // Check the special case where chart spans the international dateline
509 else if ((cte.GetLonMax() > 180.) && (cte.GetLonMin() < 180.)) {
510 if (CheckPositionWithinChart(db_index, lat, lon + 360.) &&
511 (j < MAXSTACK))
512 b_pos_add = true;
513 }
514 // Western hemisphere, some type of charts
515 else if ((cte.GetLonMax() > 180.) && (cte.GetLonMin() > 180.)) {
516 if (CheckPositionWithinChart(db_index, lat, lon + 360.) &&
517 (j < MAXSTACK))
518 b_pos_add = true;
519 }
520 }
521
522 bool b_available = true;
523 // Verify PlugIn charts are actually available
524 if (b_group_add && b_pos_add && (cte.GetChartType() == CHART_TYPE_PLUGIN)) {
525 ChartTableEntry *pcte = (ChartTableEntry *)&cte;
526 if (!IsChartAvailable(db_index)) {
527 pcte->SetAvailable(false);
528 b_available = false;
529 } else {
530 pcte->SetAvailable(true);
531 pcte->ReEnable();
532 }
533 }
534
535 if (b_group_add && b_pos_add && b_available) { // add it
536 j++;
537 cstk->nEntry = j;
538 cstk->SetDBIndex(j - 1, db_index);
539 }
540 }
541
542 cstk->nEntry = j;
543
544 // Remove exact duplicates, i.e. charts that have exactly the same file
545 // name and nearly the same mod time These charts can be in the database
546 // due to having the exact same chart in different directories, as may be
547 // desired for some grouping schemes Note that if the target name is
548 // actually a directory, then windows fails to produce a valid file
549 // modification time. Detect GetFileTime() == 0, and skip the test in this
550 // case
551 // Extended to also check for "identical" charts, having exact same EditionDate
552
553 for (int id = 0; id < j - 1; id++) {
554 if (cstk->GetDBIndex(id) != -1) {
555 const ChartTableEntry &ctem = GetChartTableEntry(cstk->GetDBIndex(id));
556
557 for (int jd = id + 1; jd < j; jd++) {
558 if (cstk->GetDBIndex(jd) != -1) {
559 const ChartTableEntry &cten =
560 GetChartTableEntry(cstk->GetDBIndex(jd));
561 bool bsameTime = false;
562 if (ctem.GetFileTime() && cten.GetFileTime()) {
563 if (labs(ctem.GetFileTime() - cten.GetFileTime()) < 60)
564 bsameTime = true;
565 }
566 if (ctem.GetChartEditionDate() == cten.GetChartEditionDate() )
567 bsameTime = true;
568
569 if(bsameTime) {
570 if (cten.GetpFileName()->IsSameAs(*(ctem.GetpFileName())))
571 cstk->SetDBIndex(jd, -1); // mark to remove
572 }
573 }
574 }
575 }
576 }
577
578 int id = 0;
579 while ((id < j)) {
580 if (cstk->GetDBIndex(id) == -1) {
581 int jd = id + 1;
582 while (jd < j) {
583 int db_index = cstk->GetDBIndex(jd);
584 cstk->SetDBIndex(jd - 1, db_index);
585 jd++;
586 }
587
588 j--;
589 cstk->nEntry = j;
590
591 id = 0;
592 } else
593 id++;
594 }
595
596 // Sort the stack on scale
597 int swap = 1;
598 int ti;
599 while (swap == 1) {
600 swap = 0;
601 for (i = 0; i < j - 1; i++) {
602 const ChartTableEntry &m = GetChartTableEntry(cstk->GetDBIndex(i));
603 const ChartTableEntry &n = GetChartTableEntry(cstk->GetDBIndex(i + 1));
604
605 if (n.GetScale() < m.GetScale()) {
606 ti = cstk->GetDBIndex(i);
607 cstk->SetDBIndex(i, cstk->GetDBIndex(i + 1));
608 cstk->SetDBIndex(i + 1, ti);
609 swap = 1;
610 }
611 }
612 }
613
614 cstk->b_valid = true;
615
616 return j;
617}
618
619bool ChartDB::IsChartInGroup(const int db_index, const int group) {
620 ChartTableEntry *pt = (ChartTableEntry *)&GetChartTableEntry(db_index);
621
622 // Check to see if the candidate chart is in the designated group
623 bool b_in_group = false;
624 if (group > 0) {
625 for (unsigned int ig = 0; ig < pt->GetGroupArray().size(); ig++) {
626 if (group == pt->GetGroupArray()[ig]) {
627 b_in_group = true;
628 break;
629 }
630 }
631 } else
632 b_in_group = true;
633
634 return b_in_group;
635}
636
637bool ChartDB::IsENCInGroup(const int groupIndex) {
638 // Walk the database, looking in specified group for any vector chart
639 bool retVal = false;
640
641 for (int db_index = 0; db_index < GetChartTableEntries(); db_index++) {
642 const ChartTableEntry &cte = GetChartTableEntry(db_index);
643
644 // Check to see if the candidate chart is in the currently active group
645 bool b_group_add = false;
646 if (groupIndex > 0) {
647 const int ng = cte.GetGroupArray().size();
648 for (int ig = 0; ig < ng; ig++) {
649 if (groupIndex == cte.GetGroupArray()[ig]) {
650 b_group_add = true;
651 break;
652 }
653 }
654 } else
655 b_group_add = true;
656
657 if (b_group_add) {
658 if (cte.GetChartFamily() == CHART_FAMILY_VECTOR) {
659 retVal = true;
660 break; // the outer for loop
661 }
662 }
663 }
664
665 return retVal;
666}
667
668bool ChartDB::IsNonMBTileInGroup(const int groupIndex) {
669 // Walk the database, looking in specified group for anything other than
670 // MBTile Return true if so.
671 bool retVal = false;
672
673 for (int db_index = 0; db_index < GetChartTableEntries(); db_index++) {
674 const ChartTableEntry &cte = GetChartTableEntry(db_index);
675
676 // Check to see if the candidate chart is in the currently active group
677 bool b_group_add = false;
678 if (groupIndex > 0) {
679 const int ng = cte.GetGroupArray().size();
680 for (int ig = 0; ig < ng; ig++) {
681 if (groupIndex == cte.GetGroupArray()[ig]) {
682 b_group_add = true;
683 break;
684 }
685 }
686 } else
687 b_group_add = true;
688
689 if (b_group_add) {
690 if (cte.GetChartType() != CHART_TYPE_MBTILES) {
691 retVal = true;
692 break; // the outer for loop
693 }
694 }
695 }
696
697 return retVal;
698}
699
700//-------------------------------------------------------------------
701// Check to see it lat/lon is within a database chart at index
702//-------------------------------------------------------------------
703bool ChartDB::CheckPositionWithinChart(int index, float lat, float lon) {
704 const ChartTableEntry *pt = &GetChartTableEntry(index);
705
706 // First check on rough Bounding box
707
708 if ((lat <= pt->GetLatMax()) && (lat >= pt->GetLatMin()) &&
709 (lon >= pt->GetLonMin()) && (lon <= pt->GetLonMax())) {
710 // Double check on Primary Ply points polygon
711
712 bool bInside = G_FloatPtInPolygon((MyFlPoint *)pt->GetpPlyTable(),
713 pt->GetnPlyEntries(), lon, lat);
714
715 if (bInside) {
716 if (pt->GetnAuxPlyEntries()) {
717 for (int k = 0; k < pt->GetnAuxPlyEntries(); k++) {
718 bool bAuxInside =
719 G_FloatPtInPolygon((MyFlPoint *)pt->GetpAuxPlyTableEntry(k),
720 pt->GetAuxCntTableEntry(k), lon, lat);
721 if (bAuxInside) return true;
722 ;
723 }
724
725 } else
726 return true;
727 }
728 }
729
730 return false;
731}
732
733//-------------------------------------------------------------------
734// Compare Chart Stacks
735//-------------------------------------------------------------------
736bool ChartDB::EqualStacks(ChartStack *pa, ChartStack *pb) {
737 if ((pa == 0) || (pb == 0)) return false;
738 if ((!pa->b_valid) || (!pb->b_valid)) return false;
739 if (pa->nEntry != pb->nEntry) return false;
740
741 for (int i = 0; i < pa->nEntry; i++) {
742 if (pa->GetDBIndex(i) != pb->GetDBIndex(i)) return false;
743 }
744
745 return true;
746}
747
748//-------------------------------------------------------------------
749// Copy Chart Stacks
750//-------------------------------------------------------------------
751bool ChartDB::CopyStack(ChartStack *pa, ChartStack *pb) {
752 if ((pa == 0) || (pb == 0)) return false;
753 pa->nEntry = pb->nEntry;
754
755 for (int i = 0; i < pa->nEntry; i++) pa->SetDBIndex(i, pb->GetDBIndex(i));
756
757 pa->CurrentStackEntry = pb->CurrentStackEntry;
758
759 pa->b_valid = pb->b_valid;
760
761 return true;
762}
763
764wxString ChartDB::GetFullPath(ChartStack *ps, int stackindex) {
765 int dbIndex = ps->GetDBIndex(stackindex);
766 return GetChartTableEntry(dbIndex).GetFullSystemPath();
767}
768
769//-------------------------------------------------------------------
770// Get PlyPoint from stack
771//-------------------------------------------------------------------
772
773int ChartDB::GetCSPlyPoint(ChartStack *ps, int stackindex, int plyindex,
774 float *lat, float *lon) {
775 int dbIndex = ps->GetDBIndex(stackindex);
776 wxASSERT(dbIndex >= 0);
777
778 const ChartTableEntry &entry = GetChartTableEntry(dbIndex);
779 if (entry.GetnPlyEntries()) {
780 float *fp = entry.GetpPlyTable();
781 fp += plyindex * 2;
782 *lat = *fp;
783 fp++;
784 *lon = *fp;
785 }
786
787 return entry.GetnPlyEntries();
788}
789
790//-------------------------------------------------------------------
791// Get Chart Scale
792//-------------------------------------------------------------------
793int ChartDB::GetStackChartScale(ChartStack *ps, int stackindex, char *buf,
794 int nbuf) {
795 int dbindex = ps->GetDBIndex(stackindex);
796 wxASSERT(dbindex >= 0);
797
798 const ChartTableEntry &entry = GetChartTableEntry(dbindex);
799 int sc = entry.GetScale();
800 if (buf) sprintf(buf, "%d", sc);
801
802 return sc;
803}
804
805//-------------------------------------------------------------------
806// Find ChartStack entry index corresponding to Full Path name, if present
807//-------------------------------------------------------------------
808int ChartDB::GetStackEntry(ChartStack *ps, wxString fp) {
809 for (int i = 0; i < ps->nEntry; i++) {
810 const ChartTableEntry &entry = GetChartTableEntry(ps->GetDBIndex(i));
811 if (fp.IsSameAs(entry.GetFullSystemPath())) return i;
812 }
813
814 return -1;
815}
816
817//-------------------------------------------------------------------
818// Get CSChart Type
819//-------------------------------------------------------------------
820ChartTypeEnum ChartDB::GetCSChartType(ChartStack *ps, int stackindex) {
821 if (IsValid()) {
822 int dbindex = ps->GetDBIndex(stackindex);
823 if (dbindex >= 0)
824 return (ChartTypeEnum)GetChartTableEntry(dbindex).GetChartType();
825 }
826 return CHART_TYPE_UNKNOWN;
827}
828
829ChartFamilyEnum ChartDB::GetCSChartFamily(ChartStack *ps, int stackindex) {
830 if (IsValid()) {
831 int dbindex = ps->GetDBIndex(stackindex);
832 if (dbindex >= 0) {
833 const ChartTableEntry &entry = GetChartTableEntry(dbindex);
834
835 ChartTypeEnum type = (ChartTypeEnum)entry.GetChartType();
836 switch (type) {
837 case CHART_TYPE_KAP:
838 return CHART_FAMILY_RASTER;
839 case CHART_TYPE_GEO:
840 return CHART_FAMILY_RASTER;
841 case CHART_TYPE_S57:
842 return CHART_FAMILY_VECTOR;
843 case CHART_TYPE_CM93:
844 return CHART_FAMILY_VECTOR;
845 case CHART_TYPE_CM93COMP:
846 return CHART_FAMILY_VECTOR;
847 case CHART_TYPE_DUMMY:
848 return CHART_FAMILY_RASTER;
849 default:
850 return CHART_FAMILY_UNKNOWN;
851 }
852 }
853 }
854 return CHART_FAMILY_UNKNOWN;
855}
856
857std::vector<int> ChartDB::GetCSArray(ChartStack *ps) {
858 std::vector<int> ret;
859
860 if (ps) {
861 ret.reserve(ps->nEntry);
862 for (int i = 0; i < ps->nEntry; i++) {
863 ret.push_back(ps->GetDBIndex(i));
864 }
865 }
866
867 return ret;
868}
869
870bool ChartDB::IsChartInCache(int dbindex) {
871 bool bInCache = false;
872
873 // Search the cache
874 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
875 unsigned int nCache = pChartCache->GetCount();
876 for (unsigned int i = 0; i < nCache; i++) {
877 CacheEntry *pce = (CacheEntry *)(pChartCache->Item(i));
878 if (pce->dbIndex == dbindex) {
879 if (pce->pChart != 0 && ((ChartBase *)pce->pChart)->IsReadyToRender())
880 bInCache = true;
881 break;
882 }
883 }
884 m_cache_mutex.Unlock();
885 }
886
887 return bInCache;
888}
889
890bool ChartDB::IsChartInCache(wxString path) {
891 bool bInCache = false;
892 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
893 // Search the cache
894 unsigned int nCache = pChartCache->GetCount();
895 for (unsigned int i = 0; i < nCache; i++) {
896 CacheEntry *pce = (CacheEntry *)(pChartCache->Item(i));
897 if (pce->FullPath == path) {
898 if (pce->pChart != 0 && ((ChartBase *)pce->pChart)->IsReadyToRender())
899 bInCache = true;
900 break;
901 }
902 }
903
904 m_cache_mutex.Unlock();
905 }
906 return bInCache;
907}
908
909bool ChartDB::IsChartLocked(int index) {
910 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
911 unsigned int nCache = pChartCache->GetCount();
912 for (unsigned int i = 0; i < nCache; i++) {
913 CacheEntry *pce = (CacheEntry *)(pChartCache->Item(i));
914 if (pce->dbIndex == index) {
915 bool ret = pce->n_lock > 0;
916 m_cache_mutex.Unlock();
917 return ret;
918 }
919 }
920 m_cache_mutex.Unlock();
921 }
922
923 return false;
924}
925
926bool ChartDB::LockCacheChart(int index) {
927 // Search the cache
928 bool ret = false;
929 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
930 unsigned int nCache = pChartCache->GetCount();
931 for (unsigned int i = 0; i < nCache; i++) {
932 CacheEntry *pce = (CacheEntry *)(pChartCache->Item(i));
933 if (pce->dbIndex == index) {
934 pce->n_lock++;
935 ret = true;
936 break;
937 }
938 }
939 m_cache_mutex.Unlock();
940 }
941 return ret;
942}
943
944void ChartDB::UnLockCacheChart(int index) {
945 // Search the cache
946 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
947 unsigned int nCache = pChartCache->GetCount();
948 for (unsigned int i = 0; i < nCache; i++) {
949 CacheEntry *pce = (CacheEntry *)(pChartCache->Item(i));
950 if (pce->dbIndex == index) {
951 if (pce->n_lock > 0) pce->n_lock--;
952 break;
953 }
954 }
955 m_cache_mutex.Unlock();
956 }
957}
958
959void ChartDB::UnLockAllCacheCharts() {
960 // Walk the cache
961 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
962 unsigned int nCache = pChartCache->GetCount();
963 for (unsigned int i = 0; i < nCache; i++) {
964 CacheEntry *pce = (CacheEntry *)(pChartCache->Item(i));
965 if (pce->n_lock > 0) pce->n_lock--;
966 }
967 m_cache_mutex.Unlock();
968 }
969}
970
971//-------------------------------------------------------------------
972// Open Chart
973//-------------------------------------------------------------------
974ChartBase *ChartDB::OpenChartFromDB(int index, ChartInitFlag init_flag) {
975 return OpenChartUsingCache(index, init_flag);
976}
977
978ChartBase *ChartDB::OpenChartFromDB(wxString chart_path,
979 ChartInitFlag init_flag) {
980 int dbii = FinddbIndex(chart_path);
981 return OpenChartUsingCache(dbii, init_flag);
982}
983
984ChartBase *ChartDB::OpenChartFromStack(ChartStack *pStack, int StackEntry,
985 ChartInitFlag init_flag) {
986 return OpenChartUsingCache(pStack->GetDBIndex(StackEntry), init_flag);
987}
988
989ChartBase *ChartDB::OpenChartFromDBAndLock(int index, ChartInitFlag init_flag,
990 bool lock) {
991 wxCriticalSectionLocker locker(m_critSect);
992 ChartBase *pret = OpenChartUsingCache(index, init_flag);
993 if (lock && pret) LockCacheChart(index);
994 return pret;
995}
996
997ChartBase *ChartDB::OpenChartFromDBAndLock(wxString chart_path,
998 ChartInitFlag init_flag) {
999 int dbii = FinddbIndex(chart_path);
1000 return OpenChartFromDBAndLock(dbii, init_flag);
1001}
1002
1003CacheEntry *ChartDB::FindOldestDeleteCandidate(bool blog) {
1004 CacheEntry *pret = 0;
1005
1006 unsigned int nCache = pChartCache->GetCount();
1007 if (nCache > 1) {
1008 if (blog) wxLogMessage(_T("Searching chart cache for oldest entry"));
1009 int LRUTime = m_ticks;
1010 int iOldest = 0;
1011 for (unsigned int i = 0; i < nCache; i++) {
1012 CacheEntry *pce = (CacheEntry *)(pChartCache->Item(i));
1013 if (pce->RecentTime < LRUTime && !pce->n_lock) {
1014 if (!isSingleChart((ChartBase *)(pce->pChart))) {
1015 LRUTime = pce->RecentTime;
1016 iOldest = i;
1017 }
1018 }
1019 }
1020 int dt = m_ticks - LRUTime;
1021
1022 CacheEntry *pce = (CacheEntry *)(pChartCache->Item(iOldest));
1023 ChartBase *pDeleteCandidate = (ChartBase *)(pce->pChart);
1024
1025 if (!pce->n_lock && !isSingleChart(pDeleteCandidate)) {
1026 if (blog)
1027 wxLogMessage(_T("Oldest unlocked cache index is %d, delta t is %d"),
1028 iOldest, dt);
1029
1030 pret = pce;
1031 } else
1032 wxLogMessage(_T("All chart in cache locked, size: %d"), nCache);
1033 }
1034
1035 return pret;
1036}
1037
1038ChartBase *ChartDB::OpenChartUsingCache(int dbindex, ChartInitFlag init_flag) {
1039 if ((dbindex < 0) || (dbindex > GetChartTableEntries() - 1)) return NULL;
1040
1041 // printf("Opening chart %d lock: %d\n", dbindex, m_b_locked);
1042
1043 const ChartTableEntry &cte = GetChartTableEntry(dbindex);
1044 wxString ChartFullPath = cte.GetFullSystemPath();
1045 ChartTypeEnum chart_type = (ChartTypeEnum)cte.GetChartType();
1046 ChartFamilyEnum chart_family = (ChartFamilyEnum)cte.GetChartFamily();
1047
1048 wxString msg1;
1049 msg1.Printf(_T("OpenChartUsingCache: type %d "), chart_type);
1050 // wxLogMessage(msg1 + ChartFullPath);
1051
1052 if (cte.GetLatMax() > 90.0) // Chart has been disabled...
1053 return NULL;
1054
1055 ChartBase *Ch = NULL;
1056 CacheEntry *pce = NULL;
1057 int old_lock = 0;
1058
1059 bool bInCache = false;
1060
1061 // Search the cache
1062 {
1063 wxMutexLocker lock(m_cache_mutex);
1064
1065 unsigned int nCache = pChartCache->GetCount();
1066 m_ticks++;
1067 for (unsigned int i = 0; i < nCache; i++) {
1068 pce = (CacheEntry *)(pChartCache->Item(i));
1069 if (pce->FullPath == ChartFullPath) {
1070 Ch = (ChartBase *)pce->pChart;
1071 bInCache = true;
1072 break;
1073 }
1074 }
1075
1076 if (bInCache) {
1077 wxString msg;
1078 msg.Printf(_T("OpenChartUsingCache, IN cache: cache size: %d\n"),
1079 (int)pChartCache->GetCount());
1080 // wxLogMessage(msg);
1081 if (FULL_INIT == init_flag) // asking for full init?
1082 {
1083 if (Ch->IsReadyToRender()) {
1084 if (pce) {
1085 pce->RecentTime = m_ticks; // chart is OK
1086 pce->b_in_use = true;
1087 }
1088 return Ch;
1089 } else {
1090 if (pthumbwin && pthumbwin->pThumbChart == Ch)
1091 pthumbwin->pThumbChart = NULL;
1092 delete Ch; // chart is not useable
1093 old_lock = pce->n_lock;
1094 pChartCache->Remove(pce); // so remove it
1095 delete pce;
1096
1097 bInCache = false;
1098 }
1099 } else // assume if in cache, the chart can do thumbnails
1100 {
1101 if (pce) {
1102 pce->RecentTime = m_ticks;
1103 pce->b_in_use = true;
1104 }
1105 return Ch;
1106 }
1107 }
1108
1109 if (!bInCache) // not in cache
1110 {
1111 m_b_busy = true;
1112 if (!m_b_locked) {
1113 // Use memory limited cache policy, if defined....
1114 if (g_memCacheLimit) {
1115 // Check memory status to see if enough room to open another chart
1116 int mem_used;
1117 GetMemoryStatus(0, &mem_used);
1118
1119 wxString msg;
1120 msg.Printf(
1121 _T("OpenChartUsingCache, NOT in cache: cache size: %d\n"),
1122 (int)pChartCache->GetCount());
1123 wxLogMessage(msg);
1124 wxString msg1;
1125 msg1.Printf(_T(" OpenChartUsingCache: type %d "), chart_type);
1126 wxLogMessage(msg1 + ChartFullPath);
1127
1128 if ((mem_used > g_memCacheLimit * 8 / 10) &&
1129 (pChartCache->GetCount() > 2)) {
1130 wxString msg(_T("Removing oldest chart from cache: "));
1131 while (1) {
1132 CacheEntry *pce = FindOldestDeleteCandidate(true);
1133 if (pce == 0) break; // no possible delete candidate
1134
1135 // purge texture cache, really need memory here
1136 DeleteCacheEntry(pce, true, msg);
1137
1138 GetMemoryStatus(0, &mem_used);
1139 if ((mem_used < g_memCacheLimit * 8 / 10) ||
1140 (pChartCache->GetCount() <= 2))
1141 break;
1142
1143 } // while
1144 }
1145 }
1146
1147 else // Use n chart cache policy, if memory-limit policy is not used
1148 {
1149 // Limit cache to n charts, tossing out the oldest when space is
1150 // needed
1151 unsigned int nCache = pChartCache->GetCount();
1152 if (nCache > (unsigned int)g_nCacheLimit && nCache > 2) {
1153 wxString msg(_T("Removing oldest chart from cache: "));
1154 while (nCache > (unsigned int)g_nCacheLimit) {
1155 CacheEntry *pce = FindOldestDeleteCandidate(true);
1156 if (pce == 0) break;
1157
1158 DeleteCacheEntry(pce, true, msg);
1159 nCache--;
1160 }
1161 }
1162 }
1163 }
1164 }
1165 } // unlock
1166
1167 if (!bInCache) // not in cache
1168 {
1169 wxLogMessage(_T("Creating new chart"));
1170
1171 if (chart_type == CHART_TYPE_KAP)
1172 Ch = new ChartKAP();
1173
1174 else if (chart_type == CHART_TYPE_GEO)
1175 Ch = new ChartGEO();
1176
1177 else if (chart_type == CHART_TYPE_MBTILES)
1178 Ch = new ChartMBTiles();
1179
1180 else if (chart_type == CHART_TYPE_S57) {
1181 LoadS57();
1182 Ch = new s57chart();
1183 s57chart *Chs57 = static_cast<s57chart *>(Ch);
1184
1185 Chs57->SetNativeScale(cte.GetScale());
1186
1187 // Explicitely set the chart extents from the database to
1188 // support the case wherein the SENC file has not yet been built
1189 Extent ext;
1190 ext.NLAT = cte.GetLatMax();
1191 ext.SLAT = cte.GetLatMin();
1192 ext.WLON = cte.GetLonMin();
1193 ext.ELON = cte.GetLonMax();
1194 Chs57->SetFullExtent(ext);
1195 }
1196
1197 else if (chart_type == CHART_TYPE_CM93) {
1198 LoadS57();
1199 Ch = new cm93chart();
1200 cm93chart *Chcm93 = static_cast<cm93chart *>(Ch);
1201
1202 Chcm93->SetNativeScale(cte.GetScale());
1203
1204 // Explicitely set the chart extents from the database to
1205 // support the case wherein the SENC file has not yet been built
1206 Extent ext;
1207 ext.NLAT = cte.GetLatMax();
1208 ext.SLAT = cte.GetLatMin();
1209 ext.WLON = cte.GetLonMin();
1210 ext.ELON = cte.GetLonMax();
1211 Chcm93->SetFullExtent(ext);
1212 }
1213
1214 else if (chart_type == CHART_TYPE_CM93COMP) {
1215 LoadS57();
1216 Ch = new cm93compchart();
1217
1218 cm93compchart *Chcm93 = static_cast<cm93compchart *>(Ch);
1219
1220 Chcm93->SetNativeScale(cte.GetScale());
1221
1222 // Explicitely set the chart extents from the database to
1223 // support the case wherein the SENC file has not yet been built
1224 Extent ext;
1225 ext.NLAT = cte.GetLatMax();
1226 ext.SLAT = cte.GetLatMin();
1227 ext.WLON = cte.GetLonMin();
1228 ext.ELON = cte.GetLonMax();
1229 Chcm93->SetFullExtent(ext);
1230 }
1231
1232 else if (chart_type == CHART_TYPE_PLUGIN) {
1233 wxFileName fn(ChartFullPath);
1234 wxString ext = fn.GetExt();
1235 ext.Prepend(_T("*."));
1236 wxString ext_upper = ext.MakeUpper();
1237 wxString ext_lower = ext.MakeLower();
1238 wxString chart_class_name;
1239
1240 // Search the array of chart class descriptors to find a match
1241 // bewteen the search mask and the the chart file extension
1242
1243 for (unsigned int i = 0; i < m_ChartClassDescriptorArray.GetCount();
1244 i++) {
1245 if (m_ChartClassDescriptorArray[i].m_descriptor_type ==
1246 PLUGIN_DESCRIPTOR) {
1247 if (m_ChartClassDescriptorArray[i].m_search_mask == ext_upper) {
1248 chart_class_name = m_ChartClassDescriptorArray[i].m_class_name;
1249 break;
1250 }
1251 if (m_ChartClassDescriptorArray[i].m_search_mask == ext_lower) {
1252 chart_class_name = m_ChartClassDescriptorArray[i].m_class_name;
1253 break;
1254 }
1255 if (ChartFullPath.Matches(
1256 m_ChartClassDescriptorArray.Item(i).m_search_mask)) {
1257 chart_class_name = m_ChartClassDescriptorArray.Item(i).m_class_name;
1258 break;
1259 }
1260 }
1261 }
1262
1263 // chart_class_name = cte.GetChartClassName();
1264 if (chart_class_name.Len()) {
1265 ChartPlugInWrapper *cpiw = new ChartPlugInWrapper(chart_class_name);
1266 Ch = (ChartBase *)cpiw;
1267 if (chart_family == CHART_FAMILY_VECTOR) LoadS57();
1268 }
1269 }
1270
1271 else {
1272 Ch = NULL;
1273 wxLogMessage(_T("Unknown chart type"));
1274 }
1275
1276 if (Ch) {
1277 InitReturn ir;
1278
1279 s52plib *plib = ps52plib;
1280 wxString msg_fn(ChartFullPath);
1281 msg_fn.Replace(_T("%"), _T("%%"));
1282
1283 // Vector charts need a PLIB for useful display....
1284 if ((chart_family != CHART_FAMILY_VECTOR) ||
1285 ((chart_family == CHART_FAMILY_VECTOR) && plib)) {
1286 wxLogMessage(
1287 wxString::Format(_T("Initializing Chart %s"), msg_fn.c_str()));
1288
1289 ir = Ch->Init(ChartFullPath, init_flag); // using the passed flag
1290 Ch->SetColorScheme(/*pParent->*/ GetColorScheme());
1291 } else {
1292 wxLogMessage(wxString::Format(
1293 _T(" No PLIB, Skipping vector chart %s"), msg_fn.c_str()));
1294
1295 ir = INIT_FAIL_REMOVE;
1296 }
1297
1298 if (INIT_OK == ir) {
1299 // always cache after a new chart has been created
1300 // or it may leak CacheEntry in createthumbnail
1301 // if(FULL_INIT == init_flag)
1302 {
1303 pce = new CacheEntry;
1304 pce->FullPath = ChartFullPath;
1305 pce->pChart = Ch;
1306 pce->dbIndex = dbindex;
1307 // printf(" Adding chart %d\n",
1308 // dbindex);
1309 pce->RecentTime = m_ticks;
1310 pce->n_lock = old_lock;
1311
1312 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
1313 pChartCache->Add((void *)pce);
1314 m_cache_mutex.Unlock();
1315 } else {
1316 delete pce;
1317 }
1318 }
1319
1320 // A performance optimization.
1321 // Hide this chart's MBTiles overlay on initial MBTile chart load, or
1322 // reload after cache purge. This can help avoid excessively long
1323 // startup and group switch time when large tilesets are in use. See
1324 // FS#2601 Further optimization: If any chart group being shown
1325 // contains only MBTiles, and the target file is less than 5 GB in
1326 // size,
1327 // then allow immediate opening. Otherwise, add this chart to the
1328 // "no-show" array for each chart.
1329 if (chart_type == CHART_TYPE_MBTILES) {
1330 wxFileName tileFile(ChartFullPath);
1331 // Size test for 5 GByte
1332 wxULongLong tileSizeMB = tileFile.GetSize() >> 20;
1333
1334 if (!CheckAnyCanvasExclusiveTileGroup() ||
1335 (tileSizeMB.GetLo() > 5000)) {
1336 // Check to see if the tile has been "clicked" in either canvas.
1337 // If so, do not add to no-show array again.
1338 bool b_clicked = false;
1339 canvasConfig *cc;
1340 ChartCanvas *canvas = NULL;
1341 switch (g_canvasConfig) {
1342 case 1:
1343 cc = g_canvasConfigArray.Item(0);
1344 if (cc) {
1345 ChartCanvas *canvas = cc->canvas;
1346 if (canvas)
1347 b_clicked |= canvas->IsTileOverlayIndexInYesShow(dbindex);
1348 }
1349 cc = g_canvasConfigArray.Item(1);
1350 if (cc) {
1351 ChartCanvas *canvas = cc->canvas;
1352 if (canvas)
1353 b_clicked |= canvas->IsTileOverlayIndexInYesShow(dbindex);
1354 }
1355 break;
1356 default:
1357 cc = g_canvasConfigArray.Item(0);
1358 if (cc) {
1359 ChartCanvas *canvas = cc->canvas;
1360 if (canvas)
1361 b_clicked |= canvas->IsTileOverlayIndexInYesShow(dbindex);
1362 }
1363 break;
1364 }
1365
1366 // Add to all canvas noshow arrays
1367 if (!b_clicked) {
1368 switch (g_canvasConfig) {
1369 case 1:
1370 cc = g_canvasConfigArray.Item(0);
1371 if (cc) {
1372 ChartCanvas *canvas = cc->canvas;
1373 if (canvas) canvas->AddTileOverlayIndexToNoShow(dbindex);
1374 }
1375 cc = g_canvasConfigArray.Item(1);
1376 if (cc) {
1377 ChartCanvas *canvas = cc->canvas;
1378 if (canvas) canvas->AddTileOverlayIndexToNoShow(dbindex);
1379 }
1380 break;
1381 default:
1382 cc = g_canvasConfigArray.Item(0);
1383 if (cc) {
1384 ChartCanvas *canvas = cc->canvas;
1385 if (canvas) canvas->AddTileOverlayIndexToNoShow(dbindex);
1386 }
1387 break;
1388 }
1389 }
1390 }
1391 }
1392 } else if (INIT_FAIL_REMOVE == ir) // some problem in chart Init()
1393 {
1394 wxLogMessage(wxString::Format(_T("Problem initializing Chart %s"),
1395 msg_fn.c_str()));
1396
1397 delete Ch;
1398 Ch = NULL;
1399
1400 // Mark this chart in the database, so that it will not be seen
1401 // during this run, but will stay in the database
1402 DisableChart(ChartFullPath);
1403 } else if ((INIT_FAIL_RETRY == ir) ||
1404 (INIT_FAIL_NOERROR ==
1405 ir)) // recoverable problem in chart Init()
1406 {
1407 wxLogMessage(wxString::Format(
1408 _T("Recoverable problem initializing Chart %s"), msg_fn.c_str()));
1409 delete Ch;
1410 Ch = NULL;
1411 }
1412
1413 if (INIT_OK != ir) {
1414 if (1 /*INIT_FAIL_NOERROR != ir*/) {
1415 wxLogMessage(
1416 wxString::Format(_T(" OpenChartFromStack... Error opening ")
1417 _T("chart %s ... return code %d"),
1418 msg_fn.c_str(), ir));
1419 }
1420 }
1421 }
1422
1423 m_b_busy = false;
1424
1425 return Ch;
1426 }
1427
1428 return NULL;
1429}
1430
1431//
1432bool ChartDB::DeleteCacheChart(ChartBase *pDeleteCandidate) {
1433 bool retval = false;
1434
1435 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
1436 if (!isSingleChart(pDeleteCandidate)) {
1437 // Find the chart in the cache
1438 CacheEntry *pce = NULL;
1439 for (unsigned int i = 0; i < pChartCache->GetCount(); i++) {
1440 pce = (CacheEntry *)(pChartCache->Item(i));
1441 if ((ChartBase *)(pce->pChart) == pDeleteCandidate) {
1442 break;
1443 }
1444 }
1445
1446 if (pce) {
1447 if (pce->n_lock > 0) pce->n_lock--;
1448
1449 if (pce->n_lock == 0) {
1450 DeleteCacheEntry(pce);
1451 retval = true;
1452 }
1453 }
1454 }
1455 m_cache_mutex.Unlock();
1456 }
1457
1458 return retval;
1459}
1460
1461/*
1462 */
1463void ChartDB::ApplyColorSchemeToCachedCharts(ColorScheme cs) {
1464 ChartBase *Ch;
1465 CacheEntry *pce;
1466 // Search the cache
1467
1468 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
1469 unsigned int nCache = pChartCache->GetCount();
1470 for (unsigned int i = 0; i < nCache; i++) {
1471 pce = (CacheEntry *)(pChartCache->Item(i));
1472 Ch = (ChartBase *)pce->pChart;
1473 if (Ch) Ch->SetColorScheme(cs, true);
1474 }
1475
1476 m_cache_mutex.Unlock();
1477 }
1478}
1479
1480//-------------------------------------------------------------------
1481// Open a chart from the stack with conditions
1482// a) Search Direction Start
1483// b) Requested Chart Type
1484//-------------------------------------------------------------------
1485
1486ChartBase *ChartDB::OpenStackChartConditional(
1487 ChartStack *ps, int index_start, bool bSearchDir, ChartTypeEnum New_Type,
1488 ChartFamilyEnum New_Family_Fallback) {
1489 int index;
1490
1491 int delta_index;
1492 ChartBase *ptc = NULL;
1493
1494 if (bSearchDir == 1)
1495 delta_index = -1;
1496
1497 else
1498 delta_index = 1;
1499
1500 index = index_start;
1501
1502 while ((index >= 0) && (index < ps->nEntry)) {
1503 ChartTypeEnum chart_type = (ChartTypeEnum)GetCSChartType(ps, index);
1504 if ((chart_type == New_Type) || (New_Type == CHART_TYPE_DONTCARE)) {
1505 ptc = OpenChartFromStack(ps, index);
1506 if (NULL != ptc) break;
1507 }
1508 index += delta_index;
1509 }
1510
1511 // Fallback, no useable chart of specified type found, so try for family
1512 // match
1513 if (NULL == ptc) {
1514 index = index_start;
1515
1516 while ((index >= 0) && (index < ps->nEntry)) {
1517 ChartFamilyEnum chart_family = GetCSChartFamily(ps, index);
1518 if (chart_family == New_Family_Fallback) {
1519 ptc = OpenChartFromStack(ps, index);
1520
1521 if (NULL != ptc) break;
1522 }
1523 index += delta_index;
1524 }
1525 }
1526
1527 return ptc;
1528}
1529
1530wxXmlDocument ChartDB::GetXMLDescription(int dbIndex, bool b_getGeom) {
1531 wxXmlDocument doc;
1532 if (!IsValid() || (dbIndex >= GetChartTableEntries())) return doc;
1533
1534 bool b_remove = !IsChartInCache(dbIndex);
1535
1536 wxXmlNode *pcell_node = NULL;
1537 wxXmlNode *node;
1538 wxXmlNode *tnode;
1539
1540 // Open the chart, without cacheing it
1541 ChartBase *pc = OpenChartFromDB(dbIndex, HEADER_ONLY);
1542 b_remove = !IsChartInCache(dbIndex);
1543 const ChartTableEntry &cte = GetChartTableEntry(dbIndex);
1544
1545 if (CHART_FAMILY_RASTER == (ChartFamilyEnum)cte.GetChartFamily()) {
1546 pcell_node = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "chart" ));
1547
1548 wxString path = GetDBChartFileName(dbIndex);
1549 node = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "path" ));
1550 pcell_node->AddChild(node);
1551 tnode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), path);
1552 node->AddChild(tnode);
1553
1554 wxFileName name(path);
1555 node = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "name" ));
1556 pcell_node->AddChild(node);
1557 tnode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), name.GetName());
1558 node->AddChild(tnode);
1559
1560 if (pc) {
1561 node = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "lname" ));
1562 pcell_node->AddChild(node);
1563 tnode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), pc->GetName());
1564 node->AddChild(tnode);
1565 }
1566
1567 wxString scale;
1568 scale.Printf(_T("%d"), cte.GetScale());
1569 node = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "cscale" ));
1570 pcell_node->AddChild(node);
1571 tnode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), scale);
1572 node->AddChild(tnode);
1573
1574 wxDateTime file_date(cte.GetFileTime());
1575 file_date.MakeUTC();
1576 wxString sfile_date = file_date.FormatISODate();
1577 sfile_date += _T("T");
1578 sfile_date += file_date.FormatISOTime();
1579 sfile_date += _T("Z");
1580 node =
1581 new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "local_file_datetime_iso8601" ));
1582 pcell_node->AddChild(node);
1583 tnode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), sfile_date);
1584 node->AddChild(tnode);
1585
1586 if (pc) {
1587 node = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "source_edition" ));
1588 pcell_node->AddChild(node);
1589 tnode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), pc->GetSE());
1590 node->AddChild(tnode);
1591
1592 wxDateTime sdt = pc->GetEditionDate();
1593 wxString ssdt = _T("Unknown");
1594 if (sdt.IsValid()) ssdt = sdt.Format(_T("%Y%m%d"));
1595
1596 node = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "source_date" ));
1597 pcell_node->AddChild(node);
1598 tnode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), ssdt);
1599 node->AddChild(tnode);
1600 }
1601
1602 /*
1603 if (s == _T("number"))
1605 if (s == _T("raster_edition"))
1606 if (s == _T("ntm_edition"))
1608 if (s == _T("ntm_date"))
1609 if (s == _T("source_edition_last_correction"))
1610 if (s == _T("raster_edition_last_correction"))
1611 if (s == _T("ntm_edition_last_correction"))
1612 */
1613 }
1614
1615 else if (CHART_FAMILY_VECTOR == (ChartFamilyEnum)cte.GetChartFamily()) {
1616 pcell_node = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "cell" ));
1617
1618 wxString path = GetDBChartFileName(dbIndex);
1619 node = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "path" ));
1620 pcell_node->AddChild(node);
1621 tnode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), path);
1622 node->AddChild(tnode);
1623
1624 wxFileName name(path);
1625 node = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "name" ));
1626 pcell_node->AddChild(node);
1627 tnode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), name.GetName());
1628 node->AddChild(tnode);
1629
1630 wxString scale;
1631 scale.Printf(_T("%d"), cte.GetScale());
1632 node = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "cscale" ));
1633 pcell_node->AddChild(node);
1634 tnode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), scale);
1635 node->AddChild(tnode);
1636
1637 wxDateTime file_date(cte.GetFileTime());
1638 file_date.MakeUTC();
1639 wxString sfile_date = file_date.FormatISODate();
1640 sfile_date += _T("T");
1641 sfile_date += file_date.FormatISOTime();
1642 sfile_date += _T("Z");
1643 node =
1644 new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "local_file_datetime_iso8601" ));
1645 pcell_node->AddChild(node);
1646 tnode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), sfile_date);
1647 node->AddChild(tnode);
1648
1649 if (pc) {
1650 node = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "edtn" ));
1651 pcell_node->AddChild(node);
1652 tnode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), pc->GetSE());
1653 node->AddChild(tnode);
1654 }
1655
1656 s57chart *pcs57 = dynamic_cast<s57chart *>(pc);
1657 if (pcs57) {
1658 node = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "isdt" ));
1659 pcell_node->AddChild(node);
1660 tnode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), pcs57->GetISDT());
1661 node->AddChild(tnode);
1662
1663 wxString LastUpdateDate;
1664 int updn =
1665 pcs57->ValidateAndCountUpdates(path, _T(""), LastUpdateDate, false);
1666
1667 wxString supdn;
1668 supdn.Printf(_T("%d"), updn);
1669 node = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "updn" ));
1670 pcell_node->AddChild(node);
1671 tnode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), supdn);
1672 node->AddChild(tnode);
1673
1674 node = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "uadt" ));
1675 pcell_node->AddChild(node);
1676 tnode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), LastUpdateDate);
1677 node->AddChild(tnode);
1678 }
1679 }
1680
1681 if (pcell_node && b_getGeom) {
1682 node = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "cov" ));
1683 pcell_node->AddChild(node);
1684
1685 // Primary table
1686 if (cte.GetnPlyEntries()) {
1687 wxXmlNode *panelnode = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "panel" ));
1688 node->AddChild(panelnode);
1689
1690 wxString panel_no;
1691 panel_no.Printf(_T("%d"), 0);
1692 wxXmlNode *anode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), panel_no);
1693 panelnode->AddChild(anode);
1694
1695 float *pf = cte.GetpPlyTable();
1696 for (int j = 0; j < cte.GetnPlyEntries(); j++) {
1697 wxXmlNode *vnode = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "vertex" ));
1698 panelnode->AddChild(vnode);
1699
1700 wxXmlNode *latnode = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "lat" ));
1701 vnode->AddChild(latnode);
1702
1703 float l = *pf++;
1704 wxString sl;
1705 sl.Printf(_T("%.5f"), l);
1706 wxXmlNode *vtnode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), sl);
1707 latnode->AddChild(vtnode);
1708
1709 wxXmlNode *lonnode = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "lon" ));
1710 vnode->AddChild(lonnode);
1711
1712 float ll = *pf++;
1713 wxString sll;
1714 sll.Printf(_T("%.5f"), ll);
1715 wxXmlNode *vtlnode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), sll);
1716 lonnode->AddChild(vtlnode);
1717 }
1718 }
1719
1720 for (int i = 0; i < cte.GetnAuxPlyEntries(); i++) {
1721 wxXmlNode *panelnode = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "panel" ));
1722 node->AddChild(panelnode);
1723
1724 wxString panel_no;
1725 panel_no.Printf(_T("%d"), i + 1);
1726 wxXmlNode *anode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), panel_no);
1727 panelnode->AddChild(anode);
1728
1729 float *pf = cte.GetpAuxPlyTableEntry(i);
1730 for (int j = 0; j < cte.GetAuxCntTableEntry(i); j++) {
1731 wxXmlNode *vnode = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "vertex" ));
1732 panelnode->AddChild(vnode);
1733
1734 wxXmlNode *latnode = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "lat" ));
1735 vnode->AddChild(latnode);
1736
1737 float l = *pf++;
1738 wxString sl;
1739 sl.Printf(_T("%.5f"), l);
1740 wxXmlNode *vtnode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), sl);
1741 latnode->AddChild(vtnode);
1742
1743 wxXmlNode *lonnode = new wxXmlNode(wxXML_ELEMENT_NODE, _T ( "lon" ));
1744 vnode->AddChild(lonnode);
1745
1746 float ll = *pf++;
1747 wxString sll;
1748 sll.Printf(_T("%.5f"), ll);
1749 wxXmlNode *vtlnode = new wxXmlNode(wxXML_TEXT_NODE, _T ( "" ), sll);
1750 lonnode->AddChild(vtlnode);
1751 }
1752 }
1753 }
1754
1755 doc.SetRoot(pcell_node);
1756
1757 if (b_remove) DeleteCacheChart(pc);
1758
1759 return doc;
1760}
1761
1762bool ChartDB::CheckExclusiveTileGroup(int canvasIndex) {
1763 // Return true if the group active in the passed canvasIndex has only MBTiles
1764 // present Also, populate the persistent member variables, so that subsequent
1765 // checks are very fast.
1766
1767 // Get the chart canvas indexed by canvasIndex
1768 canvasConfig *cc;
1769 ChartCanvas *canvas = NULL;
1770 switch (g_canvasConfig) {
1771 case 1:
1772 if (canvasIndex == 0) {
1773 cc = g_canvasConfigArray.Item(0);
1774 if (cc) canvas = cc->canvas;
1775 } else {
1776 cc = g_canvasConfigArray.Item(1);
1777 if (cc) canvas = cc->canvas;
1778 }
1779 break;
1780
1781 default:
1782 cc = g_canvasConfigArray.Item(0);
1783 if (cc) canvas = cc->canvas;
1784 }
1785
1786 if (!canvas) return false;
1787
1788 // This canvas group index already checked?
1789 if (canvas->m_groupIndex == m_checkGroupIndex[canvasIndex])
1790 return m_checkedTileOnly[canvasIndex];
1791
1792 // Check the group for anything other than MBTiles...
1793 bool rv = IsNonMBTileInGroup(canvas->m_groupIndex);
1794
1795 m_checkGroupIndex[canvasIndex] = canvas->m_groupIndex;
1796 m_checkedTileOnly[canvasIndex] = !rv;
1797
1798 return !rv; // true iff group has only MBTiles
1799}
1800
1801bool ChartDB::CheckAnyCanvasExclusiveTileGroup() {
1802 // Check to determine if any canvas group is exclusively MBTiles
1803 // if so, return true;
1804
1805 bool rv = false;
1806
1807 canvasConfig *cc;
1808 ChartCanvas *canvas = NULL;
1809 switch (g_canvasConfig) {
1810 case 1:
1811 cc = g_canvasConfigArray.Item(0);
1812 if (cc) {
1813 ChartCanvas *canvas = cc->canvas;
1814 if (canvas) {
1815 if (canvas->m_groupIndex == m_checkGroupIndex[0])
1816 rv |= m_checkedTileOnly[0];
1817 }
1818 }
1819
1820 cc = g_canvasConfigArray.Item(1);
1821 if (cc) {
1822 ChartCanvas *canvas = cc->canvas;
1823 if (canvas) {
1824 if (canvas->m_groupIndex == m_checkGroupIndex[1])
1825 rv |= m_checkedTileOnly[1];
1826 }
1827 }
1828 break;
1829
1830 default:
1831 cc = g_canvasConfigArray.Item(0);
1832 if (cc) {
1833 ChartCanvas *canvas = cc->canvas;
1834 if (canvas) {
1835 if (canvas->m_groupIndex == m_checkGroupIndex[0])
1836 rv |= m_checkedTileOnly[0];
1837 }
1838 }
1839 }
1840
1841 return rv;
1842}
1843
1844// Private version of PolyPt testing using floats instead of doubles
1845
1846bool Intersect(MyFlPoint p1, MyFlPoint p2, MyFlPoint p3, MyFlPoint p4);
1847int CCW(MyFlPoint p0, MyFlPoint p1, MyFlPoint p2);
1848
1849/*************************************************************************
1850
1851
1852 * FUNCTION: G_FloatPtInPolygon
1853 *
1854 * PURPOSE
1855 * This routine determines if the point passed is in the polygon. It uses
1856
1857 * the classical polygon hit-testing algorithm: a horizontal ray starting
1858
1859 * at the point is extended infinitely rightwards and the number of
1860 * polygon edges that intersect the ray are counted. If the number is odd,
1861 * the point is inside the polygon.
1862 *
1863 * RETURN VALUE
1864 * (bool) TRUE if the point is inside the polygon, FALSE if not.
1865 *************************************************************************/
1866
1867bool G_FloatPtInPolygon(MyFlPoint *rgpts, int wnumpts, float x, float y)
1868
1869{
1870 MyFlPoint *ppt, *ppt1;
1871 int i;
1872 MyFlPoint pt1, pt2, pt0;
1873 int wnumintsct = 0;
1874
1875 pt0.x = x;
1876 pt0.y = y;
1877
1878 pt1 = pt2 = pt0;
1879 pt2.x = 1.e6;
1880
1881 // Now go through each of the lines in the polygon and see if it
1882 // intersects
1883 for (i = 0, ppt = rgpts; i < wnumpts - 1; i++, ppt++) {
1884 ppt1 = ppt;
1885 ppt1++;
1886 if (Intersect(pt0, pt2, *ppt, *(ppt1))) wnumintsct++;
1887 }
1888
1889 // And the last line
1890 if (Intersect(pt0, pt2, *ppt, *rgpts)) wnumintsct++;
1891
1892 // return(wnumintsct&1);
1893
1894 // If result is false, check the degenerate case where test point lies
1895 // on a polygon endpoint
1896 if (!(wnumintsct & 1)) {
1897 for (i = 0, ppt = rgpts; i < wnumpts; i++, ppt++) {
1898 if (((*ppt).x == x) && ((*ppt).y == y)) return true;
1899 }
1900 } else
1901 return true;
1902
1903 return false;
1904}
1905
1906/*************************************************************************
1907
1908
1909 * FUNCTION: Intersect
1910 *
1911 * PURPOSE
1912 * Given two line segments, determine if they intersect.
1913 *
1914 * RETURN VALUE
1915 * TRUE if they intersect, FALSE if not.
1916 *************************************************************************/
1917
1918inline bool Intersect(MyFlPoint p1, MyFlPoint p2, MyFlPoint p3, MyFlPoint p4) {
1919 return (((CCW(p1, p2, p3) * CCW(p1, p2, p4)) <= 0) &&
1920 ((CCW(p3, p4, p1) * CCW(p3, p4, p2) <= 0)));
1921}
1922/*************************************************************************
1923
1924
1925 * FUNCTION: CCW (CounterClockWise)
1926 *
1927 * PURPOSE
1928 * Determines, given three points, if when travelling from the first to
1929 * the second to the third, we travel in a counterclockwise direction.
1930 *
1931 * RETURN VALUE
1932 * (int) 1 if the movement is in a counterclockwise direction, -1 if
1933 * not.
1934 *************************************************************************/
1935
1936inline int CCW(MyFlPoint p0, MyFlPoint p1, MyFlPoint p2) {
1937 float dx1, dx2;
1938 float dy1, dy2;
1939
1940 dx1 = p1.x - p0.x;
1941 dx2 = p2.x - p0.x;
1942 dy1 = p1.y - p0.y;
1943 dy2 = p2.y - p0.y;
1944
1945 /* This is basically a slope comparison: we don't do divisions because
1946
1947 * of divide by zero possibilities with pure horizontal and pure
1948 * vertical lines.
1949 */
1950 return ((dx1 * dy2 > dy1 * dx2) ? 1 : -1);
1951}
Definition: Quilt.cpp:864