26 #include <wx/wxprec.h>
32 #include <wx/arrimpl.cpp>
33 #include <wx/encconv.h>
35 #include <wx/progdlg.h>
36 #include <wx/tokenzr.h>
40 #include "chartbase.h"
41 #include "pluginmanager.h"
45 #include "LOD_reduce.h"
46 #include "shapefile_basemap.h"
49 #define UINT32 unsigned int
52 #ifdef __OCPN__ANDROID__
53 #include "androidUTIL.h"
57 extern wxString gWorldMapLocation;
58 extern wxString gWorldShapefileLocation;
61 static int s_dbVersion;
67 bool FindMatchingFile(
const wxString &theDir,
const wxChar *theRegEx,
68 int nameLength, wxString &theMatch) {
70 wxRegEx rePattern(theRegEx);
71 for (
bool fileFound = dir.GetFirst(&theMatch); fileFound;
72 fileFound = dir.GetNext(&theMatch))
73 if (theMatch.length() == (
unsigned int)nameLength &&
74 rePattern.Matches(theMatch))
79 static ChartFamilyEnum GetChartFamily(
int charttype) {
84 cf = CHART_FAMILY_RASTER;
87 cf = CHART_FAMILY_RASTER;
90 cf = CHART_FAMILY_VECTOR;
93 cf = CHART_FAMILY_VECTOR;
95 case CHART_TYPE_CM93COMP:
96 cf = CHART_FAMILY_VECTOR;
98 case CHART_TYPE_DUMMY:
99 cf = CHART_FAMILY_RASTER;
101 case CHART_TYPE_UNKNOWN:
102 cf = CHART_FAMILY_UNKNOWN;
105 cf = CHART_FAMILY_UNKNOWN;
115 void ChartTableHeader::Read(wxInputStream &is) {
119 void ChartTableHeader::Write(wxOutputStream &os) {
121 sprintf(vb,
"V%03d", DB_VERSION_CURRENT);
123 memcpy(dbVersion, vb, 4);
127 bool ChartTableHeader::CheckValid() {
129 sprintf(vb,
"V%03d", DB_VERSION_CURRENT);
130 if (strncmp(vb, dbVersion,
sizeof(dbVersion))) {
133 memcpy(vbo, dbVersion, 4);
135 msg.Append(wxString(vbo, wxConvUTF8));
136 msg.Prepend(wxT(
" Warning: found incorrect chart db version: "));
142 sprintf(vb,
"V%03d", DB_VERSION_PREVIOUS);
143 if (strncmp(vb, dbVersion,
sizeof(dbVersion)))
147 _T(
" Scheduling db upgrade to current db version on ")
148 _T(
"Options->Charts page visit..."));
155 memcpy(vbo, dbVersion, 4);
157 msg.Append(wxString(vbo, wxConvUTF8));
158 msg.Prepend(wxT(
"Loading chart db version: "));
169 void ChartTableEntry::SetScale(
int scale) {
173 if (Scale >= 1000) rounding = 5 * pow(10, log10(Scale) - 2);
176 ChartTableEntry::ChartTableEntry(
ChartBase &theChart, wxString &utf8Path) {
179 char *pt = (
char *)malloc(strlen(utf8Path.mb_str(wxConvUTF8)) + 1);
180 strcpy(pt, utf8Path.mb_str(wxConvUTF8));
183 SetScale(theChart.GetNativeScale());
185 ChartType = theChart.GetChartType();
186 ChartFamily = theChart.GetChartFamily();
188 Skew = theChart.GetChartSkew();
189 ProjectionType = theChart.GetChartProjectionType();
191 wxDateTime ed = theChart.GetEditionDate();
192 if (theChart.GetEditionDate().IsValid())
193 edition_date = theChart.GetEditionDate().GetTicks();
195 wxFileName fn(theChart.GetFullPath());
196 if (fn.GetModificationTime().IsValid())
197 file_date = fn.GetModificationTime().GetTicks();
199 m_pfilename =
new wxString;
200 *m_pfilename = fn.GetFullName();
201 m_psFullPath =
new wxString;
202 *m_psFullPath = utf8Path;
203 m_fullSystemPath = utf8Path;
205 #ifdef __OCPN__ANDROID__
206 m_fullSystemPath = wxString(utf8Path.mb_str(wxConvUTF8));
210 theChart.GetChartExtent(&ext);
216 m_bbox.Set(LatMin, LonMin, LatMax, LonMax);
221 double scale_max_zoom = Scale / 4;
223 double display_ppm = 1 / .00025;
224 double meters_per_pixel_max_scale = scale_max_zoom / display_ppm;
225 double LOD_meters = meters_per_pixel_max_scale * LOD_pixels;
230 if (theChart.GetCOVREntries() == 1) {
231 nPlyEntries = theChart.GetCOVRTablePoints(0);
233 if (nPlyEntries > 5 && (LOD_meters > .01)) {
234 std::vector<int> index_keep{0, nPlyEntries - 1, 1, nPlyEntries - 2};
236 double *DPbuffer = (
double *)malloc(2 * nPlyEntries *
sizeof(
double));
238 double *pfed = DPbuffer;
241 for (
int i = 0; i < nPlyEntries; i++) {
247 DouglasPeucker(DPbuffer, 1, nPlyEntries - 2, LOD_meters / (1852 * 60),
253 for (
unsigned int i = 0; i < index_keep.size(); i++) {
254 DPbuffer[2 * index_keep[i]] += 2000.;
257 float *pf = (
float *)malloc(2 * index_keep.size() *
sizeof(float));
260 for (
int i = 0; i < nPlyEntries; i++) {
261 if (DPbuffer[2 * i] > 1000.) {
262 *pfe++ = DPbuffer[2 * i] - 2000.;
263 *pfe++ = DPbuffer[(2 * i) + 1];
268 nPlyEntries = index_keep.size();
271 float *pf = (
float *)malloc(2 * nPlyEntries *
sizeof(
float));
276 for (
int i = 0; i < nPlyEntries; i++) {
288 float *pf1 = (
float *)malloc(2 * 4 *
sizeof(
float));
292 theChart.GetChartExtent(&fext);
308 nAuxPlyEntries = theChart.GetCOVREntries();
309 wxASSERT(nAuxPlyEntries);
310 float **pfp = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
312 int *pip = (
int *)malloc(nAuxPlyEntries *
sizeof(
int));
314 for (
int j = 0; j < nAuxPlyEntries; j++) {
315 int nPE = theChart.GetCOVRTablePoints(j);
317 if (nPE > 5 && (LOD_meters > .01)) {
318 std::vector<int> index_keep{0, nPE - 1, 1, nPE - 2};
320 double *DPbuffer = (
double *)malloc(2 * nPE *
sizeof(
double));
322 double *pfed = DPbuffer;
325 for (
int i = 0; i < nPE; i++) {
331 DouglasPeucker(DPbuffer, 1, nPE - 2, LOD_meters / (1852 * 60),
337 for (
unsigned int i = 0; i < index_keep.size(); i++) {
338 DPbuffer[2 * index_keep[i]] += 2000.;
341 float *pf = (
float *)malloc(2 * index_keep.size() *
sizeof(float));
344 for (
int i = 0; i < nPE; i++) {
345 if (DPbuffer[2 * i] > 1000.) {
346 *pfe++ = DPbuffer[2 * i] - 2000.;
347 *pfe++ = DPbuffer[(2 * i) + 1];
352 pip[j] = index_keep.size();
356 (
float *)malloc(theChart.GetCOVRTablePoints(j) * 2 *
sizeof(float));
357 memcpy(pf_entry, theChart.GetCOVRTableHead(j),
358 theChart.GetCOVRTablePoints(j) * 2 *
sizeof(
float));
360 pip[j] = theChart.GetCOVRTablePoints(j);
370 nNoCovrPlyEntries = theChart.GetNoCOVREntries();
371 if (nNoCovrPlyEntries == 0)
return;
373 float **pfpnc = (
float **)malloc(nNoCovrPlyEntries *
sizeof(
float *));
374 float **pft0nc = pfpnc;
375 int *pipnc = (
int *)malloc(nNoCovrPlyEntries *
sizeof(
int));
377 for (
int j = 0; j < nNoCovrPlyEntries; j++) {
379 (
float *)malloc(theChart.GetNoCOVRTablePoints(j) * 2 *
sizeof(float));
380 memcpy(pf_entry, theChart.GetNoCOVRTableHead(j),
381 theChart.GetNoCOVRTablePoints(j) * 2 *
sizeof(
float));
382 pft0nc[j] = pf_entry;
383 pipnc[j] = theChart.GetNoCOVRTablePoints(j);
386 pNoCovrPlyTable = pfpnc;
387 pNoCovrCntTable = pipnc;
392 ChartTableEntry::~ChartTableEntry() {
396 for (
int i = 0; i < nAuxPlyEntries; i++) free(pAuxPlyTable[i]);
400 if (nNoCovrPlyEntries) {
401 for (
int i = 0; i < nNoCovrPlyEntries; i++) free(pNoCovrPlyTable[i]);
402 free(pNoCovrPlyTable);
403 free(pNoCovrCntTable);
414 bool ChartTableEntry::IsEarlierThan(
const ChartTableEntry &cte)
const {
415 wxDateTime mine(edition_date);
416 wxDateTime theirs(cte.edition_date);
418 if (!mine.IsValid() || !theirs.IsValid())
421 return (mine.IsEarlierThan(theirs));
425 wxDateTime mine(edition_date);
426 wxDateTime theirs(cte.edition_date);
428 if (!mine.IsValid() || !theirs.IsValid())
431 return (mine.IsEqualTo(theirs));
436 static int convertChartType(
int charttype) {
439 if (s_dbVersion == 14) {
442 return CHART_TYPE_KAP;
444 return CHART_TYPE_GEO;
446 return CHART_TYPE_S57;
448 return CHART_TYPE_CM93;
450 return CHART_TYPE_CM93COMP;
452 return CHART_TYPE_UNKNOWN;
454 return CHART_TYPE_DONTCARE;
456 return CHART_TYPE_DUMMY;
458 return CHART_TYPE_UNKNOWN;
464 static int convertChartFamily(
int charttype,
int chartfamily) {
465 if (s_dbVersion < 18) {
469 return CHART_FAMILY_RASTER;
472 case CHART_TYPE_CM93:
473 case CHART_TYPE_CM93COMP:
474 return CHART_FAMILY_VECTOR;
477 return CHART_FAMILY_UNKNOWN;
483 bool ChartTableEntry::Read(
const ChartDatabase *pDb, wxInputStream &is) {
484 char path[4096], *cp;
490 int db_version = pD->GetVersion();
492 if (db_version == 18) {
494 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++)
496 pFullPath = (
char *)malloc(cp - path + 1);
497 strncpy(pFullPath, path, cp - path + 1);
498 wxLogVerbose(_T(
" Chart %s"), pFullPath);
501 m_pfilename =
new wxString;
502 wxString fullfilename(pFullPath, wxConvUTF8);
503 wxFileName fn(fullfilename);
504 *m_pfilename = fn.GetFullName();
505 m_psFullPath =
new wxString;
506 *m_psFullPath = fullfilename;
507 m_fullSystemPath = fullfilename;
509 #ifdef __OCPN__ANDROID__
510 m_fullSystemPath = wxString(fullfilename.mb_str(wxConvUTF8));
517 EntryOffset = cte.EntryOffset;
518 ChartType = cte.ChartType;
519 ChartFamily = cte.ChartFamily;
525 m_bbox.Set(LatMin, LonMin, LatMax, LonMax);
528 ProjectionType = cte.ProjectionType;
531 edition_date = cte.edition_date;
532 file_date = cte.file_date;
534 nPlyEntries = cte.nPlyEntries;
535 nAuxPlyEntries = cte.nAuxPlyEntries;
537 nNoCovrPlyEntries = cte.nNoCovrPlyEntries;
542 int npeSize = nPlyEntries * 2 *
sizeof(float);
543 pPlyTable = (
float *)malloc(npeSize);
544 is.Read(pPlyTable, npeSize);
547 if (nAuxPlyEntries) {
548 int napeSize = nAuxPlyEntries *
sizeof(int);
549 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
550 pAuxCntTable = (
int *)malloc(napeSize);
551 is.Read(pAuxCntTable, napeSize);
553 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
555 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
556 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
557 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
561 if (nNoCovrPlyEntries) {
562 int napeSize = nNoCovrPlyEntries *
sizeof(int);
563 pNoCovrCntTable = (
int *)malloc(napeSize);
564 is.Read(pNoCovrCntTable, napeSize);
566 pNoCovrPlyTable = (
float **)malloc(nNoCovrPlyEntries *
sizeof(
float *));
567 for (
int i = 0; i < nNoCovrPlyEntries; i++) {
568 int nfSize = pNoCovrCntTable[i] * 2 *
sizeof(float);
569 pNoCovrPlyTable[i] = (
float *)malloc(nfSize);
570 is.Read(pNoCovrPlyTable[i], nfSize);
575 else if (db_version == 17) {
577 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++)
579 pFullPath = (
char *)malloc(cp - path + 1);
580 strncpy(pFullPath, path, cp - path + 1);
581 wxLogVerbose(_T(
" Chart %s"), pFullPath);
584 m_pfilename =
new wxString;
585 wxString fullfilename(pFullPath, wxConvUTF8);
586 wxFileName fn(fullfilename);
587 *m_pfilename = fn.GetFullName();
588 m_psFullPath =
new wxString;
589 *m_psFullPath = fullfilename;
596 EntryOffset = cte.EntryOffset;
597 ChartType = cte.ChartType;
603 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
606 ProjectionType = cte.ProjectionType;
609 edition_date = cte.edition_date;
610 file_date = cte.file_date;
612 nPlyEntries = cte.nPlyEntries;
613 nAuxPlyEntries = cte.nAuxPlyEntries;
615 nNoCovrPlyEntries = cte.nNoCovrPlyEntries;
620 int npeSize = nPlyEntries * 2 *
sizeof(float);
621 pPlyTable = (
float *)malloc(npeSize);
622 is.Read(pPlyTable, npeSize);
625 if (nAuxPlyEntries) {
626 int napeSize = nAuxPlyEntries *
sizeof(int);
627 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
628 pAuxCntTable = (
int *)malloc(napeSize);
629 is.Read(pAuxCntTable, napeSize);
631 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
633 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
634 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
635 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
639 if (nNoCovrPlyEntries) {
640 int napeSize = nNoCovrPlyEntries *
sizeof(int);
641 pNoCovrCntTable = (
int *)malloc(napeSize);
642 is.Read(pNoCovrCntTable, napeSize);
644 pNoCovrPlyTable = (
float **)malloc(nNoCovrPlyEntries *
sizeof(
float *));
645 for (
int i = 0; i < nNoCovrPlyEntries; i++) {
646 int nfSize = pNoCovrCntTable[i] * 2 *
sizeof(float);
647 pNoCovrPlyTable[i] = (
float *)malloc(nfSize);
648 is.Read(pNoCovrPlyTable[i], nfSize);
653 else if (db_version == 16) {
655 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++)
658 pFullPath = (
char *)malloc(cp - path + 1);
659 strncpy(pFullPath, path, cp - path + 1);
660 wxLogVerbose(_T(
" Chart %s"), pFullPath);
663 m_pfilename =
new wxString;
664 wxString fullfilename(pFullPath, wxConvUTF8);
665 wxFileName fn(fullfilename);
666 *m_pfilename = fn.GetFullName();
667 m_psFullPath =
new wxString;
668 *m_psFullPath = fullfilename;
675 EntryOffset = cte.EntryOffset;
676 ChartType = cte.ChartType;
682 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
685 ProjectionType = cte.ProjectionType;
688 edition_date = cte.edition_date;
689 file_date = cte.file_date;
691 nPlyEntries = cte.nPlyEntries;
692 nAuxPlyEntries = cte.nAuxPlyEntries;
697 int npeSize = nPlyEntries * 2 *
sizeof(float);
698 pPlyTable = (
float *)malloc(npeSize);
699 is.Read(pPlyTable, npeSize);
702 if (nAuxPlyEntries) {
703 int napeSize = nAuxPlyEntries *
sizeof(int);
704 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
705 pAuxCntTable = (
int *)malloc(napeSize);
706 is.Read(pAuxCntTable, napeSize);
708 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
710 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
711 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
712 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
717 else if (db_version == 15) {
719 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++)
722 pFullPath = (
char *)malloc(cp - path + 1);
723 strncpy(pFullPath, path, cp - path + 1);
724 wxLogVerbose(_T(
" Chart %s"), pFullPath);
731 EntryOffset = cte.EntryOffset;
732 ChartType = cte.ChartType;
738 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
741 edition_date = cte.edition_date;
742 file_date = cte.file_date;
744 nPlyEntries = cte.nPlyEntries;
745 nAuxPlyEntries = cte.nAuxPlyEntries;
750 int npeSize = nPlyEntries * 2 *
sizeof(float);
751 pPlyTable = (
float *)malloc(npeSize);
752 is.Read(pPlyTable, npeSize);
755 if (nAuxPlyEntries) {
756 int napeSize = nAuxPlyEntries *
sizeof(int);
757 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
758 pAuxCntTable = (
int *)malloc(napeSize);
759 is.Read(pAuxCntTable, napeSize);
761 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
763 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
764 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
765 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
768 }
else if (db_version == 14) {
770 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++)
772 pFullPath = (
char *)malloc(cp - path + 1);
773 strncpy(pFullPath, path, cp - path + 1);
774 wxLogVerbose(_T(
" Chart %s"), pFullPath);
781 EntryOffset = cte.EntryOffset;
782 ChartType = cte.ChartType;
788 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
791 edition_date = cte.edition_date;
793 nPlyEntries = cte.nPlyEntries;
794 nAuxPlyEntries = cte.nAuxPlyEntries;
798 int npeSize = nPlyEntries * 2 *
sizeof(float);
799 pPlyTable = (
float *)malloc(npeSize);
800 is.Read(pPlyTable, npeSize);
803 if (nAuxPlyEntries) {
804 int napeSize = nAuxPlyEntries *
sizeof(int);
805 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
806 pAuxCntTable = (
int *)malloc(napeSize);
807 is.Read(pAuxCntTable, napeSize);
809 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
811 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
812 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
813 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
817 ChartFamily = convertChartFamily(ChartType, ChartFamily);
818 ChartType = convertChartType(ChartType);
825 bool ChartTableEntry::Write(
const ChartDatabase *pDb, wxOutputStream &os) {
826 os.Write(pFullPath, strlen(pFullPath) + 1);
833 cte.EntryOffset = EntryOffset;
834 cte.ChartType = ChartType;
835 cte.ChartFamily = ChartFamily;
842 cte.edition_date = edition_date;
843 cte.file_date = file_date;
845 cte.nPlyEntries = nPlyEntries;
846 cte.nAuxPlyEntries = nAuxPlyEntries;
849 cte.ProjectionType = ProjectionType;
853 cte.nNoCovrPlyEntries = nNoCovrPlyEntries;
856 wxLogVerbose(_T(
" Wrote Chart %s"), pFullPath);
860 int npeSize = nPlyEntries * 2 *
sizeof(float);
861 os.Write(pPlyTable, npeSize);
864 if (nAuxPlyEntries) {
865 int napeSize = nAuxPlyEntries *
sizeof(int);
866 os.Write(pAuxCntTable, napeSize);
868 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries; nAuxPlyEntry++) {
869 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
870 os.Write(pAuxPlyTable[nAuxPlyEntry], nfSize);
874 if (nNoCovrPlyEntries) {
875 int ncSize = nNoCovrPlyEntries *
sizeof(int);
876 os.Write(pNoCovrCntTable, ncSize);
878 for (
int i = 0; i < nNoCovrPlyEntries; i++) {
879 int nctSize = pNoCovrCntTable[i] * 2 *
sizeof(float);
880 os.Write(pNoCovrPlyTable[i], nctSize);
889 void ChartTableEntry::Clear() {
898 pNoCovrCntTable = NULL;
899 pNoCovrPlyTable = NULL;
901 nNoCovrPlyEntries = 0;
910 void ChartTableEntry::Disable() {
914 LatMax += (float)1000.;
915 LatMin += (float)1000.;
918 void ChartTableEntry::ReEnable() {
920 LatMax -= (float)1000.;
921 LatMin -= (float)1000.;
925 std::vector<float> ChartTableEntry::GetReducedPlyPoints() {
926 if (m_reducedPlyPoints.size())
return m_reducedPlyPoints;
929 float LOD_meters = 1;
931 float plylat, plylon;
932 const int nPoints = GetnPlyEntries();
934 float *fpo = GetpPlyTable();
936 double *ppd =
new double[nPoints * 2];
937 double *ppsm =
new double[nPoints * 2];
940 for (
int i = 0; i < nPoints; i++) {
942 plylon = fpo[i * 2 + 1];
945 toSM(plylat, plylon, fpo[0], fpo[1], &x, &y);
953 std::vector<int> index_keep;
955 index_keep.push_back(0);
956 index_keep.push_back(nPoints - 1);
957 index_keep.push_back(1);
958 index_keep.push_back(nPoints - 2);
960 DouglasPeuckerM(ppsm, 1, nPoints - 2, LOD_meters, &index_keep);
963 index_keep.resize(nPoints);
964 for (
int i = 0; i < nPoints; i++) index_keep[i] = i;
968 for (
int ip = 0; ip < nPoints; ip++) {
972 for (
unsigned int j = 0; j < index_keep.size(); j++) {
973 if (index_keep[j] == ip) {
974 m_reducedPlyPoints.push_back(x);
975 m_reducedPlyPoints.push_back(y);
984 int nprr = m_reducedPlyPoints.size() / 2;
986 return m_reducedPlyPoints;
989 std::vector<float> ChartTableEntry::GetReducedAuxPlyPoints(
int iTable) {
991 if (!m_reducedAuxPlyPointsVector.size()) {
992 std::vector<float> vec;
993 for (
int i = 0; i < GetnAuxPlyEntries(); i++) {
994 m_reducedAuxPlyPointsVector.push_back(vec);
998 std::vector<float> vec;
1001 if ((
unsigned int)iTable >= m_reducedAuxPlyPointsVector.size())
return vec;
1003 if (m_reducedAuxPlyPointsVector.at(iTable).size())
1004 return m_reducedAuxPlyPointsVector.at(iTable);
1007 float LOD_meters = 1.0;
1009 const int nPoints = GetAuxCntTableEntry(iTable);
1010 float *fpo = GetpAuxPlyTableEntry(iTable);
1012 double *ppd =
new double[nPoints * 2];
1013 double *ppsm =
new double[nPoints * 2];
1015 double *npsm = ppsm;
1016 float plylat, plylon;
1018 for (
int i = 0; i < nPoints; i++) {
1019 plylat = fpo[i * 2];
1020 plylon = fpo[i * 2 + 1];
1023 toSM(plylat, plylon, fpo[0], fpo[1], &x, &y);
1031 std::vector<int> index_keep;
1033 index_keep.push_back(0);
1034 index_keep.push_back(nPoints - 1);
1035 index_keep.push_back(1);
1036 index_keep.push_back(nPoints - 2);
1038 DouglasPeuckerM(ppsm, 1, nPoints - 2, LOD_meters, &index_keep);
1041 index_keep.resize(nPoints);
1042 for (
int i = 0; i < nPoints; i++) index_keep[i] = i;
1045 int nnn = index_keep.size();
1048 for (
int ip = 0; ip < nPoints; ip++) {
1052 for (
unsigned int j = 0; j < index_keep.size(); j++) {
1053 if (index_keep[j] == ip) {
1064 m_reducedAuxPlyPointsVector[iTable] = vec;
1066 int nprr = vec.size() / 2;
1075 WX_DEFINE_OBJARRAY(ChartTable);
1077 ChartDatabase::ChartDatabase() {
1081 m_ChartTableEntryDummy.Clear();
1083 UpdateChartClassDescriptorArray();
1087 void ChartDatabase::UpdateChartClassDescriptorArray(
void) {
1088 if(m_ChartClassDescriptorArray.empty()) {
1089 m_ChartClassDescriptorArray.push_back(
ChartClassDescriptor(_T(
"ChartKAP"), _T(
"*.kap"), BUILTIN_DESCRIPTOR));
1090 m_ChartClassDescriptorArray.push_back(
ChartClassDescriptor(_T(
"ChartGEO"), _T(
"*.geo"), BUILTIN_DESCRIPTOR));
1091 m_ChartClassDescriptorArray.push_back(
ChartClassDescriptor(_T(
"s57chart"), _T(
"*.000"), BUILTIN_DESCRIPTOR));
1092 m_ChartClassDescriptorArray.push_back(
ChartClassDescriptor(_T(
"s57chart"), _T(
"*.s57"), BUILTIN_DESCRIPTOR));
1093 m_ChartClassDescriptorArray.push_back(
ChartClassDescriptor(_T(
"cm93compchart"), _T(
"00300000.a"), BUILTIN_DESCRIPTOR));
1094 m_ChartClassDescriptorArray.push_back(
ChartClassDescriptor(_T(
"ChartMBTiles"), _T(
"*.mbtiles"), BUILTIN_DESCRIPTOR));
1099 m_ChartClassDescriptorArray.erase(std::remove_if(
1100 m_ChartClassDescriptorArray.begin(), m_ChartClassDescriptorArray.end(),
1102 return cd.m_descriptor_type ==PLUGIN_DESCRIPTOR;
1103 }), m_ChartClassDescriptorArray.end());
1105 wxArrayString array = g_pi_manager->GetPlugInChartClassNameArray();
1106 for (
unsigned int j = 0; j < array.GetCount(); j++) {
1109 wxString class_name = array[j];
1112 wxString mask = cpiw->GetFileSearchMask();
1115 m_ChartClassDescriptorArray.push_back(
ChartClassDescriptor(class_name, mask, PLUGIN_DESCRIPTOR));
1122 const ChartTableEntry &ChartDatabase::GetChartTableEntry(
int index)
const {
1123 if (index < GetChartTableEntries())
1124 return active_chartTable[index];
1126 return m_ChartTableEntryDummy;
1129 ChartTableEntry *ChartDatabase::GetpChartTableEntry(
int index)
const {
1130 if (index < GetChartTableEntries())
1131 return &active_chartTable[index];
1136 bool ChartDatabase::CompareChartDirArray(ArrayOfCDI &test_array) {
1140 if (test_array.GetCount() != m_dir_array.GetCount())
return false;
1143 unsigned int nfound_outer = 0;
1145 for (
unsigned int i = 0; i < test_array.GetCount(); i++) {
1147 bfound_inner =
false;
1148 for (
unsigned int j = 0; j < m_dir_array.GetCount(); j++) {
1151 if (p.fullpath.IsSameAs(q.fullpath)) {
1152 bfound_inner =
true;
1156 if (bfound_inner) nfound_outer++;
1159 return (nfound_outer == test_array.GetCount());
1162 wxString ChartDatabase::GetMagicNumberCached(wxString dir) {
1163 for (
unsigned int j = 0; j < m_dir_array.GetCount(); j++) {
1165 if (dir.IsSameAs(q.fullpath))
return q.magic_number;
1171 bool ChartDatabase::Read(
const wxString &filePath) {
1177 wxFileName file(filePath);
1178 if (!file.FileExists())
return false;
1180 m_DBFileName = filePath;
1182 wxFFileInputStream ifs(filePath);
1183 if (!ifs.Ok())
return false;
1187 if (!cth.CheckValid())
return false;
1191 memcpy(vbo, cth.GetDBVersionString(), 4);
1193 m_dbversion = atoi(&vbo[1]);
1194 s_dbVersion = m_dbversion;
1196 wxLogVerbose(wxT(
"Chartdb:Reading %d directory entries, %d table entries"),
1197 cth.GetDirEntries(), cth.GetTableEntries());
1198 wxLogMessage(_T(
"Chartdb: Chart directory list follows"));
1199 if (0 == cth.GetDirEntries()) wxLogMessage(_T(
" Nil"));
1202 for (
int iDir = 0; iDir < cth.GetDirEntries(); iDir++) {
1205 ifs.Read(&dirlen,
sizeof(
int));
1206 while (dirlen > 0) {
1208 int alen = dirlen > 1023 ? 1023 : dirlen;
1209 if (ifs.Read(&dirbuf, alen).Eof())
goto read_error;
1212 dir.Append(wxString(dirbuf, wxConvUTF8));
1215 msg.Printf(wxT(
" Chart directory #%d: "), iDir);
1218 m_chartDirs.Add(dir);
1221 entries = cth.GetTableEntries();
1222 active_chartTable.Alloc(entries);
1223 active_chartTable_pathindex.clear();
1224 while (entries-- && entry.Read(
this, ifs)) {
1225 active_chartTable_pathindex[entry.GetFullSystemPath()] = ind++;
1226 active_chartTable.Add(entry);
1231 entry.SetAvailable(
true);
1233 m_nentries = active_chartTable.GetCount();
1238 m_nentries = active_chartTable.GetCount();
1244 bool ChartDatabase::Write(
const wxString &filePath) {
1245 wxFileName file(filePath);
1247 file.GetPath(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME, wxPATH_NATIVE));
1249 if (!dir.DirExists() && !dir.Mkdir())
return false;
1251 wxFFileOutputStream ofs(filePath);
1252 if (!ofs.Ok())
return false;
1254 ChartTableHeader cth(m_chartDirs.GetCount(), active_chartTable.GetCount());
1257 for (
int iDir = 0; iDir < cth.GetDirEntries(); iDir++) {
1258 wxString &dir = m_chartDirs[iDir];
1259 int dirlen = dir.length();
1261 strncpy(s, dir.mb_str(wxConvUTF8), 199);
1264 ofs.Write(&dirlen,
sizeof(
int));
1266 ofs.Write(s, dirlen);
1269 for (UINT32 iTable = 0; iTable < active_chartTable.size(); iTable++)
1270 active_chartTable[iTable].Write(
this, ofs);
1273 m_dbversion = DB_VERSION_CURRENT;
1279 wxString SplitPath(wxString s, wxString tkd,
int nchar,
int offset,
1285 wxStringTokenizer tkz(s, tkd);
1286 while (tkz.HasMoreTokens()) {
1287 wxString token = tkz.GetNextToken();
1288 if ((rlen + (
int)token.Len() + 1) < nchar) {
1291 rlen += token.Len() + 1;
1295 for (
int i = 0; i < offset; i++) {
1300 rlen = offset + token.Len() + 1;
1304 if (pn_split) *pn_split = ncr;
1306 return r.Mid(0, r.Len() - 1);
1309 wxString ChartDatabase::GetFullChartInfo(
ChartBase *pc,
int dbIndex,
1310 int *char_width,
int *line_count) {
1313 unsigned int max_width = 0;
1315 unsigned int target_width = 60;
1321 line = _(
" ChartFile: ");
1322 wxString longline = *(cte.GetpsFullPath());
1324 if (longline.Len() > target_width) {
1325 line += SplitPath(longline, _T(
"/,\\"), target_width, 15, &ncr);
1326 max_width = wxMax(max_width, target_width + 4);
1330 max_width = wxMax(max_width, line.Len() + 4);
1339 line = _(
" Name: ");
1340 wxString longline = pc->GetName();
1343 if (longline.Find(
' ') != wxNOT_FOUND)
1348 if (longline.Len() > target_width) {
1349 line += SplitPath(pc->GetName(), tkz, target_width, 12, &ncr);
1350 max_width = wxMax(max_width, target_width + 4);
1354 max_width = wxMax(max_width, line.Len() + 4);
1363 line.Printf(_T(
" %s: 1:%d"), _(
"Scale"), pc->GetNativeScale());
1365 line.Printf(_T(
" %s: 1:%d"), _(
"Scale"), cte.GetScale());
1368 max_width = wxMax(max_width, line.Len());
1375 line += pc->GetID();
1377 max_width = wxMax(max_width, line.Len());
1382 line = _(
" Depth Units: ");
1383 line += pc->GetDepthUnits();
1385 max_width = wxMax(max_width, line.Len());
1390 line = _(
" Soundings: ");
1391 line += pc->GetSoundingsDatum();
1393 max_width = wxMax(max_width, line.Len());
1398 line = _(
" Datum: ");
1399 line += pc->GetDatumString();
1401 max_width = wxMax(max_width, line.Len());
1406 line = _(
" Projection: ");
1407 if (PROJECTION_UNKNOWN == cte.GetChartProjectionType())
1408 line += _(
"Unknown");
1409 else if (PROJECTION_MERCATOR == cte.GetChartProjectionType())
1410 line += _(
"Mercator");
1411 else if (PROJECTION_TRANSVERSE_MERCATOR == cte.GetChartProjectionType())
1412 line += _(
"Transverse Mercator");
1413 else if (PROJECTION_POLYCONIC == cte.GetChartProjectionType())
1414 line += _(
"Polyconic");
1415 else if (PROJECTION_WEB_MERCATOR == cte.GetChartProjectionType())
1416 line += _(
"Web Mercator (EPSG:3857)");
1418 max_width = wxMax(max_width, line.Len());
1424 line = _(
" Source Edition: ");
1425 line += pc->GetSE();
1427 max_width = wxMax(max_width, line.Len());
1432 wxDateTime ed = pc->GetEditionDate();
1434 line = _(
" Updated: ");
1435 line += ed.FormatISODate();
1437 max_width = wxMax(max_width, line.Len());
1444 if (pc && pc->GetExtraInfo().Len()) {
1445 line += pc->GetExtraInfo();
1447 max_width = wxMax(max_width, line.Len());
1453 if (line_count) *line_count = lc;
1455 if (char_width) *char_width = max_width;
1464 bool ChartDatabase::Create(ArrayOfCDI &dir_array,
1465 wxGenericProgressDialog *pprog) {
1466 m_dir_array = dir_array;
1470 m_chartDirs.Clear();
1471 active_chartTable.Clear();
1472 active_chartTable_pathindex.clear();
1474 Update(dir_array,
true, pprog);
1479 m_dbversion = DB_VERSION_CURRENT;
1488 bool ChartDatabase::Update(ArrayOfCDI &dir_array,
bool bForce,
1489 wxGenericProgressDialog *pprog) {
1490 m_dir_array = dir_array;
1496 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++)
1497 active_chartTable[i].SetValid(
false);
1499 m_chartDirs.Clear();
1501 if (bForce) active_chartTable.Clear();
1503 bool lbForce = bForce;
1506 if (s_dbVersion != DB_VERSION_CURRENT) {
1507 active_chartTable.Clear();
1509 s_dbVersion = DB_VERSION_CURRENT;
1510 m_dbversion = DB_VERSION_CURRENT;
1515 for (
unsigned int j = 0; j < dir_array.GetCount(); j++) {
1521 #ifdef __OCPN__ANDROID__
1522 if (!androidIsDirWritable(dir_info.fullpath))
1528 if (dir_info.fullpath.Find(_T(
"GSHHG")) != wxNOT_FOUND) {
1529 if (!wxDir::FindFirst(dir_info.fullpath,
"poly-*-1.dat").empty()) {
1534 gWorldMapLocation = dir_info.fullpath + wxFileName::GetPathSeparator();
1537 if (dir_info.fullpath.Find(_T(
"OSMSHP")) != wxNOT_FOUND) {
1538 if (!wxDir::FindFirst(dir_info.fullpath,
"basemap_*.shp").empty()) {
1539 gWorldShapefileLocation = dir_info.fullpath + wxFileName::GetPathSeparator();
1540 gShapeBasemap.Reset();
1544 TraverseDirAndAddCharts(dir_info, pprog, dir_magic, lbForce);
1548 dir_info.magic_number = dir_magic;
1549 dir_array.RemoveAt(j);
1550 dir_array.Insert(dir_info, j);
1552 m_chartDirs.Add(dir_info.fullpath);
1555 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
1556 if (!active_chartTable[i].GetbValid()) {
1557 active_chartTable.RemoveAt(i);
1563 active_chartTable_pathindex.clear();
1564 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
1565 active_chartTable_pathindex[active_chartTable[i].GetFullSystemPath()] = i;
1566 active_chartTable[i].SetEntryOffset(i);
1569 m_nentries = active_chartTable.GetCount();
1580 int ChartDatabase::FinddbIndex(wxString PathToFind) {
1583 for(
unsigned int i=0 ; i<active_chartTable.GetCount() ; i++)
1585 if(active_chartTable[i].GetpsFullPath()->IsSameAs(PathToFind))
1591 if (active_chartTable_pathindex.find(PathToFind) !=
1592 active_chartTable_pathindex.end())
1593 return active_chartTable_pathindex[PathToFind];
1603 int ChartDatabase::DisableChart(wxString &PathToDisable) {
1604 int index = FinddbIndex(PathToDisable);
1620 int ChartDatabase::TraverseDirAndAddCharts(
ChartDirInfo &dir_info,
1621 wxGenericProgressDialog *pprog,
1622 wxString &dir_magic,
bool bForce) {
1624 wxString dir_path = dir_info.fullpath;
1625 #ifdef __OCPN__ANDROID__
1626 dir_path = wxString(dir_info.fullpath.mb_str(wxConvUTF8));
1629 wxString old_magic = dir_info.magic_number;
1630 wxString new_magic = old_magic;
1631 dir_magic = old_magic;
1635 bool b_skipDetectDirChange =
false;
1636 bool b_dirchange =
false;
1639 if (!wxDir::Exists(dir_path))
return 0;
1645 bool b_cm93 = Check_CM93_Structure(dir_path);
1647 b_skipDetectDirChange =
true;
1653 if (!b_skipDetectDirChange)
1654 b_dirchange = DetectDirChange(dir_path, dir_info.fullpath, old_magic,
1657 if (!bForce && !b_dirchange) {
1658 wxString msg(_T(
" No change detected on directory "));
1659 msg.Append(dir_path);
1665 wxFileName fn_dir(dir_path, _T(
"stuff"));
1666 unsigned int dir_path_count = fn_dir.GetDirCount();
1668 if (pprog) pprog->SetTitle(_(
"OpenCPN Chart Scan...."));
1670 int nEntries = active_chartTable.GetCount();
1672 for (
int ic = 0; ic < nEntries; ic++) {
1673 wxFileName fn(active_chartTable[ic].GetFullSystemPath());
1675 while (fn.GetDirCount() >= dir_path_count) {
1676 if (fn.GetPath() == dir_path) {
1677 active_chartTable[ic].SetValid(
true);
1693 dir_magic = new_magic;
1696 for (
auto &cd : m_ChartClassDescriptorArray) {
1697 nAdd += SearchDirAndAddCharts(dir_info.fullpath,
1704 bool ChartDatabase::DetectDirChange(
const wxString &dir_path,
1705 const wxString &prog_label,
1706 const wxString &magic, wxString &new_magic,
1707 wxGenericProgressDialog *pprog) {
1708 if (pprog) pprog->SetTitle(_(
"OpenCPN Directory Scan...."));
1711 long long unsigned int nmagic;
1712 wxULongLong nacc = 0;
1714 magic.ToULongLong(&nmagic, 10);
1717 wxArrayString FileList;
1718 wxDir dir(dir_path);
1719 int n_files = dir.GetAllFiles(dir_path, &FileList);
1727 for (
int ifile = 0; ifile < n_files; ifile++) {
1728 if (pprog && (ifile % (n_files / 60 + 1)) == 0)
1729 pprog->Update(wxMin((ifile * 100) / n_files, 100), prog_label);
1731 wxFileName file(FileList[ifile]);
1736 wxString fileNameNative = file.GetFullPath();
1737 wxScopedCharBuffer fileNameUTF8 = fileNameNative.ToUTF8();
1738 hash.Update(fileNameUTF8.data(), fileNameUTF8.length());
1741 wxULongLong size = file.GetSize();
1742 wxULongLong fileSize = ((size != wxInvalidSize) ? size : 0);
1743 hash.Update(&fileSize, (
sizeof fileSize));
1746 wxDateTime t = file.GetModificationTime();
1747 wxULongLong fileTime = t.GetTicks();
1748 hash.Update(&fileTime, (
sizeof fileTime));
1752 hash.Receive(&nacc);
1755 new_magic = nacc.ToString();
1758 if (new_magic != magic)
1764 bool ChartDatabase::IsChartDirUsed(
const wxString &theDir) {
1765 wxString dir(theDir);
1766 if (dir.Last() ==
'/' || dir.Last() == wxFileName::GetPathSeparator())
1769 dir.Append(wxT(
"*"));
1770 for (UINT32 i = 0; i < active_chartTable.GetCount(); i++) {
1771 if (active_chartTable[i].GetpsFullPath()->Matches(dir))
return true;
1780 bool ChartDatabase::Check_CM93_Structure(wxString dir_name) {
1783 wxRegEx test(_T(
"[0-9]+"));
1785 wxDir dirt(dir_name);
1788 if (dirt.IsOpened())
1789 wxLogMessage(_T(
"check_cm93 opened dir OK: ") + dir_name);
1791 wxLogMessage(_T(
"check_cm93 NOT OPENED OK: ") + dir_name);
1792 wxLogMessage(_T(
"check_cm93 returns false.") + dir_name);
1796 bool b_maybe_found_cm93 =
false;
1797 bool b_cont = dirt.GetFirst(&candidate);
1800 if (test.Matches(candidate) && (candidate.Len() == 8)) {
1801 b_maybe_found_cm93 =
true;
1805 b_cont = dirt.GetNext(&candidate);
1808 if (b_maybe_found_cm93) {
1809 wxString dir_next = dir_name;
1810 dir_next += _T(
"/");
1811 dir_next += candidate;
1812 if (wxDir::Exists(dir_next)) {
1813 wxDir dir_n(dir_next);
1814 if (dirt.IsOpened()) {
1815 wxString candidate_n;
1817 wxRegEx test_n(_T(
"^[A-Ga-g]"));
1818 bool b_probably_found_cm93 =
false;
1819 bool b_cont_n = dir_n.IsOpened() && dir_n.GetFirst(&candidate_n);
1821 if (test_n.Matches(candidate_n) && (candidate_n.Len() == 1)) {
1822 b_probably_found_cm93 =
true;
1825 b_cont_n = dir_n.GetNext(&candidate_n);
1828 if (b_probably_found_cm93)
1833 wxString dir_luk = dir_next;
1835 dir_luk += candidate_n;
1836 if (wxDir::Exists(dir_luk))
return true;
1955 WX_DECLARE_STRING_HASH_MAP(
int, ChartCollisionsHashMap);
1957 int ChartDatabase::SearchDirAndAddCharts(wxString &dir_name_base,
1959 wxGenericProgressDialog *pprog) {
1960 wxString msg(_T(
"Searching directory: "));
1961 msg += dir_name_base;
1963 msg += chart_desc.m_search_mask;
1966 wxString dir_name = dir_name_base;
1968 #ifdef __OCPN__ANDROID__
1969 dir_name = wxString(dir_name_base.mb_str(wxConvUTF8));
1972 if (!wxDir::Exists(dir_name))
return 0;
1974 wxString filespec = chart_desc.m_search_mask.Upper();
1975 wxString lowerFileSpec = chart_desc.m_search_mask.Lower();
1976 wxString filespecXZ = filespec + _T(
".xz");
1977 wxString lowerFileSpecXZ = lowerFileSpec + _T(
".xz");
1981 wxArrayString FileList;
1982 int gaf_flags = wxDIR_DEFAULT;
1989 bool b_found_cm93 =
false;
1990 bool b_cm93 = Check_CM93_Structure(dir_name);
1992 if (filespec != _T(
"00300000.A"))
1995 filespec = dir_name;
1996 b_found_cm93 =
true;
2000 if (!b_found_cm93) {
2002 wxDir dir(dir_name);
2003 dir.GetAllFiles(dir_name, &FileList, filespec, gaf_flags);
2005 #ifdef __OCPN__ANDROID__
2006 if (!FileList.GetCount()) {
2007 wxArrayString afl = androidTraverseDir(dir_name, filespec);
2008 for (wxArrayString::const_iterator item = afl.begin(); item != afl.end();
2010 FileList.Add(*item);
2016 if (filespec != lowerFileSpec) {
2018 wxArrayString lowerFileList;
2019 dir.GetAllFiles(dir_name, &lowerFileList, lowerFileSpec, gaf_flags);
2022 #ifdef __OCPN__ANDROID__
2023 if (!lowerFileList.GetCount()) {
2024 wxArrayString afl = androidTraverseDir(dir_name, lowerFileSpec);
2025 for (wxArrayString::const_iterator item = afl.begin();
2026 item != afl.end(); item++)
2027 lowerFileList.Add(*item);
2031 for (wxArrayString::const_iterator item = lowerFileList.begin();
2032 item != lowerFileList.end(); item++)
2033 FileList.Add(*item);
2037 #ifdef OCPN_USE_LZMA
2039 dir.GetAllFiles(dir_name, &FileList, filespecXZ, gaf_flags);
2040 dir.GetAllFiles(dir_name, &FileList, lowerFileSpecXZ, gaf_flags);
2047 wxString dir_plus = dir_name;
2048 dir_plus += wxFileName::GetPathSeparator();
2049 FileList.Add(dir_plus);
2052 int nFile = FileList.GetCount();
2054 if (!nFile)
return false;
2061 bool bthis_dir_in_dB = IsChartDirUsed(dir_name);
2063 if (pprog) pprog->SetTitle(_(
"OpenCPN Chart Add...."));
2067 ChartCollisionsHashMap collision_map;
2068 int nEntry = active_chartTable.GetCount();
2069 for (
int i = 0; i < nEntry; i++) {
2070 wxString table_file_name = active_chartTable[i].GetFullSystemPath();
2071 wxFileName table_file(table_file_name);
2072 collision_map[table_file.GetFullName()] = i;
2075 int nFileProgressQuantum = wxMax(nFile / 100, 2);
2076 double rFileProgressRatio = 100.0 / wxMax(nFile, 1);
2078 for (
int ifile = 0; ifile < nFile; ifile++) {
2079 wxFileName file(FileList[ifile]);
2080 wxString full_name = file.GetFullPath();
2081 wxString file_name = file.GetFullName();
2082 wxString utf8_path = full_name;
2084 #ifdef __OCPN__ANDROID__
2090 wxFileName fnbase(dir_name_base);
2091 int nDirs = fnbase.GetDirCount();
2093 wxFileName file_target(FileList[ifile]);
2095 for (
int i = 0; i < nDirs + 1;
2097 file_target.RemoveDir(0);
2099 wxString leftover_path = file_target.GetFullPath();
2101 dir_name_base + leftover_path;
2107 if (!file_name.Matches(lowerFileSpec) && !file_name.Matches(filespec) &&
2108 !file_name.Matches(lowerFileSpecXZ) && !file_name.Matches(filespecXZ) &&
2114 if (pprog && ((ifile % nFileProgressQuantum) == 0))
2115 pprog->Update(
static_cast<int>(ifile * rFileProgressRatio), utf8_path);
2118 bool bAddFinal =
true;
2123 ChartCollisionsHashMap::const_iterator collision_ptr =
2124 collision_map.find(file_name);
2125 bool collision = (collision_ptr != collision_map.end());
2126 bool file_path_is_same =
false;
2127 bool file_time_is_same =
false;
2129 wxString table_file_name;
2132 pEntry = &active_chartTable[collision_ptr->second];
2133 table_file_name = pEntry->GetFullSystemPath();
2135 bthis_dir_in_dB && full_name.IsSameAs(table_file_name);
2139 if (file_path_is_same) {
2143 time_t t_oldFile = pEntry->GetFileTime();
2144 time_t t_newFile = file.GetModificationTime().GetTicks();
2146 if (t_newFile <= t_oldFile) {
2147 file_time_is_same =
true;
2149 pEntry->SetValid(
true);
2152 pEntry->SetValid(
false);
2157 wxString msg_fn(full_name);
2158 msg_fn.Replace(_T(
"%"), _T(
"%%"));
2159 if (file_time_is_same) {
2163 wxString::Format(_T(
"Loading chart data for %s"), msg_fn.c_str()));
2165 pnewChart = CreateChartTableEntry(full_name, utf8_path, chart_desc);
2168 wxLogMessage(wxString::Format(
2169 _T(
" CreateChartTableEntry() failed for file: %s"),
2174 if (!collision || !pnewChart) {
2176 }
else if (file_path_is_same) {
2178 wxString::Format(_T(
" Replacing older chart file of same path: %s"),
2180 }
else if (!file_time_is_same) {
2186 if (pnewChart->IsEarlierThan(*pEntry)) {
2187 wxFileName table_file(table_file_name);
2189 if (table_file.IsFileReadable()) {
2190 pEntry->SetValid(
true);
2192 wxLogMessage(wxString::Format(
2193 _T(
" Retaining newer chart file of same name: %s"),
2196 }
else if (pnewChart->IsEqualTo(*pEntry)) {
2204 pEntry->SetValid(
false);
2206 wxLogMessage(wxString::Format(
2207 _T(
" Replacing older chart file of same name: %s"),
2213 if (0 == b_add_msg) {
2215 wxString::Format(_T(
" Adding chart file: %s"), msg_fn.c_str()));
2217 collision_map[file_name] = active_chartTable.GetCount();
2218 active_chartTable.Add(pnewChart);
2221 if (pnewChart)
delete pnewChart;
2227 m_nentries = active_chartTable.GetCount();
2232 bool ChartDatabase::AddChart(wxString &chartfilename,
2234 wxGenericProgressDialog *pprog,
int isearch,
2235 bool bthis_dir_in_dB) {
2237 wxFileName file(chartfilename);
2238 wxString full_name = file.GetFullPath();
2239 wxString file_name = file.GetFullName();
2249 pprog->Update(wxMin((m_pdifile * 100) / m_pdnFile, 100), full_name);
2252 bool bAddFinal =
true;
2254 wxString msg_fn(full_name);
2255 msg_fn.Replace(_T(
"%"), _T(
"%%"));
2257 pnewChart = CreateChartTableEntry(full_name, full_name, chart_desc);
2260 wxLogMessage(wxString::Format(
2261 _T(
" CreateChartTableEntry() failed for file: %s"), msg_fn.c_str()));
2266 int nEntry = active_chartTable.GetCount();
2267 for (
int i = 0; i < nEntry; i++) {
2268 wxString *ptable_file_name = active_chartTable[isearch].GetpsFullPath();
2272 if (bthis_dir_in_dB && full_name.IsSameAs(*ptable_file_name)) {
2276 time_t t_oldFile = active_chartTable[isearch].GetFileTime();
2277 time_t t_newFile = file.GetModificationTime().GetTicks();
2279 if (t_newFile <= t_oldFile) {
2281 active_chartTable[isearch].SetValid(
true);
2284 active_chartTable[isearch].SetValid(
false);
2285 wxLogMessage(wxString::Format(
2286 _T(
" Replacing older chart file of same path: %s"),
2296 wxFileName table_file(*ptable_file_name);
2298 if (table_file.GetFullName() == file_name) {
2301 if (pnewChart->IsEarlierThan(active_chartTable[isearch])) {
2303 if (table_file.IsFileReadable()) {
2304 active_chartTable[isearch].SetValid(
true);
2306 wxLogMessage(wxString::Format(
2307 _T(
" Retaining newer chart file of same name: %s"),
2310 }
else if (pnewChart->IsEqualTo(active_chartTable[isearch])) {
2320 active_chartTable[isearch].SetValid(
false);
2322 wxLogMessage(wxString::Format(
2323 _T(
" Replacing older chart file of same name: %s"),
2333 if (nEntry == isearch) isearch = 0;
2338 if (0 == b_add_msg) {
2340 wxString::Format(_T(
" Adding chart file: %s"), msg_fn.c_str()));
2343 active_chartTable.Add(pnewChart);
2353 m_nentries = active_chartTable.GetCount();
2358 bool ChartDatabase::AddSingleChart(wxString &ChartFullPath,
2359 bool b_force_full_search) {
2361 wxFileName fn(ChartFullPath);
2362 wxString ext = fn.GetExt();
2363 ext.Prepend(_T(
"*."));
2364 wxString ext_upper = ext.MakeUpper();
2365 wxString ext_lower = ext.MakeLower();
2366 wxString dir_name = fn.GetPath();
2372 for (
auto &cd : m_ChartClassDescriptorArray) {
2373 if (cd.m_descriptor_type == PLUGIN_DESCRIPTOR) {
2374 if (cd.m_search_mask == ext_upper) {
2378 if (cd.m_search_mask == ext_lower) {
2387 bool b_recurse =
true;
2388 if (!b_force_full_search) b_recurse = IsChartDirUsed(dir_name);
2390 bool rv = AddChart(ChartFullPath, desc, NULL, 0, b_recurse);
2394 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
2395 if (!active_chartTable[i].GetbValid()) {
2396 active_chartTable.RemoveAt(i);
2402 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++)
2403 active_chartTable[i].SetEntryOffset(i);
2407 DetectDirChange(dir_name, _T(
""), _T(
""), new_magic, 0);
2410 bool bcfound =
false;
2411 ArrayOfCDI NewChartDirArray;
2413 ArrayOfCDI ChartDirArray = GetChartDirArray();
2414 for (
unsigned int i = 0; i < ChartDirArray.GetCount(); i++) {
2420 if (newcdi.fullpath == dir_name) {
2421 newcdi.magic_number = new_magic;
2425 NewChartDirArray.Add(newcdi);
2430 cdi.fullpath = dir_name;
2431 cdi.magic_number = new_magic;
2432 NewChartDirArray.Add(cdi);
2436 SetChartDirArray(NewChartDirArray);
2439 m_chartDirs.Clear();
2441 for (
unsigned int i = 0; i < GetChartDirArray().GetCount(); i++) {
2443 m_chartDirs.Add(cdi.fullpath);
2446 m_nentries = active_chartTable.GetCount();
2451 bool ChartDatabase::RemoveSingleChart(wxString &ChartFullPath) {
2455 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
2456 if (ChartFullPath.IsSameAs(GetChartTableEntry(i).GetFullSystemPath())) {
2457 active_chartTable.RemoveAt(i);
2465 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
2466 pcte = GetpChartTableEntry(i);
2467 pcte->SetEntryOffset(i);
2471 wxFileName fn(ChartFullPath);
2472 wxString fd = fn.GetPath();
2473 if (!IsChartDirUsed(fd)) {
2475 ArrayOfCDI NewChartDirArray;
2477 ArrayOfCDI ChartDirArray = GetChartDirArray();
2478 for (
unsigned int i = 0; i < ChartDirArray.GetCount(); i++) {
2483 if (newcdi.fullpath != fd) NewChartDirArray.Add(newcdi);
2486 SetChartDirArray(NewChartDirArray);
2490 m_chartDirs.Clear();
2491 for (
unsigned int i = 0; i < GetChartDirArray().GetCount(); i++) {
2493 m_chartDirs.Add(cdi.fullpath);
2496 m_nentries = active_chartTable.GetCount();
2505 ChartBase *ChartDatabase::GetChart(
const wxChar *theFilePath,
2516 const wxString &filePath, wxString &utf8Path,
2518 wxString msg_fn(filePath);
2519 msg_fn.Replace(_T(
"%"), _T(
"%%"));
2521 wxString::Format(_T(
"Loading chart data for %s"), msg_fn.c_str()));
2523 ChartBase *pch = GetChart(filePath, chart_desc);
2526 wxString::Format(_T(
" ...creation failed for %s"), msg_fn.c_str()));
2530 InitReturn rc = pch->Init(filePath, HEADER_ONLY);
2531 if (rc != INIT_OK) {
2533 wxLogMessage(wxString::Format(_T(
" ...initialization failed for %s"),
2539 ret_val->SetValid(
true);
2546 bool ChartDatabase::GetCentroidOfLargestScaleChart(
double *clat,
double *clon,
2547 ChartFamilyEnum family) {
2549 int cur_max_scale = 0;
2551 int nEntry = active_chartTable.GetCount();
2553 for (
int i = 0; i < nEntry; i++) {
2554 if (GetChartFamily(active_chartTable[i].GetChartType()) == family) {
2555 if (active_chartTable[i].GetScale() > cur_max_scale) {
2556 cur_max_scale = active_chartTable[i].GetScale();
2562 if (cur_max_i == -1)
2565 *clat = (active_chartTable[cur_max_i].GetLatMax() +
2566 active_chartTable[cur_max_i].GetLatMin()) /
2568 *clon = (active_chartTable[cur_max_i].GetLonMin() +
2569 active_chartTable[cur_max_i].GetLonMax()) /
2578 int ChartDatabase::GetDBChartProj(
int dbIndex) {
2579 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size()))
2580 return active_chartTable[dbIndex].GetChartProjectionType();
2582 return PROJECTION_UNKNOWN;
2588 int ChartDatabase::GetDBChartFamily(
int dbIndex) {
2589 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size()))
2590 return active_chartTable[dbIndex].GetChartFamily();
2592 return CHART_FAMILY_UNKNOWN;
2598 wxString ChartDatabase::GetDBChartFileName(
int dbIndex) {
2599 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2600 return wxString(active_chartTable[dbIndex].GetFullSystemPath());
2608 int ChartDatabase::GetDBChartType(
int dbIndex) {
2609 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size()))
2610 return active_chartTable[dbIndex].GetChartType();
2618 float ChartDatabase::GetDBChartSkew(
int dbIndex) {
2619 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size()))
2620 return active_chartTable[dbIndex].GetChartSkew();
2628 int ChartDatabase::GetDBChartScale(
int dbIndex) {
2629 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size()))
2630 return active_chartTable[dbIndex].GetScale();
2638 bool ChartDatabase::GetDBBoundingBox(
int dbIndex, LLBBox &box) {
2639 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2641 box.Set(entry.GetLatMin(), entry.GetLonMin(), entry.GetLatMax(),
2648 const LLBBox &ChartDatabase::GetDBBoundingBox(
int dbIndex) {
2649 if ((bValid) && (dbIndex >= 0)) {
2651 return entry.GetBBox();
2653 return m_dummy_bbox;
2660 int ChartDatabase::GetDBPlyPoint(
int dbIndex,
int plyindex,
float *lat,
2662 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2664 if (entry.GetnPlyEntries()) {
2665 float *fp = entry.GetpPlyTable();
2667 if (lat) *lat = *fp;
2669 if (lon) *lon = *fp;
2671 return entry.GetnPlyEntries();
2679 int ChartDatabase::GetDBAuxPlyPoint(
int dbIndex,
int plyindex,
int ply,
2680 float *lat,
float *lon) {
2681 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2683 if (entry.GetnAuxPlyEntries()) {
2684 float *fp = entry.GetpAuxPlyTableEntry(ply);
2687 if (lat) *lat = *fp;
2689 if (lon) *lon = *fp;
2692 return entry.GetAuxCntTableEntry(ply);
2697 int ChartDatabase::GetnAuxPlyEntries(
int dbIndex) {
2698 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2700 return entry.GetnAuxPlyEntries();
2708 std::vector<float> ChartDatabase::GetReducedPlyPoints(
int dbIndex) {
2709 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2711 if (pentry)
return pentry->GetReducedPlyPoints();
2714 std::vector<float> dummy;
2721 std::vector<float> ChartDatabase::GetReducedAuxPlyPoints(
int dbIndex,
2723 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2725 if (pentry)
return pentry->GetReducedAuxPlyPoints(iTable);
2728 std::vector<float> dummy;
2732 bool ChartDatabase::IsChartAvailable(
int dbIndex) {
2733 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2737 if (pentry->GetChartType() != CHART_TYPE_PLUGIN)
return true;
2739 wxString *path = pentry->GetpsFullPath();
2740 wxFileName fn(*path);
2741 wxString ext = fn.GetExt();
2742 ext.Prepend(_T(
"*."));
2743 wxString ext_upper = ext.MakeUpper();
2744 wxString ext_lower = ext.MakeLower();
2749 for (
auto &cd : m_ChartClassDescriptorArray) {
2750 if (cd.m_descriptor_type ==
2751 PLUGIN_DESCRIPTOR) {
2752 wxString search_mask = cd.m_search_mask;
2754 if (search_mask == ext_upper) {
2757 if (search_mask == ext_lower) {
2760 if (path->Matches(search_mask)) {
2770 void ChartDatabase::ApplyGroupArray(ChartGroupArray *pGroupArray) {
2771 wxString separator(wxFileName::GetPathSeparator());
2773 for (
unsigned int ic = 0; ic < active_chartTable.GetCount(); ic++) {
2776 pcte->ClearGroupArray();
2778 wxString *chart_full_path = pcte->GetpsFullPath();
2780 for (
unsigned int igroup = 0; igroup < pGroupArray->GetCount(); igroup++) {
2781 ChartGroup *pGroup = pGroupArray->Item(igroup);
2782 for (
const auto &elem : pGroup->m_element_array) {
2783 wxString element_root = elem.m_element_name;
2789 if (!chart_full_path->IsSameAs(element_root))
2790 element_root.Append(
2792 if (chart_full_path->StartsWith(element_root)) {
2794 for (
unsigned int k = 0; k < elem.m_missing_name_array.size(); k++) {
2795 const wxString &missing_item = elem.m_missing_name_array[k];
2796 if (chart_full_path->StartsWith(missing_item)) {
2797 if (chart_full_path->IsSameAs(
2803 if (wxDir::Exists(missing_item))
2812 if (b_add) pcte->AddIntToGroupArray(igroup + 1);
Set of basemaps at different resolutions.