33#include <wx/arrimpl.cpp>
35#include <wx/encconv.h>
36#include <wx/progdlg.h>
38#include <wx/tokenzr.h>
39#include <wx/evtloop.h>
46#include "LOD_reduce.h"
54#define UINT32 unsigned int
58#include "androidUTIL.h"
63static int s_dbVersion;
69static ChartFamilyEnum GetChartFamily(
int charttype) {
74 cf = CHART_FAMILY_RASTER;
77 cf = CHART_FAMILY_RASTER;
80 cf = CHART_FAMILY_VECTOR;
83 cf = CHART_FAMILY_VECTOR;
85 case CHART_TYPE_CM93COMP:
86 cf = CHART_FAMILY_VECTOR;
88 case CHART_TYPE_DUMMY:
89 cf = CHART_FAMILY_RASTER;
91 case CHART_TYPE_UNKNOWN:
92 cf = CHART_FAMILY_UNKNOWN;
95 cf = CHART_FAMILY_UNKNOWN;
105void ChartTableHeader::Read(wxInputStream &is) {
106 is.Read(
this,
sizeof(ChartTableHeader));
109void ChartTableHeader::Write(wxOutputStream &os) {
111 sprintf(vb,
"V%03d", DB_VERSION_CURRENT);
113 memcpy(dbVersion, vb, 4);
114 os.Write(
this,
sizeof(ChartTableHeader));
117bool ChartTableHeader::CheckValid() {
119 sprintf(vb,
"V%03d", DB_VERSION_CURRENT);
120 if (strncmp(vb, dbVersion,
sizeof(dbVersion))) {
123 memcpy(vbo, dbVersion, 4);
125 msg.Append(wxString(vbo, wxConvUTF8));
126 msg.Prepend(
" Warning: found incorrect chart db version: ");
132 sprintf(vb,
"V%03d", DB_VERSION_PREVIOUS);
133 if (strncmp(vb, dbVersion,
sizeof(dbVersion)))
137 " Scheduling db upgrade to current db version on "
138 "Options->Charts page visit...");
145 memcpy(vbo, dbVersion, 4);
147 msg.Append(wxString(vbo, wxConvUTF8));
148 msg.Prepend(
"Loading chart db version: ");
159void ChartTableEntry::SetScale(
int scale) {
163 if (Scale >= 1000) rounding = 5 * pow(10, log10(Scale) - 2);
166ChartTableEntry::ChartTableEntry(
ChartBase &theChart, wxString &utf8Path) {
169 m_FullPath = std::string(utf8Path.utf8_str());
171 SetScale(theChart.GetNativeScale());
173 ChartType = theChart.GetChartType();
174 ChartFamily = theChart.GetChartFamily();
176 Skew = theChart.GetChartSkew();
177 ProjectionType = theChart.GetChartProjectionType();
179 wxDateTime ed = theChart.GetEditionDate();
180 if (theChart.GetEditionDate().IsValid())
181 edition_date = theChart.GetEditionDate().GetTicks();
183 wxFileName fn(theChart.GetFullPath());
184 if (fn.GetModificationTime().IsValid())
185 file_date = fn.GetModificationTime().GetTicks();
187 m_pfilename =
new wxString;
188 *m_pfilename = fn.GetFullName();
189 m_psFullPath =
new wxString;
190 *m_psFullPath = utf8Path;
191 m_fullSystemPath = utf8Path;
194 m_fullSystemPath = wxString(utf8Path.mb_str(wxConvUTF8));
198 theChart.GetChartExtent(&ext);
204 m_bbox.Set(LatMin, LonMin, LatMax, LonMax);
209 double scale_max_zoom = Scale / 4;
211 double display_ppm = 1 / .00025;
212 double meters_per_pixel_max_scale = scale_max_zoom / display_ppm;
213 double LOD_meters = meters_per_pixel_max_scale * LOD_pixels;
218 if (theChart.GetCOVREntries() == 1) {
219 nPlyEntries = theChart.GetCOVRTablePoints(0);
221 if (nPlyEntries > 5 && (LOD_meters > .01)) {
222 std::vector<int> index_keep{0, nPlyEntries - 1, 1, nPlyEntries - 2};
224 double *DPbuffer = (
double *)malloc(2 * nPlyEntries *
sizeof(
double));
226 double *pfed = DPbuffer;
227 Plypoint *ppp = (Plypoint *)theChart.GetCOVRTableHead(0);
229 for (
int i = 0; i < nPlyEntries; i++) {
235 DouglasPeucker(DPbuffer, 1, nPlyEntries - 2, LOD_meters / (1852 * 60),
241 for (
unsigned int i = 0; i < index_keep.size(); i++) {
242 DPbuffer[2 * index_keep[i]] += 2000.;
245 float *pf = (
float *)malloc(2 * index_keep.size() *
sizeof(
float));
248 for (
int i = 0; i < nPlyEntries; i++) {
249 if (DPbuffer[2 * i] > 1000.) {
250 *pfe++ = DPbuffer[2 * i] - 2000.;
251 *pfe++ = DPbuffer[(2 * i) + 1];
256 nPlyEntries = index_keep.size();
259 float *pf = (
float *)malloc(2 * nPlyEntries *
sizeof(
float));
262 Plypoint *ppp = (Plypoint *)theChart.GetCOVRTableHead(0);
264 for (
int i = 0; i < nPlyEntries; i++) {
276 float *pf1 = (
float *)malloc(2 * 4 *
sizeof(
float));
280 theChart.GetChartExtent(&fext);
296 nAuxPlyEntries = theChart.GetCOVREntries();
297 wxASSERT(nAuxPlyEntries);
298 float **pfp = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
300 int *pip = (
int *)malloc(nAuxPlyEntries *
sizeof(
int));
302 for (
int j = 0; j < nAuxPlyEntries; j++) {
303 int nPE = theChart.GetCOVRTablePoints(j);
305 if (nPE > 5 && (LOD_meters > .01)) {
306 std::vector<int> index_keep{0, nPE - 1, 1, nPE - 2};
308 double *DPbuffer = (
double *)malloc(2 * nPE *
sizeof(
double));
310 double *pfed = DPbuffer;
311 Plypoint *ppp = (Plypoint *)theChart.GetCOVRTableHead(j);
313 for (
int i = 0; i < nPE; i++) {
319 DouglasPeucker(DPbuffer, 1, nPE - 2, LOD_meters / (1852 * 60),
325 for (
unsigned int i = 0; i < index_keep.size(); i++) {
326 DPbuffer[2 * index_keep[i]] += 2000.;
329 float *pf = (
float *)malloc(2 * index_keep.size() *
sizeof(
float));
332 for (
int i = 0; i < nPE; i++) {
333 if (DPbuffer[2 * i] > 1000.) {
334 *pfe1++ = DPbuffer[2 * i] - 2000.;
335 *pfe1++ = DPbuffer[(2 * i) + 1];
340 pip[j] = index_keep.size();
344 (
float *)malloc(theChart.GetCOVRTablePoints(j) * 2 *
sizeof(
float));
345 memcpy(pf_entry, theChart.GetCOVRTableHead(j),
346 theChart.GetCOVRTablePoints(j) * 2 *
sizeof(
float));
348 pip[j] = theChart.GetCOVRTablePoints(j);
358 nNoCovrPlyEntries = theChart.GetNoCOVREntries();
359 if (nNoCovrPlyEntries == 0)
return;
361 float **pfpnc = (
float **)malloc(nNoCovrPlyEntries *
sizeof(
float *));
362 float **pft0nc = pfpnc;
363 int *pipnc = (
int *)malloc(nNoCovrPlyEntries *
sizeof(
int));
365 for (
int j = 0; j < nNoCovrPlyEntries; j++) {
367 (
float *)malloc(theChart.GetNoCOVRTablePoints(j) * 2 *
sizeof(
float));
368 memcpy(pf_entry, theChart.GetNoCOVRTableHead(j),
369 theChart.GetNoCOVRTablePoints(j) * 2 *
sizeof(
float));
370 pft0nc[j] = pf_entry;
371 pipnc[j] = theChart.GetNoCOVRTablePoints(j);
374 pNoCovrPlyTable = pfpnc;
375 pNoCovrCntTable = pipnc;
380ChartTableEntry::~ChartTableEntry() {
383 for (
int i = 0; i < nAuxPlyEntries; i++) free(pAuxPlyTable[i]);
387 if (nNoCovrPlyEntries) {
388 for (
int i = 0; i < nNoCovrPlyEntries; i++) free(pNoCovrPlyTable[i]);
389 free(pNoCovrPlyTable);
390 free(pNoCovrCntTable);
402 wxDateTime mine(edition_date);
403 wxDateTime theirs(cte.edition_date);
405 if (!mine.IsValid() || !theirs.IsValid())
408 return (mine.IsEarlierThan(theirs));
412 wxDateTime mine(edition_date);
413 wxDateTime theirs(cte.edition_date);
415 if (!mine.IsValid() || !theirs.IsValid())
418 return (mine.IsEqualTo(theirs));
423static int convertChartType(
int charttype) {
426 if (s_dbVersion == 14) {
429 return CHART_TYPE_KAP;
431 return CHART_TYPE_GEO;
433 return CHART_TYPE_S57;
435 return CHART_TYPE_CM93;
437 return CHART_TYPE_CM93COMP;
439 return CHART_TYPE_UNKNOWN;
441 return CHART_TYPE_DONTCARE;
443 return CHART_TYPE_DUMMY;
445 return CHART_TYPE_UNKNOWN;
451static int convertChartFamily(
int charttype,
int chartfamily) {
452 if (s_dbVersion < 18) {
456 return CHART_FAMILY_RASTER;
459 case CHART_TYPE_CM93:
460 case CHART_TYPE_CM93COMP:
461 return CHART_FAMILY_VECTOR;
464 return CHART_FAMILY_UNKNOWN;
470bool ChartTableEntry::Read(
const ChartDatabase *pDb, wxInputStream &is) {
471 char path[4096], *cp;
476 ChartDatabase *pD = (ChartDatabase *)pDb;
477 int db_version = pD->GetVersion();
479 if (db_version == 18) {
481 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
483 wxLogVerbose(
" Chart %s", path);
486 m_pfilename =
new wxString;
487 wxString fullfilename(path, wxConvUTF8);
488 wxFileName fn(fullfilename);
489 *m_pfilename = fn.GetFullName();
490 m_psFullPath =
new wxString;
491 *m_psFullPath = fullfilename;
492 m_fullSystemPath = fullfilename;
495 m_fullSystemPath = wxString(fullfilename.mb_str(wxConvUTF8));
498 ChartTableEntry_onDisk_18 cte;
499 is.Read(&cte,
sizeof(ChartTableEntry_onDisk_18));
502 EntryOffset = cte.EntryOffset;
503 ChartType = cte.ChartType;
504 ChartFamily = cte.ChartFamily;
510 m_bbox.Set(LatMin, LonMin, LatMax, LonMax);
513 ProjectionType = cte.ProjectionType;
516 edition_date = cte.edition_date;
517 file_date = cte.file_date;
519 nPlyEntries = cte.nPlyEntries;
520 nAuxPlyEntries = cte.nAuxPlyEntries;
522 nNoCovrPlyEntries = cte.nNoCovrPlyEntries;
527 int npeSize = nPlyEntries * 2 *
sizeof(float);
528 pPlyTable = (
float *)malloc(npeSize);
529 is.Read(pPlyTable, npeSize);
532 if (nAuxPlyEntries) {
533 int napeSize = nAuxPlyEntries *
sizeof(int);
534 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
535 pAuxCntTable = (
int *)malloc(napeSize);
536 is.Read(pAuxCntTable, napeSize);
538 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
540 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
541 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
542 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
546 if (nNoCovrPlyEntries) {
547 int napeSize = nNoCovrPlyEntries *
sizeof(int);
548 pNoCovrCntTable = (
int *)malloc(napeSize);
549 is.Read(pNoCovrCntTable, napeSize);
551 pNoCovrPlyTable = (
float **)malloc(nNoCovrPlyEntries *
sizeof(
float *));
552 for (
int i = 0; i < nNoCovrPlyEntries; i++) {
553 int nfSize = pNoCovrCntTable[i] * 2 *
sizeof(float);
554 pNoCovrPlyTable[i] = (
float *)malloc(nfSize);
555 is.Read(pNoCovrPlyTable[i], nfSize);
560 else if (db_version == 17) {
562 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
564 wxLogVerbose(
" Chart %s", path);
567 m_pfilename =
new wxString;
568 wxString fullfilename(path, wxConvUTF8);
569 wxFileName fn(fullfilename);
570 *m_pfilename = fn.GetFullName();
571 m_psFullPath =
new wxString;
572 *m_psFullPath = fullfilename;
575 ChartTableEntry_onDisk_17 cte;
576 is.Read(&cte,
sizeof(ChartTableEntry_onDisk_17));
579 EntryOffset = cte.EntryOffset;
580 ChartType = cte.ChartType;
586 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
589 ProjectionType = cte.ProjectionType;
592 edition_date = cte.edition_date;
593 file_date = cte.file_date;
595 nPlyEntries = cte.nPlyEntries;
596 nAuxPlyEntries = cte.nAuxPlyEntries;
598 nNoCovrPlyEntries = cte.nNoCovrPlyEntries;
603 int npeSize = nPlyEntries * 2 *
sizeof(float);
604 pPlyTable = (
float *)malloc(npeSize);
605 is.Read(pPlyTable, npeSize);
608 if (nAuxPlyEntries) {
609 int napeSize = nAuxPlyEntries *
sizeof(int);
610 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
611 pAuxCntTable = (
int *)malloc(napeSize);
612 is.Read(pAuxCntTable, napeSize);
614 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
616 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
617 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
618 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
622 if (nNoCovrPlyEntries) {
623 int napeSize = nNoCovrPlyEntries *
sizeof(int);
624 pNoCovrCntTable = (
int *)malloc(napeSize);
625 is.Read(pNoCovrCntTable, napeSize);
627 pNoCovrPlyTable = (
float **)malloc(nNoCovrPlyEntries *
sizeof(
float *));
628 for (
int i = 0; i < nNoCovrPlyEntries; i++) {
629 int nfSize = pNoCovrCntTable[i] * 2 *
sizeof(float);
630 pNoCovrPlyTable[i] = (
float *)malloc(nfSize);
631 is.Read(pNoCovrPlyTable[i], nfSize);
636 else if (db_version == 16) {
638 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
641 wxLogVerbose(
" Chart %s", path);
644 m_pfilename =
new wxString;
645 wxString fullfilename(path, wxConvUTF8);
646 wxFileName fn(fullfilename);
647 *m_pfilename = fn.GetFullName();
648 m_psFullPath =
new wxString;
649 *m_psFullPath = fullfilename;
652 ChartTableEntry_onDisk_16 cte;
653 is.Read(&cte,
sizeof(ChartTableEntry_onDisk_16));
656 EntryOffset = cte.EntryOffset;
657 ChartType = cte.ChartType;
663 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
666 ProjectionType = cte.ProjectionType;
669 edition_date = cte.edition_date;
670 file_date = cte.file_date;
672 nPlyEntries = cte.nPlyEntries;
673 nAuxPlyEntries = cte.nAuxPlyEntries;
678 int npeSize = nPlyEntries * 2 *
sizeof(float);
679 pPlyTable = (
float *)malloc(npeSize);
680 is.Read(pPlyTable, npeSize);
683 if (nAuxPlyEntries) {
684 int napeSize = nAuxPlyEntries *
sizeof(int);
685 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
686 pAuxCntTable = (
int *)malloc(napeSize);
687 is.Read(pAuxCntTable, napeSize);
689 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
691 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
692 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
693 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
698 else if (db_version == 15) {
700 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
703 wxLogVerbose(
" Chart %s", path);
706 ChartTableEntry_onDisk_15 cte;
707 is.Read(&cte,
sizeof(ChartTableEntry_onDisk_15));
710 EntryOffset = cte.EntryOffset;
711 ChartType = cte.ChartType;
717 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
720 edition_date = cte.edition_date;
721 file_date = cte.file_date;
723 nPlyEntries = cte.nPlyEntries;
724 nAuxPlyEntries = cte.nAuxPlyEntries;
729 int npeSize = nPlyEntries * 2 *
sizeof(float);
730 pPlyTable = (
float *)malloc(npeSize);
731 is.Read(pPlyTable, npeSize);
734 if (nAuxPlyEntries) {
735 int napeSize = nAuxPlyEntries *
sizeof(int);
736 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
737 pAuxCntTable = (
int *)malloc(napeSize);
738 is.Read(pAuxCntTable, napeSize);
740 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
742 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
743 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
744 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
747 }
else if (db_version == 14) {
749 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
751 wxLogVerbose(
" Chart %s", path);
754 ChartTableEntry_onDisk_14 cte;
755 is.Read(&cte,
sizeof(ChartTableEntry_onDisk_14));
758 EntryOffset = cte.EntryOffset;
759 ChartType = cte.ChartType;
765 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
768 edition_date = cte.edition_date;
770 nPlyEntries = cte.nPlyEntries;
771 nAuxPlyEntries = cte.nAuxPlyEntries;
775 int npeSize = nPlyEntries * 2 *
sizeof(float);
776 pPlyTable = (
float *)malloc(npeSize);
777 is.Read(pPlyTable, npeSize);
780 if (nAuxPlyEntries) {
781 int napeSize = nAuxPlyEntries *
sizeof(int);
782 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
783 pAuxCntTable = (
int *)malloc(napeSize);
784 is.Read(pAuxCntTable, napeSize);
786 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
788 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
789 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
790 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
794 ChartFamily = convertChartFamily(ChartType, ChartFamily);
795 ChartType = convertChartType(ChartType);
802bool ChartTableEntry::Write(
const ChartDatabase *pDb, wxOutputStream &os) {
803 os.Write(m_FullPath.c_str(), m_FullPath.size() + 1);
807 ChartTableEntry_onDisk_18 cte;
810 cte.EntryOffset = EntryOffset;
811 cte.ChartType = ChartType;
812 cte.ChartFamily = ChartFamily;
819 cte.edition_date = edition_date;
820 cte.file_date = file_date;
822 cte.nPlyEntries = nPlyEntries;
823 cte.nAuxPlyEntries = nAuxPlyEntries;
826 cte.ProjectionType = ProjectionType;
830 cte.nNoCovrPlyEntries = nNoCovrPlyEntries;
832 os.Write(&cte,
sizeof(ChartTableEntry_onDisk_18));
833 wxLogVerbose(
" Wrote Chart %s", m_FullPath.c_str());
837 int npeSize = nPlyEntries * 2 *
sizeof(float);
838 os.Write(pPlyTable, npeSize);
841 if (nAuxPlyEntries) {
842 int napeSize = nAuxPlyEntries *
sizeof(int);
843 os.Write(pAuxCntTable, napeSize);
845 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries; nAuxPlyEntry++) {
846 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
847 os.Write(pAuxPlyTable[nAuxPlyEntry], nfSize);
851 if (nNoCovrPlyEntries) {
852 int ncSize = nNoCovrPlyEntries *
sizeof(int);
853 os.Write(pNoCovrCntTable, ncSize);
855 for (
int i = 0; i < nNoCovrPlyEntries; i++) {
856 int nctSize = pNoCovrCntTable[i] * 2 *
sizeof(float);
857 os.Write(pNoCovrPlyTable[i], nctSize);
866void ChartTableEntry::Clear() {
873 pNoCovrCntTable = NULL;
874 pNoCovrPlyTable = NULL;
876 nNoCovrPlyEntries = 0;
886void ChartTableEntry::Disable() {
890 LatMax += (float)1000.;
891 LatMin += (float)1000.;
894void ChartTableEntry::ReEnable() {
896 LatMax -= (float)1000.;
897 LatMin -= (float)1000.;
900bool ChartTableEntry::IsBasemap()
const {
901 wxFileName fn(GetFullPath());
902 return (fn.GetPath().Lower().Contains(
"basemap"));
905std::vector<float> ChartTableEntry::GetReducedPlyPoints() {
906 if (m_reducedPlyPoints.size())
return m_reducedPlyPoints;
909 float LOD_meters = 1;
911 float plylat, plylon;
912 const int nPoints = GetnPlyEntries();
914 float *fpo = GetpPlyTable();
916 double *ppd =
new double[nPoints * 2];
917 double *ppsm =
new double[nPoints * 2];
920 for (
int i = 0; i < nPoints; i++) {
922 plylon = fpo[i * 2 + 1];
925 toSM(plylat, plylon, fpo[0], fpo[1], &x, &y);
933 std::vector<int> index_keep;
935 index_keep.push_back(0);
936 index_keep.push_back(nPoints - 1);
937 index_keep.push_back(1);
938 index_keep.push_back(nPoints - 2);
940 DouglasPeuckerM(ppsm, 1, nPoints - 2, LOD_meters, &index_keep);
943 index_keep.resize(nPoints);
944 for (
int i = 0; i < nPoints; i++) index_keep[i] = i;
948 for (
int ip = 0; ip < nPoints; ip++) {
952 for (
unsigned int j = 0; j < index_keep.size(); j++) {
953 if (index_keep[j] == ip) {
954 m_reducedPlyPoints.push_back(x);
955 m_reducedPlyPoints.push_back(y);
964 int nprr = m_reducedPlyPoints.size() / 2;
966 return m_reducedPlyPoints;
969std::vector<float> ChartTableEntry::GetReducedAuxPlyPoints(
int iTable) {
971 if (!m_reducedAuxPlyPointsVector.size()) {
972 std::vector<float> vec;
973 for (
int i = 0; i < GetnAuxPlyEntries(); i++) {
974 m_reducedAuxPlyPointsVector.push_back(vec);
978 std::vector<float> vec;
981 if ((
unsigned int)iTable >= m_reducedAuxPlyPointsVector.size())
return vec;
983 if (m_reducedAuxPlyPointsVector.at(iTable).size())
984 return m_reducedAuxPlyPointsVector.at(iTable);
987 float LOD_meters = 1.0;
989 const int nPoints = GetAuxCntTableEntry(iTable);
990 float *fpo = GetpAuxPlyTableEntry(iTable);
992 double *ppd =
new double[nPoints * 2];
993 double *ppsm =
new double[nPoints * 2];
996 float plylat, plylon;
998 for (
int i = 0; i < nPoints; i++) {
1000 plylon = fpo[i * 2 + 1];
1003 toSM(plylat, plylon, fpo[0], fpo[1], &x, &y);
1011 std::vector<int> index_keep;
1013 index_keep.push_back(0);
1014 index_keep.push_back(nPoints - 1);
1015 index_keep.push_back(1);
1016 index_keep.push_back(nPoints - 2);
1018 DouglasPeuckerM(ppsm, 1, nPoints - 2, LOD_meters, &index_keep);
1021 index_keep.resize(nPoints);
1022 for (
int i = 0; i < nPoints; i++) index_keep[i] = i;
1025 int nnn = index_keep.size();
1028 for (
int ip = 0; ip < nPoints; ip++) {
1032 for (
unsigned int j = 0; j < index_keep.size(); j++) {
1033 if (index_keep[j] == ip) {
1044 m_reducedAuxPlyPointsVector[iTable] = vec;
1046 int nprr = vec.size() / 2;
1054const int ID_DBS_PROGRESS_UPDATE = wxNewId();
1056WX_DEFINE_OBJARRAY(ChartTable);
1058ChartDatabase::ChartDatabase() {
1062 m_ChartTableEntryDummy.Clear();
1064 Bind(wxEVT_OCPN_CHARTTABLEENTRYTHREAD, &ChartDatabase::OnEvtThread,
this);
1065 Bind(wxEVT_COMMAND_MENU_SELECTED, &ChartDatabase::OnDBSProgressUpdate,
this,
1066 ID_DBS_PROGRESS_UPDATE);
1068 m_progress_timer.SetOwner(
this, ID_TIMER_UPDATE_PROGRESS);
1069 Bind(wxEVT_TIMER, &ChartDatabase::OnProgessTimer,
this,
1070 ID_TIMER_UPDATE_PROGRESS);
1072 m_update_competion_timer.SetOwner(
this, ID_TIMER_UPDATE_COMPLETE);
1073 Bind(wxEVT_TIMER, &ChartDatabase::OnUpdateComplete,
this,
1074 ID_TIMER_UPDATE_COMPLETE);
1076 UpdateChartClassDescriptorArray();
1079void ChartDatabase::OnDBSProgressUpdate(wxCommandEvent &evt) {}
1081void ChartDatabase::OnUpdateComplete(wxTimerEvent &event) {
1082 ProcessThreadQueueEmpty();
1085void ChartDatabase::OnProgessTimer(wxTimerEvent &event) {
1086 if (m_pprog && m_progress_timer.IsRunning() && (m_jobsRemaining > 1)) {
1087 if (m_progress_dirty.exchange(
false)) {
1089 qDebug() <<
" ***Update()" << m_jobsRemaining;
1091 m_pprog->Update(m_progress_value, m_progress_message);
1093 qDebug() <<
" ---Update() out" << m_jobsRemaining;
1099void ChartDatabase::ProcessThreadQueueEmpty() {
1101 qDebug() <<
"ProcessThreadQueueEmpty";
1103 if (m_deferred_ticket_vector.size()) {
1107 for (
auto &ticket_d : m_deferred_ticket_vector) {
1108 ticket_d->m_ticket_type = 0;
1109 m_pool_deferred.Push(ticket_d);
1112 m_deferred_ticket_vector.clear();
1117 m_ticketcount = m_jobsRemaining;
1118 m_nFileProgressQuantum = 1;
1119 m_progress_message = _(
"Processing charts");
1120 m_progress_timer.Start(50, wxTIMER_CONTINUOUS);
1123 if (m_jobsRemaining) {
1124 const int workerCount = 1;
1125 if (m_pool_deferred.GetWorkerCount() < workerCount) {
1126 int threads_needed = workerCount - m_pool_deferred.GetWorkerCount();
1127 for (
int i = 0; i < threads_needed; ++i) {
1128 (
new PoolWorkerThread(m_pool_deferred,
this))->Run();
1129 m_pool_deferred.AddWorker();
1136 m_progress_timer.Stop();
1137 if (m_pprog) m_pprog->Hide();
1146 for (
auto &ticket_valid : m_ticket_vector) {
1147 if (ticket_valid->m_chart_table_entry)
1148 active_chartTable.push_back(ticket_valid->m_chart_table_entry);
1152 m_ticket_vector.clear();
1155 qDebug() <<
"FinalizeChartUpdate";
1158 FinalizeChartUpdate();
1163 qDebug() <<
"Remaining: " << m_jobsRemaining;
1166 auto ticket =
event.GetTicket();
1172 if (m_pprog && (m_jobsRemaining > 1)) {
1174 double ratio = 100. * (double)m_progcount / m_ticketcount;
1176 if (((m_progcount % m_nFileProgressQuantum) == 0)) {
1177 if (val != m_progint) {
1179 m_progress_value = val;
1180 m_progress_dirty =
true;
1185 if (!ticket->m_ticket_type) {
1186 bool collision_found =
false;
1187 if (ticket->b_thread_safe) {
1188 wxFileName fn(ticket->m_ChartPath);
1189 ChartCollisionsHashMap::iterator it;
1190 for (it = m_full_collision_map.begin(); it != m_full_collision_map.end();
1192 if (it->first.IsSameAs(fn.GetFullName())) {
1196 collision_found =
true;
1200 if (!collision_found) {
1201 m_full_collision_map[fn.GetFullName()] = 1;
1206 m_ticket_vector.push_back(ticket);
1209 int remaining = --m_jobsRemaining;
1212 m_pool.pending_events--;
1213 m_pool_deferred.pending_events--;
1215 if (remaining == 0) {
1217 m_pool_deferred.Shutdown();
1218 m_progress_timer.Stop();
1221 qDebug() <<
"Fire completion Oneshot";
1223 m_update_competion_timer.Start(100, wxTIMER_ONE_SHOT);
1227void ChartDatabase::FinalizeChartUpdate() {
1230 active_chartTable.erase(
1231 std::remove_if(active_chartTable.begin(), active_chartTable.end(),
1232 [](
const auto &cte) { return !cte->GetbValid(); }),
1233 active_chartTable.end());
1235 size_t d = active_chartTable.size();
1238 active_chartTable_pathindex.clear();
1240 for (
auto cte : active_chartTable) {
1241 active_chartTable_pathindex[cte->GetFullSystemPath()] = i;
1242 cte->SetEntryOffset(i);
1246 m_nentries = active_chartTable.size();
1252 ChartData->SaveBinary(ChartListFileName);
1253 wxLogMessage(
"Finished chart database Update");
1256 if (m_pprog) m_pprog->Destroy();
1261 if (gWorldMapLocation.empty()) {
1265 gWorldMapLocation = gDefaultWorldMapLocation;
1266 m_gshhg_chart_loc = wxEmptyString;
1270 if (gWorldMapLocation != m_gshhg_chart_loc) {
1279 gshhsCrossesLandReset();
1292bool ChartDatabase::ScrubGroupArray() {
1297 bool b_change =
false;
1298 unsigned int igroup = 0;
1299 while (igroup < g_pGroupArray->GetCount()) {
1300 bool b_chart_in_element =
false;
1303 for (
unsigned int j = 0; j < pGroup->m_element_array.size(); j++) {
1304 const wxString &element_root = pGroup->m_element_array[j].m_element_name;
1306 for (
unsigned int ic = 0;
1307 ic < (
unsigned int)
ChartData->GetChartTableEntries(); ic++) {
1308 auto &cte =
ChartData->GetChartTableEntry(ic);
1309 wxString chart_full_path = cte.GetFullSystemPath();
1311 if (chart_full_path.StartsWith(element_root)) {
1312 b_chart_in_element =
true;
1318 if (!b_chart_in_element) {
1319 wxString test_string =
"GSHH";
1320 if (element_root.Upper().Contains(test_string))
1321 b_chart_in_element =
true;
1324 if (!b_chart_in_element)
1326 pGroup->m_element_array.erase(pGroup->m_element_array.begin() + j);
1338void ChartDatabase::UpdateChartClassDescriptorArray() {
1339 if (m_ChartClassDescriptorArray.empty()) {
1340 m_ChartClassDescriptorArray.push_back(
1341 ChartClassDescriptor(
"ChartKAP",
"*.kap",
nullptr, BUILTIN_DESCRIPTOR));
1342 m_ChartClassDescriptorArray.push_back(
1343 ChartClassDescriptor(
"ChartGEO",
"*.geo",
nullptr, BUILTIN_DESCRIPTOR));
1344 m_ChartClassDescriptorArray.push_back(
1345 ChartClassDescriptor(
"s57chart",
"*.000",
nullptr, BUILTIN_DESCRIPTOR));
1346 m_ChartClassDescriptorArray.push_back(
1347 ChartClassDescriptor(
"s57chart",
"*.s57",
nullptr, BUILTIN_DESCRIPTOR));
1348 m_ChartClassDescriptorArray.push_back(ChartClassDescriptor(
1349 "cm93compchart",
"00300000.a",
nullptr, BUILTIN_DESCRIPTOR));
1350 m_ChartClassDescriptorArray.push_back(ChartClassDescriptor(
1351 "ChartMbTiles",
"*.mbtiles",
nullptr, BUILTIN_DESCRIPTOR));
1356 m_ChartClassDescriptorArray.erase(
1357 std::remove_if(m_ChartClassDescriptorArray.begin(),
1358 m_ChartClassDescriptorArray.end(),
1359 [](
const ChartClassDescriptor &cd) {
1360 return cd.m_descriptor_type == PLUGIN_DESCRIPTOR;
1362 m_ChartClassDescriptorArray.end());
1364 wxArrayString array =
g_pi_manager->GetPlugInChartClassNameArray();
1365 for (
unsigned int j = 0; j < array.GetCount(); j++) {
1368 wxString class_name = array[j];
1369 ChartPlugInWrapper *cpiw =
new ChartPlugInWrapper(class_name);
1371 wxString mask = cpiw->GetFileSearchMask();
1374 auto plugin_t =
g_pi_manager->GetProvidingPlugin(class_name);
1375 opencpn_plugin *plugin =
dynamic_cast<opencpn_plugin *
>(plugin_t);
1377 m_ChartClassDescriptorArray.push_back(
1378 ChartClassDescriptor(class_name, mask, plugin, PLUGIN_DESCRIPTOR));
1386 if (index < GetChartTableEntries())
1387 return *active_chartTable[index];
1389 return m_ChartTableEntryDummy;
1392bool ChartDatabase::CompareChartDirArray(ArrayOfCDI &test_array) {
1396 if (test_array.GetCount() != m_dir_array.GetCount())
return false;
1399 unsigned int nfound_outer = 0;
1401 for (
unsigned int i = 0; i < test_array.GetCount(); i++) {
1402 ChartDirInfo p = test_array[i];
1403 bfound_inner =
false;
1404 for (
unsigned int j = 0; j < m_dir_array.GetCount(); j++) {
1405 ChartDirInfo q = m_dir_array[j];
1407 if (p.fullpath.IsSameAs(q.fullpath)) {
1408 bfound_inner =
true;
1412 if (bfound_inner) nfound_outer++;
1415 return (nfound_outer == test_array.GetCount());
1418wxString ChartDatabase::GetMagicNumberCached(wxString dir) {
1419 for (
unsigned int j = 0; j < m_dir_array.GetCount(); j++) {
1420 ChartDirInfo q = m_dir_array[j];
1421 if (dir.IsSameAs(q.fullpath))
return q.magic_number;
1427bool ChartDatabase::Read(
const wxString &filePath) {
1428 ChartTableEntry entry;
1433 wxFileName file(filePath);
1434 if (!file.FileExists())
return false;
1436 m_DBFileName = filePath;
1438 wxFFileInputStream ifs(filePath);
1439 if (!ifs.Ok())
return false;
1441 ChartTableHeader cth;
1443 if (!cth.CheckValid())
return false;
1447 memcpy(vbo, cth.GetDBVersionString(), 4);
1449 m_dbversion = atoi(&vbo[1]);
1450 s_dbVersion = m_dbversion;
1452 wxLogVerbose(
"Chartdb:Reading %d directory entries, %d table entries",
1453 cth.GetDirEntries(), cth.GetTableEntries());
1454 wxLogMessage(
"Chartdb: Chart directory list follows");
1455 if (0 == cth.GetDirEntries()) wxLogMessage(
" Nil");
1458 for (
int iDir = 0; iDir < cth.GetDirEntries(); iDir++) {
1461 ifs.Read(&dirlen,
sizeof(
int));
1462 while (dirlen > 0) {
1464 int alen = dirlen > 1023 ? 1023 : dirlen;
1465 if (ifs.Read(&dirbuf, alen).Eof())
goto read_error;
1468 dir.Append(wxString(dirbuf, wxConvUTF8));
1471 msg.Printf(
" Chart directory #%d: ", iDir);
1474 m_chartDirs.Add(dir);
1477 entries = cth.GetTableEntries();
1479 active_chartTable_pathindex.clear();
1480 while (entries-- && entry.Read(
this, ifs)) {
1481 active_chartTable_pathindex[entry.GetFullSystemPath()] = ind++;
1482 auto sharedPtr = std::make_shared<ChartTableEntry>(entry);
1483 active_chartTable.push_back(sharedPtr);
1488 entry.SetAvailable(
true);
1490 m_nentries = active_chartTable.size();
1495 m_nentries = active_chartTable.size();
1501bool ChartDatabase::Write(
const wxString &filePath) {
1502 wxFileName file(filePath);
1504 file.GetPath(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME, wxPATH_NATIVE));
1506 if (!dir.DirExists() && !dir.Mkdir())
return false;
1508 wxFFileOutputStream ofs(filePath);
1509 if (!ofs.Ok())
return false;
1511 ChartTableHeader cth(m_chartDirs.GetCount(), active_chartTable.size());
1514 for (
int iDir = 0; iDir < cth.GetDirEntries(); iDir++) {
1515 wxString dir1 = m_chartDirs[iDir];
1516 int dirlen = dir1.length();
1518 strncpy(s, dir1.mb_str(wxConvUTF8), 199);
1521 ofs.Write(&dirlen,
sizeof(
int));
1523 ofs.Write(s, dirlen);
1526 for (UINT32 iTable = 0; iTable < active_chartTable.size(); iTable++) {
1527 auto &cte = GetChartTableEntry(iTable);
1528 cte.Write(
this, ofs);
1532 m_dbversion = DB_VERSION_CURRENT;
1538wxString SplitPath(wxString s, wxString tkd,
int nchar,
int offset,
1544 wxStringTokenizer tkz(s, tkd);
1545 while (tkz.HasMoreTokens()) {
1546 wxString token = tkz.GetNextToken();
1547 if ((rlen + (
int)token.Len() + 1) < nchar) {
1550 rlen += token.Len() + 1;
1554 for (
int i = 0; i < offset; i++) {
1559 rlen = offset + token.Len() + 1;
1563 if (pn_split) *pn_split = ncr;
1565 return r.Mid(0, r.Len() - 1);
1568wxString ChartDatabase::GetFullChartInfo(
ChartBase *pc,
int dbIndex,
1569 int *char_width,
int *line_count) {
1572 unsigned int max_width = 0;
1574 unsigned int target_width = 60;
1576 const ChartTableEntry &cte = GetChartTableEntry(dbIndex);
1580 line = _(
" Name: ");
1581 wxString longline = pc->GetName();
1584 if (longline.Find(
' ') != wxNOT_FOUND)
1589 if (longline.Len() > target_width) {
1590 line += SplitPath(pc->GetName(), tkz, target_width, 12, &ncr);
1591 max_width = wxMax(max_width, target_width + 4);
1595 max_width = wxMax(max_width, line.Len() + 4);
1604 line.Printf(
" %s: 1:%d", _(
"Scale"), pc->GetNativeScale());
1606 line.Printf(
" %s: 1:%d", _(
"Scale"), cte.GetScale());
1609 max_width = wxMax(max_width, line.Len());
1613 wxDateTime ed = pc->GetEditionDate();
1615 line = _(
" Updated: ") + ed.FormatISODate() +
"\n";
1616 max_width = wxMax(max_width, line.Len());
1621 line = _(
" Source Edition: ") + pc->GetSE() +
"\n";
1622 max_width = wxMax(max_width, line.Len());
1628 line = _(
" Depth Units: ") + pc->GetDepthUnits() +
"\n";
1629 max_width = wxMax(max_width, line.Len());
1633 line = _(
" Soundings: ") + pc->GetSoundingsDatum() +
"\n";
1634 max_width = wxMax(max_width, line.Len());
1638 line = _(
" Datum: ") + pc->GetDatumString() +
"\n";
1639 max_width = wxMax(max_width, line.Len());
1644 line = _(
" Projection: ");
1645 if (PROJECTION_UNKNOWN == cte.GetChartProjectionType())
1646 line += _(
"Unknown");
1647 else if (PROJECTION_MERCATOR == cte.GetChartProjectionType())
1648 line += _(
"Mercator");
1649 else if (PROJECTION_TRANSVERSE_MERCATOR == cte.GetChartProjectionType())
1650 line += _(
"Transverse Mercator");
1651 else if (PROJECTION_POLYCONIC == cte.GetChartProjectionType())
1652 line += _(
"Polyconic");
1653 else if (PROJECTION_WEB_MERCATOR == cte.GetChartProjectionType())
1654 line += _(
"Web Mercator (EPSG:3857)");
1656 max_width = wxMax(max_width, line.Len());
1665 if (pc && pc->GetExtraInfo().Len()) {
1666 line += pc->GetExtraInfo();
1668 max_width = wxMax(max_width, line.Len());
1675 line += pc->GetID();
1677 max_width = wxMax(max_width, line.Len());
1682 line = _(
" ChartFile: ");
1683 wxString longline = *(cte.GetpsFullPath());
1684 if (longline.Len() > target_width) {
1685 line += SplitPath(longline,
"/,\\", target_width, 15, &ncr);
1686 max_width = wxMax(max_width, target_width + 4);
1690 max_width = wxMax(max_width, line.Len() + 4);
1696 if (line_count) *line_count = lc;
1698 if (char_width) *char_width = max_width;
1708 wxGenericProgressDialog *pprog) {
1709 m_dir_array = dir_array;
1713 m_chartDirs.Clear();
1714 active_chartTable.clear();
1715 active_chartTable_pathindex.clear();
1717 Update(dir_array,
true, pprog);
1722 m_dbversion = DB_VERSION_CURRENT;
1731class GshhsTraverser :
public wxDirTraverser {
1734 virtual wxDirTraverseResult OnFile(
const wxString &filename)
override {
1735 wxFileName fn(filename);
1736 wxFileName dir(fn.GetPath());
1737 if (fn.GetFullName().Matches(
"poly-*-1.dat") &&
1738 dir.GetFullName().IsSameAs(
"GSHHG",
false)) {
1739 parent_dir = fn.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
1742 return wxDIR_CONTINUE;
1744 virtual wxDirTraverseResult OnDir(
const wxString &dirname)
override {
1746 return wxDIR_CONTINUE;
1748 wxString GetGshhsDir()
const {
return parent_dir; }
1751 wxString parent_dir;
1758wxString findGshhgDirectory(
const wxString &directory) {
1759 wxDir dir(directory);
1760 if (!dir.IsOpened()) {
1761 return wxEmptyString;
1764 dir.Traverse(traverser, wxEmptyString, wxDIR_FILES | wxDIR_DIRS);
1765 return traverser.GetGshhsDir();
1768bool ChartDatabase::UpdateChartDatabaseInplace(ArrayOfCDI &DirArray,
1770 wxGenericProgressDialog *_prog) {
1776 wxLogMessage(
"Starting chart database Update...");
1780 m_gshhg_chart_loc = gWorldMapLocation;
1781 gWorldMapLocation = wxEmptyString;
1783 Update(DirArray, b_force, m_pprog);
1794 wxGenericProgressDialog *pprog) {
1796 m_ticket_vector.clear();
1797 m_full_collision_map.clear();
1798 m_jobsRemaining = 0;
1799 m_deferred_ticket_vector.clear();
1800 SetChartDirArray(dir_array);
1807 for (
unsigned int i = 0; i < active_chartTable.size(); i++) {
1808 auto &cte = GetChartTableEntry(i);
1809 cte.SetValid(
false);
1812 m_chartDirs.Clear();
1814 if (bForce) active_chartTable.clear();
1816 bool lbForce = bForce;
1819 if (s_dbVersion != DB_VERSION_CURRENT) {
1820 active_chartTable.clear();
1822 s_dbVersion = DB_VERSION_CURRENT;
1823 m_dbversion = DB_VERSION_CURRENT;
1828 for (
unsigned int j = 0; j < dir_array.GetCount(); j++) {
1835 if (!androidIsDirWritable(dir_info.fullpath))
continue;
1841 wxString gshhg_dir = findGshhgDirectory(dir_info.fullpath);
1842 if (!gshhg_dir.empty()) {
1847 wxLogMessage(
"Updating GSHHG directory: %s", gshhg_dir.c_str());
1848 gWorldMapLocation = gshhg_dir;
1853 if (dir_info.fullpath.Find(
"OSMSHP") != wxNOT_FOUND) {
1854 if (!wxDir::FindFirst(dir_info.fullpath,
"basemap_*.shp").empty()) {
1855 wxLogMessage(
"Updating OSMSHP directory: %s", dir_info.fullpath);
1856 gWorldShapefileLocation =
1857 dir_info.fullpath + wxFileName::GetPathSeparator();
1862 TraverseDirAndAddCharts(dir_info, pprog, dir_magic, lbForce);
1865 dir_info.magic_number = dir_magic;
1866 dir_array.RemoveAt(j);
1867 dir_array.Insert(dir_info, j);
1869 m_chartDirs.Add(dir_info.fullpath);
1873 if (m_chartDirs.IsEmpty() || !m_jobsRemaining) {
1874 if (!m_deferred_ticket_vector.size()) {
1875 FinalizeChartUpdate();
1880 auto ticket_deferred = m_deferred_ticket_vector.back();
1881 ticket_deferred->m_ticket_type = 1;
1883 m_jobsRemaining = 1;
1884 evt->SetTicket(ticket_deferred);
1885 wxQueueEvent(
this, evt);
1892 m_ticketcount = m_jobsRemaining;
1893 m_nFileProgressQuantum = 1;
1894 m_progress_message = _(
"Processing charts");
1896 m_progress_timer.Start(50, wxTIMER_CONTINUOUS);
1899 if (m_jobsRemaining) {
1900 int workerCount = 4;
1904 if (m_pool.GetWorkerCount() < workerCount) {
1905 int threads_needed = workerCount - m_pool.GetWorkerCount();
1906 for (
int i = 0; i < threads_needed; ++i) {
1920int ChartDatabase::FinddbIndex(wxString PathToFind) {
1921 if (active_chartTable_pathindex.find(PathToFind) !=
1922 active_chartTable_pathindex.end())
1923 return active_chartTable_pathindex[PathToFind];
1932int ChartDatabase::DisableChart(wxString &PathToDisable) {
1933 int index = FinddbIndex(PathToDisable);
1935 auto &entry = GetChartTableEntry(index);
1949int ChartDatabase::TraverseDirAndAddCharts(
ChartDirInfo &dir_info,
1950 wxGenericProgressDialog *pprog,
1951 wxString &dir_magic,
bool bForce) {
1953 wxString dir_path = dir_info.fullpath;
1955 dir_path = wxString(dir_info.fullpath.mb_str(wxConvUTF8));
1958 wxString old_magic = dir_info.magic_number;
1959 wxString new_magic = old_magic;
1960 dir_magic = old_magic;
1964 bool b_skipDetectDirChange =
false;
1965 bool b_dirchange =
false;
1968 if (!wxDir::Exists(dir_path))
return 0;
1974 bool b_cm93 = Check_CM93_Structure(dir_path);
1976 b_skipDetectDirChange =
true;
1982 if (!b_skipDetectDirChange)
1983 b_dirchange = DetectDirChange(dir_path, dir_info.fullpath, old_magic,
1986 if (!bForce && !b_dirchange) {
1987 wxString msg(
" No change detected on directory ");
1988 msg.Append(dir_path);
1994 wxFileName fn_dir(dir_path,
"stuff");
1995 unsigned int dir_path_count = fn_dir.GetDirCount();
1997 if (pprog) pprog->SetTitle(_(
"OpenCPN Chart Scan...."));
1999 int nEntries = active_chartTable.size();
2001 for (
int ic = 0; ic < nEntries; ic++) {
2002 auto &cte = GetChartTableEntry(ic);
2003 wxFileName fn(cte.GetFullSystemPath());
2005 while (fn.GetDirCount() >= dir_path_count) {
2006 if (fn.GetPath() == dir_path) {
2007 auto &cte_a = GetChartTableEntry(ic);
2008 cte_a.SetValid(
true);
2024 dir_magic = new_magic;
2027 for (
auto &cd : m_ChartClassDescriptorArray) {
2028 nAdd += SearchDirAndAddCharts(dir_info.fullpath, cd, pprog);
2034bool ChartDatabase::DetectDirChange(
const wxString &dir_path,
2035 const wxString &prog_label,
2036 const wxString &magic, wxString &new_magic,
2037 wxGenericProgressDialog *pprog) {
2038 if (pprog) pprog->SetTitle(_(
"OpenCPN Directory Scan...."));
2041 long long unsigned int nmagic;
2042 wxULongLong nacc = 0;
2044 magic.ToULongLong(&nmagic, 10);
2047 wxArrayString FileList;
2048 wxDir dir(dir_path);
2049 int n_files = dir.GetAllFiles(dir_path, &FileList);
2052 FlexHash hash(
sizeof nacc);
2055 if (pprog) pprog->Update(0, prog_label);
2059 for (
int ifile = 0; ifile < n_files; ifile++) {
2060 wxFileName file(FileList[ifile]);
2065 wxString fileNameNative = file.GetFullPath();
2066 wxScopedCharBuffer fileNameUTF8 = fileNameNative.ToUTF8();
2067 hash.Update(fileNameUTF8.data(), fileNameUTF8.length());
2070 wxULongLong size = file.GetSize();
2071 wxULongLong fileSize = ((size != wxInvalidSize) ? size : 0);
2072 hash.Update(&fileSize, (
sizeof fileSize));
2075 wxDateTime t = file.GetModificationTime();
2076 wxULongLong fileTime = t.GetTicks();
2077 hash.Update(&fileTime, (
sizeof fileTime));
2081 hash.Receive(&nacc);
2084 new_magic = nacc.ToString();
2087 if (new_magic != magic)
2093bool ChartDatabase::IsChartDirUsed(
const wxString &theDir) {
2094 wxString dir(theDir);
2095 if (dir.Last() ==
'/' || dir.Last() == wxFileName::GetPathSeparator())
2099 for (UINT32 i = 0; i < active_chartTable.size(); i++) {
2100 auto &cte_u = GetChartTableEntry(i);
2101 if (cte_u.GetpsFullPath()->Matches(dir))
return true;
2110bool ChartDatabase::Check_CM93_Structure(wxString dir_name) {
2113 wxRegEx test(
"[0-9]+");
2115 wxDir dirt(dir_name);
2118 if (dirt.IsOpened())
2119 wxLogMessage(
"check_cm93 opened dir OK: " + dir_name);
2121 wxLogMessage(
"check_cm93 NOT OPENED OK: " + dir_name);
2122 wxLogMessage(
"check_cm93 returns false." + dir_name);
2126 bool b_maybe_found_cm93 =
false;
2127 bool b_cont = dirt.GetFirst(&candidate);
2130 if (test.Matches(candidate) && (candidate.Len() == 8)) {
2131 b_maybe_found_cm93 =
true;
2135 b_cont = dirt.GetNext(&candidate);
2138 if (b_maybe_found_cm93) {
2139 wxString dir_next = dir_name;
2141 dir_next += candidate;
2142 if (wxDir::Exists(dir_next)) {
2143 wxDir dir_n(dir_next);
2144 if (dirt.IsOpened()) {
2145 wxString candidate_n;
2147 wxRegEx test_n(
"^[A-Ga-g]");
2148 bool b_probably_found_cm93 =
false;
2149 bool b_cont_n = dir_n.IsOpened() && dir_n.GetFirst(&candidate_n);
2151 if (test_n.Matches(candidate_n) && (candidate_n.Len() == 1)) {
2152 b_probably_found_cm93 =
true;
2155 b_cont_n = dir_n.GetNext(&candidate_n);
2158 if (b_probably_found_cm93)
2163 wxString dir_luk = dir_next;
2165 dir_luk += candidate_n;
2166 if (wxDir::Exists(dir_luk))
return true;
2279int ChartDatabase::SearchDirAndAddCharts(wxString &dir_name_base,
2281 wxGenericProgressDialog *pprog) {
2282 wxString msg(
"Searching directory: ");
2283 msg += dir_name_base;
2285 msg += chart_desc.m_search_mask;
2288 wxString dir_name = dir_name_base;
2291 dir_name = wxString(dir_name_base.mb_str(wxConvUTF8));
2294 if (!wxDir::Exists(dir_name))
return 0;
2296 wxString filespec = chart_desc.m_search_mask.Upper();
2297 wxString lowerFileSpec = chart_desc.m_search_mask.Lower();
2298 wxString filespecXZ = filespec +
".xz";
2299 wxString lowerFileSpecXZ = lowerFileSpec +
".xz";
2303 wxArrayString FileList;
2304 int gaf_flags = wxDIR_DEFAULT;
2311 bool b_found_cm93 =
false;
2312 bool b_cm93 = Check_CM93_Structure(dir_name);
2314 if (filespec !=
"00300000.A")
2317 filespec = dir_name;
2318 b_found_cm93 =
true;
2322 if (!b_found_cm93) {
2323 wxDir dir(dir_name);
2324 dir.GetAllFiles(dir_name, &FileList, filespec, gaf_flags);
2327 if (!FileList.GetCount()) {
2328 wxArrayString afl = androidTraverseDir(dir_name, filespec);
2329 for (wxArrayString::const_iterator item = afl.begin(); item != afl.end();
2331 FileList.Add(*item);
2336 if (filespec != lowerFileSpec) {
2338 wxArrayString lowerFileList;
2339 dir.GetAllFiles(dir_name, &lowerFileList, lowerFileSpec, gaf_flags);
2342 if (!lowerFileList.GetCount()) {
2343 wxArrayString afl = androidTraverseDir(dir_name, lowerFileSpec);
2344 for (wxArrayString::const_iterator item = afl.begin();
2345 item != afl.end(); item++)
2346 lowerFileList.Add(*item);
2350 for (wxArrayString::const_iterator item = lowerFileList.begin();
2351 item != lowerFileList.end(); item++)
2352 FileList.Add(*item);
2358 dir.GetAllFiles(dir_name, &FileList, filespecXZ, gaf_flags);
2359 dir.GetAllFiles(dir_name, &FileList, lowerFileSpecXZ, gaf_flags);
2365 wxString dir_plus = dir_name;
2366 dir_plus += wxFileName::GetPathSeparator();
2367 FileList.Add(dir_plus);
2370 int nFile = FileList.GetCount();
2372 if (!nFile)
return false;
2381 if (pprog) pprog->SetTitle(_(
"OpenCPN Chart Add...."));
2385 ChartCollisionsHashMap collision_map;
2394 std::vector<std::shared_ptr<ChartTableEntryJobTicket>> ticket_vector;
2396 for (
int ifile = 0; ifile < nFile; ifile++) {
2397 wxFileName file(FileList[ifile]);
2398 wxString full_path = file.GetFullPath();
2399 wxString file_name = file.GetFullName();
2400 wxString utf8_path = full_path;
2408 wxFileName fnbase(dir_name_base);
2409 int nDirs = fnbase.GetDirCount();
2411 wxFileName file_target(FileList[ifile]);
2413 for (
int i = 0; i < nDirs + 1;
2415 file_target.RemoveDir(0);
2417 wxString leftover_path = file_target.GetFullPath();
2419 dir_name_base + leftover_path;
2425 if (!file_name.Matches(lowerFileSpec) && !file_name.Matches(filespec) &&
2426 !file_name.Matches(lowerFileSpecXZ) && !file_name.Matches(filespecXZ) &&
2432 ChartTableEntry *pnewChart = NULL;
2433 bool bAddFinal =
true;
2449 if (b_found_cm93) collision =
false;
2452 pEntry = &active_chartTable[collision_ptr->second];
2453 table_file_name = pEntry->GetFullSystemPath();
2455 bthis_dir_in_dB && full_name.IsSameAs(table_file_name);
2459 if (file_path_is_same) {
2463 time_t t_oldFile = pEntry->GetFileTime();
2464 time_t t_newFile = file.GetModificationTime().GetTicks();
2466 if (t_newFile <= t_oldFile) {
2467 file_time_is_same =
true;
2469 pEntry->SetValid(
true);
2472 pEntry->SetValid(
false);
2479 bool collision_found =
false;
2480 ChartCollisionsHashMap::iterator it;
2481 for (it = collision_map.begin(); it != collision_map.end(); ++it) {
2482 if (it->first.IsSameAs(file_name)) {
2486 collision_found =
true;
2491 if (!collision_found) {
2493 auto ticket = std::make_shared<ChartTableEntryJobTicket>();
2494 ticket->m_ChartPath = full_path;
2495 ticket->m_ChartPathUTF8 = full_path;
2496 ticket->chart_desc = chart_desc;
2498 ticket_vector.push_back(ticket);
2499 collision_map[file_name] = ifile;
2506 bool is_kap =
false;
2507 if (chart_desc.m_descriptor_type == PLUGIN_DESCRIPTOR || is_kap) {
2509 opencpn_plugin *plugin = chart_desc.m_plugin;
2510 opencpn_plugin_118 *provider_plugin118 =
dynamic_cast<opencpn_plugin_118 *
>(
2511 dynamic_cast<opencpn_plugin *
>(plugin));
2513 if (!provider_plugin118 || is_kap) {
2517 for (
auto &ticket : ticket_vector) {
2518 ticket->b_thread_safe =
false;
2519 ticket->m_provider_type = 1;
2520 ticket->provider_class_name = chart_desc.m_class_name;
2521 m_deferred_ticket_vector.push_back(ticket);
2530 for (
auto &ticket : ticket_vector) {
2531 m_pool.Push(ticket);
2539bool ChartDatabase::AddChart(wxString &chartfilename,
2541 wxGenericProgressDialog *pprog,
int isearch,
2542 bool bthis_dir_in_dB) {
2544 wxFileName file(chartfilename);
2545 wxString full_name = file.GetFullPath();
2546 wxString file_name = file.GetFullName();
2556 pprog->Update(wxMin((m_pdifile * 100) / m_pdnFile, 100), full_name);
2558 ChartTableEntry *pnewChart = NULL;
2559 bool bAddFinal =
true;
2561 wxString msg_fn(full_name);
2562 msg_fn.Replace(
"%",
"%%");
2564 pnewChart = CreateChartTableEntry(full_name, full_name, chart_desc);
2567 wxLogMessage(wxString::Format(
2568 " CreateChartTableEntry() failed for file: %s", msg_fn.c_str()));
2573 int nEntry = active_chartTable.size();
2574 for (
int i = 0; i < nEntry; i++) {
2575 auto &cte_a = GetChartTableEntry(i);
2576 wxString *ptable_file_name = cte_a.GetpsFullPath();
2580 if (bthis_dir_in_dB && full_name.IsSameAs(*ptable_file_name)) {
2584 auto &cte_search = GetChartTableEntry(isearch);
2585 time_t t_oldFile = cte_search.GetFileTime();
2586 time_t t_newFile = file.GetModificationTime().GetTicks();
2588 if (t_newFile <= t_oldFile) {
2590 cte_search.SetValid(
true);
2593 cte_search.SetValid(
false);
2595 wxString::Format(
" Replacing older chart file of same path: %s",
2605 wxFileName table_file(*ptable_file_name);
2607 if (table_file.GetFullName() == file_name) {
2612 if (pnewChart->IsEarlierThan(active_chartTable[isearch])) {
2614 if (table_file.IsFileReadable()) {
2615 active_chartTable[isearch].SetValid(
true);
2617 wxLogMessage(wxString::Format(
2618 " Retaining newer chart file of same name: %s",
2621 }
else if (pnewChart->IsEqualTo(active_chartTable[isearch])) {
2631 active_chartTable[isearch].SetValid(
false);
2634 wxString::Format(
" Replacing older chart file of same name: %s",
2644 if (nEntry == isearch) isearch = 0;
2649 if (0 == b_add_msg) {
2651 wxString::Format(
" Adding chart file: %s", msg_fn.c_str()));
2653 std::shared_ptr<ChartTableEntry> sharedPtr(pnewChart);
2654 active_chartTable.push_back(sharedPtr);
2663 m_nentries = active_chartTable.size();
2668bool ChartDatabase::AddSingleChart(wxString &ChartFullPath,
2669 bool b_force_full_search) {
2671 wxFileName fn(ChartFullPath);
2672 wxString ext = fn.GetExt();
2674 wxString ext_upper = ext.MakeUpper();
2675 wxString ext_lower = ext.MakeLower();
2676 wxString dir_name = fn.GetPath();
2681 ChartClassDescriptor desc;
2682 for (
auto &cd : m_ChartClassDescriptorArray) {
2683 if (cd.m_descriptor_type == PLUGIN_DESCRIPTOR) {
2684 if (cd.m_search_mask == ext_upper) {
2688 if (cd.m_search_mask == ext_lower) {
2697 bool b_recurse =
true;
2698 if (!b_force_full_search) b_recurse = IsChartDirUsed(dir_name);
2700 bool rv = AddChart(ChartFullPath, desc, NULL, 0, b_recurse);
2704 for (
unsigned int i = 0; i < active_chartTable.size(); i++) {
2705 auto &cte_r = GetChartTableEntry(i);
2706 if (!cte_r.GetbValid()) {
2714 for (
unsigned int i = 0; i < active_chartTable.size(); i++) {
2715 auto &cte_ef = GetChartTableEntry(i);
2716 cte_ef.SetEntryOffset(i);
2721 DetectDirChange(dir_name,
"",
"", new_magic, 0);
2724 bool bcfound =
false;
2725 ArrayOfCDI NewChartDirArray;
2727 ArrayOfCDI ChartDirArray = GetChartDirArray();
2728 for (
unsigned int i = 0; i < ChartDirArray.GetCount(); i++) {
2729 ChartDirInfo cdi = ChartDirArray[i];
2731 ChartDirInfo newcdi = cdi;
2734 if (newcdi.fullpath == dir_name) {
2735 newcdi.magic_number = new_magic;
2739 NewChartDirArray.Add(newcdi);
2744 cdi.fullpath = dir_name;
2745 cdi.magic_number = new_magic;
2746 NewChartDirArray.Add(cdi);
2750 SetChartDirArray(NewChartDirArray);
2753 m_chartDirs.Clear();
2755 for (
unsigned int i = 0; i < GetChartDirArray().GetCount(); i++) {
2756 ChartDirInfo cdi = GetChartDirArray()[i];
2757 m_chartDirs.Add(cdi.fullpath);
2760 m_nentries = active_chartTable.size();
2762 ChartData->UpdateChartDatabaseInplace(NewChartDirArray,
false,
nullptr);
2766bool ChartDatabase::RemoveSingleChart(wxString &ChartFullPath) {
2770 for (
unsigned int i = 0; i < active_chartTable.size(); i++) {
2771 auto &cte = GetChartTableEntry(i);
2772 if (ChartFullPath.IsSameAs(cte.GetFullSystemPath())) {
2774 std::swap(active_chartTable[i], active_chartTable.back());
2775 active_chartTable.pop_back();
2781 for (
unsigned int i = 0; i < active_chartTable.size(); i++) {
2782 auto &pcte = GetChartTableEntry(i);
2783 pcte.SetEntryOffset(i);
2787 wxFileName fn(ChartFullPath);
2788 wxString fd = fn.GetPath();
2789 if (!IsChartDirUsed(fd)) {
2791 ArrayOfCDI NewChartDirArray;
2793 ArrayOfCDI ChartDirArray = GetChartDirArray();
2794 for (
unsigned int i = 0; i < ChartDirArray.GetCount(); i++) {
2795 ChartDirInfo cdi = ChartDirArray[i];
2797 ChartDirInfo newcdi = cdi;
2799 if (newcdi.fullpath != fd) NewChartDirArray.Add(newcdi);
2802 SetChartDirArray(NewChartDirArray);
2806 m_chartDirs.Clear();
2807 for (
unsigned int i = 0; i < GetChartDirArray().GetCount(); i++) {
2808 ChartDirInfo cdi = GetChartDirArray()[i];
2809 m_chartDirs.Add(cdi.fullpath);
2812 m_nentries = active_chartTable.size();
2813 ChartData->UpdateChartDatabaseInplace(m_dir_array,
false,
nullptr);
2822ChartBase *ChartDatabase::GetChart(
const wxChar *theFilePath,
2833 const wxString &filePath, wxString &utf8Path,
2835 wxString msg_fn(filePath);
2836 msg_fn.Replace(
"%",
"%%");
2837 wxLogMessage(wxString::Format(
"Loading chart data for %s", msg_fn.c_str()));
2840 qDebug() <<
" CCTE: " << filePath.ToStdString().c_str();
2842 ChartBase *pch = GetChart(filePath, chart_desc);
2845 wxString::Format(
" ...creation failed for %s", msg_fn.c_str()));
2849 InitReturn rc = pch->Init(filePath, HEADER_ONLY);
2850 if (rc != INIT_OK) {
2853 wxString::Format(
" ...initialization failed for %s", msg_fn.c_str()));
2857 ChartTableEntry *ret_val =
new ChartTableEntry(*pch, utf8Path);
2858 ret_val->SetValid(
true);
2865bool ChartDatabase::GetCentroidOfLargestScaleChart(
double *clat,
double *clon,
2866 ChartFamilyEnum family) {
2868 int cur_max_scale = 0;
2870 int nEntry = active_chartTable.size();
2872 for (
int i = 0; i < nEntry; i++) {
2873 auto &cte_fam = GetChartTableEntry(i);
2874 if (GetChartFamily(cte_fam.GetChartType()) == family) {
2875 if (cte_fam.GetScale() > cur_max_scale) {
2876 cur_max_scale = cte_fam.GetScale();
2882 if (cur_max_i == -1)
2885 auto &cte_sel = GetChartTableEntry(cur_max_i);
2886 *clat = (cte_sel.GetLatMax() + cte_sel.GetLatMin()) / 2.;
2887 *clon = (cte_sel.GetLonMin() + cte_sel.GetLonMax()) / 2.;
2895int ChartDatabase::GetDBChartProj(
int dbIndex) {
2896 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2897 auto &cte = GetChartTableEntry(dbIndex);
2898 return cte.GetChartProjectionType();
2900 return PROJECTION_UNKNOWN;
2906int ChartDatabase::GetDBChartFamily(
int dbIndex) {
2907 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2908 auto &cte = GetChartTableEntry(dbIndex);
2909 return cte.GetChartFamily();
2911 return CHART_FAMILY_UNKNOWN;
2917wxString ChartDatabase::GetDBChartFileName(
int dbIndex) {
2918 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2919 auto &cte = GetChartTableEntry(dbIndex);
2920 return wxString(cte.GetFullSystemPath());
2928int ChartDatabase::GetDBChartType(
int dbIndex) {
2929 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2930 auto &cte = GetChartTableEntry(dbIndex);
2931 return cte.GetChartType();
2939float ChartDatabase::GetDBChartSkew(
int dbIndex) {
2940 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2941 auto &cte = GetChartTableEntry(dbIndex);
2942 return cte.GetChartSkew();
2950int ChartDatabase::GetDBChartScale(
int dbIndex) {
2951 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2952 auto &cte = GetChartTableEntry(dbIndex);
2953 return cte.GetScale();
2961bool ChartDatabase::GetDBBoundingBox(
int dbIndex, LLBBox &box) {
2962 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2963 auto &entry = GetChartTableEntry(dbIndex);
2964 box.Set(entry.GetLatMin(), entry.GetLonMin(), entry.GetLatMax(),
2971const LLBBox &ChartDatabase::GetDBBoundingBox(
int dbIndex) {
2972 if ((bValid) && (dbIndex >= 0)) {
2973 auto &entry = GetChartTableEntry(dbIndex);
2974 return entry.GetBBox();
2976 return m_dummy_bbox;
2983int ChartDatabase::GetDBPlyPoint(
int dbIndex,
int plyindex,
float *lat,
2985 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2986 const ChartTableEntry &entry = GetChartTableEntry(dbIndex);
2987 if (entry.GetnPlyEntries()) {
2988 float *fp = entry.GetpPlyTable();
2990 if (lat) *lat = *fp;
2992 if (lon) *lon = *fp;
2994 return entry.GetnPlyEntries();
3002int ChartDatabase::GetDBAuxPlyPoint(
int dbIndex,
int plyindex,
int ply,
3003 float *lat,
float *lon) {
3004 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
3005 const ChartTableEntry &entry = GetChartTableEntry(dbIndex);
3006 if (entry.GetnAuxPlyEntries()) {
3007 float *fp = entry.GetpAuxPlyTableEntry(ply);
3010 if (lat) *lat = *fp;
3012 if (lon) *lon = *fp;
3015 return entry.GetAuxCntTableEntry(ply);
3020int ChartDatabase::GetnAuxPlyEntries(
int dbIndex) {
3021 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
3022 const ChartTableEntry &entry = GetChartTableEntry(dbIndex);
3023 return entry.GetnAuxPlyEntries();
3031std::vector<float> ChartDatabase::GetReducedPlyPoints(
int dbIndex) {
3032 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
3033 ChartTableEntry &cte = GetChartTableEntry(dbIndex);
3034 return cte.GetReducedPlyPoints();
3037 std::vector<float> dummy;
3044std::vector<float> ChartDatabase::GetReducedAuxPlyPoints(
int dbIndex,
3046 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
3047 auto &cte = GetChartTableEntry(dbIndex);
3048 return cte.GetReducedAuxPlyPoints(iTable);
3051 std::vector<float> dummy;
3055bool ChartDatabase::IsChartAvailable(
int dbIndex) {
3056 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
3057 auto &cte = GetChartTableEntry(dbIndex);
3060 if (cte.GetChartType() != CHART_TYPE_PLUGIN)
return true;
3062 wxString *path = cte.GetpsFullPath();
3063 wxFileName fn(*path);
3064 wxString ext = fn.GetExt();
3066 wxString ext_upper = ext.MakeUpper();
3067 wxString ext_lower = ext.MakeLower();
3072 for (
auto &cd : m_ChartClassDescriptorArray) {
3073 if (cd.m_descriptor_type == PLUGIN_DESCRIPTOR) {
3074 wxString search_mask = cd.m_search_mask;
3076 if (search_mask == ext_upper) {
3079 if (search_mask == ext_lower) {
3082 if (path->Matches(search_mask)) {
3092void ChartDatabase::ApplyGroupArray(ChartGroupArray *pGroupArray) {
3093 wxString separator(wxFileName::GetPathSeparator());
3095 for (
unsigned int ic = 0; ic < active_chartTable.size(); ic++) {
3096 auto &cte = GetChartTableEntry(ic);
3098 cte.ClearGroupArray();
3100 wxString *chart_full_path = cte.GetpsFullPath();
3102 for (
unsigned int igroup = 0; igroup < pGroupArray->GetCount(); igroup++) {
3103 ChartGroup *pGroup = pGroupArray->Item(igroup);
3104 for (
const auto &elem : pGroup->m_element_array) {
3105 wxString element_root = elem.m_element_name;
3111 if (!chart_full_path->IsSameAs(element_root))
3112 element_root.Append(
3114 if (chart_full_path->StartsWith(element_root)) {
3116 for (
unsigned int k = 0; k < elem.m_missing_name_array.size(); k++) {
3117 const wxString &missing_item = elem.m_missing_name_array[k];
3118 if (chart_full_path->StartsWith(missing_item)) {
3119 if (chart_full_path->IsSameAs(
3125 if (wxDir::Exists(missing_item))
3134 if (b_add) cte.AddIntToGroupArray(igroup + 1);
General chart base definitions.
ChartDB * ChartData
Global instance.
Define threaded chart database classes.
ChartGroupArray * g_pGroupArray
Global instance.
Basic chart info storage.
ChartGroupArray * g_pGroupArray
Global instance.
Base class for all chart types.
Manages a database of charts, including reading, writing, and querying chart information.
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.
obs::EventVar on_finalize_chartdbs
Notified when chartdbs async operations complete, to reload charts.
obs::EventVar options_on_finalize_chartdbs
Notified when chartdbs async operations complete, to finalize settings.
void Notify() override
Notify all listeners, no data supplied.
Hash of arbitrary length.
Misc GUI event vars, a singleton.
PlugInManager * g_pi_manager
Global instance.
PlugInManager and helper classes – Mostly gui parts (dialogs) and plugin API stuff.
ShapeBaseChartSet gShapeBasemap
global instance
Represents an entry in the chart table, containing information about a single chart.