32#include <wx/arrimpl.cpp>
33#include <wx/encconv.h>
35#include <wx/progdlg.h>
36#include <wx/tokenzr.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"
57extern wxString gWorldMapLocation;
58extern wxString gWorldShapefileLocation;
61static int s_dbVersion;
67bool 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))
79static 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;
115void ChartTableHeader::Read(wxInputStream &is) {
119void ChartTableHeader::Write(wxOutputStream &os) {
121 sprintf(vb,
"V%03d", DB_VERSION_CURRENT);
123 memcpy(dbVersion, vb, 4);
127bool 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: "));
169void ChartTableEntry::SetScale(
int scale) {
173 if (Scale >= 1000) rounding = 5 * pow(10, log10(Scale) - 2);
176ChartTableEntry::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;
392ChartTableEntry::~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);
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));
436static 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;
464static 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;
483bool 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++);
495 pFullPath = (
char *)malloc(cp - path + 1);
496 strncpy(pFullPath, path, cp - path + 1);
497 wxLogVerbose(_T(
" Chart %s"), pFullPath);
500 m_pfilename =
new wxString;
501 wxString fullfilename(pFullPath, wxConvUTF8);
502 wxFileName fn(fullfilename);
503 *m_pfilename = fn.GetFullName();
504 m_psFullPath =
new wxString;
505 *m_psFullPath = fullfilename;
506 m_fullSystemPath = fullfilename;
508#ifdef __OCPN__ANDROID__
509 m_fullSystemPath = wxString(fullfilename.mb_str(wxConvUTF8));
516 EntryOffset = cte.EntryOffset;
517 ChartType = cte.ChartType;
518 ChartFamily = cte.ChartFamily;
524 m_bbox.Set(LatMin, LonMin, LatMax, LonMax);
527 ProjectionType = cte.ProjectionType;
530 edition_date = cte.edition_date;
531 file_date = cte.file_date;
533 nPlyEntries = cte.nPlyEntries;
534 nAuxPlyEntries = cte.nAuxPlyEntries;
536 nNoCovrPlyEntries = cte.nNoCovrPlyEntries;
541 int npeSize = nPlyEntries * 2 *
sizeof(float);
542 pPlyTable = (
float *)malloc(npeSize);
543 is.Read(pPlyTable, npeSize);
546 if (nAuxPlyEntries) {
547 int napeSize = nAuxPlyEntries *
sizeof(int);
548 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
549 pAuxCntTable = (
int *)malloc(napeSize);
550 is.Read(pAuxCntTable, napeSize);
552 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
554 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
555 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
556 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
560 if (nNoCovrPlyEntries) {
561 int napeSize = nNoCovrPlyEntries *
sizeof(int);
562 pNoCovrCntTable = (
int *)malloc(napeSize);
563 is.Read(pNoCovrCntTable, napeSize);
565 pNoCovrPlyTable = (
float **)malloc(nNoCovrPlyEntries *
sizeof(
float *));
566 for (
int i = 0; i < nNoCovrPlyEntries; i++) {
567 int nfSize = pNoCovrCntTable[i] * 2 *
sizeof(float);
568 pNoCovrPlyTable[i] = (
float *)malloc(nfSize);
569 is.Read(pNoCovrPlyTable[i], nfSize);
574 else if (db_version == 17) {
576 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
577 pFullPath = (
char *)malloc(cp - path + 1);
578 strncpy(pFullPath, path, cp - path + 1);
579 wxLogVerbose(_T(
" Chart %s"), pFullPath);
582 m_pfilename =
new wxString;
583 wxString fullfilename(pFullPath, wxConvUTF8);
584 wxFileName fn(fullfilename);
585 *m_pfilename = fn.GetFullName();
586 m_psFullPath =
new wxString;
587 *m_psFullPath = fullfilename;
594 EntryOffset = cte.EntryOffset;
595 ChartType = cte.ChartType;
601 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
604 ProjectionType = cte.ProjectionType;
607 edition_date = cte.edition_date;
608 file_date = cte.file_date;
610 nPlyEntries = cte.nPlyEntries;
611 nAuxPlyEntries = cte.nAuxPlyEntries;
613 nNoCovrPlyEntries = cte.nNoCovrPlyEntries;
618 int npeSize = nPlyEntries * 2 *
sizeof(float);
619 pPlyTable = (
float *)malloc(npeSize);
620 is.Read(pPlyTable, npeSize);
623 if (nAuxPlyEntries) {
624 int napeSize = nAuxPlyEntries *
sizeof(int);
625 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
626 pAuxCntTable = (
int *)malloc(napeSize);
627 is.Read(pAuxCntTable, napeSize);
629 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
631 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
632 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
633 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
637 if (nNoCovrPlyEntries) {
638 int napeSize = nNoCovrPlyEntries *
sizeof(int);
639 pNoCovrCntTable = (
int *)malloc(napeSize);
640 is.Read(pNoCovrCntTable, napeSize);
642 pNoCovrPlyTable = (
float **)malloc(nNoCovrPlyEntries *
sizeof(
float *));
643 for (
int i = 0; i < nNoCovrPlyEntries; i++) {
644 int nfSize = pNoCovrCntTable[i] * 2 *
sizeof(float);
645 pNoCovrPlyTable[i] = (
float *)malloc(nfSize);
646 is.Read(pNoCovrPlyTable[i], nfSize);
651 else if (db_version == 16) {
653 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
655 pFullPath = (
char *)malloc(cp - path + 1);
656 strncpy(pFullPath, path, cp - path + 1);
657 wxLogVerbose(_T(
" Chart %s"), pFullPath);
660 m_pfilename =
new wxString;
661 wxString fullfilename(pFullPath, wxConvUTF8);
662 wxFileName fn(fullfilename);
663 *m_pfilename = fn.GetFullName();
664 m_psFullPath =
new wxString;
665 *m_psFullPath = fullfilename;
672 EntryOffset = cte.EntryOffset;
673 ChartType = cte.ChartType;
679 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
682 ProjectionType = cte.ProjectionType;
685 edition_date = cte.edition_date;
686 file_date = cte.file_date;
688 nPlyEntries = cte.nPlyEntries;
689 nAuxPlyEntries = cte.nAuxPlyEntries;
694 int npeSize = nPlyEntries * 2 *
sizeof(float);
695 pPlyTable = (
float *)malloc(npeSize);
696 is.Read(pPlyTable, npeSize);
699 if (nAuxPlyEntries) {
700 int napeSize = nAuxPlyEntries *
sizeof(int);
701 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
702 pAuxCntTable = (
int *)malloc(napeSize);
703 is.Read(pAuxCntTable, napeSize);
705 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
707 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
708 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
709 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
714 else if (db_version == 15) {
716 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
718 pFullPath = (
char *)malloc(cp - path + 1);
719 strncpy(pFullPath, path, cp - path + 1);
720 wxLogVerbose(_T(
" Chart %s"), pFullPath);
727 EntryOffset = cte.EntryOffset;
728 ChartType = cte.ChartType;
734 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
737 edition_date = cte.edition_date;
738 file_date = cte.file_date;
740 nPlyEntries = cte.nPlyEntries;
741 nAuxPlyEntries = cte.nAuxPlyEntries;
746 int npeSize = nPlyEntries * 2 *
sizeof(float);
747 pPlyTable = (
float *)malloc(npeSize);
748 is.Read(pPlyTable, npeSize);
751 if (nAuxPlyEntries) {
752 int napeSize = nAuxPlyEntries *
sizeof(int);
753 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
754 pAuxCntTable = (
int *)malloc(napeSize);
755 is.Read(pAuxCntTable, napeSize);
757 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
759 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
760 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
761 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
764 }
else if (db_version == 14) {
766 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
767 pFullPath = (
char *)malloc(cp - path + 1);
768 strncpy(pFullPath, path, cp - path + 1);
769 wxLogVerbose(_T(
" Chart %s"), pFullPath);
776 EntryOffset = cte.EntryOffset;
777 ChartType = cte.ChartType;
783 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
786 edition_date = cte.edition_date;
788 nPlyEntries = cte.nPlyEntries;
789 nAuxPlyEntries = cte.nAuxPlyEntries;
793 int npeSize = nPlyEntries * 2 *
sizeof(float);
794 pPlyTable = (
float *)malloc(npeSize);
795 is.Read(pPlyTable, npeSize);
798 if (nAuxPlyEntries) {
799 int napeSize = nAuxPlyEntries *
sizeof(int);
800 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
801 pAuxCntTable = (
int *)malloc(napeSize);
802 is.Read(pAuxCntTable, napeSize);
804 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
806 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
807 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
808 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
812 ChartFamily = convertChartFamily(ChartType, ChartFamily);
813 ChartType = convertChartType(ChartType);
820bool ChartTableEntry::Write(
const ChartDatabase *pDb, wxOutputStream &os) {
821 os.Write(pFullPath, strlen(pFullPath) + 1);
828 cte.EntryOffset = EntryOffset;
829 cte.ChartType = ChartType;
830 cte.ChartFamily = ChartFamily;
837 cte.edition_date = edition_date;
838 cte.file_date = file_date;
840 cte.nPlyEntries = nPlyEntries;
841 cte.nAuxPlyEntries = nAuxPlyEntries;
844 cte.ProjectionType = ProjectionType;
848 cte.nNoCovrPlyEntries = nNoCovrPlyEntries;
851 wxLogVerbose(_T(
" Wrote Chart %s"), pFullPath);
855 int npeSize = nPlyEntries * 2 *
sizeof(float);
856 os.Write(pPlyTable, npeSize);
859 if (nAuxPlyEntries) {
860 int napeSize = nAuxPlyEntries *
sizeof(int);
861 os.Write(pAuxCntTable, napeSize);
863 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries; nAuxPlyEntry++) {
864 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
865 os.Write(pAuxPlyTable[nAuxPlyEntry], nfSize);
869 if (nNoCovrPlyEntries) {
870 int ncSize = nNoCovrPlyEntries *
sizeof(int);
871 os.Write(pNoCovrCntTable, ncSize);
873 for (
int i = 0; i < nNoCovrPlyEntries; i++) {
874 int nctSize = pNoCovrCntTable[i] * 2 *
sizeof(float);
875 os.Write(pNoCovrPlyTable[i], nctSize);
884void ChartTableEntry::Clear() {
893 pNoCovrCntTable = NULL;
894 pNoCovrPlyTable = NULL;
896 nNoCovrPlyEntries = 0;
905void ChartTableEntry::Disable() {
909 LatMax += (float)1000.;
910 LatMin += (float)1000.;
913void ChartTableEntry::ReEnable() {
915 LatMax -= (float)1000.;
916 LatMin -= (float)1000.;
920std::vector<float> ChartTableEntry::GetReducedPlyPoints() {
921 if (m_reducedPlyPoints.size())
return m_reducedPlyPoints;
924 float LOD_meters = 1;
926 float plylat, plylon;
927 const int nPoints = GetnPlyEntries();
929 float *fpo = GetpPlyTable();
931 double *ppd =
new double[nPoints * 2];
932 double *ppsm =
new double[nPoints * 2];
935 for (
int i = 0; i < nPoints; i++) {
937 plylon = fpo[i * 2 + 1];
940 toSM(plylat, plylon, fpo[0], fpo[1], &x, &y);
948 std::vector<int> index_keep;
950 index_keep.push_back(0);
951 index_keep.push_back(nPoints - 1);
952 index_keep.push_back(1);
953 index_keep.push_back(nPoints - 2);
955 DouglasPeuckerM(ppsm, 1, nPoints - 2, LOD_meters, &index_keep);
958 index_keep.resize(nPoints);
959 for (
int i = 0; i < nPoints; i++) index_keep[i] = i;
963 for (
int ip = 0; ip < nPoints; ip++) {
967 for (
unsigned int j = 0; j < index_keep.size(); j++) {
968 if (index_keep[j] == ip) {
969 m_reducedPlyPoints.push_back(x);
970 m_reducedPlyPoints.push_back(y);
979 int nprr = m_reducedPlyPoints.size() / 2;
981 return m_reducedPlyPoints;
984std::vector<float> ChartTableEntry::GetReducedAuxPlyPoints(
int iTable) {
986 if (!m_reducedAuxPlyPointsVector.size()) {
987 std::vector<float> vec;
988 for (
int i = 0; i < GetnAuxPlyEntries(); i++) {
989 m_reducedAuxPlyPointsVector.push_back(vec);
993 std::vector<float> vec;
996 if ((
unsigned int)iTable >= m_reducedAuxPlyPointsVector.size())
return vec;
998 if (m_reducedAuxPlyPointsVector.at(iTable).size())
999 return m_reducedAuxPlyPointsVector.at(iTable);
1002 float LOD_meters = 1.0;
1004 const int nPoints = GetAuxCntTableEntry(iTable);
1005 float *fpo = GetpAuxPlyTableEntry(iTable);
1007 double *ppd =
new double[nPoints * 2];
1008 double *ppsm =
new double[nPoints * 2];
1010 double *npsm = ppsm;
1011 float plylat, plylon;
1013 for (
int i = 0; i < nPoints; i++) {
1014 plylat = fpo[i * 2];
1015 plylon = fpo[i * 2 + 1];
1018 toSM(plylat, plylon, fpo[0], fpo[1], &x, &y);
1026 std::vector<int> index_keep;
1028 index_keep.push_back(0);
1029 index_keep.push_back(nPoints - 1);
1030 index_keep.push_back(1);
1031 index_keep.push_back(nPoints - 2);
1033 DouglasPeuckerM(ppsm, 1, nPoints - 2, LOD_meters, &index_keep);
1036 index_keep.resize(nPoints);
1037 for (
int i = 0; i < nPoints; i++) index_keep[i] = i;
1040 int nnn = index_keep.size();
1043 for (
int ip = 0; ip < nPoints; ip++) {
1047 for (
unsigned int j = 0; j < index_keep.size(); j++) {
1048 if (index_keep[j] == ip) {
1059 m_reducedAuxPlyPointsVector[iTable] = vec;
1061 int nprr = vec.size() / 2;
1070WX_DEFINE_OBJARRAY(ChartTable);
1072ChartDatabase::ChartDatabase() {
1076 m_ChartTableEntryDummy.Clear();
1078 UpdateChartClassDescriptorArray();
1081void ChartDatabase::UpdateChartClassDescriptorArray(
void) {
1082 if (m_ChartClassDescriptorArray.empty()) {
1083 m_ChartClassDescriptorArray.push_back(
1085 m_ChartClassDescriptorArray.push_back(
1087 m_ChartClassDescriptorArray.push_back(
1089 m_ChartClassDescriptorArray.push_back(
1092 _T(
"cm93compchart"), _T(
"00300000.a"), BUILTIN_DESCRIPTOR));
1094 _T(
"ChartMbTiles"), _T(
"*.mbtiles"), BUILTIN_DESCRIPTOR));
1099 m_ChartClassDescriptorArray.erase(
1100 std::remove_if(m_ChartClassDescriptorArray.begin(),
1101 m_ChartClassDescriptorArray.end(),
1103 return cd.m_descriptor_type == PLUGIN_DESCRIPTOR;
1105 m_ChartClassDescriptorArray.end());
1107 wxArrayString array = g_pi_manager->GetPlugInChartClassNameArray();
1108 for (
unsigned int j = 0; j < array.GetCount(); j++) {
1111 wxString class_name = array[j];
1114 wxString mask = cpiw->GetFileSearchMask();
1117 m_ChartClassDescriptorArray.push_back(
1125const ChartTableEntry &ChartDatabase::GetChartTableEntry(
int index)
const {
1126 if (index < GetChartTableEntries())
1127 return active_chartTable[index];
1129 return m_ChartTableEntryDummy;
1133 if (index < GetChartTableEntries())
1134 return &active_chartTable[index];
1139bool ChartDatabase::CompareChartDirArray(ArrayOfCDI &test_array) {
1143 if (test_array.GetCount() != m_dir_array.GetCount())
return false;
1146 unsigned int nfound_outer = 0;
1148 for (
unsigned int i = 0; i < test_array.GetCount(); i++) {
1150 bfound_inner =
false;
1151 for (
unsigned int j = 0; j < m_dir_array.GetCount(); j++) {
1154 if (p.fullpath.IsSameAs(q.fullpath)) {
1155 bfound_inner =
true;
1159 if (bfound_inner) nfound_outer++;
1162 return (nfound_outer == test_array.GetCount());
1165wxString ChartDatabase::GetMagicNumberCached(wxString dir) {
1166 for (
unsigned int j = 0; j < m_dir_array.GetCount(); j++) {
1168 if (dir.IsSameAs(q.fullpath))
return q.magic_number;
1174bool ChartDatabase::Read(
const wxString &filePath) {
1180 wxFileName file(filePath);
1181 if (!file.FileExists())
return false;
1183 m_DBFileName = filePath;
1185 wxFFileInputStream ifs(filePath);
1186 if (!ifs.Ok())
return false;
1190 if (!cth.CheckValid())
return false;
1194 memcpy(vbo, cth.GetDBVersionString(), 4);
1196 m_dbversion = atoi(&vbo[1]);
1197 s_dbVersion = m_dbversion;
1199 wxLogVerbose(wxT(
"Chartdb:Reading %d directory entries, %d table entries"),
1200 cth.GetDirEntries(), cth.GetTableEntries());
1201 wxLogMessage(_T(
"Chartdb: Chart directory list follows"));
1202 if (0 == cth.GetDirEntries()) wxLogMessage(_T(
" Nil"));
1205 for (
int iDir = 0; iDir < cth.GetDirEntries(); iDir++) {
1208 ifs.Read(&dirlen,
sizeof(
int));
1209 while (dirlen > 0) {
1211 int alen = dirlen > 1023 ? 1023 : dirlen;
1212 if (ifs.Read(&dirbuf, alen).Eof())
goto read_error;
1215 dir.Append(wxString(dirbuf, wxConvUTF8));
1218 msg.Printf(wxT(
" Chart directory #%d: "), iDir);
1221 m_chartDirs.Add(dir);
1224 entries = cth.GetTableEntries();
1225 active_chartTable.Alloc(entries);
1226 active_chartTable_pathindex.clear();
1227 while (entries-- && entry.Read(
this, ifs)) {
1228 active_chartTable_pathindex[entry.GetFullSystemPath()] = ind++;
1229 active_chartTable.Add(entry);
1234 entry.SetAvailable(
true);
1236 m_nentries = active_chartTable.GetCount();
1241 m_nentries = active_chartTable.GetCount();
1247bool ChartDatabase::Write(
const wxString &filePath) {
1248 wxFileName file(filePath);
1250 file.GetPath(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME, wxPATH_NATIVE));
1252 if (!dir.DirExists() && !dir.Mkdir())
return false;
1254 wxFFileOutputStream ofs(filePath);
1255 if (!ofs.Ok())
return false;
1257 ChartTableHeader cth(m_chartDirs.GetCount(), active_chartTable.GetCount());
1260 for (
int iDir = 0; iDir < cth.GetDirEntries(); iDir++) {
1261 wxString &dir = m_chartDirs[iDir];
1262 int dirlen = dir.length();
1264 strncpy(s, dir.mb_str(wxConvUTF8), 199);
1267 ofs.Write(&dirlen,
sizeof(
int));
1269 ofs.Write(s, dirlen);
1272 for (UINT32 iTable = 0; iTable < active_chartTable.size(); iTable++)
1273 active_chartTable[iTable].Write(
this, ofs);
1276 m_dbversion = DB_VERSION_CURRENT;
1282wxString SplitPath(wxString s, wxString tkd,
int nchar,
int offset,
1288 wxStringTokenizer tkz(s, tkd);
1289 while (tkz.HasMoreTokens()) {
1290 wxString token = tkz.GetNextToken();
1291 if ((rlen + (
int)token.Len() + 1) < nchar) {
1294 rlen += token.Len() + 1;
1298 for (
int i = 0; i < offset; i++) {
1303 rlen = offset + token.Len() + 1;
1307 if (pn_split) *pn_split = ncr;
1309 return r.Mid(0, r.Len() - 1);
1312wxString ChartDatabase::GetFullChartInfo(
ChartBase *pc,
int dbIndex,
1313 int *char_width,
int *line_count) {
1316 unsigned int max_width = 0;
1318 unsigned int target_width = 60;
1324 line = _(
" ChartFile: ");
1325 wxString longline = *(cte.GetpsFullPath());
1327 if (longline.Len() > target_width) {
1328 line += SplitPath(longline, _T(
"/,\\"), target_width, 15, &ncr);
1329 max_width = wxMax(max_width, target_width + 4);
1333 max_width = wxMax(max_width, line.Len() + 4);
1342 line = _(
" Name: ");
1343 wxString longline = pc->GetName();
1346 if (longline.Find(
' ') != wxNOT_FOUND)
1351 if (longline.Len() > target_width) {
1352 line += SplitPath(pc->GetName(), tkz, target_width, 12, &ncr);
1353 max_width = wxMax(max_width, target_width + 4);
1357 max_width = wxMax(max_width, line.Len() + 4);
1366 line.Printf(_T(
" %s: 1:%d"), _(
"Scale"), pc->GetNativeScale());
1368 line.Printf(_T(
" %s: 1:%d"), _(
"Scale"), cte.GetScale());
1371 max_width = wxMax(max_width, line.Len());
1378 line += pc->GetID();
1380 max_width = wxMax(max_width, line.Len());
1385 line = _(
" Depth Units: ");
1386 line += pc->GetDepthUnits();
1388 max_width = wxMax(max_width, line.Len());
1393 line = _(
" Soundings: ");
1394 line += pc->GetSoundingsDatum();
1396 max_width = wxMax(max_width, line.Len());
1401 line = _(
" Datum: ");
1402 line += pc->GetDatumString();
1404 max_width = wxMax(max_width, line.Len());
1409 line = _(
" Projection: ");
1410 if (PROJECTION_UNKNOWN == cte.GetChartProjectionType())
1411 line += _(
"Unknown");
1412 else if (PROJECTION_MERCATOR == cte.GetChartProjectionType())
1413 line += _(
"Mercator");
1414 else if (PROJECTION_TRANSVERSE_MERCATOR == cte.GetChartProjectionType())
1415 line += _(
"Transverse Mercator");
1416 else if (PROJECTION_POLYCONIC == cte.GetChartProjectionType())
1417 line += _(
"Polyconic");
1418 else if (PROJECTION_WEB_MERCATOR == cte.GetChartProjectionType())
1419 line += _(
"Web Mercator (EPSG:3857)");
1421 max_width = wxMax(max_width, line.Len());
1427 line = _(
" Source Edition: ");
1428 line += pc->GetSE();
1430 max_width = wxMax(max_width, line.Len());
1435 wxDateTime ed = pc->GetEditionDate();
1437 line = _(
" Updated: ");
1438 line += ed.FormatISODate();
1440 max_width = wxMax(max_width, line.Len());
1447 if (pc && pc->GetExtraInfo().Len()) {
1448 line += pc->GetExtraInfo();
1450 max_width = wxMax(max_width, line.Len());
1456 if (line_count) *line_count = lc;
1458 if (char_width) *char_width = max_width;
1468 wxGenericProgressDialog *pprog) {
1469 m_dir_array = dir_array;
1473 m_chartDirs.Clear();
1474 active_chartTable.Clear();
1475 active_chartTable_pathindex.clear();
1477 Update(dir_array,
true, pprog);
1482 m_dbversion = DB_VERSION_CURRENT;
1492 wxGenericProgressDialog *pprog) {
1493 m_dir_array = dir_array;
1499 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++)
1500 active_chartTable[i].SetValid(
false);
1502 m_chartDirs.Clear();
1504 if (bForce) active_chartTable.Clear();
1506 bool lbForce = bForce;
1509 if (s_dbVersion != DB_VERSION_CURRENT) {
1510 active_chartTable.Clear();
1512 s_dbVersion = DB_VERSION_CURRENT;
1513 m_dbversion = DB_VERSION_CURRENT;
1518 for (
unsigned int j = 0; j < dir_array.GetCount(); j++) {
1524#ifdef __OCPN__ANDROID__
1525 if (!androidIsDirWritable(dir_info.fullpath))
continue;
1530 if (dir_info.fullpath.Find(_T(
"GSHHG")) != wxNOT_FOUND) {
1531 if (!wxDir::FindFirst(dir_info.fullpath,
"poly-*-1.dat").empty()) {
1536 gWorldMapLocation = dir_info.fullpath + wxFileName::GetPathSeparator();
1539 if (dir_info.fullpath.Find(_T(
"OSMSHP")) != wxNOT_FOUND) {
1540 if (!wxDir::FindFirst(dir_info.fullpath,
"basemap_*.shp").empty()) {
1541 gWorldShapefileLocation =
1542 dir_info.fullpath + wxFileName::GetPathSeparator();
1543 gShapeBasemap.Reset();
1547 TraverseDirAndAddCharts(dir_info, pprog, dir_magic, lbForce);
1551 dir_info.magic_number = dir_magic;
1552 dir_array.RemoveAt(j);
1553 dir_array.Insert(dir_info, j);
1555 m_chartDirs.Add(dir_info.fullpath);
1558 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
1559 if (!active_chartTable[i].GetbValid()) {
1560 active_chartTable.RemoveAt(i);
1566 active_chartTable_pathindex.clear();
1567 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
1568 active_chartTable_pathindex[active_chartTable[i].GetFullSystemPath()] = i;
1569 active_chartTable[i].SetEntryOffset(i);
1572 m_nentries = active_chartTable.GetCount();
1583int ChartDatabase::FinddbIndex(wxString PathToFind) {
1586 for(
unsigned int i=0 ; i<active_chartTable.GetCount() ; i++)
1588 if(active_chartTable[i].GetpsFullPath()->IsSameAs(PathToFind))
1594 if (active_chartTable_pathindex.find(PathToFind) !=
1595 active_chartTable_pathindex.end())
1596 return active_chartTable_pathindex[PathToFind];
1606int ChartDatabase::DisableChart(wxString &PathToDisable) {
1607 int index = FinddbIndex(PathToDisable);
1623int ChartDatabase::TraverseDirAndAddCharts(
ChartDirInfo &dir_info,
1624 wxGenericProgressDialog *pprog,
1625 wxString &dir_magic,
bool bForce) {
1627 wxString dir_path = dir_info.fullpath;
1628#ifdef __OCPN__ANDROID__
1629 dir_path = wxString(dir_info.fullpath.mb_str(wxConvUTF8));
1632 wxString old_magic = dir_info.magic_number;
1633 wxString new_magic = old_magic;
1634 dir_magic = old_magic;
1638 bool b_skipDetectDirChange =
false;
1639 bool b_dirchange =
false;
1642 if (!wxDir::Exists(dir_path))
return 0;
1648 bool b_cm93 = Check_CM93_Structure(dir_path);
1650 b_skipDetectDirChange =
true;
1656 if (!b_skipDetectDirChange)
1657 b_dirchange = DetectDirChange(dir_path, dir_info.fullpath, old_magic,
1660 if (!bForce && !b_dirchange) {
1661 wxString msg(_T(
" No change detected on directory "));
1662 msg.Append(dir_path);
1668 wxFileName fn_dir(dir_path, _T(
"stuff"));
1669 unsigned int dir_path_count = fn_dir.GetDirCount();
1671 if (pprog) pprog->SetTitle(_(
"OpenCPN Chart Scan...."));
1673 int nEntries = active_chartTable.GetCount();
1675 for (
int ic = 0; ic < nEntries; ic++) {
1676 wxFileName fn(active_chartTable[ic].GetFullSystemPath());
1678 while (fn.GetDirCount() >= dir_path_count) {
1679 if (fn.GetPath() == dir_path) {
1680 active_chartTable[ic].SetValid(
true);
1696 dir_magic = new_magic;
1699 for (
auto &cd : m_ChartClassDescriptorArray) {
1700 nAdd += SearchDirAndAddCharts(dir_info.fullpath, cd, pprog);
1706bool ChartDatabase::DetectDirChange(
const wxString &dir_path,
1707 const wxString &prog_label,
1708 const wxString &magic, wxString &new_magic,
1709 wxGenericProgressDialog *pprog) {
1710 if (pprog) pprog->SetTitle(_(
"OpenCPN Directory Scan...."));
1713 long long unsigned int nmagic;
1714 wxULongLong nacc = 0;
1716 magic.ToULongLong(&nmagic, 10);
1719 wxArrayString FileList;
1720 wxDir dir(dir_path);
1721 int n_files = dir.GetAllFiles(dir_path, &FileList);
1729 for (
int ifile = 0; ifile < n_files; ifile++) {
1730 if (pprog && (ifile % (n_files / 60 + 1)) == 0)
1731 pprog->Update(wxMin((ifile * 100) / n_files, 100), prog_label);
1733 wxFileName file(FileList[ifile]);
1738 wxString fileNameNative = file.GetFullPath();
1739 wxScopedCharBuffer fileNameUTF8 = fileNameNative.ToUTF8();
1740 hash.Update(fileNameUTF8.data(), fileNameUTF8.length());
1743 wxULongLong size = file.GetSize();
1744 wxULongLong fileSize = ((size != wxInvalidSize) ? size : 0);
1745 hash.Update(&fileSize, (
sizeof fileSize));
1748 wxDateTime t = file.GetModificationTime();
1749 wxULongLong fileTime = t.GetTicks();
1750 hash.Update(&fileTime, (
sizeof fileTime));
1754 hash.Receive(&nacc);
1757 new_magic = nacc.ToString();
1760 if (new_magic != magic)
1766bool ChartDatabase::IsChartDirUsed(
const wxString &theDir) {
1767 wxString dir(theDir);
1768 if (dir.Last() ==
'/' || dir.Last() == wxFileName::GetPathSeparator())
1771 dir.Append(wxT(
"*"));
1772 for (UINT32 i = 0; i < active_chartTable.GetCount(); i++) {
1773 if (active_chartTable[i].GetpsFullPath()->Matches(dir))
return true;
1782bool ChartDatabase::Check_CM93_Structure(wxString dir_name) {
1785 wxRegEx test(_T(
"[0-9]+"));
1787 wxDir dirt(dir_name);
1790 if (dirt.IsOpened())
1791 wxLogMessage(_T(
"check_cm93 opened dir OK: ") + dir_name);
1793 wxLogMessage(_T(
"check_cm93 NOT OPENED OK: ") + dir_name);
1794 wxLogMessage(_T(
"check_cm93 returns false.") + dir_name);
1798 bool b_maybe_found_cm93 =
false;
1799 bool b_cont = dirt.GetFirst(&candidate);
1802 if (test.Matches(candidate) && (candidate.Len() == 8)) {
1803 b_maybe_found_cm93 =
true;
1807 b_cont = dirt.GetNext(&candidate);
1810 if (b_maybe_found_cm93) {
1811 wxString dir_next = dir_name;
1812 dir_next += _T(
"/");
1813 dir_next += candidate;
1814 if (wxDir::Exists(dir_next)) {
1815 wxDir dir_n(dir_next);
1816 if (dirt.IsOpened()) {
1817 wxString candidate_n;
1819 wxRegEx test_n(_T(
"^[A-Ga-g]"));
1820 bool b_probably_found_cm93 =
false;
1821 bool b_cont_n = dir_n.IsOpened() && dir_n.GetFirst(&candidate_n);
1823 if (test_n.Matches(candidate_n) && (candidate_n.Len() == 1)) {
1824 b_probably_found_cm93 =
true;
1827 b_cont_n = dir_n.GetNext(&candidate_n);
1830 if (b_probably_found_cm93)
1835 wxString dir_luk = dir_next;
1837 dir_luk += candidate_n;
1838 if (wxDir::Exists(dir_luk))
return true;
1957WX_DECLARE_STRING_HASH_MAP(
int, ChartCollisionsHashMap);
1959int ChartDatabase::SearchDirAndAddCharts(wxString &dir_name_base,
1961 wxGenericProgressDialog *pprog) {
1962 wxString msg(_T(
"Searching directory: "));
1963 msg += dir_name_base;
1965 msg += chart_desc.m_search_mask;
1968 wxString dir_name = dir_name_base;
1970#ifdef __OCPN__ANDROID__
1971 dir_name = wxString(dir_name_base.mb_str(wxConvUTF8));
1974 if (!wxDir::Exists(dir_name))
return 0;
1976 wxString filespec = chart_desc.m_search_mask.Upper();
1977 wxString lowerFileSpec = chart_desc.m_search_mask.Lower();
1978 wxString filespecXZ = filespec + _T(
".xz");
1979 wxString lowerFileSpecXZ = lowerFileSpec + _T(
".xz");
1983 wxArrayString FileList;
1984 int gaf_flags = wxDIR_DEFAULT;
1991 bool b_found_cm93 =
false;
1992 bool b_cm93 = Check_CM93_Structure(dir_name);
1994 if (filespec != _T(
"00300000.A"))
1997 filespec = dir_name;
1998 b_found_cm93 =
true;
2002 if (!b_found_cm93) {
2003 wxDir dir(dir_name);
2004 dir.GetAllFiles(dir_name, &FileList, filespec, gaf_flags);
2006#ifdef __OCPN__ANDROID__
2007 if (!FileList.GetCount()) {
2008 wxArrayString afl = androidTraverseDir(dir_name, filespec);
2009 for (wxArrayString::const_iterator item = afl.begin(); item != afl.end();
2011 FileList.Add(*item);
2016 if (filespec != lowerFileSpec) {
2018 wxArrayString lowerFileList;
2019 dir.GetAllFiles(dir_name, &lowerFileList, lowerFileSpec, gaf_flags);
2021#ifdef __OCPN__ANDROID__
2022 if (!lowerFileList.GetCount()) {
2023 wxArrayString afl = androidTraverseDir(dir_name, lowerFileSpec);
2024 for (wxArrayString::const_iterator item = afl.begin();
2025 item != afl.end(); item++)
2026 lowerFileList.Add(*item);
2030 for (wxArrayString::const_iterator item = lowerFileList.begin();
2031 item != lowerFileList.end(); item++)
2032 FileList.Add(*item);
2038 dir.GetAllFiles(dir_name, &FileList, filespecXZ, gaf_flags);
2039 dir.GetAllFiles(dir_name, &FileList, lowerFileSpecXZ, gaf_flags);
2045 wxString dir_plus = dir_name;
2046 dir_plus += wxFileName::GetPathSeparator();
2047 FileList.Add(dir_plus);
2050 int nFile = FileList.GetCount();
2052 if (!nFile)
return false;
2059 bool bthis_dir_in_dB = IsChartDirUsed(dir_name);
2061 if (pprog) pprog->SetTitle(_(
"OpenCPN Chart Add...."));
2065 ChartCollisionsHashMap collision_map;
2066 int nEntry = active_chartTable.GetCount();
2067 for (
int i = 0; i < nEntry; i++) {
2068 wxString table_file_name = active_chartTable[i].GetFullSystemPath();
2069 wxFileName table_file(table_file_name);
2070 collision_map[table_file.GetFullName()] = i;
2073 int nFileProgressQuantum = wxMax(nFile / 100, 2);
2074 double rFileProgressRatio = 100.0 / wxMax(nFile, 1);
2076 for (
int ifile = 0; ifile < nFile; ifile++) {
2077 wxFileName file(FileList[ifile]);
2078 wxString full_name = file.GetFullPath();
2079 wxString file_name = file.GetFullName();
2080 wxString utf8_path = full_name;
2082#ifdef __OCPN__ANDROID__
2088 wxFileName fnbase(dir_name_base);
2089 int nDirs = fnbase.GetDirCount();
2091 wxFileName file_target(FileList[ifile]);
2093 for (
int i = 0; i < nDirs + 1;
2095 file_target.RemoveDir(0);
2097 wxString leftover_path = file_target.GetFullPath();
2099 dir_name_base + leftover_path;
2105 if (!file_name.Matches(lowerFileSpec) && !file_name.Matches(filespec) &&
2106 !file_name.Matches(lowerFileSpecXZ) && !file_name.Matches(filespecXZ) &&
2112 if (pprog && ((ifile % nFileProgressQuantum) == 0))
2113 pprog->Update(
static_cast<int>(ifile * rFileProgressRatio), utf8_path);
2116 bool bAddFinal =
true;
2121 ChartCollisionsHashMap::const_iterator collision_ptr =
2122 collision_map.find(file_name);
2123 bool collision = (collision_ptr != collision_map.end());
2124 bool file_path_is_same =
false;
2125 bool file_time_is_same =
false;
2127 wxString table_file_name;
2130 pEntry = &active_chartTable[collision_ptr->second];
2131 table_file_name = pEntry->GetFullSystemPath();
2133 bthis_dir_in_dB && full_name.IsSameAs(table_file_name);
2137 if (file_path_is_same) {
2141 time_t t_oldFile = pEntry->GetFileTime();
2142 time_t t_newFile = file.GetModificationTime().GetTicks();
2144 if (t_newFile <= t_oldFile) {
2145 file_time_is_same =
true;
2147 pEntry->SetValid(
true);
2150 pEntry->SetValid(
false);
2155 wxString msg_fn(full_name);
2156 msg_fn.Replace(_T(
"%"), _T(
"%%"));
2157 if (file_time_is_same) {
2161 wxString::Format(_T(
"Loading chart data for %s"), msg_fn.c_str()));
2163 pnewChart = CreateChartTableEntry(full_name, utf8_path, chart_desc);
2166 wxLogMessage(wxString::Format(
2167 _T(
" CreateChartTableEntry() failed for file: %s"),
2172 if (!collision || !pnewChart) {
2174 }
else if (file_path_is_same) {
2176 wxString::Format(_T(
" Replacing older chart file of same path: %s"),
2178 }
else if (!file_time_is_same) {
2184 if (pnewChart->IsEarlierThan(*pEntry)) {
2185 wxFileName table_file(table_file_name);
2187 if (table_file.IsFileReadable()) {
2188 pEntry->SetValid(
true);
2190 wxLogMessage(wxString::Format(
2191 _T(
" Retaining newer chart file of same name: %s"),
2194 }
else if (pnewChart->IsEqualTo(*pEntry)) {
2202 pEntry->SetValid(
false);
2204 wxLogMessage(wxString::Format(
2205 _T(
" Replacing older chart file of same name: %s"),
2211 if (0 == b_add_msg) {
2213 wxString::Format(_T(
" Adding chart file: %s"), msg_fn.c_str()));
2215 collision_map[file_name] = active_chartTable.GetCount();
2216 active_chartTable.Add(pnewChart);
2219 if (pnewChart)
delete pnewChart;
2225 m_nentries = active_chartTable.GetCount();
2230bool ChartDatabase::AddChart(wxString &chartfilename,
2232 wxGenericProgressDialog *pprog,
int isearch,
2233 bool bthis_dir_in_dB) {
2235 wxFileName file(chartfilename);
2236 wxString full_name = file.GetFullPath();
2237 wxString file_name = file.GetFullName();
2247 pprog->Update(wxMin((m_pdifile * 100) / m_pdnFile, 100), full_name);
2250 bool bAddFinal =
true;
2252 wxString msg_fn(full_name);
2253 msg_fn.Replace(_T(
"%"), _T(
"%%"));
2255 pnewChart = CreateChartTableEntry(full_name, full_name, chart_desc);
2258 wxLogMessage(wxString::Format(
2259 _T(
" CreateChartTableEntry() failed for file: %s"), msg_fn.c_str()));
2264 int nEntry = active_chartTable.GetCount();
2265 for (
int i = 0; i < nEntry; i++) {
2266 wxString *ptable_file_name = active_chartTable[isearch].GetpsFullPath();
2270 if (bthis_dir_in_dB && full_name.IsSameAs(*ptable_file_name)) {
2274 time_t t_oldFile = active_chartTable[isearch].GetFileTime();
2275 time_t t_newFile = file.GetModificationTime().GetTicks();
2277 if (t_newFile <= t_oldFile) {
2279 active_chartTable[isearch].SetValid(
true);
2282 active_chartTable[isearch].SetValid(
false);
2283 wxLogMessage(wxString::Format(
2284 _T(
" Replacing older chart file of same path: %s"),
2294 wxFileName table_file(*ptable_file_name);
2296 if (table_file.GetFullName() == file_name) {
2299 if (pnewChart->IsEarlierThan(active_chartTable[isearch])) {
2301 if (table_file.IsFileReadable()) {
2302 active_chartTable[isearch].SetValid(
true);
2304 wxLogMessage(wxString::Format(
2305 _T(
" Retaining newer chart file of same name: %s"),
2308 }
else if (pnewChart->IsEqualTo(active_chartTable[isearch])) {
2318 active_chartTable[isearch].SetValid(
false);
2320 wxLogMessage(wxString::Format(
2321 _T(
" Replacing older chart file of same name: %s"),
2331 if (nEntry == isearch) isearch = 0;
2336 if (0 == b_add_msg) {
2338 wxString::Format(_T(
" Adding chart file: %s"), msg_fn.c_str()));
2341 active_chartTable.Add(pnewChart);
2351 m_nentries = active_chartTable.GetCount();
2356bool ChartDatabase::AddSingleChart(wxString &ChartFullPath,
2357 bool b_force_full_search) {
2359 wxFileName fn(ChartFullPath);
2360 wxString ext = fn.GetExt();
2361 ext.Prepend(_T(
"*."));
2362 wxString ext_upper = ext.MakeUpper();
2363 wxString ext_lower = ext.MakeLower();
2364 wxString dir_name = fn.GetPath();
2370 for (
auto &cd : m_ChartClassDescriptorArray) {
2371 if (cd.m_descriptor_type == PLUGIN_DESCRIPTOR) {
2372 if (cd.m_search_mask == ext_upper) {
2376 if (cd.m_search_mask == ext_lower) {
2385 bool b_recurse =
true;
2386 if (!b_force_full_search) b_recurse = IsChartDirUsed(dir_name);
2388 bool rv = AddChart(ChartFullPath, desc, NULL, 0, b_recurse);
2392 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
2393 if (!active_chartTable[i].GetbValid()) {
2394 active_chartTable.RemoveAt(i);
2400 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++)
2401 active_chartTable[i].SetEntryOffset(i);
2405 DetectDirChange(dir_name, _T(
""), _T(
""), new_magic, 0);
2408 bool bcfound =
false;
2409 ArrayOfCDI NewChartDirArray;
2411 ArrayOfCDI ChartDirArray = GetChartDirArray();
2412 for (
unsigned int i = 0; i < ChartDirArray.GetCount(); i++) {
2418 if (newcdi.fullpath == dir_name) {
2419 newcdi.magic_number = new_magic;
2423 NewChartDirArray.Add(newcdi);
2428 cdi.fullpath = dir_name;
2429 cdi.magic_number = new_magic;
2430 NewChartDirArray.Add(cdi);
2434 SetChartDirArray(NewChartDirArray);
2437 m_chartDirs.Clear();
2439 for (
unsigned int i = 0; i < GetChartDirArray().GetCount(); i++) {
2441 m_chartDirs.Add(cdi.fullpath);
2444 m_nentries = active_chartTable.GetCount();
2449bool ChartDatabase::RemoveSingleChart(wxString &ChartFullPath) {
2453 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
2454 if (ChartFullPath.IsSameAs(GetChartTableEntry(i).GetFullSystemPath())) {
2455 active_chartTable.RemoveAt(i);
2463 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
2464 pcte = GetpChartTableEntry(i);
2465 pcte->SetEntryOffset(i);
2469 wxFileName fn(ChartFullPath);
2470 wxString fd = fn.GetPath();
2471 if (!IsChartDirUsed(fd)) {
2473 ArrayOfCDI NewChartDirArray;
2475 ArrayOfCDI ChartDirArray = GetChartDirArray();
2476 for (
unsigned int i = 0; i < ChartDirArray.GetCount(); i++) {
2481 if (newcdi.fullpath != fd) NewChartDirArray.Add(newcdi);
2484 SetChartDirArray(NewChartDirArray);
2488 m_chartDirs.Clear();
2489 for (
unsigned int i = 0; i < GetChartDirArray().GetCount(); i++) {
2491 m_chartDirs.Add(cdi.fullpath);
2494 m_nentries = active_chartTable.GetCount();
2503ChartBase *ChartDatabase::GetChart(
const wxChar *theFilePath,
2514 const wxString &filePath, wxString &utf8Path,
2516 wxString msg_fn(filePath);
2517 msg_fn.Replace(_T(
"%"), _T(
"%%"));
2519 wxString::Format(_T(
"Loading chart data for %s"), msg_fn.c_str()));
2521 ChartBase *pch = GetChart(filePath, chart_desc);
2524 wxString::Format(_T(
" ...creation failed for %s"), msg_fn.c_str()));
2528 InitReturn rc = pch->Init(filePath, HEADER_ONLY);
2529 if (rc != INIT_OK) {
2531 wxLogMessage(wxString::Format(_T(
" ...initialization failed for %s"),
2537 ret_val->SetValid(
true);
2544bool ChartDatabase::GetCentroidOfLargestScaleChart(
double *clat,
double *clon,
2545 ChartFamilyEnum family) {
2547 int cur_max_scale = 0;
2549 int nEntry = active_chartTable.GetCount();
2551 for (
int i = 0; i < nEntry; i++) {
2552 if (GetChartFamily(active_chartTable[i].GetChartType()) == family) {
2553 if (active_chartTable[i].GetScale() > cur_max_scale) {
2554 cur_max_scale = active_chartTable[i].GetScale();
2560 if (cur_max_i == -1)
2563 *clat = (active_chartTable[cur_max_i].GetLatMax() +
2564 active_chartTable[cur_max_i].GetLatMin()) /
2566 *clon = (active_chartTable[cur_max_i].GetLonMin() +
2567 active_chartTable[cur_max_i].GetLonMax()) /
2576int ChartDatabase::GetDBChartProj(
int dbIndex) {
2577 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size()))
2578 return active_chartTable[dbIndex].GetChartProjectionType();
2580 return PROJECTION_UNKNOWN;
2586int ChartDatabase::GetDBChartFamily(
int dbIndex) {
2587 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size()))
2588 return active_chartTable[dbIndex].GetChartFamily();
2590 return CHART_FAMILY_UNKNOWN;
2596wxString ChartDatabase::GetDBChartFileName(
int dbIndex) {
2597 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2598 return wxString(active_chartTable[dbIndex].GetFullSystemPath());
2606int ChartDatabase::GetDBChartType(
int dbIndex) {
2607 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size()))
2608 return active_chartTable[dbIndex].GetChartType();
2616float ChartDatabase::GetDBChartSkew(
int dbIndex) {
2617 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size()))
2618 return active_chartTable[dbIndex].GetChartSkew();
2626int ChartDatabase::GetDBChartScale(
int dbIndex) {
2627 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size()))
2628 return active_chartTable[dbIndex].GetScale();
2636bool ChartDatabase::GetDBBoundingBox(
int dbIndex, LLBBox &box) {
2637 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2639 box.Set(entry.GetLatMin(), entry.GetLonMin(), entry.GetLatMax(),
2646const LLBBox &ChartDatabase::GetDBBoundingBox(
int dbIndex) {
2647 if ((bValid) && (dbIndex >= 0)) {
2649 return entry.GetBBox();
2651 return m_dummy_bbox;
2658int ChartDatabase::GetDBPlyPoint(
int dbIndex,
int plyindex,
float *lat,
2660 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2662 if (entry.GetnPlyEntries()) {
2663 float *fp = entry.GetpPlyTable();
2665 if (lat) *lat = *fp;
2667 if (lon) *lon = *fp;
2669 return entry.GetnPlyEntries();
2677int ChartDatabase::GetDBAuxPlyPoint(
int dbIndex,
int plyindex,
int ply,
2678 float *lat,
float *lon) {
2679 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2681 if (entry.GetnAuxPlyEntries()) {
2682 float *fp = entry.GetpAuxPlyTableEntry(ply);
2685 if (lat) *lat = *fp;
2687 if (lon) *lon = *fp;
2690 return entry.GetAuxCntTableEntry(ply);
2695int ChartDatabase::GetnAuxPlyEntries(
int dbIndex) {
2696 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2698 return entry.GetnAuxPlyEntries();
2706std::vector<float> ChartDatabase::GetReducedPlyPoints(
int dbIndex) {
2707 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2709 if (pentry)
return pentry->GetReducedPlyPoints();
2712 std::vector<float> dummy;
2719std::vector<float> ChartDatabase::GetReducedAuxPlyPoints(
int dbIndex,
2721 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2723 if (pentry)
return pentry->GetReducedAuxPlyPoints(iTable);
2726 std::vector<float> dummy;
2730bool ChartDatabase::IsChartAvailable(
int dbIndex) {
2731 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2735 if (pentry->GetChartType() != CHART_TYPE_PLUGIN)
return true;
2737 wxString *path = pentry->GetpsFullPath();
2738 wxFileName fn(*path);
2739 wxString ext = fn.GetExt();
2740 ext.Prepend(_T(
"*."));
2741 wxString ext_upper = ext.MakeUpper();
2742 wxString ext_lower = ext.MakeLower();
2747 for (
auto &cd : m_ChartClassDescriptorArray) {
2748 if (cd.m_descriptor_type == PLUGIN_DESCRIPTOR) {
2749 wxString search_mask = cd.m_search_mask;
2751 if (search_mask == ext_upper) {
2754 if (search_mask == ext_lower) {
2757 if (path->Matches(search_mask)) {
2767void ChartDatabase::ApplyGroupArray(ChartGroupArray *pGroupArray) {
2768 wxString separator(wxFileName::GetPathSeparator());
2770 for (
unsigned int ic = 0; ic < active_chartTable.GetCount(); ic++) {
2773 pcte->ClearGroupArray();
2775 wxString *chart_full_path = pcte->GetpsFullPath();
2777 for (
unsigned int igroup = 0; igroup < pGroupArray->GetCount(); igroup++) {
2778 ChartGroup *pGroup = pGroupArray->Item(igroup);
2779 for (
const auto &elem : pGroup->m_element_array) {
2780 wxString element_root = elem.m_element_name;
2786 if (!chart_full_path->IsSameAs(element_root))
2787 element_root.Append(
2789 if (chart_full_path->StartsWith(element_root)) {
2791 for (
unsigned int k = 0; k < elem.m_missing_name_array.size(); k++) {
2792 const wxString &missing_item = elem.m_missing_name_array[k];
2793 if (chart_full_path->StartsWith(missing_item)) {
2794 if (chart_full_path->IsSameAs(
2800 if (wxDir::Exists(missing_item))
2809 if (b_add) pcte->AddIntToGroupArray(igroup + 1);
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.
Represents a user-defined collection of logically related charts.
Wrapper class for plugin-based charts.
A class for computing hash of arbitrary length.
Manages a set of ShapeBaseChart objects at different resolutions.
Represents an entry in the chart table, containing information about a single chart.