OpenCPN Partial API docs
Loading...
Searching...
No Matches
chartdbs.cpp
Go to the documentation of this file.
1/**************************************************************************
2 * Copyright (C) 2010 by David S. Register *
3 * Copyright (C) 2010 by Mark A Sikes *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
17 **************************************************************************/
18
25#include "gl_headers.h"
26
27#include <wx/wxprec.h>
28
29#ifndef WX_PRECOMP
30#include <wx/wx.h>
31#endif
32
33#include <wx/arrimpl.cpp>
34#include <wx/dir.h>
35#include <wx/encconv.h>
36#include <wx/progdlg.h>
37#include <wx/regex.h>
38#include <wx/tokenzr.h>
39
40#include "chartbase.h"
41#include "chartdbs.h"
42#include "flex_hash.h"
43#include "LOD_reduce.h"
44#include "mbtiles.h"
45#include "pluginmanager.h"
46#include "shapefile_basemap.h"
47
48#ifndef UINT32
49#define UINT32 unsigned int
50#endif
51
52#ifdef __ANDROID__
53#include "androidUTIL.h"
54#endif
55
56ChartGroupArray *g_pGroupArray;
57
58static int s_dbVersion; // Database version currently in use at runtime
59 // Needed for ChartTableEntry::GetChartType() only
60 // TODO This can go away at opencpn Version 1.3.8 and
61 // above....
63
64static ChartFamilyEnum GetChartFamily(int charttype) {
65 ChartFamilyEnum cf;
66
67 switch (charttype) {
68 case CHART_TYPE_KAP:
69 cf = CHART_FAMILY_RASTER;
70 break;
71 case CHART_TYPE_GEO:
72 cf = CHART_FAMILY_RASTER;
73 break;
74 case CHART_TYPE_S57:
75 cf = CHART_FAMILY_VECTOR;
76 break;
77 case CHART_TYPE_CM93:
78 cf = CHART_FAMILY_VECTOR;
79 break;
80 case CHART_TYPE_CM93COMP:
81 cf = CHART_FAMILY_VECTOR;
82 break;
83 case CHART_TYPE_DUMMY:
84 cf = CHART_FAMILY_RASTER;
85 break;
86 case CHART_TYPE_UNKNOWN:
87 cf = CHART_FAMILY_UNKNOWN;
88 break;
89 default:
90 cf = CHART_FAMILY_UNKNOWN;
91 break;
92 }
93 return cf;
94}
95
97// ChartTableHeader
99
100void ChartTableHeader::Read(wxInputStream &is) {
101 is.Read(this, sizeof(ChartTableHeader));
102}
103
104void ChartTableHeader::Write(wxOutputStream &os) {
105 char vb[5];
106 sprintf(vb, "V%03d", DB_VERSION_CURRENT);
107
108 memcpy(dbVersion, vb, 4);
109 os.Write(this, sizeof(ChartTableHeader));
110}
111
112bool ChartTableHeader::CheckValid() {
113 char vb[5];
114 sprintf(vb, "V%03d", DB_VERSION_CURRENT);
115 if (strncmp(vb, dbVersion, sizeof(dbVersion))) {
116 wxString msg;
117 char vbo[5];
118 memcpy(vbo, dbVersion, 4);
119 vbo[4] = 0;
120 msg.Append(wxString(vbo, wxConvUTF8));
121 msg.Prepend(" Warning: found incorrect chart db version: ");
122 wxLogMessage(msg);
123
124 // return false; // no match....
125
126 // Try previous version....
127 sprintf(vb, "V%03d", DB_VERSION_PREVIOUS);
128 if (strncmp(vb, dbVersion, sizeof(dbVersion)))
129 return false;
130 else {
131 wxLogMessage(
132 " Scheduling db upgrade to current db version on "
133 "Options->Charts page visit...");
134 return true;
135 }
136
137 } else {
138 wxString msg;
139 char vbo[5];
140 memcpy(vbo, dbVersion, 4);
141 vbo[4] = 0;
142 msg.Append(wxString(vbo, wxConvUTF8));
143 msg.Prepend("Loading chart db version: ");
144 wxLogMessage(msg);
145 }
146
147 return true;
148}
149
151// ChartTableEntry
153
154void ChartTableEntry::SetScale(int scale) {
155 Scale = scale;
156 rounding = 0;
157 // XXX find the right rounding
158 if (Scale >= 1000) rounding = 5 * pow(10, log10(Scale) - 2);
159}
160
161ChartTableEntry::ChartTableEntry(ChartBase &theChart, wxString &utf8Path) {
162 Clear();
163
164 char *pt = (char *)malloc(strlen(utf8Path.mb_str(wxConvUTF8)) + 1);
165 strcpy(pt, utf8Path.mb_str(wxConvUTF8));
166 pFullPath = pt;
167
168 SetScale(theChart.GetNativeScale());
169
170 ChartType = theChart.GetChartType();
171 ChartFamily = theChart.GetChartFamily();
172
173 Skew = theChart.GetChartSkew();
174 ProjectionType = theChart.GetChartProjectionType();
175
176 wxDateTime ed = theChart.GetEditionDate();
177 if (theChart.GetEditionDate().IsValid())
178 edition_date = theChart.GetEditionDate().GetTicks();
179
180 wxFileName fn(theChart.GetFullPath());
181 if (fn.GetModificationTime().IsValid())
182 file_date = fn.GetModificationTime().GetTicks();
183
184 m_pfilename = new wxString; // create and populate helper members
185 *m_pfilename = fn.GetFullName();
186 m_psFullPath = new wxString;
187 *m_psFullPath = utf8Path;
188 m_fullSystemPath = utf8Path;
189 m_FullPath = std::string(pFullPath);
190
191#ifdef __ANDROID__
192 m_fullSystemPath = wxString(utf8Path.mb_str(wxConvUTF8));
193#endif
194
195 Extent ext;
196 theChart.GetChartExtent(&ext);
197 LatMax = ext.NLAT;
198 LatMin = ext.SLAT;
199 LonMin = ext.WLON;
200 LonMax = ext.ELON;
201
202 m_bbox.Set(LatMin, LonMin, LatMax, LonMax);
203
204 // Fill in the PLY information
205 // LOD calculation
206 int LOD_pixels = 1;
207 double scale_max_zoom = Scale / 4;
208
209 double display_ppm = 1 / .00025; // nominal for most LCD displays
210 double meters_per_pixel_max_scale = scale_max_zoom / display_ppm;
211 double LOD_meters = meters_per_pixel_max_scale * LOD_pixels;
212
213 // double LOD_meters = 5;
214
215 // If COVR table has only one entry, us it for the primary Ply Table
216 if (theChart.GetCOVREntries() == 1) {
217 nPlyEntries = theChart.GetCOVRTablePoints(0);
218
219 if (nPlyEntries > 5 && (LOD_meters > .01)) {
220 std::vector<int> index_keep{0, nPlyEntries - 1, 1, nPlyEntries - 2};
221
222 double *DPbuffer = (double *)malloc(2 * nPlyEntries * sizeof(double));
223
224 double *pfed = DPbuffer;
225 Plypoint *ppp = (Plypoint *)theChart.GetCOVRTableHead(0);
226
227 for (int i = 0; i < nPlyEntries; i++) {
228 *pfed++ = ppp->ltp;
229 *pfed++ = ppp->lnp;
230 ppp++;
231 }
232
233 DouglasPeucker(DPbuffer, 1, nPlyEntries - 2, LOD_meters / (1852 * 60),
234 &index_keep);
235 // printf("DB DP Reduction: %d/%d\n", index_keep.size(),
236 // nPlyEntries);
237
238 // Mark the keepers by adding a simple constant to ltp
239 for (unsigned int i = 0; i < index_keep.size(); i++) {
240 DPbuffer[2 * index_keep[i]] += 2000.;
241 }
242
243 float *pf = (float *)malloc(2 * index_keep.size() * sizeof(float));
244 float *pfe = pf;
245
246 for (int i = 0; i < nPlyEntries; i++) {
247 if (DPbuffer[2 * i] > 1000.) {
248 *pfe++ = DPbuffer[2 * i] - 2000.;
249 *pfe++ = DPbuffer[(2 * i) + 1];
250 }
251 }
252
253 pPlyTable = pf;
254 nPlyEntries = index_keep.size();
255 free(DPbuffer);
256 } else {
257 float *pf = (float *)malloc(2 * nPlyEntries * sizeof(float));
258 pPlyTable = pf;
259 float *pfe = pf;
260 Plypoint *ppp = (Plypoint *)theChart.GetCOVRTableHead(0);
261
262 for (int i = 0; i < nPlyEntries; i++) {
263 *pfe++ = ppp->ltp;
264 *pfe++ = ppp->lnp;
265 ppp++;
266 }
267 }
268 }
269 // Else create a rectangular primary Ply Table from the chart extents
270 // and create AuxPly table from the COVR tables
271 else {
272 // Create new artificial Ply table from chart extents
273 nPlyEntries = 4;
274 float *pf1 = (float *)malloc(2 * 4 * sizeof(float));
275 pPlyTable = pf1;
276 float *pfe = pf1;
277 Extent fext;
278 theChart.GetChartExtent(&fext);
279
280 *pfe++ = fext.NLAT; // LatMax;
281 *pfe++ = fext.WLON; // LonMin;
282
283 *pfe++ = fext.NLAT; // LatMax;
284 *pfe++ = fext.ELON; // LonMax;
285
286 *pfe++ = fext.SLAT; // LatMin;
287 *pfe++ = fext.ELON; // LonMax;
288
289 *pfe++ = fext.SLAT; // LatMin;
290 *pfe++ = fext.WLON; // LonMin;
291
292 // Fill in the structure for pAuxPlyTable
293
294 nAuxPlyEntries = theChart.GetCOVREntries();
295 wxASSERT(nAuxPlyEntries);
296 float **pfp = (float **)malloc(nAuxPlyEntries * sizeof(float *));
297 float **pft0 = pfp;
298 int *pip = (int *)malloc(nAuxPlyEntries * sizeof(int));
299
300 for (int j = 0; j < nAuxPlyEntries; j++) {
301 int nPE = theChart.GetCOVRTablePoints(j);
302
303 if (nPE > 5 && (LOD_meters > .01)) {
304 std::vector<int> index_keep{0, nPE - 1, 1, nPE - 2};
305
306 double *DPbuffer = (double *)malloc(2 * nPE * sizeof(double));
307
308 double *pfed = DPbuffer;
309 Plypoint *ppp = (Plypoint *)theChart.GetCOVRTableHead(j);
310
311 for (int i = 0; i < nPE; i++) {
312 *pfed++ = ppp->ltp;
313 *pfed++ = ppp->lnp;
314 ppp++;
315 }
316
317 DouglasPeucker(DPbuffer, 1, nPE - 2, LOD_meters / (1852 * 60),
318 &index_keep);
319 // printf("DBa DP Reduction: %d/%d\n",
320 // index_keep.size(), nPE);
321
322 // Mark the keepers by adding a simple constant to ltp
323 for (unsigned int i = 0; i < index_keep.size(); i++) {
324 DPbuffer[2 * index_keep[i]] += 2000.;
325 }
326
327 float *pf = (float *)malloc(2 * index_keep.size() * sizeof(float));
328 float *pfe1 = pf;
329
330 for (int i = 0; i < nPE; i++) {
331 if (DPbuffer[2 * i] > 1000.) {
332 *pfe1++ = DPbuffer[2 * i] - 2000.;
333 *pfe1++ = DPbuffer[(2 * i) + 1];
334 }
335 }
336
337 pft0[j] = pf;
338 pip[j] = index_keep.size();
339 free(DPbuffer);
340 } else {
341 float *pf_entry =
342 (float *)malloc(theChart.GetCOVRTablePoints(j) * 2 * sizeof(float));
343 memcpy(pf_entry, theChart.GetCOVRTableHead(j),
344 theChart.GetCOVRTablePoints(j) * 2 * sizeof(float));
345 pft0[j] = pf_entry;
346 pip[j] = theChart.GetCOVRTablePoints(j);
347 }
348 }
349
350 pAuxPlyTable = pfp;
351 pAuxCntTable = pip;
352 }
353
354 // Get and populate the NoCovr tables
355
356 nNoCovrPlyEntries = theChart.GetNoCOVREntries();
357 if (nNoCovrPlyEntries == 0) return;
358
359 float **pfpnc = (float **)malloc(nNoCovrPlyEntries * sizeof(float *));
360 float **pft0nc = pfpnc;
361 int *pipnc = (int *)malloc(nNoCovrPlyEntries * sizeof(int));
362
363 for (int j = 0; j < nNoCovrPlyEntries; j++) {
364 float *pf_entry =
365 (float *)malloc(theChart.GetNoCOVRTablePoints(j) * 2 * sizeof(float));
366 memcpy(pf_entry, theChart.GetNoCOVRTableHead(j),
367 theChart.GetNoCOVRTablePoints(j) * 2 * sizeof(float));
368 pft0nc[j] = pf_entry;
369 pipnc[j] = theChart.GetNoCOVRTablePoints(j);
370 }
371
372 pNoCovrPlyTable = pfpnc;
373 pNoCovrCntTable = pipnc;
374}
375
377
378ChartTableEntry::~ChartTableEntry() {
379 free(pFullPath);
380 free(pPlyTable);
381
382 for (int i = 0; i < nAuxPlyEntries; i++) free(pAuxPlyTable[i]);
383 free(pAuxPlyTable);
384 free(pAuxCntTable);
385
386 if (nNoCovrPlyEntries) {
387 for (int i = 0; i < nNoCovrPlyEntries; i++) free(pNoCovrPlyTable[i]);
388 free(pNoCovrPlyTable);
389 free(pNoCovrCntTable);
390 }
391
392 delete m_pfilename;
393 delete m_psFullPath;
394}
395
397
399
400bool ChartTableEntry::IsEarlierThan(const ChartTableEntry &cte) const {
401 wxDateTime mine(edition_date);
402 wxDateTime theirs(cte.edition_date);
403
404 if (!mine.IsValid() || !theirs.IsValid())
405 return false; // will have the effect of keeping all questionable charts
406
407 return (mine.IsEarlierThan(theirs));
408}
409
410bool ChartTableEntry::IsEqualTo(const ChartTableEntry &cte) const {
411 wxDateTime mine(edition_date);
412 wxDateTime theirs(cte.edition_date);
413
414 if (!mine.IsValid() || !theirs.IsValid())
415 return true; // will have the effect of keeping all questionable charts
416
417 return (mine.IsEqualTo(theirs));
418}
419
421
422static int convertChartType(int charttype) {
423 // Hackeroo here....
424 // dB version 14 had different ChartType Enum, patch it here
425 if (s_dbVersion == 14) {
426 switch (charttype) {
427 case 0:
428 return CHART_TYPE_KAP;
429 case 1:
430 return CHART_TYPE_GEO;
431 case 2:
432 return CHART_TYPE_S57;
433 case 3:
434 return CHART_TYPE_CM93;
435 case 4:
436 return CHART_TYPE_CM93COMP;
437 case 5:
438 return CHART_TYPE_UNKNOWN;
439 case 6:
440 return CHART_TYPE_DONTCARE;
441 case 7:
442 return CHART_TYPE_DUMMY;
443 default:
444 return CHART_TYPE_UNKNOWN;
445 }
446 }
447 return charttype;
448}
449
450static int convertChartFamily(int charttype, int chartfamily) {
451 if (s_dbVersion < 18) {
452 switch (charttype) {
453 case CHART_TYPE_KAP:
454 case CHART_TYPE_GEO:
455 return CHART_FAMILY_RASTER;
456
457 case CHART_TYPE_S57:
458 case CHART_TYPE_CM93:
459 case CHART_TYPE_CM93COMP:
460 return CHART_FAMILY_VECTOR;
461
462 default:
463 return CHART_FAMILY_UNKNOWN;
464 }
465 }
466 return chartfamily;
467}
468
469bool ChartTableEntry::Read(const ChartDatabase *pDb, wxInputStream &is) {
470 char path[4096], *cp;
471
472 Clear();
473
474 // Allow reading of current db format, and maybe others
475 ChartDatabase *pD = (ChartDatabase *)pDb;
476 int db_version = pD->GetVersion();
477
478 if (db_version == 18) {
479 // Read the path first
480 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
481 pFullPath = (char *)malloc(cp - path + 1);
482 strncpy(pFullPath, path, cp - path + 1);
483 wxLogVerbose(" Chart %s", pFullPath);
484
485 // Create and populate the helper members
486 m_pfilename = new wxString;
487 wxString fullfilename(pFullPath, wxConvUTF8);
488 wxFileName fn(fullfilename);
489 *m_pfilename = fn.GetFullName();
490 m_psFullPath = new wxString;
491 *m_psFullPath = fullfilename;
492 m_fullSystemPath = fullfilename;
493 m_FullPath = std::string(pFullPath);
494
495#ifdef __ANDROID__
496 m_fullSystemPath = wxString(fullfilename.mb_str(wxConvUTF8));
497#endif
498 // Read the table entry
500 is.Read(&cte, sizeof(ChartTableEntry_onDisk_18));
501
502 // Transcribe the elements....
503 EntryOffset = cte.EntryOffset;
504 ChartType = cte.ChartType;
505 ChartFamily = cte.ChartFamily;
506 LatMax = cte.LatMax;
507 LatMin = cte.LatMin;
508 LonMax = cte.LonMax;
509 LonMin = cte.LonMin;
510
511 m_bbox.Set(LatMin, LonMin, LatMax, LonMax);
512
513 Skew = cte.skew;
514 ProjectionType = cte.ProjectionType;
515
516 SetScale(cte.Scale);
517 edition_date = cte.edition_date;
518 file_date = cte.file_date;
519
520 nPlyEntries = cte.nPlyEntries;
521 nAuxPlyEntries = cte.nAuxPlyEntries;
522
523 nNoCovrPlyEntries = cte.nNoCovrPlyEntries;
524
525 bValid = cte.bValid;
526
527 if (nPlyEntries) {
528 int npeSize = nPlyEntries * 2 * sizeof(float);
529 pPlyTable = (float *)malloc(npeSize);
530 is.Read(pPlyTable, npeSize);
531 }
532
533 if (nAuxPlyEntries) {
534 int napeSize = nAuxPlyEntries * sizeof(int);
535 pAuxPlyTable = (float **)malloc(nAuxPlyEntries * sizeof(float *));
536 pAuxCntTable = (int *)malloc(napeSize);
537 is.Read(pAuxCntTable, napeSize);
538
539 for (int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
540 nAuxPlyEntry++) {
541 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 * sizeof(float);
542 pAuxPlyTable[nAuxPlyEntry] = (float *)malloc(nfSize);
543 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
544 }
545 }
546
547 if (nNoCovrPlyEntries) {
548 int napeSize = nNoCovrPlyEntries * sizeof(int);
549 pNoCovrCntTable = (int *)malloc(napeSize);
550 is.Read(pNoCovrCntTable, napeSize);
551
552 pNoCovrPlyTable = (float **)malloc(nNoCovrPlyEntries * sizeof(float *));
553 for (int i = 0; i < nNoCovrPlyEntries; i++) {
554 int nfSize = pNoCovrCntTable[i] * 2 * sizeof(float);
555 pNoCovrPlyTable[i] = (float *)malloc(nfSize);
556 is.Read(pNoCovrPlyTable[i], nfSize);
557 }
558 }
559 }
560
561 else if (db_version == 17) {
562 // Read the path first
563 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
564 pFullPath = (char *)malloc(cp - path + 1);
565 strncpy(pFullPath, path, cp - path + 1);
566 wxLogVerbose(" Chart %s", pFullPath);
567
568 // Create and populate the helper members
569 m_pfilename = new wxString;
570 wxString fullfilename(pFullPath, wxConvUTF8);
571 wxFileName fn(fullfilename);
572 *m_pfilename = fn.GetFullName();
573 m_psFullPath = new wxString;
574 *m_psFullPath = fullfilename;
575 m_FullPath = std::string(pFullPath);
576
577 // Read the table entry
579 is.Read(&cte, sizeof(ChartTableEntry_onDisk_17));
580
581 // Transcribe the elements....
582 EntryOffset = cte.EntryOffset;
583 ChartType = cte.ChartType;
584 LatMax = cte.LatMax;
585 LatMin = cte.LatMin;
586 LonMax = cte.LonMax;
587 LonMin = cte.LonMin;
588
589 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
590
591 Skew = cte.skew;
592 ProjectionType = cte.ProjectionType;
593
594 SetScale(cte.Scale);
595 edition_date = cte.edition_date;
596 file_date = cte.file_date;
597
598 nPlyEntries = cte.nPlyEntries;
599 nAuxPlyEntries = cte.nAuxPlyEntries;
600
601 nNoCovrPlyEntries = cte.nNoCovrPlyEntries;
602
603 bValid = cte.bValid;
604
605 if (nPlyEntries) {
606 int npeSize = nPlyEntries * 2 * sizeof(float);
607 pPlyTable = (float *)malloc(npeSize);
608 is.Read(pPlyTable, npeSize);
609 }
610
611 if (nAuxPlyEntries) {
612 int napeSize = nAuxPlyEntries * sizeof(int);
613 pAuxPlyTable = (float **)malloc(nAuxPlyEntries * sizeof(float *));
614 pAuxCntTable = (int *)malloc(napeSize);
615 is.Read(pAuxCntTable, napeSize);
616
617 for (int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
618 nAuxPlyEntry++) {
619 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 * sizeof(float);
620 pAuxPlyTable[nAuxPlyEntry] = (float *)malloc(nfSize);
621 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
622 }
623 }
624
625 if (nNoCovrPlyEntries) {
626 int napeSize = nNoCovrPlyEntries * sizeof(int);
627 pNoCovrCntTable = (int *)malloc(napeSize);
628 is.Read(pNoCovrCntTable, napeSize);
629
630 pNoCovrPlyTable = (float **)malloc(nNoCovrPlyEntries * sizeof(float *));
631 for (int i = 0; i < nNoCovrPlyEntries; i++) {
632 int nfSize = pNoCovrCntTable[i] * 2 * sizeof(float);
633 pNoCovrPlyTable[i] = (float *)malloc(nfSize);
634 is.Read(pNoCovrPlyTable[i], nfSize);
635 }
636 }
637 }
638
639 else if (db_version == 16) {
640 // Read the path first
641 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
642 // TODO: optimize prepended dir
643 pFullPath = (char *)malloc(cp - path + 1);
644 strncpy(pFullPath, path, cp - path + 1);
645 wxLogVerbose(" Chart %s", pFullPath);
646
647 // Create and populate the helper members
648 m_pfilename = new wxString;
649 wxString fullfilename(pFullPath, wxConvUTF8);
650 wxFileName fn(fullfilename);
651 *m_pfilename = fn.GetFullName();
652 m_psFullPath = new wxString;
653 *m_psFullPath = fullfilename;
654 m_FullPath = std::string(pFullPath);
655
656 // Read the table entry
658 is.Read(&cte, sizeof(ChartTableEntry_onDisk_16));
659
660 // Transcribe the elements....
661 EntryOffset = cte.EntryOffset;
662 ChartType = cte.ChartType;
663 LatMax = cte.LatMax;
664 LatMin = cte.LatMin;
665 LonMax = cte.LonMax;
666 LonMin = cte.LonMin;
667
668 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
669
670 Skew = cte.skew;
671 ProjectionType = cte.ProjectionType;
672
673 SetScale(cte.Scale);
674 edition_date = cte.edition_date;
675 file_date = cte.file_date;
676
677 nPlyEntries = cte.nPlyEntries;
678 nAuxPlyEntries = cte.nAuxPlyEntries;
679
680 bValid = cte.bValid;
681
682 if (nPlyEntries) {
683 int npeSize = nPlyEntries * 2 * sizeof(float);
684 pPlyTable = (float *)malloc(npeSize);
685 is.Read(pPlyTable, npeSize);
686 }
687
688 if (nAuxPlyEntries) {
689 int napeSize = nAuxPlyEntries * sizeof(int);
690 pAuxPlyTable = (float **)malloc(nAuxPlyEntries * sizeof(float *));
691 pAuxCntTable = (int *)malloc(napeSize);
692 is.Read(pAuxCntTable, napeSize);
693
694 for (int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
695 nAuxPlyEntry++) {
696 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 * sizeof(float);
697 pAuxPlyTable[nAuxPlyEntry] = (float *)malloc(nfSize);
698 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
699 }
700 }
701 }
702
703 else if (db_version == 15) {
704 // Read the path first
705 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
706 // TODO: optimize prepended dir
707 pFullPath = (char *)malloc(cp - path + 1);
708 strncpy(pFullPath, path, cp - path + 1);
709 wxLogVerbose(" Chart %s", pFullPath);
710
711 // Read the table entry
713 is.Read(&cte, sizeof(ChartTableEntry_onDisk_15));
714
715 // Transcribe the elements....
716 EntryOffset = cte.EntryOffset;
717 ChartType = cte.ChartType;
718 LatMax = cte.LatMax;
719 LatMin = cte.LatMin;
720 LonMax = cte.LonMax;
721 LonMin = cte.LonMin;
722
723 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
724
725 SetScale(cte.Scale);
726 edition_date = cte.edition_date;
727 file_date = cte.file_date;
728
729 nPlyEntries = cte.nPlyEntries;
730 nAuxPlyEntries = cte.nAuxPlyEntries;
731
732 bValid = cte.bValid;
733
734 if (nPlyEntries) {
735 int npeSize = nPlyEntries * 2 * sizeof(float);
736 pPlyTable = (float *)malloc(npeSize);
737 is.Read(pPlyTable, npeSize);
738 }
739
740 if (nAuxPlyEntries) {
741 int napeSize = nAuxPlyEntries * sizeof(int);
742 pAuxPlyTable = (float **)malloc(nAuxPlyEntries * sizeof(float *));
743 pAuxCntTable = (int *)malloc(napeSize);
744 is.Read(pAuxCntTable, napeSize);
745
746 for (int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
747 nAuxPlyEntry++) {
748 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 * sizeof(float);
749 pAuxPlyTable[nAuxPlyEntry] = (float *)malloc(nfSize);
750 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
751 }
752 }
753 } else if (db_version == 14) {
754 // Read the path first
755 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
756 pFullPath = (char *)malloc(cp - path + 1);
757 strncpy(pFullPath, path, cp - path + 1);
758 wxLogVerbose(" Chart %s", pFullPath);
759
760 // Read the table entry
762 is.Read(&cte, sizeof(ChartTableEntry_onDisk_14));
763
764 // Transcribe the elements....
765 EntryOffset = cte.EntryOffset;
766 ChartType = cte.ChartType;
767 LatMax = cte.LatMax;
768 LatMin = cte.LatMin;
769 LonMax = cte.LonMax;
770 LonMin = cte.LonMin;
771
772 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
773
774 SetScale(cte.Scale);
775 edition_date = cte.edition_date;
776 file_date = 0; // file_date does not exist in V14;
777 nPlyEntries = cte.nPlyEntries;
778 nAuxPlyEntries = cte.nAuxPlyEntries;
779 bValid = cte.bValid;
780
781 if (nPlyEntries) {
782 int npeSize = nPlyEntries * 2 * sizeof(float);
783 pPlyTable = (float *)malloc(npeSize);
784 is.Read(pPlyTable, npeSize);
785 }
786
787 if (nAuxPlyEntries) {
788 int napeSize = nAuxPlyEntries * sizeof(int);
789 pAuxPlyTable = (float **)malloc(nAuxPlyEntries * sizeof(float *));
790 pAuxCntTable = (int *)malloc(napeSize);
791 is.Read(pAuxCntTable, napeSize);
792
793 for (int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
794 nAuxPlyEntry++) {
795 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 * sizeof(float);
796 pAuxPlyTable[nAuxPlyEntry] = (float *)malloc(nfSize);
797 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
798 }
799 }
800 }
801 ChartFamily = convertChartFamily(ChartType, ChartFamily);
802 ChartType = convertChartType(ChartType);
803
804 return true;
805}
806
808
809bool ChartTableEntry::Write(const ChartDatabase *pDb, wxOutputStream &os) {
810 os.Write(pFullPath, strlen(pFullPath) + 1);
811
812 // Write the current version type only
813 // Create an on_disk table entry
815
816 // Transcribe the elements....
817 cte.EntryOffset = EntryOffset;
818 cte.ChartType = ChartType;
819 cte.ChartFamily = ChartFamily;
820 cte.LatMax = LatMax;
821 cte.LatMin = LatMin;
822 cte.LonMax = LonMax;
823 cte.LonMin = LonMin;
824
825 cte.Scale = Scale;
826 cte.edition_date = edition_date;
827 cte.file_date = file_date;
828
829 cte.nPlyEntries = nPlyEntries;
830 cte.nAuxPlyEntries = nAuxPlyEntries;
831
832 cte.skew = Skew;
833 cte.ProjectionType = ProjectionType;
834
835 cte.bValid = bValid;
836
837 cte.nNoCovrPlyEntries = nNoCovrPlyEntries;
838
839 os.Write(&cte, sizeof(ChartTableEntry_onDisk_18));
840 wxLogVerbose(" Wrote Chart %s", pFullPath);
841
842 // Write out the tables
843 if (nPlyEntries) {
844 int npeSize = nPlyEntries * 2 * sizeof(float);
845 os.Write(pPlyTable, npeSize);
846 }
847
848 if (nAuxPlyEntries) {
849 int napeSize = nAuxPlyEntries * sizeof(int);
850 os.Write(pAuxCntTable, napeSize);
851
852 for (int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries; nAuxPlyEntry++) {
853 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 * sizeof(float);
854 os.Write(pAuxPlyTable[nAuxPlyEntry], nfSize);
855 }
856 }
857
858 if (nNoCovrPlyEntries) {
859 int ncSize = nNoCovrPlyEntries * sizeof(int);
860 os.Write(pNoCovrCntTable, ncSize);
861
862 for (int i = 0; i < nNoCovrPlyEntries; i++) {
863 int nctSize = pNoCovrCntTable[i] * 2 * sizeof(float);
864 os.Write(pNoCovrPlyTable[i], nctSize);
865 }
866 }
867
868 return true;
869}
870
872
873void ChartTableEntry::Clear() {
874 // memset(this, 0, sizeof(ChartTableEntry));
875
876 pFullPath = NULL;
877 pPlyTable = NULL;
878 pAuxPlyTable = NULL;
879 pAuxCntTable = NULL;
880 bValid = false;
881 ;
882 pNoCovrCntTable = NULL;
883 pNoCovrPlyTable = NULL;
884
885 nNoCovrPlyEntries = 0;
886 nAuxPlyEntries = 0;
887
888 m_pfilename = NULL; // a helper member, not on disk
889 m_psFullPath = NULL;
890}
891
893
894void ChartTableEntry::Disable() {
895 // Mark this chart in the database, so that it will not be seen during this
896 // run How? By setting the chart bounding box to an absurd value
897 // TODO... Fix this heinous hack
898 LatMax += (float)1000.;
899 LatMin += (float)1000.;
900}
901
902void ChartTableEntry::ReEnable() {
903 if (LatMax > 90.) {
904 LatMax -= (float)1000.;
905 LatMin -= (float)1000.;
906 }
907}
908
909std::vector<float> ChartTableEntry::GetReducedPlyPoints() {
910 if (m_reducedPlyPoints.size()) return m_reducedPlyPoints;
911
912 // Reduce the LOD of the chart outline PlyPoints
913 float LOD_meters = 1;
914
915 float plylat, plylon;
916 const int nPoints = GetnPlyEntries();
917
918 float *fpo = GetpPlyTable();
919
920 double *ppd = new double[nPoints * 2];
921 double *ppsm = new double[nPoints * 2];
922 double *npr = ppd;
923 double *npsm = ppsm;
924 for (int i = 0; i < nPoints; i++) {
925 plylat = fpo[i * 2];
926 plylon = fpo[i * 2 + 1];
927
928 double x, y;
929 toSM(plylat, plylon, fpo[0], fpo[1], &x, &y);
930
931 *npr++ = plylon;
932 *npr++ = plylat;
933 *npsm++ = x;
934 *npsm++ = y;
935 }
936
937 std::vector<int> index_keep;
938 if (nPoints > 10) {
939 index_keep.push_back(0);
940 index_keep.push_back(nPoints - 1);
941 index_keep.push_back(1);
942 index_keep.push_back(nPoints - 2);
943
944 DouglasPeuckerM(ppsm, 1, nPoints - 2, LOD_meters, &index_keep);
945
946 } else {
947 index_keep.resize(nPoints);
948 for (int i = 0; i < nPoints; i++) index_keep[i] = i;
949 }
950
951 double *ppr = ppd;
952 for (int ip = 0; ip < nPoints; ip++) {
953 double x = *ppr++;
954 double y = *ppr++;
955
956 for (unsigned int j = 0; j < index_keep.size(); j++) {
957 if (index_keep[j] == ip) {
958 m_reducedPlyPoints.push_back(x);
959 m_reducedPlyPoints.push_back(y);
960 break;
961 }
962 }
963 }
964
965 delete[] ppd;
966 delete[] ppsm;
967
968 int nprr = m_reducedPlyPoints.size() / 2;
969
970 return m_reducedPlyPoints;
971}
972
973std::vector<float> ChartTableEntry::GetReducedAuxPlyPoints(int iTable) {
974 // Maybe need to initialize the vector
975 if (!m_reducedAuxPlyPointsVector.size()) {
976 std::vector<float> vec;
977 for (int i = 0; i < GetnAuxPlyEntries(); i++) {
978 m_reducedAuxPlyPointsVector.push_back(vec);
979 }
980 }
981
982 std::vector<float> vec;
983
984 // Invalid parameter
985 if ((unsigned int)iTable >= m_reducedAuxPlyPointsVector.size()) return vec;
986
987 if (m_reducedAuxPlyPointsVector.at(iTable).size())
988 return m_reducedAuxPlyPointsVector.at(iTable);
989
990 // Reduce the LOD of the chart outline PlyPoints
991 float LOD_meters = 1.0;
992
993 const int nPoints = GetAuxCntTableEntry(iTable);
994 float *fpo = GetpAuxPlyTableEntry(iTable);
995
996 double *ppd = new double[nPoints * 2];
997 double *ppsm = new double[nPoints * 2];
998 double *npr = ppd;
999 double *npsm = ppsm;
1000 float plylat, plylon;
1001
1002 for (int i = 0; i < nPoints; i++) {
1003 plylat = fpo[i * 2];
1004 plylon = fpo[i * 2 + 1];
1005
1006 double x, y;
1007 toSM(plylat, plylon, fpo[0], fpo[1], &x, &y);
1008
1009 *npr++ = plylon;
1010 *npr++ = plylat;
1011 *npsm++ = x;
1012 *npsm++ = y;
1013 }
1014
1015 std::vector<int> index_keep;
1016 if (nPoints > 10) {
1017 index_keep.push_back(0);
1018 index_keep.push_back(nPoints - 1);
1019 index_keep.push_back(1);
1020 index_keep.push_back(nPoints - 2);
1021
1022 DouglasPeuckerM(ppsm, 1, nPoints - 2, LOD_meters, &index_keep);
1023
1024 } else {
1025 index_keep.resize(nPoints);
1026 for (int i = 0; i < nPoints; i++) index_keep[i] = i;
1027 }
1028
1029 int nnn = index_keep.size();
1030
1031 double *ppr = ppd;
1032 for (int ip = 0; ip < nPoints; ip++) {
1033 double x = *ppr++;
1034 double y = *ppr++;
1035
1036 for (unsigned int j = 0; j < index_keep.size(); j++) {
1037 if (index_keep[j] == ip) {
1038 vec.push_back(x);
1039 vec.push_back(y);
1040 break;
1041 }
1042 }
1043 }
1044
1045 delete[] ppd;
1046 delete[] ppsm;
1047
1048 m_reducedAuxPlyPointsVector[iTable] = vec;
1049
1050 int nprr = vec.size() / 2;
1051
1052 return vec;
1053}
1054
1056// ChartDatabase
1058
1059WX_DEFINE_OBJARRAY(ChartTable);
1060
1061ChartDatabase::ChartDatabase() {
1062 bValid = false;
1063 m_b_busy = false;
1064
1065 m_ChartTableEntryDummy.Clear();
1066
1067 UpdateChartClassDescriptorArray();
1068}
1069
1070void ChartDatabase::UpdateChartClassDescriptorArray() {
1071 if (m_ChartClassDescriptorArray.empty()) {
1072 m_ChartClassDescriptorArray.push_back(
1073 ChartClassDescriptor("ChartKAP", "*.kap", BUILTIN_DESCRIPTOR));
1074 m_ChartClassDescriptorArray.push_back(
1075 ChartClassDescriptor("ChartGEO", "*.geo", BUILTIN_DESCRIPTOR));
1076 m_ChartClassDescriptorArray.push_back(
1077 ChartClassDescriptor("s57chart", "*.000", BUILTIN_DESCRIPTOR));
1078 m_ChartClassDescriptorArray.push_back(
1079 ChartClassDescriptor("s57chart", "*.s57", BUILTIN_DESCRIPTOR));
1080 m_ChartClassDescriptorArray.push_back(ChartClassDescriptor(
1081 "cm93compchart", "00300000.a", BUILTIN_DESCRIPTOR));
1082 m_ChartClassDescriptorArray.push_back(
1083 ChartClassDescriptor("ChartMbTiles", "*.mbtiles", BUILTIN_DESCRIPTOR));
1084 }
1085 // If the PlugIn Manager exists, get the array of dynamically loadable
1086 // chart class names
1087 if (g_pi_manager) {
1088 m_ChartClassDescriptorArray.erase(
1089 std::remove_if(m_ChartClassDescriptorArray.begin(),
1090 m_ChartClassDescriptorArray.end(),
1091 [](const ChartClassDescriptor &cd) {
1092 return cd.m_descriptor_type == PLUGIN_DESCRIPTOR;
1093 }),
1094 m_ChartClassDescriptorArray.end());
1095
1096 wxArrayString array = g_pi_manager->GetPlugInChartClassNameArray();
1097 for (unsigned int j = 0; j < array.GetCount(); j++) {
1098 // Instantiate a blank chart to retrieve the directory search mask for
1099 // this chart type
1100 wxString class_name = array[j];
1101 ChartPlugInWrapper *cpiw = new ChartPlugInWrapper(class_name);
1102 if (cpiw) {
1103 wxString mask = cpiw->GetFileSearchMask();
1104
1105 // Create a new descriptor and add it to the database
1106 m_ChartClassDescriptorArray.push_back(
1107 ChartClassDescriptor(class_name, mask, PLUGIN_DESCRIPTOR));
1108 delete cpiw;
1109 }
1110 }
1111 }
1112}
1113
1114const ChartTableEntry &ChartDatabase::GetChartTableEntry(int index) const {
1115 if (index < GetChartTableEntries())
1116 return active_chartTable[index];
1117 else
1118 return m_ChartTableEntryDummy;
1119}
1120
1121ChartTableEntry *ChartDatabase::GetpChartTableEntry(int index) const {
1122 if (index < GetChartTableEntries())
1123 return &active_chartTable[index];
1124 else
1125 return (ChartTableEntry *)&m_ChartTableEntryDummy;
1126}
1127
1128bool ChartDatabase::CompareChartDirArray(ArrayOfCDI &test_array) {
1129 // Compare the parameter "test_array" with this.m_dir_array
1130 // Return true if functionally identical (order does not signify).
1131
1132 if (test_array.GetCount() != m_dir_array.GetCount()) return false;
1133
1134 bool bfound_inner;
1135 unsigned int nfound_outer = 0;
1136
1137 for (unsigned int i = 0; i < test_array.GetCount(); i++) {
1138 ChartDirInfo p = test_array[i];
1139 bfound_inner = false;
1140 for (unsigned int j = 0; j < m_dir_array.GetCount(); j++) {
1141 ChartDirInfo q = m_dir_array[j];
1142
1143 if (p.fullpath.IsSameAs(q.fullpath)) {
1144 bfound_inner = true;
1145 break;
1146 }
1147 }
1148 if (bfound_inner) nfound_outer++;
1149 }
1150
1151 return (nfound_outer == test_array.GetCount());
1152}
1153
1154wxString ChartDatabase::GetMagicNumberCached(wxString dir) {
1155 for (unsigned int j = 0; j < m_dir_array.GetCount(); j++) {
1156 ChartDirInfo q = m_dir_array[j];
1157 if (dir.IsSameAs(q.fullpath)) return q.magic_number;
1158 }
1159
1160 return "";
1161}
1162
1163bool ChartDatabase::Read(const wxString &filePath) {
1164 ChartTableEntry entry;
1165 int entries;
1166
1167 bValid = false;
1168
1169 wxFileName file(filePath);
1170 if (!file.FileExists()) return false;
1171
1172 m_DBFileName = filePath;
1173
1174 wxFFileInputStream ifs(filePath);
1175 if (!ifs.Ok()) return false;
1176
1177 ChartTableHeader cth;
1178 cth.Read(ifs);
1179 if (!cth.CheckValid()) return false;
1180
1181 // Capture the version number
1182 char vbo[5];
1183 memcpy(vbo, cth.GetDBVersionString(), 4);
1184 vbo[4] = 0;
1185 m_dbversion = atoi(&vbo[1]);
1186 s_dbVersion = m_dbversion; // save the static copy
1187
1188 wxLogVerbose("Chartdb:Reading %d directory entries, %d table entries",
1189 cth.GetDirEntries(), cth.GetTableEntries());
1190 wxLogMessage("Chartdb: Chart directory list follows");
1191 if (0 == cth.GetDirEntries()) wxLogMessage(" Nil");
1192
1193 int ind = 0;
1194 for (int iDir = 0; iDir < cth.GetDirEntries(); iDir++) {
1195 wxString dir;
1196 int dirlen;
1197 ifs.Read(&dirlen, sizeof(int));
1198 while (dirlen > 0) {
1199 char dirbuf[1024];
1200 int alen = dirlen > 1023 ? 1023 : dirlen;
1201 if (ifs.Read(&dirbuf, alen).Eof()) goto read_error;
1202 dirbuf[alen] = 0;
1203 dirlen -= alen;
1204 dir.Append(wxString(dirbuf, wxConvUTF8));
1205 }
1206 wxString msg;
1207 msg.Printf(" Chart directory #%d: ", iDir);
1208 msg.Append(dir);
1209 wxLogMessage(msg);
1210 m_chartDirs.Add(dir);
1211 }
1212
1213 entries = cth.GetTableEntries();
1214 active_chartTable.Alloc(entries);
1215 active_chartTable_pathindex.clear();
1216 while (entries-- && entry.Read(this, ifs)) {
1217 active_chartTable_pathindex[entry.GetFullSystemPath()] = ind++;
1218 active_chartTable.Add(entry);
1219 }
1220
1221 entry.Clear();
1222 bValid = true;
1223 entry.SetAvailable(true);
1224
1225 m_nentries = active_chartTable.GetCount();
1226 return true;
1227
1228read_error:
1229 bValid = false;
1230 m_nentries = active_chartTable.GetCount();
1231 return false;
1232}
1233
1235
1236bool ChartDatabase::Write(const wxString &filePath) {
1237 wxFileName file(filePath);
1238 wxFileName dir(
1239 file.GetPath(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME, wxPATH_NATIVE));
1240
1241 if (!dir.DirExists() && !dir.Mkdir()) return false;
1242
1243 wxFFileOutputStream ofs(filePath);
1244 if (!ofs.Ok()) return false;
1245
1246 ChartTableHeader cth(m_chartDirs.GetCount(), active_chartTable.GetCount());
1247 cth.Write(ofs);
1248
1249 for (int iDir = 0; iDir < cth.GetDirEntries(); iDir++) {
1250 wxString dir1 = m_chartDirs[iDir];
1251 int dirlen = dir1.length();
1252 char s[200];
1253 strncpy(s, dir1.mb_str(wxConvUTF8), 199);
1254 s[199] = 0;
1255 dirlen = strlen(s);
1256 ofs.Write(&dirlen, sizeof(int));
1257 // ofs.Write(dir.fn_str(), dirlen);
1258 ofs.Write(s, dirlen);
1259 }
1260
1261 for (UINT32 iTable = 0; iTable < active_chartTable.size(); iTable++)
1262 active_chartTable[iTable].Write(this, ofs);
1263
1264 // Explicitly set the version
1265 m_dbversion = DB_VERSION_CURRENT;
1266
1267 return true;
1268}
1269
1271wxString SplitPath(wxString s, wxString tkd, int nchar, int offset,
1272 int *pn_split) {
1273 wxString r;
1274 int ncr = 0;
1275
1276 int rlen = offset;
1277 wxStringTokenizer tkz(s, tkd);
1278 while (tkz.HasMoreTokens()) {
1279 wxString token = tkz.GetNextToken();
1280 if ((rlen + (int)token.Len() + 1) < nchar) {
1281 r += token;
1282 r += tkd[0];
1283 rlen += token.Len() + 1;
1284 } else {
1285 r += "\n";
1286 ncr++;
1287 for (int i = 0; i < offset; i++) {
1288 r += " ";
1289 }
1290 r += token;
1291 r += tkd[0];
1292 rlen = offset + token.Len() + 1;
1293 }
1294 }
1295
1296 if (pn_split) *pn_split = ncr;
1297
1298 return r.Mid(0, r.Len() - 1); // strip the last separator char
1299}
1300
1301wxString ChartDatabase::GetFullChartInfo(ChartBase *pc, int dbIndex,
1302 int *char_width, int *line_count) {
1303 wxString r;
1304 int lc = 0;
1305 unsigned int max_width = 0;
1306 int ncr;
1307 unsigned int target_width = 60;
1308
1309 const ChartTableEntry &cte = GetChartTableEntry(dbIndex);
1310 wxString line;
1311 line.Empty();
1312 if (pc) {
1313 line = _(" Name: ");
1314 wxString longline = pc->GetName();
1315
1316 wxString tkz;
1317 if (longline.Find(' ') != wxNOT_FOUND) // assume a proper name
1318 tkz = " ";
1319 else
1320 tkz = "/,\\"; // else a file name
1321
1322 if (longline.Len() > target_width) {
1323 line += SplitPath(pc->GetName(), tkz, target_width, 12, &ncr);
1324 max_width = wxMax(max_width, target_width + 4);
1325 lc += ncr;
1326 } else {
1327 line += longline;
1328 max_width = wxMax(max_width, line.Len() + 4);
1329 }
1330 }
1331
1332 line += "\n";
1333 r += line;
1334 lc++;
1335
1336 if (pc) // chart is loaded and available
1337 line.Printf(" %s: 1:%d", _("Scale"), pc->GetNativeScale());
1338 else
1339 line.Printf(" %s: 1:%d", _("Scale"), cte.GetScale());
1340
1341 line += "\n";
1342 max_width = wxMax(max_width, line.Len());
1343 r += line;
1344 lc++;
1345 if (pc) {
1346 wxDateTime ed = pc->GetEditionDate();
1347 if (ed.IsValid()) {
1348 line = _(" Updated: ") + ed.FormatISODate() + "\n";
1349 max_width = wxMax(max_width, line.Len());
1350 r += line;
1351 }
1352 lc++;
1353
1354 line = _(" Source Edition: ") + pc->GetSE() + "\n";
1355 max_width = wxMax(max_width, line.Len());
1356 r += line;
1357 lc++;
1358 }
1359
1360 if (pc) {
1361 line = _(" Depth Units: ") + pc->GetDepthUnits() + "\n";
1362 max_width = wxMax(max_width, line.Len());
1363 r += line;
1364 lc++;
1365
1366 line = _(" Soundings: ") + pc->GetSoundingsDatum() + "\n";
1367 max_width = wxMax(max_width, line.Len());
1368 r += line;
1369 lc++;
1370
1371 line = _(" Datum: ") + pc->GetDatumString() + "\n";
1372 max_width = wxMax(max_width, line.Len());
1373 r += line;
1374 lc++;
1375 }
1376
1377 line = _(" Projection: ");
1378 if (PROJECTION_UNKNOWN == cte.GetChartProjectionType())
1379 line += _("Unknown");
1380 else if (PROJECTION_MERCATOR == cte.GetChartProjectionType())
1381 line += _("Mercator");
1382 else if (PROJECTION_TRANSVERSE_MERCATOR == cte.GetChartProjectionType())
1383 line += _("Transverse Mercator");
1384 else if (PROJECTION_POLYCONIC == cte.GetChartProjectionType())
1385 line += _("Polyconic");
1386 else if (PROJECTION_WEB_MERCATOR == cte.GetChartProjectionType())
1387 line += _("Web Mercator (EPSG:3857)");
1388 line += "\n";
1389 max_width = wxMax(max_width, line.Len());
1390 r += line;
1391 lc++;
1392
1393 line.Empty();
1394 if (pc) {
1395 }
1396
1397 line.Empty();
1398 if (pc && pc->GetExtraInfo().Len()) {
1399 line += pc->GetExtraInfo();
1400 line += "\n";
1401 max_width = wxMax(max_width, line.Len());
1402 r += line;
1403 lc++;
1404 }
1405 if (pc) {
1406 line.Empty();
1407 line = _(" ID: ");
1408 line += pc->GetID();
1409 line += "\n";
1410 max_width = wxMax(max_width, line.Len());
1411 r += line;
1412 lc++;
1413 }
1414
1415 line = _(" ChartFile: ");
1416 wxString longline = *(cte.GetpsFullPath());
1417 if (longline.Len() > target_width) {
1418 line += SplitPath(longline, "/,\\", target_width, 15, &ncr);
1419 max_width = wxMax(max_width, target_width + 4);
1420 lc += ncr;
1421 } else {
1422 line += longline;
1423 max_width = wxMax(max_width, line.Len() + 4);
1424 }
1425 r += line;
1426 r += "\n";
1427 lc++;
1428
1429 if (line_count) *line_count = lc;
1430
1431 if (char_width) *char_width = max_width;
1432
1433 return r;
1434}
1435
1436// ----------------------------------------------------------------------------
1437// Create Chart Table Database by directory search
1438// resulting in valid pChartTable in (this)
1439// ----------------------------------------------------------------------------
1440bool ChartDatabase::Create(ArrayOfCDI &dir_array,
1441 wxGenericProgressDialog *pprog) {
1442 m_dir_array = dir_array;
1443
1444 bValid = false;
1445
1446 m_chartDirs.Clear();
1447 active_chartTable.Clear();
1448 active_chartTable_pathindex.clear();
1449
1450 Update(dir_array, true, pprog); // force the update the reload everything
1451
1452 bValid = true;
1453
1454 // Explicitly set the version
1455 m_dbversion = DB_VERSION_CURRENT;
1456
1457 return true;
1458}
1459
1460/*
1461 * Traverse a directory recursively and find the GSHHG directory
1462 * that contains GSHHG data files.
1463 */
1464class GshhsTraverser : public wxDirTraverser {
1465public:
1466 GshhsTraverser() {}
1467 virtual wxDirTraverseResult OnFile(const wxString &filename) override {
1468 wxFileName fn(filename);
1469 wxFileName dir(fn.GetPath());
1470 if (fn.GetFullName().Matches("poly-*-1.dat") &&
1471 dir.GetFullName().IsSameAs("GSHHG", false)) {
1472 parent_dir = fn.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
1473 return wxDIR_STOP;
1474 }
1475 return wxDIR_CONTINUE;
1476 }
1477 virtual wxDirTraverseResult OnDir(const wxString &dirname) override {
1478 // Always recurse into directories.
1479 return wxDIR_CONTINUE;
1480 }
1481 wxString GetGshhsDir() const { return parent_dir; }
1482
1483private:
1484 wxString parent_dir;
1485};
1486
1487/*
1488 * Find and return the full path a of directory containing GSHHG data files.
1489 * Search recursively starting from directory.
1490 */
1491wxString findGshhgDirectory(const wxString &directory) {
1492 wxDir dir(directory);
1493 if (!dir.IsOpened()) {
1494 return wxEmptyString;
1495 }
1496 GshhsTraverser traverser;
1497 dir.Traverse(traverser, wxEmptyString, wxDIR_FILES | wxDIR_DIRS);
1498 return traverser.GetGshhsDir();
1499}
1500
1501// ----------------------------------------------------------------------------
1502// Update existing ChartTable Database by directory search
1503// resulting in valid pChartTable in (this)
1504// ----------------------------------------------------------------------------
1505bool ChartDatabase::Update(ArrayOfCDI &dir_array, bool bForce,
1506 wxGenericProgressDialog *pprog) {
1507 m_dir_array = dir_array;
1508
1509 bValid = false; // database is not useable right now...
1510 m_b_busy = true;
1511
1512 // Mark all charts provisionally invalid
1513 for (unsigned int i = 0; i < active_chartTable.GetCount(); i++)
1514 active_chartTable[i].SetValid(false);
1515
1516 m_chartDirs.Clear();
1517
1518 if (bForce) active_chartTable.Clear();
1519
1520 bool lbForce = bForce;
1521
1522 // Do a dB Version upgrade if the current one is obsolete
1523 if (s_dbVersion != DB_VERSION_CURRENT) {
1524 active_chartTable.Clear();
1525 lbForce = true;
1526 s_dbVersion = DB_VERSION_CURRENT; // Update the static indicator
1527 m_dbversion = DB_VERSION_CURRENT; // and the member
1528 }
1529
1530 // Get the new charts
1531
1532 for (unsigned int j = 0; j < dir_array.GetCount(); j++) {
1533 ChartDirInfo dir_info = dir_array[j];
1534
1535 // On Android, with SDK >= 30, traversal of a folder that is
1536 // on within the "scoped storage" domain is very slow.
1537 // Aviod it....
1538#ifdef __ANDROID__
1539 if (!androidIsDirWritable(dir_info.fullpath)) continue;
1540#endif
1541
1542 wxString dir_magic;
1543 // Recursively search for a directory that contains GSHHG files starting
1544 // from dir_info.
1545 wxString gshhg_dir = findGshhgDirectory(dir_info.fullpath);
1546 if (!gshhg_dir.empty()) {
1547 // If some polygons exist in the directory, set it as the one to use for
1548 // GSHHG
1549 // TODO: We should probably compare the version and maybe resolutions
1550 // available with what is currently used...
1551 wxLogMessage("Updating GSHHG directory: %s", gshhg_dir.c_str());
1552 gWorldMapLocation = gshhg_dir;
1553 }
1554 if (dir_info.fullpath.Find("OSMSHP") != wxNOT_FOUND) {
1555 if (!wxDir::FindFirst(dir_info.fullpath, "basemap_*.shp").empty()) {
1556 gWorldShapefileLocation =
1557 dir_info.fullpath + wxFileName::GetPathSeparator();
1558 gShapeBasemap.Reset();
1559 }
1560 }
1561
1562 TraverseDirAndAddCharts(dir_info, pprog, dir_magic, lbForce);
1563
1564 // Update the dir_list entry, even if the magic values are the same
1565
1566 dir_info.magic_number = dir_magic;
1567 dir_array.RemoveAt(j);
1568 dir_array.Insert(dir_info, j);
1569
1570 m_chartDirs.Add(dir_info.fullpath);
1571 } // for
1572
1573 for (unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
1574 if (!active_chartTable[i].GetbValid()) {
1575 active_chartTable.RemoveAt(i);
1576 i--; // entry is gone, recheck this index for next entry
1577 }
1578 }
1579
1580 // And once more, setting the Entry index field
1581 active_chartTable_pathindex.clear();
1582 for (unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
1583 active_chartTable_pathindex[active_chartTable[i].GetFullSystemPath()] = i;
1584 active_chartTable[i].SetEntryOffset(i);
1585 }
1586
1587 m_nentries = active_chartTable.GetCount();
1588
1589 bValid = true;
1590 m_b_busy = false;
1591 return true;
1592}
1593
1594//-------------------------------------------------------------------
1595// Find Chart dbIndex
1596//-------------------------------------------------------------------
1597
1598int ChartDatabase::FinddbIndex(wxString PathToFind) {
1599#if 0
1600 // Find the chart
1601 for(unsigned int i=0 ; i<active_chartTable.GetCount() ; i++)
1602 {
1603 if(active_chartTable[i].GetpsFullPath()->IsSameAs(PathToFind))
1604 {
1605 return i;
1606 }
1607 }
1608#else
1609 if (active_chartTable_pathindex.find(PathToFind) !=
1610 active_chartTable_pathindex.end())
1611 return active_chartTable_pathindex[PathToFind];
1612#endif
1613
1614 return -1;
1615}
1616
1617//-------------------------------------------------------------------
1618// Disable Chart
1619//-------------------------------------------------------------------
1620
1621int ChartDatabase::DisableChart(wxString &PathToDisable) {
1622 int index = FinddbIndex(PathToDisable);
1623 if (index != -1) {
1624 ChartTableEntry *pentry = &active_chartTable[index];
1625 pentry->Disable();
1626 return 1;
1627 }
1628 return 0;
1629}
1630
1631// ----------------------------------------------------------------------------
1632// Traverse the given directory looking for charts
1633// If bupdate is true, also search the existing database for a name match.
1634// If target chart is already in database, mark the entry valid and skip
1635// additional processing
1636// ----------------------------------------------------------------------------
1637
1638int ChartDatabase::TraverseDirAndAddCharts(ChartDirInfo &dir_info,
1639 wxGenericProgressDialog *pprog,
1640 wxString &dir_magic, bool bForce) {
1641 // Extract the true dir name and magic number from the compound string
1642 wxString dir_path = dir_info.fullpath;
1643#ifdef __ANDROID__
1644 dir_path = wxString(dir_info.fullpath.mb_str(wxConvUTF8));
1645#endif
1646
1647 wxString old_magic = dir_info.magic_number;
1648 wxString new_magic = old_magic;
1649 dir_magic = old_magic; // provisionally the same
1650
1651 int nAdd = 0;
1652
1653 bool b_skipDetectDirChange = false;
1654 bool b_dirchange = false;
1655
1656 // Does this directory actually exist?
1657 if (!wxDir::Exists(dir_path)) return 0;
1658
1659 // Check to see if this is a cm93 directory root
1660 // If so, skip the DetectDirChange since it may be very slow
1661 // and give no information
1662 // Assume a change has happened, and process accordingly
1663 bool b_cm93 = Check_CM93_Structure(dir_path);
1664 if (b_cm93) {
1665 b_skipDetectDirChange = true;
1666 b_dirchange = true;
1667 }
1668
1669 // Quick scan the directory to see if it has changed
1670 // If not, there is no need to scan again.....
1671 if (!b_skipDetectDirChange)
1672 b_dirchange = DetectDirChange(dir_path, dir_info.fullpath, old_magic,
1673 new_magic, pprog);
1674
1675 if (!bForce && !b_dirchange) {
1676 wxString msg(" No change detected on directory ");
1677 msg.Append(dir_path);
1678 wxLogMessage(msg);
1679
1680 // Traverse the database, and mark as valid all charts coming from this
1681 // dir, or anywhere in its tree
1682
1683 wxFileName fn_dir(dir_path, "stuff");
1684 unsigned int dir_path_count = fn_dir.GetDirCount();
1685
1686 if (pprog) pprog->SetTitle(_("OpenCPN Chart Scan...."));
1687
1688 int nEntries = active_chartTable.GetCount();
1689
1690 for (int ic = 0; ic < nEntries; ic++) {
1691 wxFileName fn(active_chartTable[ic].GetFullSystemPath());
1692
1693 while (fn.GetDirCount() >= dir_path_count) {
1694 if (fn.GetPath() == dir_path) {
1695 active_chartTable[ic].SetValid(true);
1696 // if(pprog)
1697 // pprog->Update((ic * 100)
1698 // /nEntries, fn.GetFullPath());
1699
1700 break;
1701 }
1702 fn.RemoveLastDir();
1703 }
1704 }
1705
1706 return 0;
1707 }
1708
1709 // There presumably was a change in the directory contents. Return the new
1710 // magic number
1711 dir_magic = new_magic;
1712
1713 // Look for all possible defined chart classes
1714 for (auto &cd : m_ChartClassDescriptorArray) {
1715 nAdd += SearchDirAndAddCharts(dir_info.fullpath, cd, pprog);
1716 }
1717
1718 return nAdd;
1719}
1720
1721bool ChartDatabase::DetectDirChange(const wxString &dir_path,
1722 const wxString &prog_label,
1723 const wxString &magic, wxString &new_magic,
1724 wxGenericProgressDialog *pprog) {
1725 if (pprog) pprog->SetTitle(_("OpenCPN Directory Scan...."));
1726
1727 // parse the magic number
1728 long long unsigned int nmagic;
1729 wxULongLong nacc = 0;
1730
1731 magic.ToULongLong(&nmagic, 10);
1732
1733 // Get an arraystring of all files
1734 wxArrayString FileList;
1735 wxDir dir(dir_path);
1736 int n_files = dir.GetAllFiles(dir_path, &FileList);
1737 FileList.Sort(); // Ensure persistent order of items being hashed.
1738
1739 FlexHash hash(sizeof nacc);
1740 hash.Reset();
1741
1742 // Traverse the list of files, getting their interesting stuff to add to
1743 // accumulator
1744 for (int ifile = 0; ifile < n_files; ifile++) {
1745 if (pprog && (ifile % (n_files / 60 + 1)) == 0)
1746 pprog->Update(wxMin((ifile * 100) / n_files, 100), prog_label);
1747
1748 wxFileName file(FileList[ifile]);
1749
1750 // NOTE. Do not ever try to optimize this code by combining `wxString`
1751 // calls. Otherwise `fileNameUTF8` will point to a stale buffer overwritten
1752 // by garbage.
1753 wxString fileNameNative = file.GetFullPath();
1754 wxScopedCharBuffer fileNameUTF8 = fileNameNative.ToUTF8();
1755 hash.Update(fileNameUTF8.data(), fileNameUTF8.length());
1756
1757 // File Size;
1758 wxULongLong size = file.GetSize();
1759 wxULongLong fileSize = ((size != wxInvalidSize) ? size : 0);
1760 hash.Update(&fileSize, (sizeof fileSize));
1761
1762 // Mod time, in ticks
1763 wxDateTime t = file.GetModificationTime();
1764 wxULongLong fileTime = t.GetTicks();
1765 hash.Update(&fileTime, (sizeof fileTime));
1766 }
1767
1768 hash.Finish();
1769 hash.Receive(&nacc);
1770
1771 // Return the calculated magic number
1772 new_magic = nacc.ToString();
1773
1774 // And do the test
1775 if (new_magic != magic)
1776 return true;
1777 else
1778 return false;
1779}
1780
1781bool ChartDatabase::IsChartDirUsed(const wxString &theDir) {
1782 wxString dir(theDir);
1783 if (dir.Last() == '/' || dir.Last() == wxFileName::GetPathSeparator())
1784 dir.RemoveLast();
1785
1786 dir.Append("*");
1787 for (UINT32 i = 0; i < active_chartTable.GetCount(); i++) {
1788 if (active_chartTable[i].GetpsFullPath()->Matches(dir)) return true;
1789 }
1790 return false;
1791}
1792
1793//-----------------------------------------------------------------------------
1794// Validate a given directory as a cm93 root database
1795// If it appears to be a cm93 database, then return true
1796//-----------------------------------------------------------------------------
1797bool ChartDatabase::Check_CM93_Structure(wxString dir_name) {
1798 wxString filespec;
1799
1800 wxRegEx test("[0-9]+");
1801
1802 wxDir dirt(dir_name);
1803 wxString candidate;
1804
1805 if (dirt.IsOpened())
1806 wxLogMessage("check_cm93 opened dir OK: " + dir_name);
1807 else {
1808 wxLogMessage("check_cm93 NOT OPENED OK: " + dir_name);
1809 wxLogMessage("check_cm93 returns false." + dir_name);
1810 return false;
1811 }
1812
1813 bool b_maybe_found_cm93 = false;
1814 bool b_cont = dirt.GetFirst(&candidate);
1815
1816 while (b_cont) {
1817 if (test.Matches(candidate) && (candidate.Len() == 8)) {
1818 b_maybe_found_cm93 = true;
1819 break;
1820 }
1821
1822 b_cont = dirt.GetNext(&candidate);
1823 }
1824
1825 if (b_maybe_found_cm93) {
1826 wxString dir_next = dir_name;
1827 dir_next += "/";
1828 dir_next += candidate;
1829 if (wxDir::Exists(dir_next)) {
1830 wxDir dir_n(dir_next);
1831 if (dirt.IsOpened()) {
1832 wxString candidate_n;
1833
1834 wxRegEx test_n("^[A-Ga-g]");
1835 bool b_probably_found_cm93 = false;
1836 bool b_cont_n = dir_n.IsOpened() && dir_n.GetFirst(&candidate_n);
1837 while (b_cont_n) {
1838 if (test_n.Matches(candidate_n) && (candidate_n.Len() == 1)) {
1839 b_probably_found_cm93 = true;
1840 break;
1841 }
1842 b_cont_n = dir_n.GetNext(&candidate_n);
1843 }
1844
1845 if (b_probably_found_cm93) // found a directory that looks
1846 // like {dir_name}/12345678/A
1847 // probably cm93
1848 {
1849 // make sure the dir exists
1850 wxString dir_luk = dir_next;
1851 dir_luk += "/";
1852 dir_luk += candidate_n;
1853 if (wxDir::Exists(dir_luk)) return true;
1854 }
1855 }
1856 }
1857 }
1858
1859 return false;
1860}
1861
1862/*
1863//-----------------------------------------------------------------------------
1864// Validate a given directory as a cm93 root database
1865// If it appears to be a cm93 database, then return the name of an existing cell
1866file
1867// File name will be unique with respect to member element m_cm93_filename_array
1868// If not cm93, return empty string
1869//-----------------------------------------------------------------------------
1870wxString ChartDatabase::Get_CM93_FileName(wxString dir_name)
1871{
1872 wxString filespec;
1873
1874 wxRegEx test("[0-9]+");
1875
1876 wxDir dirt(dir_name);
1877 wxString candidate;
1878
1879 bool b_maybe_found_cm93 = false;
1880 bool b_cont = dirt.GetFirst(&candidate);
1881
1882 while(b_cont)
1883 {
1884 if(test.Matches(candidate)&& (candidate.Len() == 8))
1885 {
1886 b_maybe_found_cm93 = true;
1887 break;
1888 }
1889
1890 b_cont = dirt.GetNext(&candidate);
1891
1892 }
1893
1894 if(b_maybe_found_cm93)
1895 {
1896 wxString dir_next = dir_name;
1897 dir_next += "/";
1898 dir_next += candidate;
1899 if(wxDir::Exists(dir_next))
1900 {
1901 wxDir dir_n(dir_next);
1902 wxString candidate_n;
1903
1904 wxRegEx test_n("^[A-Ga-g]");
1905 bool b_probably_found_cm93 = false;
1906 bool b_cont_n = dir_n.GetFirst(&candidate_n);
1907 while(b_cont_n)
1908 {
1909 if(test_n.Matches(candidate_n) && (candidate_n.Len() ==
19101))
1911 {
1912 b_probably_found_cm93 = true;
1913 break;
1914 }
1915 b_cont_n = dir_n.GetNext(&candidate_n);
1916 }
1917
1918 if(b_probably_found_cm93) // found a directory that
1919looks like {dir_name}/12345678/A probably cm93 { // and we want to try and
1920shorten the recursive search
1921 // make sure the dir exists
1922 wxString dir_luk = dir_next;
1923 dir_luk += "/";
1924 dir_luk += candidate_n;
1925 if(wxDir::Exists(dir_luk))
1926 {
1927 wxString msg("Found probable CM93 database in
1928"); msg += dir_name; wxLogMessage(msg);
1929
1930 wxString dir_name_plus = dir_luk; // be very
1931specific about the dir_name,
1932
1933 wxDir dir_get(dir_name_plus);
1934 wxString one_file;
1935 dir_get.GetFirst(&one_file);
1936
1937 // We must return a unique file name, i.e. one
1938that has not bee seen
1939 // before in this invocation of chart dir
1940scans. bool find_unique = false; while(!find_unique)
1941 {
1942 find_unique = true;
1943 for(unsigned int ifile=0; ifile <
1944m_cm93_filename_array.GetCount(); ifile++)
1945 {
1946 if(m_cm93_filename_array[ifile] ==
1947one_file) find_unique = false;
1948 }
1949 if(!find_unique)
1950 dir_get.GetNext(&one_file);
1951 }
1952
1953 m_cm93_filename_array.Add(one_file);
1954
1955 filespec = one_file;
1956 }
1957
1958 }
1959 }
1960 }
1961
1962 return filespec;
1963}
1964*/
1965
1966// ----------------------------------------------------------------------------
1967// Populate Chart Table by directory search for specified file type
1968// If bupdate flag is true, search the Chart Table for matching chart.
1969// if target chart is already in table, mark it valid and skip chart processing
1970// ----------------------------------------------------------------------------
1971
1972WX_DECLARE_STRING_HASH_MAP(int, ChartCollisionsHashMap);
1973
1974int ChartDatabase::SearchDirAndAddCharts(wxString &dir_name_base,
1975 ChartClassDescriptor &chart_desc,
1976 wxGenericProgressDialog *pprog) {
1977 wxString msg("Searching directory: ");
1978 msg += dir_name_base;
1979 msg += " for ";
1980 msg += chart_desc.m_search_mask;
1981 wxLogMessage(msg);
1982
1983 wxString dir_name = dir_name_base;
1984
1985#ifdef __ANDROID__
1986 dir_name = wxString(dir_name_base.mb_str(wxConvUTF8)); // android
1987#endif
1988
1989 if (!wxDir::Exists(dir_name)) return 0;
1990
1991 wxString filespec = chart_desc.m_search_mask.Upper();
1992 wxString lowerFileSpec = chart_desc.m_search_mask.Lower();
1993 wxString filespecXZ = filespec + ".xz";
1994 wxString lowerFileSpecXZ = lowerFileSpec + ".xz";
1995 wxString filename;
1996
1997 // Count the files
1998 wxArrayString FileList;
1999 int gaf_flags = wxDIR_DEFAULT; // as default, recurse into subdirs
2000
2001 // Here is an optimization for MSW/cm93 especially
2002 // If this directory seems to be a cm93, and we are not explicitely looking
2003 // for cm93, then abort Otherwise, we will be looking thru entire cm93 tree
2004 // for non-existent .KAP files, etc.
2005
2006 bool b_found_cm93 = false;
2007 bool b_cm93 = Check_CM93_Structure(dir_name);
2008 if (b_cm93) {
2009 if (filespec != "00300000.A")
2010 return false;
2011 else {
2012 filespec = dir_name;
2013 b_found_cm93 = true;
2014 }
2015 }
2016
2017 if (!b_found_cm93) {
2018 wxDir dir(dir_name);
2019 dir.GetAllFiles(dir_name, &FileList, filespec, gaf_flags);
2020
2021#ifdef __ANDROID__
2022 if (!FileList.GetCount()) {
2023 wxArrayString afl = androidTraverseDir(dir_name, filespec);
2024 for (wxArrayString::const_iterator item = afl.begin(); item != afl.end();
2025 item++)
2026 FileList.Add(*item);
2027 }
2028#endif
2029
2030#ifndef __WXMSW__
2031 if (filespec != lowerFileSpec) {
2032 // add lowercase filespec files too
2033 wxArrayString lowerFileList;
2034 dir.GetAllFiles(dir_name, &lowerFileList, lowerFileSpec, gaf_flags);
2035
2036#ifdef __ANDROID__
2037 if (!lowerFileList.GetCount()) {
2038 wxArrayString afl = androidTraverseDir(dir_name, lowerFileSpec);
2039 for (wxArrayString::const_iterator item = afl.begin();
2040 item != afl.end(); item++)
2041 lowerFileList.Add(*item);
2042 }
2043#endif
2044
2045 for (wxArrayString::const_iterator item = lowerFileList.begin();
2046 item != lowerFileList.end(); item++)
2047 FileList.Add(*item);
2048 }
2049#endif
2050
2051#ifdef OCPN_USE_LZMA
2052 // add xz compressed files;
2053 dir.GetAllFiles(dir_name, &FileList, filespecXZ, gaf_flags);
2054 dir.GetAllFiles(dir_name, &FileList, lowerFileSpecXZ, gaf_flags);
2055#endif
2056
2057 FileList.Sort(); // Sorted processing order makes the progress bar more
2058 // meaningful to the user.
2059 } else { // This is a cm93 dataset, specified as yada/yada/cm93
2060 wxString dir_plus = dir_name;
2061 dir_plus += wxFileName::GetPathSeparator();
2062 FileList.Add(dir_plus);
2063 }
2064
2065 int nFile = FileList.GetCount();
2066
2067 if (!nFile) return false;
2068
2069 int nDirEntry = 0;
2070
2071 // Check to see if there are any charts in the DB which refer to this
2072 // directory If none at all, there is no need to scan the DB for fullpath
2073 // match of each potential addition and bthis_dir_in_dB is false.
2074 bool bthis_dir_in_dB = IsChartDirUsed(dir_name);
2075
2076 if (pprog) pprog->SetTitle(_("OpenCPN Chart Add...."));
2077
2078 // build a hash table based on filename (without directory prefix) of
2079 // the chart to fast to detect identical charts
2080 ChartCollisionsHashMap collision_map;
2081 int nEntry = active_chartTable.GetCount();
2082 for (int i = 0; i < nEntry; i++) {
2083 wxString table_file_name = active_chartTable[i].GetFullSystemPath();
2084 wxFileName table_file(table_file_name);
2085 collision_map[table_file.GetFullName()] = i;
2086 }
2087
2088 int nFileProgressQuantum = wxMax(nFile / 100, 2);
2089 double rFileProgressRatio = 100.0 / wxMax(nFile, 1);
2090
2091 for (int ifile = 0; ifile < nFile; ifile++) {
2092 wxFileName file(FileList[ifile]);
2093 wxString full_name = file.GetFullPath();
2094 wxString file_name = file.GetFullName();
2095 wxString utf8_path = full_name;
2096
2097#ifdef __ANDROID__
2098 // The full path (full_name) is the broken Android files system
2099 // interpretation, which does not display well onscreen. So, here we
2100 // reconstruct a full path spec in UTF-8 encoding for later use in string
2101 // displays. This utf-8 string will be used to construct the chart database
2102 // entry if required.
2103 wxFileName fnbase(dir_name_base);
2104 int nDirs = fnbase.GetDirCount();
2105
2106 wxFileName file_target(FileList[ifile]);
2107
2108 for (int i = 0; i < nDirs + 1;
2109 i++) // strip off the erroneous intial directories
2110 file_target.RemoveDir(0);
2111
2112 wxString leftover_path = file_target.GetFullPath();
2113 utf8_path =
2114 dir_name_base + leftover_path; // reconstruct a fully utf-8 version
2115#endif
2116
2117 // Validate the file name again, considering MSW's semi-random treatment
2118 // of case....
2119 // TODO...something fishy here - may need to normalize saved name?
2120 if (!file_name.Matches(lowerFileSpec) && !file_name.Matches(filespec) &&
2121 !file_name.Matches(lowerFileSpecXZ) && !file_name.Matches(filespecXZ) &&
2122 !b_found_cm93) {
2123 // wxLogMessage("FileSpec test failed for:" + file_name);
2124 continue;
2125 }
2126
2127 if (pprog && ((ifile % nFileProgressQuantum) == 0))
2128 pprog->Update(static_cast<int>(ifile * rFileProgressRatio), utf8_path);
2129
2130 ChartTableEntry *pnewChart = NULL;
2131 bool bAddFinal = true;
2132 int b_add_msg = 0;
2133
2134 // Check the collisions map looking for duplicates, and choosing the right
2135 // one.
2136 ChartCollisionsHashMap::const_iterator collision_ptr =
2137 collision_map.find(file_name);
2138 bool collision = (collision_ptr != collision_map.end());
2139 bool file_path_is_same = false;
2140 bool file_time_is_same = false;
2141 ChartTableEntry *pEntry = NULL;
2142 wxString table_file_name;
2143
2144 // Allow multiple cm93 chart sets #4217
2145 if (b_found_cm93) collision = false;
2146
2147 if (collision) {
2148 pEntry = &active_chartTable[collision_ptr->second];
2149 table_file_name = pEntry->GetFullSystemPath();
2150 file_path_is_same =
2151 bthis_dir_in_dB && full_name.IsSameAs(table_file_name);
2152
2153 // If the chart full file paths are exactly the same, select the newer
2154 // one.
2155 if (file_path_is_same) {
2156 b_add_msg++;
2157
2158 // Check the file modification time
2159 time_t t_oldFile = pEntry->GetFileTime();
2160 time_t t_newFile = file.GetModificationTime().GetTicks();
2161
2162 if (t_newFile <= t_oldFile) {
2163 file_time_is_same = true;
2164 bAddFinal = false;
2165 pEntry->SetValid(true);
2166 } else {
2167 bAddFinal = true;
2168 pEntry->SetValid(false);
2169 }
2170 }
2171 }
2172
2173 wxString msg_fn(full_name);
2174 msg_fn.Replace("%", "%%");
2175 if (file_time_is_same) {
2176 // Produce the same output without actually calling
2177 // `CreateChartTableEntry()`.
2178 wxLogMessage(
2179 wxString::Format("Loading chart data for %s", msg_fn.c_str()));
2180 } else {
2181 pnewChart = CreateChartTableEntry(full_name, utf8_path, chart_desc);
2182 if (!pnewChart) {
2183 bAddFinal = false;
2184 wxLogMessage(wxString::Format(
2185 " CreateChartTableEntry() failed for file: %s", msg_fn.c_str()));
2186 }
2187 }
2188
2189 if (!collision || !pnewChart) {
2190 // Do nothing.
2191 } else if (file_path_is_same) {
2192 wxLogMessage(wxString::Format(
2193 " Replacing older chart file of same path: %s", msg_fn.c_str()));
2194 } else if (!file_time_is_same) {
2195 // Look at the chart file name (without directory prefix) for a further
2196 // check for duplicates This catches the case in which the "same" chart
2197 // is in different locations, and one may be newer than the other.
2198 b_add_msg++;
2199
2200 if (pnewChart->IsEarlierThan(*pEntry)) {
2201 wxFileName table_file(table_file_name);
2202 // Make sure the compare file actually exists
2203 if (table_file.IsFileReadable()) {
2204 pEntry->SetValid(true);
2205 bAddFinal = false;
2206 wxLogMessage(
2207 wxString::Format(" Retaining newer chart file of same name: %s",
2208 msg_fn.c_str()));
2209 }
2210 } else if (pnewChart->IsEqualTo(*pEntry)) {
2211 // The file names (without dir prefix) are identical,
2212 // and the mod times are identical
2213 // Prsume that this is intentional, in order to facilitate
2214 // having the same chart in multiple groups.
2215 // So, add this chart.
2216 bAddFinal = true;
2217 } else {
2218 pEntry->SetValid(false);
2219 bAddFinal = true;
2220 wxLogMessage(wxString::Format(
2221 " Replacing older chart file of same name: %s", msg_fn.c_str()));
2222 }
2223 }
2224
2225 if (bAddFinal) {
2226 if (0 == b_add_msg) {
2227 wxLogMessage(
2228 wxString::Format(" Adding chart file: %s", msg_fn.c_str()));
2229 }
2230 collision_map[file_name] = active_chartTable.GetCount();
2231 active_chartTable.Add(pnewChart);
2232 nDirEntry++;
2233 } else {
2234 if (pnewChart) delete pnewChart;
2235 // wxLogMessage(wxString::Format(" Not adding
2236 // chart file: %s", msg_fn.c_str()));
2237 }
2238 }
2239
2240 m_nentries = active_chartTable.GetCount();
2241
2242 return nDirEntry;
2243}
2244
2245bool ChartDatabase::AddChart(wxString &chartfilename,
2246 ChartClassDescriptor &chart_desc,
2247 wxGenericProgressDialog *pprog, int isearch,
2248 bool bthis_dir_in_dB) {
2249 bool rv = false;
2250 wxFileName file(chartfilename);
2251 wxString full_name = file.GetFullPath();
2252 wxString file_name = file.GetFullName();
2253
2254 // Validate the file name again, considering MSW's semi-random treatment of
2255 // case....
2256 // TODO...something fishy here - may need to normalize saved name?
2257 // if(!file_name.Matches(lowerFileSpec) && !file_name.Matches(filespec) &&
2258 // !b_found_cm93)
2259 // continue;
2260
2261 if (pprog)
2262 pprog->Update(wxMin((m_pdifile * 100) / m_pdnFile, 100), full_name);
2263
2264 ChartTableEntry *pnewChart = NULL;
2265 bool bAddFinal = true;
2266 int b_add_msg = 0;
2267 wxString msg_fn(full_name);
2268 msg_fn.Replace("%", "%%");
2269
2270 pnewChart = CreateChartTableEntry(full_name, full_name, chart_desc);
2271 if (!pnewChart) {
2272 bAddFinal = false;
2273 wxLogMessage(wxString::Format(
2274 " CreateChartTableEntry() failed for file: %s", msg_fn.c_str()));
2275 return false;
2276 } else // traverse the existing database looking for duplicates, and choosing
2277 // the right one
2278 {
2279 int nEntry = active_chartTable.GetCount();
2280 for (int i = 0; i < nEntry; i++) {
2281 wxString *ptable_file_name = active_chartTable[isearch].GetpsFullPath();
2282
2283 // If the chart full file paths are exactly the same, select the newer
2284 // one
2285 if (bthis_dir_in_dB && full_name.IsSameAs(*ptable_file_name)) {
2286 b_add_msg++;
2287
2288 // Check the file modification time
2289 time_t t_oldFile = active_chartTable[isearch].GetFileTime();
2290 time_t t_newFile = file.GetModificationTime().GetTicks();
2291
2292 if (t_newFile <= t_oldFile) {
2293 bAddFinal = false;
2294 active_chartTable[isearch].SetValid(true);
2295 } else {
2296 bAddFinal = true;
2297 active_chartTable[isearch].SetValid(false);
2298 wxLogMessage(
2299 wxString::Format(" Replacing older chart file of same path: %s",
2300 msg_fn.c_str()));
2301 }
2302
2303 break;
2304 }
2305
2306 // Look at the chart file name (without directory prefix) for a further
2307 // check for duplicates This catches the case in which the "same" chart
2308 // is in different locations, and one may be newer than the other.
2309 wxFileName table_file(*ptable_file_name);
2310
2311 if (table_file.GetFullName() == file_name) {
2312 b_add_msg++;
2313
2314 if (pnewChart->IsEarlierThan(active_chartTable[isearch])) {
2315 // Make sure the compare file actually exists
2316 if (table_file.IsFileReadable()) {
2317 active_chartTable[isearch].SetValid(true);
2318 bAddFinal = false;
2319 wxLogMessage(wxString::Format(
2320 " Retaining newer chart file of same name: %s",
2321 msg_fn.c_str()));
2322 }
2323 } else if (pnewChart->IsEqualTo(active_chartTable[isearch])) {
2324 // The file names (without dir prefix) are identical,
2325 // and the mod times are identical
2326 // Prsume that this is intentional, in order to facilitate
2327 // having the same chart in multiple groups.
2328 // So, add this chart.
2329 bAddFinal = true;
2330 }
2331
2332 else {
2333 active_chartTable[isearch].SetValid(false);
2334 bAddFinal = true;
2335 wxLogMessage(
2336 wxString::Format(" Replacing older chart file of same name: %s",
2337 msg_fn.c_str()));
2338 }
2339
2340 break;
2341 }
2342
2343 // TODO Look at the chart ID as a further check against duplicates
2344
2345 isearch++;
2346 if (nEntry == isearch) isearch = 0;
2347 } // for
2348 }
2349
2350 if (bAddFinal) {
2351 if (0 == b_add_msg) {
2352 wxLogMessage(
2353 wxString::Format(" Adding chart file: %s", msg_fn.c_str()));
2354 }
2355
2356 active_chartTable.Add(pnewChart);
2357
2358 rv = true;
2359 } else {
2360 delete pnewChart;
2361 // wxLogMessage(wxString::Format(" Not adding chart
2362 // file: %s", msg_fn.c_str()));
2363 rv = false;
2364 }
2365
2366 m_nentries = active_chartTable.GetCount();
2367
2368 return rv;
2369}
2370
2371bool ChartDatabase::AddSingleChart(wxString &ChartFullPath,
2372 bool b_force_full_search) {
2373 // Find a relevant chart class descriptor
2374 wxFileName fn(ChartFullPath);
2375 wxString ext = fn.GetExt();
2376 ext.Prepend("*.");
2377 wxString ext_upper = ext.MakeUpper();
2378 wxString ext_lower = ext.MakeLower();
2379 wxString dir_name = fn.GetPath();
2380
2381 // Search the array of chart class descriptors to find a match
2382 // bewteen the search mask and the the chart file extension
2383
2385 for (auto &cd : m_ChartClassDescriptorArray) {
2386 if (cd.m_descriptor_type == PLUGIN_DESCRIPTOR) {
2387 if (cd.m_search_mask == ext_upper) {
2388 desc = cd;
2389 break;
2390 }
2391 if (cd.m_search_mask == ext_lower) {
2392 desc = cd;
2393 break;
2394 }
2395 }
2396 }
2397
2398 // If we know that we need to do a full recursive search of the db,
2399 // then there is no need to verify it by doing a directory match
2400 bool b_recurse = true;
2401 if (!b_force_full_search) b_recurse = IsChartDirUsed(dir_name);
2402
2403 bool rv = AddChart(ChartFullPath, desc, NULL, 0, b_recurse);
2404
2405 // remove duplicates marked in AddChart()
2406
2407 for (unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
2408 if (!active_chartTable[i].GetbValid()) {
2409 active_chartTable.RemoveAt(i);
2410 i--; // entry is gone, recheck this index for next entry
2411 }
2412 }
2413
2414 // Update the Entry index fields
2415 for (unsigned int i = 0; i < active_chartTable.GetCount(); i++)
2416 active_chartTable[i].SetEntryOffset(i);
2417
2418 // Get a new magic number
2419 wxString new_magic;
2420 DetectDirChange(dir_name, "", "", new_magic, 0);
2421
2422 // Update (clone) the CDI array
2423 bool bcfound = false;
2424 ArrayOfCDI NewChartDirArray;
2425
2426 ArrayOfCDI ChartDirArray = GetChartDirArray();
2427 for (unsigned int i = 0; i < ChartDirArray.GetCount(); i++) {
2428 ChartDirInfo cdi = ChartDirArray[i];
2429
2430 ChartDirInfo newcdi = cdi;
2431
2432 // If entry is found that matches this cell, clear the magic number.
2433 if (newcdi.fullpath == dir_name) {
2434 newcdi.magic_number = new_magic;
2435 bcfound = true;
2436 }
2437
2438 NewChartDirArray.Add(newcdi);
2439 }
2440
2441 if (!bcfound) {
2442 ChartDirInfo cdi;
2443 cdi.fullpath = dir_name;
2444 cdi.magic_number = new_magic;
2445 NewChartDirArray.Add(cdi);
2446 }
2447
2448 // Update the database master copy of the CDI array
2449 SetChartDirArray(NewChartDirArray);
2450
2451 // Update the list of chart dirs.
2452 m_chartDirs.Clear();
2453
2454 for (unsigned int i = 0; i < GetChartDirArray().GetCount(); i++) {
2455 ChartDirInfo cdi = GetChartDirArray()[i];
2456 m_chartDirs.Add(cdi.fullpath);
2457 }
2458
2459 m_nentries = active_chartTable.GetCount();
2460
2461 return rv;
2462}
2463
2464bool ChartDatabase::RemoveSingleChart(wxString &ChartFullPath) {
2465 bool rv = false;
2466
2467 // Walk the chart table, looking for the target
2468 for (unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
2469 if (ChartFullPath.IsSameAs(GetChartTableEntry(i).GetFullSystemPath())) {
2470 active_chartTable.RemoveAt(i);
2471 break;
2472 }
2473 }
2474
2475 // Update the EntryOffset fields for the array
2476 ChartTableEntry *pcte;
2477
2478 for (unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
2479 pcte = GetpChartTableEntry(i);
2480 pcte->SetEntryOffset(i);
2481 }
2482
2483 // Check and update the dir array
2484 wxFileName fn(ChartFullPath);
2485 wxString fd = fn.GetPath();
2486 if (!IsChartDirUsed(fd)) {
2487 // Clone a new array, removing the unused directory,
2488 ArrayOfCDI NewChartDirArray;
2489
2490 ArrayOfCDI ChartDirArray = GetChartDirArray();
2491 for (unsigned int i = 0; i < ChartDirArray.GetCount(); i++) {
2492 ChartDirInfo cdi = ChartDirArray[i];
2493
2494 ChartDirInfo newcdi = cdi;
2495
2496 if (newcdi.fullpath != fd) NewChartDirArray.Add(newcdi);
2497 }
2498
2499 SetChartDirArray(NewChartDirArray);
2500 }
2501
2502 // Update the list of chart dirs.
2503 m_chartDirs.Clear();
2504 for (unsigned int i = 0; i < GetChartDirArray().GetCount(); i++) {
2505 ChartDirInfo cdi = GetChartDirArray()[i];
2506 m_chartDirs.Add(cdi.fullpath);
2507 }
2508
2509 m_nentries = active_chartTable.GetCount();
2510
2511 return rv;
2512}
2513
2515// Create a Chart object
2517
2518ChartBase *ChartDatabase::GetChart(const wxChar *theFilePath,
2519 ChartClassDescriptor &chart_desc) const {
2520 // TODO: support non-UI chart factory
2521 return NULL;
2522}
2523
2525// Create Chart Table entry by reading chart header info, etc.
2527
2528ChartTableEntry *ChartDatabase::CreateChartTableEntry(
2529 const wxString &filePath, wxString &utf8Path,
2530 ChartClassDescriptor &chart_desc) {
2531 wxString msg_fn(filePath);
2532 msg_fn.Replace("%", "%%");
2533 wxLogMessage(wxString::Format("Loading chart data for %s", msg_fn.c_str()));
2534
2535 ChartBase *pch = GetChart(filePath, chart_desc);
2536 if (pch == NULL) {
2537 wxLogMessage(
2538 wxString::Format(" ...creation failed for %s", msg_fn.c_str()));
2539 return NULL;
2540 }
2541
2542 InitReturn rc = pch->Init(filePath, HEADER_ONLY);
2543 if (rc != INIT_OK) {
2544 delete pch;
2545 wxLogMessage(
2546 wxString::Format(" ...initialization failed for %s", msg_fn.c_str()));
2547 return NULL;
2548 }
2549
2550 ChartTableEntry *ret_val = new ChartTableEntry(*pch, utf8Path);
2551 ret_val->SetValid(true);
2552
2553 delete pch;
2554
2555 return ret_val;
2556}
2557
2558bool ChartDatabase::GetCentroidOfLargestScaleChart(double *clat, double *clon,
2559 ChartFamilyEnum family) {
2560 int cur_max_i = -1;
2561 int cur_max_scale = 0;
2562
2563 int nEntry = active_chartTable.GetCount();
2564
2565 for (int i = 0; i < nEntry; i++) {
2566 if (GetChartFamily(active_chartTable[i].GetChartType()) == family) {
2567 if (active_chartTable[i].GetScale() > cur_max_scale) {
2568 cur_max_scale = active_chartTable[i].GetScale();
2569 cur_max_i = i;
2570 }
2571 }
2572 }
2573
2574 if (cur_max_i == -1)
2575 return false; // nothing found
2576 else {
2577 *clat = (active_chartTable[cur_max_i].GetLatMax() +
2578 active_chartTable[cur_max_i].GetLatMin()) /
2579 2.;
2580 *clon = (active_chartTable[cur_max_i].GetLonMin() +
2581 active_chartTable[cur_max_i].GetLonMax()) /
2582 2.;
2583 }
2584 return true;
2585}
2586
2587//-------------------------------------------------------------------
2588// Get DBChart Projection
2589//-------------------------------------------------------------------
2590int ChartDatabase::GetDBChartProj(int dbIndex) {
2591 if ((bValid) && (dbIndex >= 0) && (dbIndex < (int)active_chartTable.size()))
2592 return active_chartTable[dbIndex].GetChartProjectionType();
2593 else
2594 return PROJECTION_UNKNOWN;
2595}
2596
2597//-------------------------------------------------------------------
2598// Get DBChart Family
2599//-------------------------------------------------------------------
2600int ChartDatabase::GetDBChartFamily(int dbIndex) {
2601 if ((bValid) && (dbIndex >= 0) && (dbIndex < (int)active_chartTable.size()))
2602 return active_chartTable[dbIndex].GetChartFamily();
2603 else
2604 return CHART_FAMILY_UNKNOWN;
2605}
2606
2607//-------------------------------------------------------------------
2608// Get DBChart FullFileName
2609//-------------------------------------------------------------------
2610wxString ChartDatabase::GetDBChartFileName(int dbIndex) {
2611 if ((bValid) && (dbIndex >= 0) && (dbIndex < (int)active_chartTable.size())) {
2612 return wxString(active_chartTable[dbIndex].GetFullSystemPath());
2613 } else
2614 return "";
2615}
2616
2617//-------------------------------------------------------------------
2618// Get DBChart Type
2619//-------------------------------------------------------------------
2620int ChartDatabase::GetDBChartType(int dbIndex) {
2621 if ((bValid) && (dbIndex >= 0) && (dbIndex < (int)active_chartTable.size()))
2622 return active_chartTable[dbIndex].GetChartType();
2623 else
2624 return 0;
2625}
2626
2627//-------------------------------------------------------------------
2628// Get DBChart Skew
2629//-------------------------------------------------------------------
2630float ChartDatabase::GetDBChartSkew(int dbIndex) {
2631 if ((bValid) && (dbIndex >= 0) && (dbIndex < (int)active_chartTable.size()))
2632 return active_chartTable[dbIndex].GetChartSkew();
2633 else
2634 return 0.;
2635}
2636
2637//-------------------------------------------------------------------
2638// Get DBChart Scale
2639//-------------------------------------------------------------------
2640int ChartDatabase::GetDBChartScale(int dbIndex) {
2641 if ((bValid) && (dbIndex >= 0) && (dbIndex < (int)active_chartTable.size()))
2642 return active_chartTable[dbIndex].GetScale();
2643 else
2644 return 1;
2645}
2646
2647//-------------------------------------------------------------------
2648// Get Lat/Lon Bounding Box from db
2649//-------------------------------------------------------------------
2650bool ChartDatabase::GetDBBoundingBox(int dbIndex, LLBBox &box) {
2651 if ((bValid) && (dbIndex >= 0) && (dbIndex < (int)active_chartTable.size())) {
2652 const ChartTableEntry &entry = GetChartTableEntry(dbIndex);
2653 box.Set(entry.GetLatMin(), entry.GetLonMin(), entry.GetLatMax(),
2654 entry.GetLonMax());
2655 }
2656
2657 return true;
2658}
2659
2660const LLBBox &ChartDatabase::GetDBBoundingBox(int dbIndex) {
2661 if ((bValid) && (dbIndex >= 0)) {
2662 const ChartTableEntry &entry = GetChartTableEntry(dbIndex);
2663 return entry.GetBBox();
2664 } else {
2665 return m_dummy_bbox;
2666 }
2667}
2668
2669//-------------------------------------------------------------------
2670// Get PlyPoint from Database
2671//-------------------------------------------------------------------
2672int ChartDatabase::GetDBPlyPoint(int dbIndex, int plyindex, float *lat,
2673 float *lon) {
2674 if ((bValid) && (dbIndex >= 0) && (dbIndex < (int)active_chartTable.size())) {
2675 const ChartTableEntry &entry = GetChartTableEntry(dbIndex);
2676 if (entry.GetnPlyEntries()) {
2677 float *fp = entry.GetpPlyTable();
2678 fp += plyindex * 2;
2679 if (lat) *lat = *fp;
2680 fp++;
2681 if (lon) *lon = *fp;
2682 }
2683 return entry.GetnPlyEntries();
2684 } else
2685 return 0;
2686}
2687
2688//-------------------------------------------------------------------
2689// Get AuxPlyPoint from Database
2690//-------------------------------------------------------------------
2691int ChartDatabase::GetDBAuxPlyPoint(int dbIndex, int plyindex, int ply,
2692 float *lat, float *lon) {
2693 if ((bValid) && (dbIndex >= 0) && (dbIndex < (int)active_chartTable.size())) {
2694 const ChartTableEntry &entry = GetChartTableEntry(dbIndex);
2695 if (entry.GetnAuxPlyEntries()) {
2696 float *fp = entry.GetpAuxPlyTableEntry(ply);
2697
2698 fp += plyindex * 2;
2699 if (lat) *lat = *fp;
2700 fp++;
2701 if (lon) *lon = *fp;
2702 }
2703
2704 return entry.GetAuxCntTableEntry(ply);
2705 } else
2706 return 0;
2707}
2708
2709int ChartDatabase::GetnAuxPlyEntries(int dbIndex) {
2710 if ((bValid) && (dbIndex >= 0) && (dbIndex < (int)active_chartTable.size())) {
2711 const ChartTableEntry &entry = GetChartTableEntry(dbIndex);
2712 return entry.GetnAuxPlyEntries();
2713 } else
2714 return 0;
2715}
2716
2717//-------------------------------------------------------------------
2718// Get vector of reduced Plypoints
2719//-------------------------------------------------------------------
2720std::vector<float> ChartDatabase::GetReducedPlyPoints(int dbIndex) {
2721 if ((bValid) && (dbIndex >= 0) && (dbIndex < (int)active_chartTable.size())) {
2722 ChartTableEntry *pentry = GetpChartTableEntry(dbIndex);
2723 if (pentry) return pentry->GetReducedPlyPoints();
2724 }
2725
2726 std::vector<float> dummy;
2727 return dummy;
2728}
2729
2730//-------------------------------------------------------------------
2731// Get vector of reduced AuxPlypoints
2732//-------------------------------------------------------------------
2733std::vector<float> ChartDatabase::GetReducedAuxPlyPoints(int dbIndex,
2734 int iTable) {
2735 if ((bValid) && (dbIndex >= 0) && (dbIndex < (int)active_chartTable.size())) {
2736 ChartTableEntry *pentry = GetpChartTableEntry(dbIndex);
2737 if (pentry) return pentry->GetReducedAuxPlyPoints(iTable);
2738 }
2739
2740 std::vector<float> dummy;
2741 return dummy;
2742}
2743
2744bool ChartDatabase::IsChartAvailable(int dbIndex) {
2745 if ((bValid) && (dbIndex >= 0) && (dbIndex < (int)active_chartTable.size())) {
2746 ChartTableEntry *pentry = GetpChartTableEntry(dbIndex);
2747
2748 // If not PLugIn chart, assume always available
2749 if (pentry->GetChartType() != CHART_TYPE_PLUGIN) return true;
2750
2751 wxString *path = pentry->GetpsFullPath();
2752 wxFileName fn(*path);
2753 wxString ext = fn.GetExt();
2754 ext.Prepend("*.");
2755 wxString ext_upper = ext.MakeUpper();
2756 wxString ext_lower = ext.MakeLower();
2757
2758 // Search the array of chart class descriptors to find a match
2759 // between the search mask and the the chart file extension
2760
2761 for (auto &cd : m_ChartClassDescriptorArray) {
2762 if (cd.m_descriptor_type == PLUGIN_DESCRIPTOR) {
2763 wxString search_mask = cd.m_search_mask;
2764
2765 if (search_mask == ext_upper) {
2766 return true;
2767 }
2768 if (search_mask == ext_lower) {
2769 return true;
2770 }
2771 if (path->Matches(search_mask)) {
2772 return true;
2773 }
2774 }
2775 }
2776 }
2777
2778 return false;
2779}
2780
2781void ChartDatabase::ApplyGroupArray(ChartGroupArray *pGroupArray) {
2782 wxString separator(wxFileName::GetPathSeparator());
2783
2784 for (unsigned int ic = 0; ic < active_chartTable.GetCount(); ic++) {
2785 ChartTableEntry *pcte = &active_chartTable[ic];
2786
2787 pcte->ClearGroupArray();
2788
2789 wxString *chart_full_path = pcte->GetpsFullPath();
2790
2791 for (unsigned int igroup = 0; igroup < pGroupArray->GetCount(); igroup++) {
2792 ChartGroup *pGroup = pGroupArray->Item(igroup);
2793 for (const auto &elem : pGroup->m_element_array) {
2794 wxString element_root = elem.m_element_name;
2795
2796 // The element may be a full single chart name
2797 // If so, add it
2798 // Otherwise, append a sep character so that similar paths are
2799 // distinguished. See FS#1060
2800 if (!chart_full_path->IsSameAs(element_root))
2801 element_root.Append(
2802 separator); // Prevent comingling similar looking path names
2803 if (chart_full_path->StartsWith(element_root)) {
2804 bool b_add = true;
2805 for (unsigned int k = 0; k < elem.m_missing_name_array.size(); k++) {
2806 const wxString &missing_item = elem.m_missing_name_array[k];
2807 if (chart_full_path->StartsWith(missing_item)) {
2808 if (chart_full_path->IsSameAs(
2809 missing_item)) // missing item is full chart name
2810 {
2811 b_add = false;
2812 break;
2813 } else {
2814 if (wxDir::Exists(missing_item)) // missing item is a dir
2815 {
2816 b_add = false;
2817 break;
2818 }
2819 }
2820 }
2821 }
2822
2823 if (b_add) pcte->AddIntToGroupArray(igroup + 1);
2824 }
2825 }
2826 }
2827 }
2828}
General chart base definitions.
ChartGroupArray * g_pGroupArray
Global instance.
Definition chartdbs.cpp:56
Basic chart info storage.
Base class for all chart types.
Definition chartbase.h:125
Manages a database of charts, including reading, writing, and querying chart information.
Definition chartdbs.h:312
bool Create(ArrayOfCDI &dir_array, wxGenericProgressDialog *pprog)
Creates a new chart database from a list of directories.
bool Update(ArrayOfCDI &dir_array, bool bForce, wxGenericProgressDialog *pprog)
Updates the chart database.
Represents a user-defined collection of logically related charts.
Definition chartdbs.h:468
Wrapper class for plugin-based charts.
Definition chartimg.h:389
Class for computing hash of arbitrary length.
Definition flex_hash.h:34
Hash of arbitrary length.
Platform independent GL includes.
PlugInManager * g_pi_manager
Global instance.
PlugInManager and helper classes – Mostly gui parts (dialogs) and plugin API stuff.
ShapeBaseChartSet gShapeBasemap
global instance
Shapefile basemap.
Represents an entry in the chart table, containing information about a single chart.
Definition chartdbs.h:183