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