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;
67static ChartFamilyEnum GetChartFamily(
int charttype) {
72 cf = CHART_FAMILY_RASTER;
75 cf = CHART_FAMILY_RASTER;
78 cf = CHART_FAMILY_VECTOR;
81 cf = CHART_FAMILY_VECTOR;
83 case CHART_TYPE_CM93COMP:
84 cf = CHART_FAMILY_VECTOR;
86 case CHART_TYPE_DUMMY:
87 cf = CHART_FAMILY_RASTER;
89 case CHART_TYPE_UNKNOWN:
90 cf = CHART_FAMILY_UNKNOWN;
93 cf = CHART_FAMILY_UNKNOWN;
103void ChartTableHeader::Read(wxInputStream &is) {
107void ChartTableHeader::Write(wxOutputStream &os) {
109 sprintf(vb,
"V%03d", DB_VERSION_CURRENT);
111 memcpy(dbVersion, vb, 4);
115bool ChartTableHeader::CheckValid() {
117 sprintf(vb,
"V%03d", DB_VERSION_CURRENT);
118 if (strncmp(vb, dbVersion,
sizeof(dbVersion))) {
121 memcpy(vbo, dbVersion, 4);
123 msg.Append(wxString(vbo, wxConvUTF8));
124 msg.Prepend(wxT(
" Warning: found incorrect chart db version: "));
130 sprintf(vb,
"V%03d", DB_VERSION_PREVIOUS);
131 if (strncmp(vb, dbVersion,
sizeof(dbVersion)))
135 _T(
" Scheduling db upgrade to current db version on ")
136 _T(
"Options->Charts page visit..."));
143 memcpy(vbo, dbVersion, 4);
145 msg.Append(wxString(vbo, wxConvUTF8));
146 msg.Prepend(wxT(
"Loading chart db version: "));
157void ChartTableEntry::SetScale(
int scale) {
161 if (Scale >= 1000) rounding = 5 * pow(10, log10(Scale) - 2);
164ChartTableEntry::ChartTableEntry(
ChartBase &theChart, wxString &utf8Path) {
167 char *pt = (
char *)malloc(strlen(utf8Path.mb_str(wxConvUTF8)) + 1);
168 strcpy(pt, utf8Path.mb_str(wxConvUTF8));
171 SetScale(theChart.GetNativeScale());
173 ChartType = theChart.GetChartType();
174 ChartFamily = theChart.GetChartFamily();
176 Skew = theChart.GetChartSkew();
177 ProjectionType = theChart.GetChartProjectionType();
179 wxDateTime ed = theChart.GetEditionDate();
180 if (theChart.GetEditionDate().IsValid())
181 edition_date = theChart.GetEditionDate().GetTicks();
183 wxFileName fn(theChart.GetFullPath());
184 if (fn.GetModificationTime().IsValid())
185 file_date = fn.GetModificationTime().GetTicks();
187 m_pfilename =
new wxString;
188 *m_pfilename = fn.GetFullName();
189 m_psFullPath =
new wxString;
190 *m_psFullPath = utf8Path;
191 m_fullSystemPath = utf8Path;
193#ifdef __OCPN__ANDROID__
194 m_fullSystemPath = wxString(utf8Path.mb_str(wxConvUTF8));
198 theChart.GetChartExtent(&ext);
204 m_bbox.Set(LatMin, LonMin, LatMax, LonMax);
209 double scale_max_zoom = Scale / 4;
211 double display_ppm = 1 / .00025;
212 double meters_per_pixel_max_scale = scale_max_zoom / display_ppm;
213 double LOD_meters = meters_per_pixel_max_scale * LOD_pixels;
218 if (theChart.GetCOVREntries() == 1) {
219 nPlyEntries = theChart.GetCOVRTablePoints(0);
221 if (nPlyEntries > 5 && (LOD_meters > .01)) {
222 std::vector<int> index_keep{0, nPlyEntries - 1, 1, nPlyEntries - 2};
224 double *DPbuffer = (
double *)malloc(2 * nPlyEntries *
sizeof(
double));
226 double *pfed = DPbuffer;
229 for (
int i = 0; i < nPlyEntries; i++) {
235 DouglasPeucker(DPbuffer, 1, nPlyEntries - 2, LOD_meters / (1852 * 60),
241 for (
unsigned int i = 0; i < index_keep.size(); i++) {
242 DPbuffer[2 * index_keep[i]] += 2000.;
245 float *pf = (
float *)malloc(2 * index_keep.size() *
sizeof(float));
248 for (
int i = 0; i < nPlyEntries; i++) {
249 if (DPbuffer[2 * i] > 1000.) {
250 *pfe++ = DPbuffer[2 * i] - 2000.;
251 *pfe++ = DPbuffer[(2 * i) + 1];
256 nPlyEntries = index_keep.size();
259 float *pf = (
float *)malloc(2 * nPlyEntries *
sizeof(
float));
264 for (
int i = 0; i < nPlyEntries; i++) {
276 float *pf1 = (
float *)malloc(2 * 4 *
sizeof(
float));
280 theChart.GetChartExtent(&fext);
296 nAuxPlyEntries = theChart.GetCOVREntries();
297 wxASSERT(nAuxPlyEntries);
298 float **pfp = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
300 int *pip = (
int *)malloc(nAuxPlyEntries *
sizeof(
int));
302 for (
int j = 0; j < nAuxPlyEntries; j++) {
303 int nPE = theChart.GetCOVRTablePoints(j);
305 if (nPE > 5 && (LOD_meters > .01)) {
306 std::vector<int> index_keep{0, nPE - 1, 1, nPE - 2};
308 double *DPbuffer = (
double *)malloc(2 * nPE *
sizeof(
double));
310 double *pfed = DPbuffer;
313 for (
int i = 0; i < nPE; i++) {
319 DouglasPeucker(DPbuffer, 1, nPE - 2, LOD_meters / (1852 * 60),
325 for (
unsigned int i = 0; i < index_keep.size(); i++) {
326 DPbuffer[2 * index_keep[i]] += 2000.;
329 float *pf = (
float *)malloc(2 * index_keep.size() *
sizeof(float));
332 for (
int i = 0; i < nPE; i++) {
333 if (DPbuffer[2 * i] > 1000.) {
334 *pfe++ = DPbuffer[2 * i] - 2000.;
335 *pfe++ = DPbuffer[(2 * i) + 1];
340 pip[j] = index_keep.size();
344 (
float *)malloc(theChart.GetCOVRTablePoints(j) * 2 *
sizeof(float));
345 memcpy(pf_entry, theChart.GetCOVRTableHead(j),
346 theChart.GetCOVRTablePoints(j) * 2 *
sizeof(
float));
348 pip[j] = theChart.GetCOVRTablePoints(j);
358 nNoCovrPlyEntries = theChart.GetNoCOVREntries();
359 if (nNoCovrPlyEntries == 0)
return;
361 float **pfpnc = (
float **)malloc(nNoCovrPlyEntries *
sizeof(
float *));
362 float **pft0nc = pfpnc;
363 int *pipnc = (
int *)malloc(nNoCovrPlyEntries *
sizeof(
int));
365 for (
int j = 0; j < nNoCovrPlyEntries; j++) {
367 (
float *)malloc(theChart.GetNoCOVRTablePoints(j) * 2 *
sizeof(float));
368 memcpy(pf_entry, theChart.GetNoCOVRTableHead(j),
369 theChart.GetNoCOVRTablePoints(j) * 2 *
sizeof(
float));
370 pft0nc[j] = pf_entry;
371 pipnc[j] = theChart.GetNoCOVRTablePoints(j);
374 pNoCovrPlyTable = pfpnc;
375 pNoCovrCntTable = pipnc;
380ChartTableEntry::~ChartTableEntry() {
384 for (
int i = 0; i < nAuxPlyEntries; i++) free(pAuxPlyTable[i]);
388 if (nNoCovrPlyEntries) {
389 for (
int i = 0; i < nNoCovrPlyEntries; i++) free(pNoCovrPlyTable[i]);
390 free(pNoCovrPlyTable);
391 free(pNoCovrCntTable);
403 wxDateTime mine(edition_date);
404 wxDateTime theirs(cte.edition_date);
406 if (!mine.IsValid() || !theirs.IsValid())
409 return (mine.IsEarlierThan(theirs));
413 wxDateTime mine(edition_date);
414 wxDateTime theirs(cte.edition_date);
416 if (!mine.IsValid() || !theirs.IsValid())
419 return (mine.IsEqualTo(theirs));
424static int convertChartType(
int charttype) {
427 if (s_dbVersion == 14) {
430 return CHART_TYPE_KAP;
432 return CHART_TYPE_GEO;
434 return CHART_TYPE_S57;
436 return CHART_TYPE_CM93;
438 return CHART_TYPE_CM93COMP;
440 return CHART_TYPE_UNKNOWN;
442 return CHART_TYPE_DONTCARE;
444 return CHART_TYPE_DUMMY;
446 return CHART_TYPE_UNKNOWN;
452static int convertChartFamily(
int charttype,
int chartfamily) {
453 if (s_dbVersion < 18) {
457 return CHART_FAMILY_RASTER;
460 case CHART_TYPE_CM93:
461 case CHART_TYPE_CM93COMP:
462 return CHART_FAMILY_VECTOR;
465 return CHART_FAMILY_UNKNOWN;
471bool ChartTableEntry::Read(
const ChartDatabase *pDb, wxInputStream &is) {
472 char path[4096], *cp;
478 int db_version = pD->GetVersion();
480 if (db_version == 18) {
482 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
483 pFullPath = (
char *)malloc(cp - path + 1);
484 strncpy(pFullPath, path, cp - path + 1);
485 wxLogVerbose(_T(
" Chart %s"), pFullPath);
488 m_pfilename =
new wxString;
489 wxString fullfilename(pFullPath, wxConvUTF8);
490 wxFileName fn(fullfilename);
491 *m_pfilename = fn.GetFullName();
492 m_psFullPath =
new wxString;
493 *m_psFullPath = fullfilename;
494 m_fullSystemPath = fullfilename;
496#ifdef __OCPN__ANDROID__
497 m_fullSystemPath = wxString(fullfilename.mb_str(wxConvUTF8));
504 EntryOffset = cte.EntryOffset;
505 ChartType = cte.ChartType;
506 ChartFamily = cte.ChartFamily;
512 m_bbox.Set(LatMin, LonMin, LatMax, LonMax);
515 ProjectionType = cte.ProjectionType;
518 edition_date = cte.edition_date;
519 file_date = cte.file_date;
521 nPlyEntries = cte.nPlyEntries;
522 nAuxPlyEntries = cte.nAuxPlyEntries;
524 nNoCovrPlyEntries = cte.nNoCovrPlyEntries;
529 int npeSize = nPlyEntries * 2 *
sizeof(float);
530 pPlyTable = (
float *)malloc(npeSize);
531 is.Read(pPlyTable, npeSize);
534 if (nAuxPlyEntries) {
535 int napeSize = nAuxPlyEntries *
sizeof(int);
536 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
537 pAuxCntTable = (
int *)malloc(napeSize);
538 is.Read(pAuxCntTable, napeSize);
540 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
542 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
543 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
544 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
548 if (nNoCovrPlyEntries) {
549 int napeSize = nNoCovrPlyEntries *
sizeof(int);
550 pNoCovrCntTable = (
int *)malloc(napeSize);
551 is.Read(pNoCovrCntTable, napeSize);
553 pNoCovrPlyTable = (
float **)malloc(nNoCovrPlyEntries *
sizeof(
float *));
554 for (
int i = 0; i < nNoCovrPlyEntries; i++) {
555 int nfSize = pNoCovrCntTable[i] * 2 *
sizeof(float);
556 pNoCovrPlyTable[i] = (
float *)malloc(nfSize);
557 is.Read(pNoCovrPlyTable[i], nfSize);
562 else if (db_version == 17) {
564 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
565 pFullPath = (
char *)malloc(cp - path + 1);
566 strncpy(pFullPath, path, cp - path + 1);
567 wxLogVerbose(_T(
" Chart %s"), pFullPath);
570 m_pfilename =
new wxString;
571 wxString fullfilename(pFullPath, wxConvUTF8);
572 wxFileName fn(fullfilename);
573 *m_pfilename = fn.GetFullName();
574 m_psFullPath =
new wxString;
575 *m_psFullPath = fullfilename;
582 EntryOffset = cte.EntryOffset;
583 ChartType = cte.ChartType;
589 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
592 ProjectionType = cte.ProjectionType;
595 edition_date = cte.edition_date;
596 file_date = cte.file_date;
598 nPlyEntries = cte.nPlyEntries;
599 nAuxPlyEntries = cte.nAuxPlyEntries;
601 nNoCovrPlyEntries = cte.nNoCovrPlyEntries;
606 int npeSize = nPlyEntries * 2 *
sizeof(float);
607 pPlyTable = (
float *)malloc(npeSize);
608 is.Read(pPlyTable, npeSize);
611 if (nAuxPlyEntries) {
612 int napeSize = nAuxPlyEntries *
sizeof(int);
613 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
614 pAuxCntTable = (
int *)malloc(napeSize);
615 is.Read(pAuxCntTable, napeSize);
617 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
619 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
620 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
621 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
625 if (nNoCovrPlyEntries) {
626 int napeSize = nNoCovrPlyEntries *
sizeof(int);
627 pNoCovrCntTable = (
int *)malloc(napeSize);
628 is.Read(pNoCovrCntTable, napeSize);
630 pNoCovrPlyTable = (
float **)malloc(nNoCovrPlyEntries *
sizeof(
float *));
631 for (
int i = 0; i < nNoCovrPlyEntries; i++) {
632 int nfSize = pNoCovrCntTable[i] * 2 *
sizeof(float);
633 pNoCovrPlyTable[i] = (
float *)malloc(nfSize);
634 is.Read(pNoCovrPlyTable[i], nfSize);
639 else if (db_version == 16) {
641 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
643 pFullPath = (
char *)malloc(cp - path + 1);
644 strncpy(pFullPath, path, cp - path + 1);
645 wxLogVerbose(_T(
" Chart %s"), pFullPath);
648 m_pfilename =
new wxString;
649 wxString fullfilename(pFullPath, wxConvUTF8);
650 wxFileName fn(fullfilename);
651 *m_pfilename = fn.GetFullName();
652 m_psFullPath =
new wxString;
653 *m_psFullPath = fullfilename;
660 EntryOffset = cte.EntryOffset;
661 ChartType = cte.ChartType;
667 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
670 ProjectionType = cte.ProjectionType;
673 edition_date = cte.edition_date;
674 file_date = cte.file_date;
676 nPlyEntries = cte.nPlyEntries;
677 nAuxPlyEntries = cte.nAuxPlyEntries;
682 int npeSize = nPlyEntries * 2 *
sizeof(float);
683 pPlyTable = (
float *)malloc(npeSize);
684 is.Read(pPlyTable, npeSize);
687 if (nAuxPlyEntries) {
688 int napeSize = nAuxPlyEntries *
sizeof(int);
689 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
690 pAuxCntTable = (
int *)malloc(napeSize);
691 is.Read(pAuxCntTable, napeSize);
693 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
695 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
696 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
697 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
702 else if (db_version == 15) {
704 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
706 pFullPath = (
char *)malloc(cp - path + 1);
707 strncpy(pFullPath, path, cp - path + 1);
708 wxLogVerbose(_T(
" Chart %s"), pFullPath);
715 EntryOffset = cte.EntryOffset;
716 ChartType = cte.ChartType;
722 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
725 edition_date = cte.edition_date;
726 file_date = cte.file_date;
728 nPlyEntries = cte.nPlyEntries;
729 nAuxPlyEntries = cte.nAuxPlyEntries;
734 int npeSize = nPlyEntries * 2 *
sizeof(float);
735 pPlyTable = (
float *)malloc(npeSize);
736 is.Read(pPlyTable, npeSize);
739 if (nAuxPlyEntries) {
740 int napeSize = nAuxPlyEntries *
sizeof(int);
741 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
742 pAuxCntTable = (
int *)malloc(napeSize);
743 is.Read(pAuxCntTable, napeSize);
745 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
747 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
748 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
749 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
752 }
else if (db_version == 14) {
754 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
755 pFullPath = (
char *)malloc(cp - path + 1);
756 strncpy(pFullPath, path, cp - path + 1);
757 wxLogVerbose(_T(
" Chart %s"), pFullPath);
764 EntryOffset = cte.EntryOffset;
765 ChartType = cte.ChartType;
771 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
774 edition_date = cte.edition_date;
776 nPlyEntries = cte.nPlyEntries;
777 nAuxPlyEntries = cte.nAuxPlyEntries;
781 int npeSize = nPlyEntries * 2 *
sizeof(float);
782 pPlyTable = (
float *)malloc(npeSize);
783 is.Read(pPlyTable, npeSize);
786 if (nAuxPlyEntries) {
787 int napeSize = nAuxPlyEntries *
sizeof(int);
788 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
789 pAuxCntTable = (
int *)malloc(napeSize);
790 is.Read(pAuxCntTable, napeSize);
792 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
794 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
795 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
796 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
800 ChartFamily = convertChartFamily(ChartType, ChartFamily);
801 ChartType = convertChartType(ChartType);
808bool ChartTableEntry::Write(
const ChartDatabase *pDb, wxOutputStream &os) {
809 os.Write(pFullPath, strlen(pFullPath) + 1);
816 cte.EntryOffset = EntryOffset;
817 cte.ChartType = ChartType;
818 cte.ChartFamily = ChartFamily;
825 cte.edition_date = edition_date;
826 cte.file_date = file_date;
828 cte.nPlyEntries = nPlyEntries;
829 cte.nAuxPlyEntries = nAuxPlyEntries;
832 cte.ProjectionType = ProjectionType;
836 cte.nNoCovrPlyEntries = nNoCovrPlyEntries;
839 wxLogVerbose(_T(
" Wrote Chart %s"), pFullPath);
843 int npeSize = nPlyEntries * 2 *
sizeof(float);
844 os.Write(pPlyTable, npeSize);
847 if (nAuxPlyEntries) {
848 int napeSize = nAuxPlyEntries *
sizeof(int);
849 os.Write(pAuxCntTable, napeSize);
851 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries; nAuxPlyEntry++) {
852 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
853 os.Write(pAuxPlyTable[nAuxPlyEntry], nfSize);
857 if (nNoCovrPlyEntries) {
858 int ncSize = nNoCovrPlyEntries *
sizeof(int);
859 os.Write(pNoCovrCntTable, ncSize);
861 for (
int i = 0; i < nNoCovrPlyEntries; i++) {
862 int nctSize = pNoCovrCntTable[i] * 2 *
sizeof(float);
863 os.Write(pNoCovrPlyTable[i], nctSize);
872void ChartTableEntry::Clear() {
881 pNoCovrCntTable = NULL;
882 pNoCovrPlyTable = NULL;
884 nNoCovrPlyEntries = 0;
893void ChartTableEntry::Disable() {
897 LatMax += (float)1000.;
898 LatMin += (float)1000.;
901void ChartTableEntry::ReEnable() {
903 LatMax -= (float)1000.;
904 LatMin -= (float)1000.;
908std::vector<float> ChartTableEntry::GetReducedPlyPoints() {
909 if (m_reducedPlyPoints.size())
return m_reducedPlyPoints;
912 float LOD_meters = 1;
914 float plylat, plylon;
915 const int nPoints = GetnPlyEntries();
917 float *fpo = GetpPlyTable();
919 double *ppd =
new double[nPoints * 2];
920 double *ppsm =
new double[nPoints * 2];
923 for (
int i = 0; i < nPoints; i++) {
925 plylon = fpo[i * 2 + 1];
928 toSM(plylat, plylon, fpo[0], fpo[1], &x, &y);
936 std::vector<int> index_keep;
938 index_keep.push_back(0);
939 index_keep.push_back(nPoints - 1);
940 index_keep.push_back(1);
941 index_keep.push_back(nPoints - 2);
943 DouglasPeuckerM(ppsm, 1, nPoints - 2, LOD_meters, &index_keep);
946 index_keep.resize(nPoints);
947 for (
int i = 0; i < nPoints; i++) index_keep[i] = i;
951 for (
int ip = 0; ip < nPoints; ip++) {
955 for (
unsigned int j = 0; j < index_keep.size(); j++) {
956 if (index_keep[j] == ip) {
957 m_reducedPlyPoints.push_back(x);
958 m_reducedPlyPoints.push_back(y);
967 int nprr = m_reducedPlyPoints.size() / 2;
969 return m_reducedPlyPoints;
972std::vector<float> ChartTableEntry::GetReducedAuxPlyPoints(
int iTable) {
974 if (!m_reducedAuxPlyPointsVector.size()) {
975 std::vector<float> vec;
976 for (
int i = 0; i < GetnAuxPlyEntries(); i++) {
977 m_reducedAuxPlyPointsVector.push_back(vec);
981 std::vector<float> vec;
984 if ((
unsigned int)iTable >= m_reducedAuxPlyPointsVector.size())
return vec;
986 if (m_reducedAuxPlyPointsVector.at(iTable).size())
987 return m_reducedAuxPlyPointsVector.at(iTable);
990 float LOD_meters = 1.0;
992 const int nPoints = GetAuxCntTableEntry(iTable);
993 float *fpo = GetpAuxPlyTableEntry(iTable);
995 double *ppd =
new double[nPoints * 2];
996 double *ppsm =
new double[nPoints * 2];
999 float plylat, plylon;
1001 for (
int i = 0; i < nPoints; i++) {
1002 plylat = fpo[i * 2];
1003 plylon = fpo[i * 2 + 1];
1006 toSM(plylat, plylon, fpo[0], fpo[1], &x, &y);
1014 std::vector<int> index_keep;
1016 index_keep.push_back(0);
1017 index_keep.push_back(nPoints - 1);
1018 index_keep.push_back(1);
1019 index_keep.push_back(nPoints - 2);
1021 DouglasPeuckerM(ppsm, 1, nPoints - 2, LOD_meters, &index_keep);
1024 index_keep.resize(nPoints);
1025 for (
int i = 0; i < nPoints; i++) index_keep[i] = i;
1028 int nnn = index_keep.size();
1031 for (
int ip = 0; ip < nPoints; ip++) {
1035 for (
unsigned int j = 0; j < index_keep.size(); j++) {
1036 if (index_keep[j] == ip) {
1047 m_reducedAuxPlyPointsVector[iTable] = vec;
1049 int nprr = vec.size() / 2;
1058WX_DEFINE_OBJARRAY(ChartTable);
1060ChartDatabase::ChartDatabase() {
1064 m_ChartTableEntryDummy.Clear();
1066 UpdateChartClassDescriptorArray();
1069void ChartDatabase::UpdateChartClassDescriptorArray(
void) {
1070 if (m_ChartClassDescriptorArray.empty()) {
1071 m_ChartClassDescriptorArray.push_back(
1073 m_ChartClassDescriptorArray.push_back(
1075 m_ChartClassDescriptorArray.push_back(
1077 m_ChartClassDescriptorArray.push_back(
1080 _T(
"cm93compchart"), _T(
"00300000.a"), BUILTIN_DESCRIPTOR));
1082 _T(
"ChartMbTiles"), _T(
"*.mbtiles"), BUILTIN_DESCRIPTOR));
1087 m_ChartClassDescriptorArray.erase(
1088 std::remove_if(m_ChartClassDescriptorArray.begin(),
1089 m_ChartClassDescriptorArray.end(),
1091 return cd.m_descriptor_type == PLUGIN_DESCRIPTOR;
1093 m_ChartClassDescriptorArray.end());
1095 wxArrayString array = g_pi_manager->GetPlugInChartClassNameArray();
1096 for (
unsigned int j = 0; j < array.GetCount(); j++) {
1099 wxString class_name = array[j];
1102 wxString mask = cpiw->GetFileSearchMask();
1105 m_ChartClassDescriptorArray.push_back(
1113const ChartTableEntry &ChartDatabase::GetChartTableEntry(
int index)
const {
1114 if (index < GetChartTableEntries())
1115 return active_chartTable[index];
1117 return m_ChartTableEntryDummy;
1121 if (index < GetChartTableEntries())
1122 return &active_chartTable[index];
1127bool ChartDatabase::CompareChartDirArray(ArrayOfCDI &test_array) {
1131 if (test_array.GetCount() != m_dir_array.GetCount())
return false;
1134 unsigned int nfound_outer = 0;
1136 for (
unsigned int i = 0; i < test_array.GetCount(); i++) {
1138 bfound_inner =
false;
1139 for (
unsigned int j = 0; j < m_dir_array.GetCount(); j++) {
1142 if (p.fullpath.IsSameAs(q.fullpath)) {
1143 bfound_inner =
true;
1147 if (bfound_inner) nfound_outer++;
1150 return (nfound_outer == test_array.GetCount());
1153wxString ChartDatabase::GetMagicNumberCached(wxString dir) {
1154 for (
unsigned int j = 0; j < m_dir_array.GetCount(); j++) {
1156 if (dir.IsSameAs(q.fullpath))
return q.magic_number;
1162bool ChartDatabase::Read(
const wxString &filePath) {
1168 wxFileName file(filePath);
1169 if (!file.FileExists())
return false;
1171 m_DBFileName = filePath;
1173 wxFFileInputStream ifs(filePath);
1174 if (!ifs.Ok())
return false;
1178 if (!cth.CheckValid())
return false;
1182 memcpy(vbo, cth.GetDBVersionString(), 4);
1184 m_dbversion = atoi(&vbo[1]);
1185 s_dbVersion = m_dbversion;
1187 wxLogVerbose(wxT(
"Chartdb:Reading %d directory entries, %d table entries"),
1188 cth.GetDirEntries(), cth.GetTableEntries());
1189 wxLogMessage(_T(
"Chartdb: Chart directory list follows"));
1190 if (0 == cth.GetDirEntries()) wxLogMessage(_T(
" Nil"));
1193 for (
int iDir = 0; iDir < cth.GetDirEntries(); iDir++) {
1196 ifs.Read(&dirlen,
sizeof(
int));
1197 while (dirlen > 0) {
1199 int alen = dirlen > 1023 ? 1023 : dirlen;
1200 if (ifs.Read(&dirbuf, alen).Eof())
goto read_error;
1203 dir.Append(wxString(dirbuf, wxConvUTF8));
1206 msg.Printf(wxT(
" Chart directory #%d: "), iDir);
1209 m_chartDirs.Add(dir);
1212 entries = cth.GetTableEntries();
1213 active_chartTable.Alloc(entries);
1214 active_chartTable_pathindex.clear();
1215 while (entries-- && entry.Read(
this, ifs)) {
1216 active_chartTable_pathindex[entry.GetFullSystemPath()] = ind++;
1217 active_chartTable.Add(entry);
1222 entry.SetAvailable(
true);
1224 m_nentries = active_chartTable.GetCount();
1229 m_nentries = active_chartTable.GetCount();
1235bool ChartDatabase::Write(
const wxString &filePath) {
1236 wxFileName file(filePath);
1238 file.GetPath(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME, wxPATH_NATIVE));
1240 if (!dir.DirExists() && !dir.Mkdir())
return false;
1242 wxFFileOutputStream ofs(filePath);
1243 if (!ofs.Ok())
return false;
1245 ChartTableHeader cth(m_chartDirs.GetCount(), active_chartTable.GetCount());
1248 for (
int iDir = 0; iDir < cth.GetDirEntries(); iDir++) {
1249 wxString &dir = m_chartDirs[iDir];
1250 int dirlen = dir.length();
1252 strncpy(s, dir.mb_str(wxConvUTF8), 199);
1255 ofs.Write(&dirlen,
sizeof(
int));
1257 ofs.Write(s, dirlen);
1260 for (UINT32 iTable = 0; iTable < active_chartTable.size(); iTable++)
1261 active_chartTable[iTable].Write(
this, ofs);
1264 m_dbversion = DB_VERSION_CURRENT;
1270wxString SplitPath(wxString s, wxString tkd,
int nchar,
int offset,
1276 wxStringTokenizer tkz(s, tkd);
1277 while (tkz.HasMoreTokens()) {
1278 wxString token = tkz.GetNextToken();
1279 if ((rlen + (
int)token.Len() + 1) < nchar) {
1282 rlen += token.Len() + 1;
1286 for (
int i = 0; i < offset; i++) {
1291 rlen = offset + token.Len() + 1;
1295 if (pn_split) *pn_split = ncr;
1297 return r.Mid(0, r.Len() - 1);
1300wxString ChartDatabase::GetFullChartInfo(
ChartBase *pc,
int dbIndex,
1301 int *char_width,
int *line_count) {
1304 unsigned int max_width = 0;
1306 unsigned int target_width = 60;
1312 line = _(
" Name: ");
1313 wxString longline = pc->GetName();
1316 if (longline.Find(
' ') != wxNOT_FOUND)
1321 if (longline.Len() > target_width) {
1322 line += SplitPath(pc->GetName(), tkz, target_width, 12, &ncr);
1323 max_width = wxMax(max_width, target_width + 4);
1327 max_width = wxMax(max_width, line.Len() + 4);
1336 line.Printf(_T(
" %s: 1:%d"), _(
"Scale"), pc->GetNativeScale());
1338 line.Printf(_T(
" %s: 1:%d"), _(
"Scale"), cte.GetScale());
1341 max_width = wxMax(max_width, line.Len());
1345 wxDateTime ed = pc->GetEditionDate();
1347 line = _(
" Updated: ") + ed.FormatISODate() +
"\n";
1348 max_width = wxMax(max_width, line.Len());
1353 line = _(
" Source Edition: ") + pc->GetSE() +
"\n";
1354 max_width = wxMax(max_width, line.Len());
1360 line = _(
" Depth Units: ") + pc->GetDepthUnits() +
"\n";
1361 max_width = wxMax(max_width, line.Len());
1365 line = _(
" Soundings: ") + pc->GetSoundingsDatum() +
"\n";
1366 max_width = wxMax(max_width, line.Len());
1370 line = _(
" Datum: ") + pc->GetDatumString() +
"\n";
1371 max_width = wxMax(max_width, line.Len());
1376 line = _(
" Projection: ");
1377 if (PROJECTION_UNKNOWN == cte.GetChartProjectionType())
1378 line += _(
"Unknown");
1379 else if (PROJECTION_MERCATOR == cte.GetChartProjectionType())
1380 line += _(
"Mercator");
1381 else if (PROJECTION_TRANSVERSE_MERCATOR == cte.GetChartProjectionType())
1382 line += _(
"Transverse Mercator");
1383 else if (PROJECTION_POLYCONIC == cte.GetChartProjectionType())
1384 line += _(
"Polyconic");
1385 else if (PROJECTION_WEB_MERCATOR == cte.GetChartProjectionType())
1386 line += _(
"Web Mercator (EPSG:3857)");
1388 max_width = wxMax(max_width, line.Len());
1397 if (pc && pc->GetExtraInfo().Len()) {
1398 line += pc->GetExtraInfo();
1400 max_width = wxMax(max_width, line.Len());
1407 line += pc->GetID();
1409 max_width = wxMax(max_width, line.Len());
1414 line = _(
" ChartFile: ");
1415 wxString longline = *(cte.GetpsFullPath());
1416 if (longline.Len() > target_width) {
1417 line += SplitPath(longline, _T(
"/,\\"), target_width, 15, &ncr);
1418 max_width = wxMax(max_width, target_width + 4);
1422 max_width = wxMax(max_width, line.Len() + 4);
1428 if (line_count) *line_count = lc;
1430 if (char_width) *char_width = max_width;
1440 wxGenericProgressDialog *pprog) {
1441 m_dir_array = dir_array;
1445 m_chartDirs.Clear();
1446 active_chartTable.Clear();
1447 active_chartTable_pathindex.clear();
1449 Update(dir_array,
true, pprog);
1454 m_dbversion = DB_VERSION_CURRENT;
1466 virtual wxDirTraverseResult OnFile(
const wxString &filename)
override {
1467 wxFileName fn(filename);
1468 wxFileName dir(fn.GetPath());
1469 if (fn.GetFullName().Matches(_T(
"poly-*-1.dat")) &&
1470 dir.GetFullName().IsSameAs(_T(
"GSHHG"),
false)) {
1471 parent_dir = fn.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
1474 return wxDIR_CONTINUE;
1476 virtual wxDirTraverseResult OnDir(
const wxString &dirname)
override {
1478 return wxDIR_CONTINUE;
1480 wxString GetGshhsDir()
const {
return parent_dir; }
1483 wxString parent_dir;
1490wxString findGshhgDirectory(
const wxString &directory) {
1491 wxDir dir(directory);
1492 if (!dir.IsOpened()) {
1493 return wxEmptyString;
1496 dir.Traverse(traverser, wxEmptyString, wxDIR_FILES | wxDIR_DIRS);
1497 return traverser.GetGshhsDir();
1505 wxGenericProgressDialog *pprog) {
1506 m_dir_array = dir_array;
1512 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++)
1513 active_chartTable[i].SetValid(
false);
1515 m_chartDirs.Clear();
1517 if (bForce) active_chartTable.Clear();
1519 bool lbForce = bForce;
1522 if (s_dbVersion != DB_VERSION_CURRENT) {
1523 active_chartTable.Clear();
1525 s_dbVersion = DB_VERSION_CURRENT;
1526 m_dbversion = DB_VERSION_CURRENT;
1531 for (
unsigned int j = 0; j < dir_array.GetCount(); j++) {
1537#ifdef __OCPN__ANDROID__
1538 if (!androidIsDirWritable(dir_info.fullpath))
continue;
1544 wxString gshhg_dir = findGshhgDirectory(dir_info.fullpath);
1545 if (!gshhg_dir.empty()) {
1550 wxLogMessage(
"Updating GSHHG directory: %s", gshhg_dir.c_str());
1551 gWorldMapLocation = gshhg_dir;
1553 if (dir_info.fullpath.Find(_T(
"OSMSHP")) != wxNOT_FOUND) {
1554 if (!wxDir::FindFirst(dir_info.fullpath,
"basemap_*.shp").empty()) {
1555 gWorldShapefileLocation =
1556 dir_info.fullpath + wxFileName::GetPathSeparator();
1557 gShapeBasemap.Reset();
1561 TraverseDirAndAddCharts(dir_info, pprog, dir_magic, lbForce);
1565 dir_info.magic_number = dir_magic;
1566 dir_array.RemoveAt(j);
1567 dir_array.Insert(dir_info, j);
1569 m_chartDirs.Add(dir_info.fullpath);
1572 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
1573 if (!active_chartTable[i].GetbValid()) {
1574 active_chartTable.RemoveAt(i);
1580 active_chartTable_pathindex.clear();
1581 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
1582 active_chartTable_pathindex[active_chartTable[i].GetFullSystemPath()] = i;
1583 active_chartTable[i].SetEntryOffset(i);
1586 m_nentries = active_chartTable.GetCount();
1597int ChartDatabase::FinddbIndex(wxString PathToFind) {
1600 for(
unsigned int i=0 ; i<active_chartTable.GetCount() ; i++)
1602 if(active_chartTable[i].GetpsFullPath()->IsSameAs(PathToFind))
1608 if (active_chartTable_pathindex.find(PathToFind) !=
1609 active_chartTable_pathindex.end())
1610 return active_chartTable_pathindex[PathToFind];
1620int ChartDatabase::DisableChart(wxString &PathToDisable) {
1621 int index = FinddbIndex(PathToDisable);
1637int ChartDatabase::TraverseDirAndAddCharts(
ChartDirInfo &dir_info,
1638 wxGenericProgressDialog *pprog,
1639 wxString &dir_magic,
bool bForce) {
1641 wxString dir_path = dir_info.fullpath;
1642#ifdef __OCPN__ANDROID__
1643 dir_path = wxString(dir_info.fullpath.mb_str(wxConvUTF8));
1646 wxString old_magic = dir_info.magic_number;
1647 wxString new_magic = old_magic;
1648 dir_magic = old_magic;
1652 bool b_skipDetectDirChange =
false;
1653 bool b_dirchange =
false;
1656 if (!wxDir::Exists(dir_path))
return 0;
1662 bool b_cm93 = Check_CM93_Structure(dir_path);
1664 b_skipDetectDirChange =
true;
1670 if (!b_skipDetectDirChange)
1671 b_dirchange = DetectDirChange(dir_path, dir_info.fullpath, old_magic,
1674 if (!bForce && !b_dirchange) {
1675 wxString msg(_T(
" No change detected on directory "));
1676 msg.Append(dir_path);
1682 wxFileName fn_dir(dir_path, _T(
"stuff"));
1683 unsigned int dir_path_count = fn_dir.GetDirCount();
1685 if (pprog) pprog->SetTitle(_(
"OpenCPN Chart Scan...."));
1687 int nEntries = active_chartTable.GetCount();
1689 for (
int ic = 0; ic < nEntries; ic++) {
1690 wxFileName fn(active_chartTable[ic].GetFullSystemPath());
1692 while (fn.GetDirCount() >= dir_path_count) {
1693 if (fn.GetPath() == dir_path) {
1694 active_chartTable[ic].SetValid(
true);
1710 dir_magic = new_magic;
1713 for (
auto &cd : m_ChartClassDescriptorArray) {
1714 nAdd += SearchDirAndAddCharts(dir_info.fullpath, cd, pprog);
1720bool ChartDatabase::DetectDirChange(
const wxString &dir_path,
1721 const wxString &prog_label,
1722 const wxString &magic, wxString &new_magic,
1723 wxGenericProgressDialog *pprog) {
1724 if (pprog) pprog->SetTitle(_(
"OpenCPN Directory Scan...."));
1727 long long unsigned int nmagic;
1728 wxULongLong nacc = 0;
1730 magic.ToULongLong(&nmagic, 10);
1733 wxArrayString FileList;
1734 wxDir dir(dir_path);
1735 int n_files = dir.GetAllFiles(dir_path, &FileList);
1743 for (
int ifile = 0; ifile < n_files; ifile++) {
1744 if (pprog && (ifile % (n_files / 60 + 1)) == 0)
1745 pprog->Update(wxMin((ifile * 100) / n_files, 100), prog_label);
1747 wxFileName file(FileList[ifile]);
1752 wxString fileNameNative = file.GetFullPath();
1753 wxScopedCharBuffer fileNameUTF8 = fileNameNative.ToUTF8();
1754 hash.Update(fileNameUTF8.data(), fileNameUTF8.length());
1757 wxULongLong size = file.GetSize();
1758 wxULongLong fileSize = ((size != wxInvalidSize) ? size : 0);
1759 hash.Update(&fileSize, (
sizeof fileSize));
1762 wxDateTime t = file.GetModificationTime();
1763 wxULongLong fileTime = t.GetTicks();
1764 hash.Update(&fileTime, (
sizeof fileTime));
1768 hash.Receive(&nacc);
1771 new_magic = nacc.ToString();
1774 if (new_magic != magic)
1780bool ChartDatabase::IsChartDirUsed(
const wxString &theDir) {
1781 wxString dir(theDir);
1782 if (dir.Last() ==
'/' || dir.Last() == wxFileName::GetPathSeparator())
1785 dir.Append(wxT(
"*"));
1786 for (UINT32 i = 0; i < active_chartTable.GetCount(); i++) {
1787 if (active_chartTable[i].GetpsFullPath()->Matches(dir))
return true;
1796bool ChartDatabase::Check_CM93_Structure(wxString dir_name) {
1799 wxRegEx test(_T(
"[0-9]+"));
1801 wxDir dirt(dir_name);
1804 if (dirt.IsOpened())
1805 wxLogMessage(_T(
"check_cm93 opened dir OK: ") + dir_name);
1807 wxLogMessage(_T(
"check_cm93 NOT OPENED OK: ") + dir_name);
1808 wxLogMessage(_T(
"check_cm93 returns false.") + dir_name);
1812 bool b_maybe_found_cm93 =
false;
1813 bool b_cont = dirt.GetFirst(&candidate);
1816 if (test.Matches(candidate) && (candidate.Len() == 8)) {
1817 b_maybe_found_cm93 =
true;
1821 b_cont = dirt.GetNext(&candidate);
1824 if (b_maybe_found_cm93) {
1825 wxString dir_next = dir_name;
1826 dir_next += _T(
"/");
1827 dir_next += candidate;
1828 if (wxDir::Exists(dir_next)) {
1829 wxDir dir_n(dir_next);
1830 if (dirt.IsOpened()) {
1831 wxString candidate_n;
1833 wxRegEx test_n(_T(
"^[A-Ga-g]"));
1834 bool b_probably_found_cm93 =
false;
1835 bool b_cont_n = dir_n.IsOpened() && dir_n.GetFirst(&candidate_n);
1837 if (test_n.Matches(candidate_n) && (candidate_n.Len() == 1)) {
1838 b_probably_found_cm93 =
true;
1841 b_cont_n = dir_n.GetNext(&candidate_n);
1844 if (b_probably_found_cm93)
1849 wxString dir_luk = dir_next;
1851 dir_luk += candidate_n;
1852 if (wxDir::Exists(dir_luk))
return true;
1971WX_DECLARE_STRING_HASH_MAP(
int, ChartCollisionsHashMap);
1973int ChartDatabase::SearchDirAndAddCharts(wxString &dir_name_base,
1975 wxGenericProgressDialog *pprog) {
1976 wxString msg(_T(
"Searching directory: "));
1977 msg += dir_name_base;
1979 msg += chart_desc.m_search_mask;
1982 wxString dir_name = dir_name_base;
1984#ifdef __OCPN__ANDROID__
1985 dir_name = wxString(dir_name_base.mb_str(wxConvUTF8));
1988 if (!wxDir::Exists(dir_name))
return 0;
1990 wxString filespec = chart_desc.m_search_mask.Upper();
1991 wxString lowerFileSpec = chart_desc.m_search_mask.Lower();
1992 wxString filespecXZ = filespec + _T(
".xz");
1993 wxString lowerFileSpecXZ = lowerFileSpec + _T(
".xz");
1997 wxArrayString FileList;
1998 int gaf_flags = wxDIR_DEFAULT;
2005 bool b_found_cm93 =
false;
2006 bool b_cm93 = Check_CM93_Structure(dir_name);
2008 if (filespec != _T(
"00300000.A"))
2011 filespec = dir_name;
2012 b_found_cm93 =
true;
2016 if (!b_found_cm93) {
2017 wxDir dir(dir_name);
2018 dir.GetAllFiles(dir_name, &FileList, filespec, gaf_flags);
2020#ifdef __OCPN__ANDROID__
2021 if (!FileList.GetCount()) {
2022 wxArrayString afl = androidTraverseDir(dir_name, filespec);
2023 for (wxArrayString::const_iterator item = afl.begin(); item != afl.end();
2025 FileList.Add(*item);
2030 if (filespec != lowerFileSpec) {
2032 wxArrayString lowerFileList;
2033 dir.GetAllFiles(dir_name, &lowerFileList, lowerFileSpec, gaf_flags);
2035#ifdef __OCPN__ANDROID__
2036 if (!lowerFileList.GetCount()) {
2037 wxArrayString afl = androidTraverseDir(dir_name, lowerFileSpec);
2038 for (wxArrayString::const_iterator item = afl.begin();
2039 item != afl.end(); item++)
2040 lowerFileList.Add(*item);
2044 for (wxArrayString::const_iterator item = lowerFileList.begin();
2045 item != lowerFileList.end(); item++)
2046 FileList.Add(*item);
2052 dir.GetAllFiles(dir_name, &FileList, filespecXZ, gaf_flags);
2053 dir.GetAllFiles(dir_name, &FileList, lowerFileSpecXZ, gaf_flags);
2059 wxString dir_plus = dir_name;
2060 dir_plus += wxFileName::GetPathSeparator();
2061 FileList.Add(dir_plus);
2064 int nFile = FileList.GetCount();
2066 if (!nFile)
return false;
2073 bool bthis_dir_in_dB = IsChartDirUsed(dir_name);
2075 if (pprog) pprog->SetTitle(_(
"OpenCPN Chart Add...."));
2079 ChartCollisionsHashMap collision_map;
2080 int nEntry = active_chartTable.GetCount();
2081 for (
int i = 0; i < nEntry; i++) {
2082 wxString table_file_name = active_chartTable[i].GetFullSystemPath();
2083 wxFileName table_file(table_file_name);
2084 collision_map[table_file.GetFullName()] = i;
2087 int nFileProgressQuantum = wxMax(nFile / 100, 2);
2088 double rFileProgressRatio = 100.0 / wxMax(nFile, 1);
2090 for (
int ifile = 0; ifile < nFile; ifile++) {
2091 wxFileName file(FileList[ifile]);
2092 wxString full_name = file.GetFullPath();
2093 wxString file_name = file.GetFullName();
2094 wxString utf8_path = full_name;
2096#ifdef __OCPN__ANDROID__
2102 wxFileName fnbase(dir_name_base);
2103 int nDirs = fnbase.GetDirCount();
2105 wxFileName file_target(FileList[ifile]);
2107 for (
int i = 0; i < nDirs + 1;
2109 file_target.RemoveDir(0);
2111 wxString leftover_path = file_target.GetFullPath();
2113 dir_name_base + leftover_path;
2119 if (!file_name.Matches(lowerFileSpec) && !file_name.Matches(filespec) &&
2120 !file_name.Matches(lowerFileSpecXZ) && !file_name.Matches(filespecXZ) &&
2126 if (pprog && ((ifile % nFileProgressQuantum) == 0))
2127 pprog->Update(
static_cast<int>(ifile * rFileProgressRatio), utf8_path);
2130 bool bAddFinal =
true;
2135 ChartCollisionsHashMap::const_iterator collision_ptr =
2136 collision_map.find(file_name);
2137 bool collision = (collision_ptr != collision_map.end());
2138 bool file_path_is_same =
false;
2139 bool file_time_is_same =
false;
2141 wxString table_file_name;
2144 if (b_found_cm93) collision =
false;
2147 pEntry = &active_chartTable[collision_ptr->second];
2148 table_file_name = pEntry->GetFullSystemPath();
2150 bthis_dir_in_dB && full_name.IsSameAs(table_file_name);
2154 if (file_path_is_same) {
2158 time_t t_oldFile = pEntry->GetFileTime();
2159 time_t t_newFile = file.GetModificationTime().GetTicks();
2161 if (t_newFile <= t_oldFile) {
2162 file_time_is_same =
true;
2164 pEntry->SetValid(
true);
2167 pEntry->SetValid(
false);
2172 wxString msg_fn(full_name);
2173 msg_fn.Replace(_T(
"%"), _T(
"%%"));
2174 if (file_time_is_same) {
2178 wxString::Format(_T(
"Loading chart data for %s"), msg_fn.c_str()));
2180 pnewChart = CreateChartTableEntry(full_name, utf8_path, chart_desc);
2183 wxLogMessage(wxString::Format(
2184 _T(
" CreateChartTableEntry() failed for file: %s"),
2189 if (!collision || !pnewChart) {
2191 }
else if (file_path_is_same) {
2193 wxString::Format(_T(
" Replacing older chart file of same path: %s"),
2195 }
else if (!file_time_is_same) {
2201 if (pnewChart->IsEarlierThan(*pEntry)) {
2202 wxFileName table_file(table_file_name);
2204 if (table_file.IsFileReadable()) {
2205 pEntry->SetValid(
true);
2207 wxLogMessage(wxString::Format(
2208 _T(
" Retaining newer chart file of same name: %s"),
2211 }
else if (pnewChart->IsEqualTo(*pEntry)) {
2219 pEntry->SetValid(
false);
2221 wxLogMessage(wxString::Format(
2222 _T(
" Replacing older chart file of same name: %s"),
2228 if (0 == b_add_msg) {
2230 wxString::Format(_T(
" Adding chart file: %s"), msg_fn.c_str()));
2232 collision_map[file_name] = active_chartTable.GetCount();
2233 active_chartTable.Add(pnewChart);
2236 if (pnewChart)
delete pnewChart;
2242 m_nentries = active_chartTable.GetCount();
2247bool ChartDatabase::AddChart(wxString &chartfilename,
2249 wxGenericProgressDialog *pprog,
int isearch,
2250 bool bthis_dir_in_dB) {
2252 wxFileName file(chartfilename);
2253 wxString full_name = file.GetFullPath();
2254 wxString file_name = file.GetFullName();
2264 pprog->Update(wxMin((m_pdifile * 100) / m_pdnFile, 100), full_name);
2267 bool bAddFinal =
true;
2269 wxString msg_fn(full_name);
2270 msg_fn.Replace(_T(
"%"), _T(
"%%"));
2272 pnewChart = CreateChartTableEntry(full_name, full_name, chart_desc);
2275 wxLogMessage(wxString::Format(
2276 _T(
" CreateChartTableEntry() failed for file: %s"), msg_fn.c_str()));
2281 int nEntry = active_chartTable.GetCount();
2282 for (
int i = 0; i < nEntry; i++) {
2283 wxString *ptable_file_name = active_chartTable[isearch].GetpsFullPath();
2287 if (bthis_dir_in_dB && full_name.IsSameAs(*ptable_file_name)) {
2291 time_t t_oldFile = active_chartTable[isearch].GetFileTime();
2292 time_t t_newFile = file.GetModificationTime().GetTicks();
2294 if (t_newFile <= t_oldFile) {
2296 active_chartTable[isearch].SetValid(
true);
2299 active_chartTable[isearch].SetValid(
false);
2300 wxLogMessage(wxString::Format(
2301 _T(
" Replacing older chart file of same path: %s"),
2311 wxFileName table_file(*ptable_file_name);
2313 if (table_file.GetFullName() == file_name) {
2316 if (pnewChart->IsEarlierThan(active_chartTable[isearch])) {
2318 if (table_file.IsFileReadable()) {
2319 active_chartTable[isearch].SetValid(
true);
2321 wxLogMessage(wxString::Format(
2322 _T(
" Retaining newer chart file of same name: %s"),
2325 }
else if (pnewChart->IsEqualTo(active_chartTable[isearch])) {
2335 active_chartTable[isearch].SetValid(
false);
2337 wxLogMessage(wxString::Format(
2338 _T(
" Replacing older chart file of same name: %s"),
2348 if (nEntry == isearch) isearch = 0;
2353 if (0 == b_add_msg) {
2355 wxString::Format(_T(
" Adding chart file: %s"), msg_fn.c_str()));
2358 active_chartTable.Add(pnewChart);
2368 m_nentries = active_chartTable.GetCount();
2373bool ChartDatabase::AddSingleChart(wxString &ChartFullPath,
2374 bool b_force_full_search) {
2376 wxFileName fn(ChartFullPath);
2377 wxString ext = fn.GetExt();
2378 ext.Prepend(_T(
"*."));
2379 wxString ext_upper = ext.MakeUpper();
2380 wxString ext_lower = ext.MakeLower();
2381 wxString dir_name = fn.GetPath();
2387 for (
auto &cd : m_ChartClassDescriptorArray) {
2388 if (cd.m_descriptor_type == PLUGIN_DESCRIPTOR) {
2389 if (cd.m_search_mask == ext_upper) {
2393 if (cd.m_search_mask == ext_lower) {
2402 bool b_recurse =
true;
2403 if (!b_force_full_search) b_recurse = IsChartDirUsed(dir_name);
2405 bool rv = AddChart(ChartFullPath, desc, NULL, 0, b_recurse);
2409 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
2410 if (!active_chartTable[i].GetbValid()) {
2411 active_chartTable.RemoveAt(i);
2417 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++)
2418 active_chartTable[i].SetEntryOffset(i);
2422 DetectDirChange(dir_name, _T(
""), _T(
""), new_magic, 0);
2425 bool bcfound =
false;
2426 ArrayOfCDI NewChartDirArray;
2428 ArrayOfCDI ChartDirArray = GetChartDirArray();
2429 for (
unsigned int i = 0; i < ChartDirArray.GetCount(); i++) {
2435 if (newcdi.fullpath == dir_name) {
2436 newcdi.magic_number = new_magic;
2440 NewChartDirArray.Add(newcdi);
2445 cdi.fullpath = dir_name;
2446 cdi.magic_number = new_magic;
2447 NewChartDirArray.Add(cdi);
2451 SetChartDirArray(NewChartDirArray);
2454 m_chartDirs.Clear();
2456 for (
unsigned int i = 0; i < GetChartDirArray().GetCount(); i++) {
2458 m_chartDirs.Add(cdi.fullpath);
2461 m_nentries = active_chartTable.GetCount();
2466bool ChartDatabase::RemoveSingleChart(wxString &ChartFullPath) {
2470 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
2471 if (ChartFullPath.IsSameAs(GetChartTableEntry(i).GetFullSystemPath())) {
2472 active_chartTable.RemoveAt(i);
2480 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
2481 pcte = GetpChartTableEntry(i);
2482 pcte->SetEntryOffset(i);
2486 wxFileName fn(ChartFullPath);
2487 wxString fd = fn.GetPath();
2488 if (!IsChartDirUsed(fd)) {
2490 ArrayOfCDI NewChartDirArray;
2492 ArrayOfCDI ChartDirArray = GetChartDirArray();
2493 for (
unsigned int i = 0; i < ChartDirArray.GetCount(); i++) {
2498 if (newcdi.fullpath != fd) NewChartDirArray.Add(newcdi);
2501 SetChartDirArray(NewChartDirArray);
2505 m_chartDirs.Clear();
2506 for (
unsigned int i = 0; i < GetChartDirArray().GetCount(); i++) {
2508 m_chartDirs.Add(cdi.fullpath);
2511 m_nentries = active_chartTable.GetCount();
2520ChartBase *ChartDatabase::GetChart(
const wxChar *theFilePath,
2531 const wxString &filePath, wxString &utf8Path,
2533 wxString msg_fn(filePath);
2534 msg_fn.Replace(_T(
"%"), _T(
"%%"));
2536 wxString::Format(_T(
"Loading chart data for %s"), msg_fn.c_str()));
2538 ChartBase *pch = GetChart(filePath, chart_desc);
2541 wxString::Format(_T(
" ...creation failed for %s"), msg_fn.c_str()));
2545 InitReturn rc = pch->Init(filePath, HEADER_ONLY);
2546 if (rc != INIT_OK) {
2548 wxLogMessage(wxString::Format(_T(
" ...initialization failed for %s"),
2554 ret_val->SetValid(
true);
2561bool ChartDatabase::GetCentroidOfLargestScaleChart(
double *clat,
double *clon,
2562 ChartFamilyEnum family) {
2564 int cur_max_scale = 0;
2566 int nEntry = active_chartTable.GetCount();
2568 for (
int i = 0; i < nEntry; i++) {
2569 if (GetChartFamily(active_chartTable[i].GetChartType()) == family) {
2570 if (active_chartTable[i].GetScale() > cur_max_scale) {
2571 cur_max_scale = active_chartTable[i].GetScale();
2577 if (cur_max_i == -1)
2580 *clat = (active_chartTable[cur_max_i].GetLatMax() +
2581 active_chartTable[cur_max_i].GetLatMin()) /
2583 *clon = (active_chartTable[cur_max_i].GetLonMin() +
2584 active_chartTable[cur_max_i].GetLonMax()) /
2593int ChartDatabase::GetDBChartProj(
int dbIndex) {
2594 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size()))
2595 return active_chartTable[dbIndex].GetChartProjectionType();
2597 return PROJECTION_UNKNOWN;
2603int ChartDatabase::GetDBChartFamily(
int dbIndex) {
2604 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size()))
2605 return active_chartTable[dbIndex].GetChartFamily();
2607 return CHART_FAMILY_UNKNOWN;
2613wxString ChartDatabase::GetDBChartFileName(
int dbIndex) {
2614 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2615 return wxString(active_chartTable[dbIndex].GetFullSystemPath());
2623int ChartDatabase::GetDBChartType(
int dbIndex) {
2624 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size()))
2625 return active_chartTable[dbIndex].GetChartType();
2633float ChartDatabase::GetDBChartSkew(
int dbIndex) {
2634 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size()))
2635 return active_chartTable[dbIndex].GetChartSkew();
2643int ChartDatabase::GetDBChartScale(
int dbIndex) {
2644 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size()))
2645 return active_chartTable[dbIndex].GetScale();
2653bool ChartDatabase::GetDBBoundingBox(
int dbIndex, LLBBox &box) {
2654 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2656 box.Set(entry.GetLatMin(), entry.GetLonMin(), entry.GetLatMax(),
2663const LLBBox &ChartDatabase::GetDBBoundingBox(
int dbIndex) {
2664 if ((bValid) && (dbIndex >= 0)) {
2666 return entry.GetBBox();
2668 return m_dummy_bbox;
2675int ChartDatabase::GetDBPlyPoint(
int dbIndex,
int plyindex,
float *lat,
2677 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2679 if (entry.GetnPlyEntries()) {
2680 float *fp = entry.GetpPlyTable();
2682 if (lat) *lat = *fp;
2684 if (lon) *lon = *fp;
2686 return entry.GetnPlyEntries();
2694int ChartDatabase::GetDBAuxPlyPoint(
int dbIndex,
int plyindex,
int ply,
2695 float *lat,
float *lon) {
2696 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2698 if (entry.GetnAuxPlyEntries()) {
2699 float *fp = entry.GetpAuxPlyTableEntry(ply);
2702 if (lat) *lat = *fp;
2704 if (lon) *lon = *fp;
2707 return entry.GetAuxCntTableEntry(ply);
2712int ChartDatabase::GetnAuxPlyEntries(
int dbIndex) {
2713 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2715 return entry.GetnAuxPlyEntries();
2723std::vector<float> ChartDatabase::GetReducedPlyPoints(
int dbIndex) {
2724 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2726 if (pentry)
return pentry->GetReducedPlyPoints();
2729 std::vector<float> dummy;
2736std::vector<float> ChartDatabase::GetReducedAuxPlyPoints(
int dbIndex,
2738 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2740 if (pentry)
return pentry->GetReducedAuxPlyPoints(iTable);
2743 std::vector<float> dummy;
2747bool ChartDatabase::IsChartAvailable(
int dbIndex) {
2748 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2752 if (pentry->GetChartType() != CHART_TYPE_PLUGIN)
return true;
2754 wxString *path = pentry->GetpsFullPath();
2755 wxFileName fn(*path);
2756 wxString ext = fn.GetExt();
2757 ext.Prepend(_T(
"*."));
2758 wxString ext_upper = ext.MakeUpper();
2759 wxString ext_lower = ext.MakeLower();
2764 for (
auto &cd : m_ChartClassDescriptorArray) {
2765 if (cd.m_descriptor_type == PLUGIN_DESCRIPTOR) {
2766 wxString search_mask = cd.m_search_mask;
2768 if (search_mask == ext_upper) {
2771 if (search_mask == ext_lower) {
2774 if (path->Matches(search_mask)) {
2784void ChartDatabase::ApplyGroupArray(ChartGroupArray *pGroupArray) {
2785 wxString separator(wxFileName::GetPathSeparator());
2787 for (
unsigned int ic = 0; ic < active_chartTable.GetCount(); ic++) {
2790 pcte->ClearGroupArray();
2792 wxString *chart_full_path = pcte->GetpsFullPath();
2794 for (
unsigned int igroup = 0; igroup < pGroupArray->GetCount(); igroup++) {
2795 ChartGroup *pGroup = pGroupArray->Item(igroup);
2796 for (
const auto &elem : pGroup->m_element_array) {
2797 wxString element_root = elem.m_element_name;
2803 if (!chart_full_path->IsSameAs(element_root))
2804 element_root.Append(
2806 if (chart_full_path->StartsWith(element_root)) {
2808 for (
unsigned int k = 0; k < elem.m_missing_name_array.size(); k++) {
2809 const wxString &missing_item = elem.m_missing_name_array[k];
2810 if (chart_full_path->StartsWith(missing_item)) {
2811 if (chart_full_path->IsSameAs(
2817 if (wxDir::Exists(missing_item))
2826 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.