32#include <wx/arrimpl.cpp>
33#include <wx/encconv.h>
35#include <wx/progdlg.h>
36#include <wx/tokenzr.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;
192 m_FullPath = std::string(pFullPath);
194#ifdef __OCPN__ANDROID__
195 m_fullSystemPath = wxString(utf8Path.mb_str(wxConvUTF8));
199 theChart.GetChartExtent(&ext);
205 m_bbox.Set(LatMin, LonMin, LatMax, LonMax);
210 double scale_max_zoom = Scale / 4;
212 double display_ppm = 1 / .00025;
213 double meters_per_pixel_max_scale = scale_max_zoom / display_ppm;
214 double LOD_meters = meters_per_pixel_max_scale * LOD_pixels;
219 if (theChart.GetCOVREntries() == 1) {
220 nPlyEntries = theChart.GetCOVRTablePoints(0);
222 if (nPlyEntries > 5 && (LOD_meters > .01)) {
223 std::vector<int> index_keep{0, nPlyEntries - 1, 1, nPlyEntries - 2};
225 double *DPbuffer = (
double *)malloc(2 * nPlyEntries *
sizeof(
double));
227 double *pfed = DPbuffer;
230 for (
int i = 0; i < nPlyEntries; i++) {
236 DouglasPeucker(DPbuffer, 1, nPlyEntries - 2, LOD_meters / (1852 * 60),
242 for (
unsigned int i = 0; i < index_keep.size(); i++) {
243 DPbuffer[2 * index_keep[i]] += 2000.;
246 float *pf = (
float *)malloc(2 * index_keep.size() *
sizeof(float));
249 for (
int i = 0; i < nPlyEntries; i++) {
250 if (DPbuffer[2 * i] > 1000.) {
251 *pfe++ = DPbuffer[2 * i] - 2000.;
252 *pfe++ = DPbuffer[(2 * i) + 1];
257 nPlyEntries = index_keep.size();
260 float *pf = (
float *)malloc(2 * nPlyEntries *
sizeof(
float));
265 for (
int i = 0; i < nPlyEntries; i++) {
277 float *pf1 = (
float *)malloc(2 * 4 *
sizeof(
float));
281 theChart.GetChartExtent(&fext);
297 nAuxPlyEntries = theChart.GetCOVREntries();
298 wxASSERT(nAuxPlyEntries);
299 float **pfp = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
301 int *pip = (
int *)malloc(nAuxPlyEntries *
sizeof(
int));
303 for (
int j = 0; j < nAuxPlyEntries; j++) {
304 int nPE = theChart.GetCOVRTablePoints(j);
306 if (nPE > 5 && (LOD_meters > .01)) {
307 std::vector<int> index_keep{0, nPE - 1, 1, nPE - 2};
309 double *DPbuffer = (
double *)malloc(2 * nPE *
sizeof(
double));
311 double *pfed = DPbuffer;
314 for (
int i = 0; i < nPE; i++) {
320 DouglasPeucker(DPbuffer, 1, nPE - 2, LOD_meters / (1852 * 60),
326 for (
unsigned int i = 0; i < index_keep.size(); i++) {
327 DPbuffer[2 * index_keep[i]] += 2000.;
330 float *pf = (
float *)malloc(2 * index_keep.size() *
sizeof(float));
333 for (
int i = 0; i < nPE; i++) {
334 if (DPbuffer[2 * i] > 1000.) {
335 *pfe++ = DPbuffer[2 * i] - 2000.;
336 *pfe++ = DPbuffer[(2 * i) + 1];
341 pip[j] = index_keep.size();
345 (
float *)malloc(theChart.GetCOVRTablePoints(j) * 2 *
sizeof(float));
346 memcpy(pf_entry, theChart.GetCOVRTableHead(j),
347 theChart.GetCOVRTablePoints(j) * 2 *
sizeof(
float));
349 pip[j] = theChart.GetCOVRTablePoints(j);
359 nNoCovrPlyEntries = theChart.GetNoCOVREntries();
360 if (nNoCovrPlyEntries == 0)
return;
362 float **pfpnc = (
float **)malloc(nNoCovrPlyEntries *
sizeof(
float *));
363 float **pft0nc = pfpnc;
364 int *pipnc = (
int *)malloc(nNoCovrPlyEntries *
sizeof(
int));
366 for (
int j = 0; j < nNoCovrPlyEntries; j++) {
368 (
float *)malloc(theChart.GetNoCOVRTablePoints(j) * 2 *
sizeof(float));
369 memcpy(pf_entry, theChart.GetNoCOVRTableHead(j),
370 theChart.GetNoCOVRTablePoints(j) * 2 *
sizeof(
float));
371 pft0nc[j] = pf_entry;
372 pipnc[j] = theChart.GetNoCOVRTablePoints(j);
375 pNoCovrPlyTable = pfpnc;
376 pNoCovrCntTable = pipnc;
381ChartTableEntry::~ChartTableEntry() {
385 for (
int i = 0; i < nAuxPlyEntries; i++) free(pAuxPlyTable[i]);
389 if (nNoCovrPlyEntries) {
390 for (
int i = 0; i < nNoCovrPlyEntries; i++) free(pNoCovrPlyTable[i]);
391 free(pNoCovrPlyTable);
392 free(pNoCovrCntTable);
404 wxDateTime mine(edition_date);
405 wxDateTime theirs(cte.edition_date);
407 if (!mine.IsValid() || !theirs.IsValid())
410 return (mine.IsEarlierThan(theirs));
414 wxDateTime mine(edition_date);
415 wxDateTime theirs(cte.edition_date);
417 if (!mine.IsValid() || !theirs.IsValid())
420 return (mine.IsEqualTo(theirs));
425static int convertChartType(
int charttype) {
428 if (s_dbVersion == 14) {
431 return CHART_TYPE_KAP;
433 return CHART_TYPE_GEO;
435 return CHART_TYPE_S57;
437 return CHART_TYPE_CM93;
439 return CHART_TYPE_CM93COMP;
441 return CHART_TYPE_UNKNOWN;
443 return CHART_TYPE_DONTCARE;
445 return CHART_TYPE_DUMMY;
447 return CHART_TYPE_UNKNOWN;
453static int convertChartFamily(
int charttype,
int chartfamily) {
454 if (s_dbVersion < 18) {
458 return CHART_FAMILY_RASTER;
461 case CHART_TYPE_CM93:
462 case CHART_TYPE_CM93COMP:
463 return CHART_FAMILY_VECTOR;
466 return CHART_FAMILY_UNKNOWN;
472bool ChartTableEntry::Read(
const ChartDatabase *pDb, wxInputStream &is) {
473 char path[4096], *cp;
479 int db_version = pD->GetVersion();
481 if (db_version == 18) {
483 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
484 pFullPath = (
char *)malloc(cp - path + 1);
485 strncpy(pFullPath, path, cp - path + 1);
486 wxLogVerbose(_T(
" Chart %s"), pFullPath);
489 m_pfilename =
new wxString;
490 wxString fullfilename(pFullPath, wxConvUTF8);
491 wxFileName fn(fullfilename);
492 *m_pfilename = fn.GetFullName();
493 m_psFullPath =
new wxString;
494 *m_psFullPath = fullfilename;
495 m_fullSystemPath = fullfilename;
496 m_FullPath = std::string(pFullPath);
498#ifdef __OCPN__ANDROID__
499 m_fullSystemPath = wxString(fullfilename.mb_str(wxConvUTF8));
506 EntryOffset = cte.EntryOffset;
507 ChartType = cte.ChartType;
508 ChartFamily = cte.ChartFamily;
514 m_bbox.Set(LatMin, LonMin, LatMax, LonMax);
517 ProjectionType = cte.ProjectionType;
520 edition_date = cte.edition_date;
521 file_date = cte.file_date;
523 nPlyEntries = cte.nPlyEntries;
524 nAuxPlyEntries = cte.nAuxPlyEntries;
526 nNoCovrPlyEntries = cte.nNoCovrPlyEntries;
531 int npeSize = nPlyEntries * 2 *
sizeof(float);
532 pPlyTable = (
float *)malloc(npeSize);
533 is.Read(pPlyTable, npeSize);
536 if (nAuxPlyEntries) {
537 int napeSize = nAuxPlyEntries *
sizeof(int);
538 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
539 pAuxCntTable = (
int *)malloc(napeSize);
540 is.Read(pAuxCntTable, napeSize);
542 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
544 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
545 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
546 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
550 if (nNoCovrPlyEntries) {
551 int napeSize = nNoCovrPlyEntries *
sizeof(int);
552 pNoCovrCntTable = (
int *)malloc(napeSize);
553 is.Read(pNoCovrCntTable, napeSize);
555 pNoCovrPlyTable = (
float **)malloc(nNoCovrPlyEntries *
sizeof(
float *));
556 for (
int i = 0; i < nNoCovrPlyEntries; i++) {
557 int nfSize = pNoCovrCntTable[i] * 2 *
sizeof(float);
558 pNoCovrPlyTable[i] = (
float *)malloc(nfSize);
559 is.Read(pNoCovrPlyTable[i], nfSize);
564 else if (db_version == 17) {
566 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
567 pFullPath = (
char *)malloc(cp - path + 1);
568 strncpy(pFullPath, path, cp - path + 1);
569 wxLogVerbose(_T(
" Chart %s"), pFullPath);
572 m_pfilename =
new wxString;
573 wxString fullfilename(pFullPath, wxConvUTF8);
574 wxFileName fn(fullfilename);
575 *m_pfilename = fn.GetFullName();
576 m_psFullPath =
new wxString;
577 *m_psFullPath = fullfilename;
578 m_FullPath = std::string(pFullPath);
585 EntryOffset = cte.EntryOffset;
586 ChartType = cte.ChartType;
592 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
595 ProjectionType = cte.ProjectionType;
598 edition_date = cte.edition_date;
599 file_date = cte.file_date;
601 nPlyEntries = cte.nPlyEntries;
602 nAuxPlyEntries = cte.nAuxPlyEntries;
604 nNoCovrPlyEntries = cte.nNoCovrPlyEntries;
609 int npeSize = nPlyEntries * 2 *
sizeof(float);
610 pPlyTable = (
float *)malloc(npeSize);
611 is.Read(pPlyTable, npeSize);
614 if (nAuxPlyEntries) {
615 int napeSize = nAuxPlyEntries *
sizeof(int);
616 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
617 pAuxCntTable = (
int *)malloc(napeSize);
618 is.Read(pAuxCntTable, napeSize);
620 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
622 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
623 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
624 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
628 if (nNoCovrPlyEntries) {
629 int napeSize = nNoCovrPlyEntries *
sizeof(int);
630 pNoCovrCntTable = (
int *)malloc(napeSize);
631 is.Read(pNoCovrCntTable, napeSize);
633 pNoCovrPlyTable = (
float **)malloc(nNoCovrPlyEntries *
sizeof(
float *));
634 for (
int i = 0; i < nNoCovrPlyEntries; i++) {
635 int nfSize = pNoCovrCntTable[i] * 2 *
sizeof(float);
636 pNoCovrPlyTable[i] = (
float *)malloc(nfSize);
637 is.Read(pNoCovrPlyTable[i], nfSize);
642 else if (db_version == 16) {
644 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
646 pFullPath = (
char *)malloc(cp - path + 1);
647 strncpy(pFullPath, path, cp - path + 1);
648 wxLogVerbose(_T(
" Chart %s"), pFullPath);
651 m_pfilename =
new wxString;
652 wxString fullfilename(pFullPath, wxConvUTF8);
653 wxFileName fn(fullfilename);
654 *m_pfilename = fn.GetFullName();
655 m_psFullPath =
new wxString;
656 *m_psFullPath = fullfilename;
657 m_FullPath = std::string(pFullPath);
664 EntryOffset = cte.EntryOffset;
665 ChartType = cte.ChartType;
671 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
674 ProjectionType = cte.ProjectionType;
677 edition_date = cte.edition_date;
678 file_date = cte.file_date;
680 nPlyEntries = cte.nPlyEntries;
681 nAuxPlyEntries = cte.nAuxPlyEntries;
686 int npeSize = nPlyEntries * 2 *
sizeof(float);
687 pPlyTable = (
float *)malloc(npeSize);
688 is.Read(pPlyTable, npeSize);
691 if (nAuxPlyEntries) {
692 int napeSize = nAuxPlyEntries *
sizeof(int);
693 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
694 pAuxCntTable = (
int *)malloc(napeSize);
695 is.Read(pAuxCntTable, napeSize);
697 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
699 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
700 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
701 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
706 else if (db_version == 15) {
708 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
710 pFullPath = (
char *)malloc(cp - path + 1);
711 strncpy(pFullPath, path, cp - path + 1);
712 wxLogVerbose(_T(
" Chart %s"), pFullPath);
719 EntryOffset = cte.EntryOffset;
720 ChartType = cte.ChartType;
726 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
729 edition_date = cte.edition_date;
730 file_date = cte.file_date;
732 nPlyEntries = cte.nPlyEntries;
733 nAuxPlyEntries = cte.nAuxPlyEntries;
738 int npeSize = nPlyEntries * 2 *
sizeof(float);
739 pPlyTable = (
float *)malloc(npeSize);
740 is.Read(pPlyTable, npeSize);
743 if (nAuxPlyEntries) {
744 int napeSize = nAuxPlyEntries *
sizeof(int);
745 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
746 pAuxCntTable = (
int *)malloc(napeSize);
747 is.Read(pAuxCntTable, napeSize);
749 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
751 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
752 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
753 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
756 }
else if (db_version == 14) {
758 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
759 pFullPath = (
char *)malloc(cp - path + 1);
760 strncpy(pFullPath, path, cp - path + 1);
761 wxLogVerbose(_T(
" Chart %s"), pFullPath);
768 EntryOffset = cte.EntryOffset;
769 ChartType = cte.ChartType;
775 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
778 edition_date = cte.edition_date;
780 nPlyEntries = cte.nPlyEntries;
781 nAuxPlyEntries = cte.nAuxPlyEntries;
785 int npeSize = nPlyEntries * 2 *
sizeof(float);
786 pPlyTable = (
float *)malloc(npeSize);
787 is.Read(pPlyTable, npeSize);
790 if (nAuxPlyEntries) {
791 int napeSize = nAuxPlyEntries *
sizeof(int);
792 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
793 pAuxCntTable = (
int *)malloc(napeSize);
794 is.Read(pAuxCntTable, napeSize);
796 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
798 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
799 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
800 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
804 ChartFamily = convertChartFamily(ChartType, ChartFamily);
805 ChartType = convertChartType(ChartType);
812bool ChartTableEntry::Write(
const ChartDatabase *pDb, wxOutputStream &os) {
813 os.Write(pFullPath, strlen(pFullPath) + 1);
820 cte.EntryOffset = EntryOffset;
821 cte.ChartType = ChartType;
822 cte.ChartFamily = ChartFamily;
829 cte.edition_date = edition_date;
830 cte.file_date = file_date;
832 cte.nPlyEntries = nPlyEntries;
833 cte.nAuxPlyEntries = nAuxPlyEntries;
836 cte.ProjectionType = ProjectionType;
840 cte.nNoCovrPlyEntries = nNoCovrPlyEntries;
843 wxLogVerbose(_T(
" Wrote Chart %s"), pFullPath);
847 int npeSize = nPlyEntries * 2 *
sizeof(float);
848 os.Write(pPlyTable, npeSize);
851 if (nAuxPlyEntries) {
852 int napeSize = nAuxPlyEntries *
sizeof(int);
853 os.Write(pAuxCntTable, napeSize);
855 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries; nAuxPlyEntry++) {
856 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
857 os.Write(pAuxPlyTable[nAuxPlyEntry], nfSize);
861 if (nNoCovrPlyEntries) {
862 int ncSize = nNoCovrPlyEntries *
sizeof(int);
863 os.Write(pNoCovrCntTable, ncSize);
865 for (
int i = 0; i < nNoCovrPlyEntries; i++) {
866 int nctSize = pNoCovrCntTable[i] * 2 *
sizeof(float);
867 os.Write(pNoCovrPlyTable[i], nctSize);
876void ChartTableEntry::Clear() {
885 pNoCovrCntTable = NULL;
886 pNoCovrPlyTable = NULL;
888 nNoCovrPlyEntries = 0;
897void ChartTableEntry::Disable() {
901 LatMax += (float)1000.;
902 LatMin += (float)1000.;
905void ChartTableEntry::ReEnable() {
907 LatMax -= (float)1000.;
908 LatMin -= (float)1000.;
912std::vector<float> ChartTableEntry::GetReducedPlyPoints() {
913 if (m_reducedPlyPoints.size())
return m_reducedPlyPoints;
916 float LOD_meters = 1;
918 float plylat, plylon;
919 const int nPoints = GetnPlyEntries();
921 float *fpo = GetpPlyTable();
923 double *ppd =
new double[nPoints * 2];
924 double *ppsm =
new double[nPoints * 2];
927 for (
int i = 0; i < nPoints; i++) {
929 plylon = fpo[i * 2 + 1];
932 toSM(plylat, plylon, fpo[0], fpo[1], &x, &y);
940 std::vector<int> index_keep;
942 index_keep.push_back(0);
943 index_keep.push_back(nPoints - 1);
944 index_keep.push_back(1);
945 index_keep.push_back(nPoints - 2);
947 DouglasPeuckerM(ppsm, 1, nPoints - 2, LOD_meters, &index_keep);
950 index_keep.resize(nPoints);
951 for (
int i = 0; i < nPoints; i++) index_keep[i] = i;
955 for (
int ip = 0; ip < nPoints; ip++) {
959 for (
unsigned int j = 0; j < index_keep.size(); j++) {
960 if (index_keep[j] == ip) {
961 m_reducedPlyPoints.push_back(x);
962 m_reducedPlyPoints.push_back(y);
971 int nprr = m_reducedPlyPoints.size() / 2;
973 return m_reducedPlyPoints;
976std::vector<float> ChartTableEntry::GetReducedAuxPlyPoints(
int iTable) {
978 if (!m_reducedAuxPlyPointsVector.size()) {
979 std::vector<float> vec;
980 for (
int i = 0; i < GetnAuxPlyEntries(); i++) {
981 m_reducedAuxPlyPointsVector.push_back(vec);
985 std::vector<float> vec;
988 if ((
unsigned int)iTable >= m_reducedAuxPlyPointsVector.size())
return vec;
990 if (m_reducedAuxPlyPointsVector.at(iTable).size())
991 return m_reducedAuxPlyPointsVector.at(iTable);
994 float LOD_meters = 1.0;
996 const int nPoints = GetAuxCntTableEntry(iTable);
997 float *fpo = GetpAuxPlyTableEntry(iTable);
999 double *ppd =
new double[nPoints * 2];
1000 double *ppsm =
new double[nPoints * 2];
1002 double *npsm = ppsm;
1003 float plylat, plylon;
1005 for (
int i = 0; i < nPoints; i++) {
1006 plylat = fpo[i * 2];
1007 plylon = fpo[i * 2 + 1];
1010 toSM(plylat, plylon, fpo[0], fpo[1], &x, &y);
1018 std::vector<int> index_keep;
1020 index_keep.push_back(0);
1021 index_keep.push_back(nPoints - 1);
1022 index_keep.push_back(1);
1023 index_keep.push_back(nPoints - 2);
1025 DouglasPeuckerM(ppsm, 1, nPoints - 2, LOD_meters, &index_keep);
1028 index_keep.resize(nPoints);
1029 for (
int i = 0; i < nPoints; i++) index_keep[i] = i;
1032 int nnn = index_keep.size();
1035 for (
int ip = 0; ip < nPoints; ip++) {
1039 for (
unsigned int j = 0; j < index_keep.size(); j++) {
1040 if (index_keep[j] == ip) {
1051 m_reducedAuxPlyPointsVector[iTable] = vec;
1053 int nprr = vec.size() / 2;
1062WX_DEFINE_OBJARRAY(ChartTable);
1064ChartDatabase::ChartDatabase() {
1068 m_ChartTableEntryDummy.Clear();
1070 UpdateChartClassDescriptorArray();
1073void ChartDatabase::UpdateChartClassDescriptorArray(
void) {
1074 if (m_ChartClassDescriptorArray.empty()) {
1075 m_ChartClassDescriptorArray.push_back(
1077 m_ChartClassDescriptorArray.push_back(
1079 m_ChartClassDescriptorArray.push_back(
1081 m_ChartClassDescriptorArray.push_back(
1084 _T(
"cm93compchart"), _T(
"00300000.a"), BUILTIN_DESCRIPTOR));
1086 _T(
"ChartMbTiles"), _T(
"*.mbtiles"), BUILTIN_DESCRIPTOR));
1091 m_ChartClassDescriptorArray.erase(
1092 std::remove_if(m_ChartClassDescriptorArray.begin(),
1093 m_ChartClassDescriptorArray.end(),
1095 return cd.m_descriptor_type == PLUGIN_DESCRIPTOR;
1097 m_ChartClassDescriptorArray.end());
1099 wxArrayString array = g_pi_manager->GetPlugInChartClassNameArray();
1100 for (
unsigned int j = 0; j < array.GetCount(); j++) {
1103 wxString class_name = array[j];
1106 wxString mask = cpiw->GetFileSearchMask();
1109 m_ChartClassDescriptorArray.push_back(
1117const ChartTableEntry &ChartDatabase::GetChartTableEntry(
int index)
const {
1118 if (index < GetChartTableEntries())
1119 return active_chartTable[index];
1121 return m_ChartTableEntryDummy;
1125 if (index < GetChartTableEntries())
1126 return &active_chartTable[index];
1131bool ChartDatabase::CompareChartDirArray(ArrayOfCDI &test_array) {
1135 if (test_array.GetCount() != m_dir_array.GetCount())
return false;
1138 unsigned int nfound_outer = 0;
1140 for (
unsigned int i = 0; i < test_array.GetCount(); i++) {
1142 bfound_inner =
false;
1143 for (
unsigned int j = 0; j < m_dir_array.GetCount(); j++) {
1146 if (p.fullpath.IsSameAs(q.fullpath)) {
1147 bfound_inner =
true;
1151 if (bfound_inner) nfound_outer++;
1154 return (nfound_outer == test_array.GetCount());
1157wxString ChartDatabase::GetMagicNumberCached(wxString dir) {
1158 for (
unsigned int j = 0; j < m_dir_array.GetCount(); j++) {
1160 if (dir.IsSameAs(q.fullpath))
return q.magic_number;
1166bool ChartDatabase::Read(
const wxString &filePath) {
1172 wxFileName file(filePath);
1173 if (!file.FileExists())
return false;
1175 m_DBFileName = filePath;
1177 wxFFileInputStream ifs(filePath);
1178 if (!ifs.Ok())
return false;
1182 if (!cth.CheckValid())
return false;
1186 memcpy(vbo, cth.GetDBVersionString(), 4);
1188 m_dbversion = atoi(&vbo[1]);
1189 s_dbVersion = m_dbversion;
1191 wxLogVerbose(wxT(
"Chartdb:Reading %d directory entries, %d table entries"),
1192 cth.GetDirEntries(), cth.GetTableEntries());
1193 wxLogMessage(_T(
"Chartdb: Chart directory list follows"));
1194 if (0 == cth.GetDirEntries()) wxLogMessage(_T(
" Nil"));
1197 for (
int iDir = 0; iDir < cth.GetDirEntries(); iDir++) {
1200 ifs.Read(&dirlen,
sizeof(
int));
1201 while (dirlen > 0) {
1203 int alen = dirlen > 1023 ? 1023 : dirlen;
1204 if (ifs.Read(&dirbuf, alen).Eof())
goto read_error;
1207 dir.Append(wxString(dirbuf, wxConvUTF8));
1210 msg.Printf(wxT(
" Chart directory #%d: "), iDir);
1213 m_chartDirs.Add(dir);
1216 entries = cth.GetTableEntries();
1217 active_chartTable.Alloc(entries);
1218 active_chartTable_pathindex.clear();
1219 while (entries-- && entry.Read(
this, ifs)) {
1220 active_chartTable_pathindex[entry.GetFullSystemPath()] = ind++;
1221 active_chartTable.Add(entry);
1226 entry.SetAvailable(
true);
1228 m_nentries = active_chartTable.GetCount();
1233 m_nentries = active_chartTable.GetCount();
1239bool ChartDatabase::Write(
const wxString &filePath) {
1240 wxFileName file(filePath);
1242 file.GetPath(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME, wxPATH_NATIVE));
1244 if (!dir.DirExists() && !dir.Mkdir())
return false;
1246 wxFFileOutputStream ofs(filePath);
1247 if (!ofs.Ok())
return false;
1249 ChartTableHeader cth(m_chartDirs.GetCount(), active_chartTable.GetCount());
1252 for (
int iDir = 0; iDir < cth.GetDirEntries(); iDir++) {
1253 wxString &dir = m_chartDirs[iDir];
1254 int dirlen = dir.length();
1256 strncpy(s, dir.mb_str(wxConvUTF8), 199);
1259 ofs.Write(&dirlen,
sizeof(
int));
1261 ofs.Write(s, dirlen);
1264 for (UINT32 iTable = 0; iTable < active_chartTable.size(); iTable++)
1265 active_chartTable[iTable].Write(
this, ofs);
1268 m_dbversion = DB_VERSION_CURRENT;
1274wxString SplitPath(wxString s, wxString tkd,
int nchar,
int offset,
1280 wxStringTokenizer tkz(s, tkd);
1281 while (tkz.HasMoreTokens()) {
1282 wxString token = tkz.GetNextToken();
1283 if ((rlen + (
int)token.Len() + 1) < nchar) {
1286 rlen += token.Len() + 1;
1290 for (
int i = 0; i < offset; i++) {
1295 rlen = offset + token.Len() + 1;
1299 if (pn_split) *pn_split = ncr;
1301 return r.Mid(0, r.Len() - 1);
1304wxString ChartDatabase::GetFullChartInfo(
ChartBase *pc,
int dbIndex,
1305 int *char_width,
int *line_count) {
1308 unsigned int max_width = 0;
1310 unsigned int target_width = 60;
1316 line = _(
" Name: ");
1317 wxString longline = pc->GetName();
1320 if (longline.Find(
' ') != wxNOT_FOUND)
1325 if (longline.Len() > target_width) {
1326 line += SplitPath(pc->GetName(), tkz, target_width, 12, &ncr);
1327 max_width = wxMax(max_width, target_width + 4);
1331 max_width = wxMax(max_width, line.Len() + 4);
1340 line.Printf(_T(
" %s: 1:%d"), _(
"Scale"), pc->GetNativeScale());
1342 line.Printf(_T(
" %s: 1:%d"), _(
"Scale"), cte.GetScale());
1345 max_width = wxMax(max_width, line.Len());
1349 wxDateTime ed = pc->GetEditionDate();
1351 line = _(
" Updated: ") + ed.FormatISODate() +
"\n";
1352 max_width = wxMax(max_width, line.Len());
1357 line = _(
" Source Edition: ") + pc->GetSE() +
"\n";
1358 max_width = wxMax(max_width, line.Len());
1364 line = _(
" Depth Units: ") + pc->GetDepthUnits() +
"\n";
1365 max_width = wxMax(max_width, line.Len());
1369 line = _(
" Soundings: ") + pc->GetSoundingsDatum() +
"\n";
1370 max_width = wxMax(max_width, line.Len());
1374 line = _(
" Datum: ") + pc->GetDatumString() +
"\n";
1375 max_width = wxMax(max_width, line.Len());
1380 line = _(
" Projection: ");
1381 if (PROJECTION_UNKNOWN == cte.GetChartProjectionType())
1382 line += _(
"Unknown");
1383 else if (PROJECTION_MERCATOR == cte.GetChartProjectionType())
1384 line += _(
"Mercator");
1385 else if (PROJECTION_TRANSVERSE_MERCATOR == cte.GetChartProjectionType())
1386 line += _(
"Transverse Mercator");
1387 else if (PROJECTION_POLYCONIC == cte.GetChartProjectionType())
1388 line += _(
"Polyconic");
1389 else if (PROJECTION_WEB_MERCATOR == cte.GetChartProjectionType())
1390 line += _(
"Web Mercator (EPSG:3857)");
1392 max_width = wxMax(max_width, line.Len());
1401 if (pc && pc->GetExtraInfo().Len()) {
1402 line += pc->GetExtraInfo();
1404 max_width = wxMax(max_width, line.Len());
1411 line += pc->GetID();
1413 max_width = wxMax(max_width, line.Len());
1418 line = _(
" ChartFile: ");
1419 wxString longline = *(cte.GetpsFullPath());
1420 if (longline.Len() > target_width) {
1421 line += SplitPath(longline, _T(
"/,\\"), target_width, 15, &ncr);
1422 max_width = wxMax(max_width, target_width + 4);
1426 max_width = wxMax(max_width, line.Len() + 4);
1432 if (line_count) *line_count = lc;
1434 if (char_width) *char_width = max_width;
1444 wxGenericProgressDialog *pprog) {
1445 m_dir_array = dir_array;
1449 m_chartDirs.Clear();
1450 active_chartTable.Clear();
1451 active_chartTable_pathindex.clear();
1453 Update(dir_array,
true, pprog);
1458 m_dbversion = DB_VERSION_CURRENT;
1470 virtual wxDirTraverseResult OnFile(
const wxString &filename)
override {
1471 wxFileName fn(filename);
1472 wxFileName dir(fn.GetPath());
1473 if (fn.GetFullName().Matches(_T(
"poly-*-1.dat")) &&
1474 dir.GetFullName().IsSameAs(_T(
"GSHHG"),
false)) {
1475 parent_dir = fn.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
1478 return wxDIR_CONTINUE;
1480 virtual wxDirTraverseResult OnDir(
const wxString &dirname)
override {
1482 return wxDIR_CONTINUE;
1484 wxString GetGshhsDir()
const {
return parent_dir; }
1487 wxString parent_dir;
1494wxString findGshhgDirectory(
const wxString &directory) {
1495 wxDir dir(directory);
1496 if (!dir.IsOpened()) {
1497 return wxEmptyString;
1500 dir.Traverse(traverser, wxEmptyString, wxDIR_FILES | wxDIR_DIRS);
1501 return traverser.GetGshhsDir();
1509 wxGenericProgressDialog *pprog) {
1510 m_dir_array = dir_array;
1516 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++)
1517 active_chartTable[i].SetValid(
false);
1519 m_chartDirs.Clear();
1521 if (bForce) active_chartTable.Clear();
1523 bool lbForce = bForce;
1526 if (s_dbVersion != DB_VERSION_CURRENT) {
1527 active_chartTable.Clear();
1529 s_dbVersion = DB_VERSION_CURRENT;
1530 m_dbversion = DB_VERSION_CURRENT;
1535 for (
unsigned int j = 0; j < dir_array.GetCount(); j++) {
1541#ifdef __OCPN__ANDROID__
1542 if (!androidIsDirWritable(dir_info.fullpath))
continue;
1548 wxString gshhg_dir = findGshhgDirectory(dir_info.fullpath);
1549 if (!gshhg_dir.empty()) {
1554 wxLogMessage(
"Updating GSHHG directory: %s", gshhg_dir.c_str());
1555 gWorldMapLocation = gshhg_dir;
1557 if (dir_info.fullpath.Find(_T(
"OSMSHP")) != wxNOT_FOUND) {
1558 if (!wxDir::FindFirst(dir_info.fullpath,
"basemap_*.shp").empty()) {
1559 gWorldShapefileLocation =
1560 dir_info.fullpath + wxFileName::GetPathSeparator();
1561 gShapeBasemap.Reset();
1565 TraverseDirAndAddCharts(dir_info, pprog, dir_magic, lbForce);
1569 dir_info.magic_number = dir_magic;
1570 dir_array.RemoveAt(j);
1571 dir_array.Insert(dir_info, j);
1573 m_chartDirs.Add(dir_info.fullpath);
1576 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
1577 if (!active_chartTable[i].GetbValid()) {
1578 active_chartTable.RemoveAt(i);
1584 active_chartTable_pathindex.clear();
1585 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
1586 active_chartTable_pathindex[active_chartTable[i].GetFullSystemPath()] = i;
1587 active_chartTable[i].SetEntryOffset(i);
1590 m_nentries = active_chartTable.GetCount();
1601int ChartDatabase::FinddbIndex(wxString PathToFind) {
1604 for(
unsigned int i=0 ; i<active_chartTable.GetCount() ; i++)
1606 if(active_chartTable[i].GetpsFullPath()->IsSameAs(PathToFind))
1612 if (active_chartTable_pathindex.find(PathToFind) !=
1613 active_chartTable_pathindex.end())
1614 return active_chartTable_pathindex[PathToFind];
1624int ChartDatabase::DisableChart(wxString &PathToDisable) {
1625 int index = FinddbIndex(PathToDisable);
1641int ChartDatabase::TraverseDirAndAddCharts(
ChartDirInfo &dir_info,
1642 wxGenericProgressDialog *pprog,
1643 wxString &dir_magic,
bool bForce) {
1645 wxString dir_path = dir_info.fullpath;
1646#ifdef __OCPN__ANDROID__
1647 dir_path = wxString(dir_info.fullpath.mb_str(wxConvUTF8));
1650 wxString old_magic = dir_info.magic_number;
1651 wxString new_magic = old_magic;
1652 dir_magic = old_magic;
1656 bool b_skipDetectDirChange =
false;
1657 bool b_dirchange =
false;
1660 if (!wxDir::Exists(dir_path))
return 0;
1666 bool b_cm93 = Check_CM93_Structure(dir_path);
1668 b_skipDetectDirChange =
true;
1674 if (!b_skipDetectDirChange)
1675 b_dirchange = DetectDirChange(dir_path, dir_info.fullpath, old_magic,
1678 if (!bForce && !b_dirchange) {
1679 wxString msg(_T(
" No change detected on directory "));
1680 msg.Append(dir_path);
1686 wxFileName fn_dir(dir_path, _T(
"stuff"));
1687 unsigned int dir_path_count = fn_dir.GetDirCount();
1689 if (pprog) pprog->SetTitle(_(
"OpenCPN Chart Scan...."));
1691 int nEntries = active_chartTable.GetCount();
1693 for (
int ic = 0; ic < nEntries; ic++) {
1694 wxFileName fn(active_chartTable[ic].GetFullSystemPath());
1696 while (fn.GetDirCount() >= dir_path_count) {
1697 if (fn.GetPath() == dir_path) {
1698 active_chartTable[ic].SetValid(
true);
1714 dir_magic = new_magic;
1717 for (
auto &cd : m_ChartClassDescriptorArray) {
1718 nAdd += SearchDirAndAddCharts(dir_info.fullpath, cd, pprog);
1724bool ChartDatabase::DetectDirChange(
const wxString &dir_path,
1725 const wxString &prog_label,
1726 const wxString &magic, wxString &new_magic,
1727 wxGenericProgressDialog *pprog) {
1728 if (pprog) pprog->SetTitle(_(
"OpenCPN Directory Scan...."));
1731 long long unsigned int nmagic;
1732 wxULongLong nacc = 0;
1734 magic.ToULongLong(&nmagic, 10);
1737 wxArrayString FileList;
1738 wxDir dir(dir_path);
1739 int n_files = dir.GetAllFiles(dir_path, &FileList);
1747 for (
int ifile = 0; ifile < n_files; ifile++) {
1748 if (pprog && (ifile % (n_files / 60 + 1)) == 0)
1749 pprog->Update(wxMin((ifile * 100) / n_files, 100), prog_label);
1751 wxFileName file(FileList[ifile]);
1756 wxString fileNameNative = file.GetFullPath();
1757 wxScopedCharBuffer fileNameUTF8 = fileNameNative.ToUTF8();
1758 hash.Update(fileNameUTF8.data(), fileNameUTF8.length());
1761 wxULongLong size = file.GetSize();
1762 wxULongLong fileSize = ((size != wxInvalidSize) ? size : 0);
1763 hash.Update(&fileSize, (
sizeof fileSize));
1766 wxDateTime t = file.GetModificationTime();
1767 wxULongLong fileTime = t.GetTicks();
1768 hash.Update(&fileTime, (
sizeof fileTime));
1772 hash.Receive(&nacc);
1775 new_magic = nacc.ToString();
1778 if (new_magic != magic)
1784bool ChartDatabase::IsChartDirUsed(
const wxString &theDir) {
1785 wxString dir(theDir);
1786 if (dir.Last() ==
'/' || dir.Last() == wxFileName::GetPathSeparator())
1789 dir.Append(wxT(
"*"));
1790 for (UINT32 i = 0; i < active_chartTable.GetCount(); i++) {
1791 if (active_chartTable[i].GetpsFullPath()->Matches(dir))
return true;
1800bool ChartDatabase::Check_CM93_Structure(wxString dir_name) {
1803 wxRegEx test(_T(
"[0-9]+"));
1805 wxDir dirt(dir_name);
1808 if (dirt.IsOpened())
1809 wxLogMessage(_T(
"check_cm93 opened dir OK: ") + dir_name);
1811 wxLogMessage(_T(
"check_cm93 NOT OPENED OK: ") + dir_name);
1812 wxLogMessage(_T(
"check_cm93 returns false.") + dir_name);
1816 bool b_maybe_found_cm93 =
false;
1817 bool b_cont = dirt.GetFirst(&candidate);
1820 if (test.Matches(candidate) && (candidate.Len() == 8)) {
1821 b_maybe_found_cm93 =
true;
1825 b_cont = dirt.GetNext(&candidate);
1828 if (b_maybe_found_cm93) {
1829 wxString dir_next = dir_name;
1830 dir_next += _T(
"/");
1831 dir_next += candidate;
1832 if (wxDir::Exists(dir_next)) {
1833 wxDir dir_n(dir_next);
1834 if (dirt.IsOpened()) {
1835 wxString candidate_n;
1837 wxRegEx test_n(_T(
"^[A-Ga-g]"));
1838 bool b_probably_found_cm93 =
false;
1839 bool b_cont_n = dir_n.IsOpened() && dir_n.GetFirst(&candidate_n);
1841 if (test_n.Matches(candidate_n) && (candidate_n.Len() == 1)) {
1842 b_probably_found_cm93 =
true;
1845 b_cont_n = dir_n.GetNext(&candidate_n);
1848 if (b_probably_found_cm93)
1853 wxString dir_luk = dir_next;
1855 dir_luk += candidate_n;
1856 if (wxDir::Exists(dir_luk))
return true;
1975WX_DECLARE_STRING_HASH_MAP(
int, ChartCollisionsHashMap);
1977int ChartDatabase::SearchDirAndAddCharts(wxString &dir_name_base,
1979 wxGenericProgressDialog *pprog) {
1980 wxString msg(_T(
"Searching directory: "));
1981 msg += dir_name_base;
1983 msg += chart_desc.m_search_mask;
1986 wxString dir_name = dir_name_base;
1988#ifdef __OCPN__ANDROID__
1989 dir_name = wxString(dir_name_base.mb_str(wxConvUTF8));
1992 if (!wxDir::Exists(dir_name))
return 0;
1994 wxString filespec = chart_desc.m_search_mask.Upper();
1995 wxString lowerFileSpec = chart_desc.m_search_mask.Lower();
1996 wxString filespecXZ = filespec + _T(
".xz");
1997 wxString lowerFileSpecXZ = lowerFileSpec + _T(
".xz");
2001 wxArrayString FileList;
2002 int gaf_flags = wxDIR_DEFAULT;
2009 bool b_found_cm93 =
false;
2010 bool b_cm93 = Check_CM93_Structure(dir_name);
2012 if (filespec != _T(
"00300000.A"))
2015 filespec = dir_name;
2016 b_found_cm93 =
true;
2020 if (!b_found_cm93) {
2021 wxDir dir(dir_name);
2022 dir.GetAllFiles(dir_name, &FileList, filespec, gaf_flags);
2024#ifdef __OCPN__ANDROID__
2025 if (!FileList.GetCount()) {
2026 wxArrayString afl = androidTraverseDir(dir_name, filespec);
2027 for (wxArrayString::const_iterator item = afl.begin(); item != afl.end();
2029 FileList.Add(*item);
2034 if (filespec != lowerFileSpec) {
2036 wxArrayString lowerFileList;
2037 dir.GetAllFiles(dir_name, &lowerFileList, lowerFileSpec, gaf_flags);
2039#ifdef __OCPN__ANDROID__
2040 if (!lowerFileList.GetCount()) {
2041 wxArrayString afl = androidTraverseDir(dir_name, lowerFileSpec);
2042 for (wxArrayString::const_iterator item = afl.begin();
2043 item != afl.end(); item++)
2044 lowerFileList.Add(*item);
2048 for (wxArrayString::const_iterator item = lowerFileList.begin();
2049 item != lowerFileList.end(); item++)
2050 FileList.Add(*item);
2056 dir.GetAllFiles(dir_name, &FileList, filespecXZ, gaf_flags);
2057 dir.GetAllFiles(dir_name, &FileList, lowerFileSpecXZ, gaf_flags);
2063 wxString dir_plus = dir_name;
2064 dir_plus += wxFileName::GetPathSeparator();
2065 FileList.Add(dir_plus);
2068 int nFile = FileList.GetCount();
2070 if (!nFile)
return false;
2077 bool bthis_dir_in_dB = IsChartDirUsed(dir_name);
2079 if (pprog) pprog->SetTitle(_(
"OpenCPN Chart Add...."));
2083 ChartCollisionsHashMap collision_map;
2084 int nEntry = active_chartTable.GetCount();
2085 for (
int i = 0; i < nEntry; i++) {
2086 wxString table_file_name = active_chartTable[i].GetFullSystemPath();
2087 wxFileName table_file(table_file_name);
2088 collision_map[table_file.GetFullName()] = i;
2091 int nFileProgressQuantum = wxMax(nFile / 100, 2);
2092 double rFileProgressRatio = 100.0 / wxMax(nFile, 1);
2094 for (
int ifile = 0; ifile < nFile; ifile++) {
2095 wxFileName file(FileList[ifile]);
2096 wxString full_name = file.GetFullPath();
2097 wxString file_name = file.GetFullName();
2098 wxString utf8_path = full_name;
2100#ifdef __OCPN__ANDROID__
2106 wxFileName fnbase(dir_name_base);
2107 int nDirs = fnbase.GetDirCount();
2109 wxFileName file_target(FileList[ifile]);
2111 for (
int i = 0; i < nDirs + 1;
2113 file_target.RemoveDir(0);
2115 wxString leftover_path = file_target.GetFullPath();
2117 dir_name_base + leftover_path;
2123 if (!file_name.Matches(lowerFileSpec) && !file_name.Matches(filespec) &&
2124 !file_name.Matches(lowerFileSpecXZ) && !file_name.Matches(filespecXZ) &&
2130 if (pprog && ((ifile % nFileProgressQuantum) == 0))
2131 pprog->Update(
static_cast<int>(ifile * rFileProgressRatio), utf8_path);
2134 bool bAddFinal =
true;
2139 ChartCollisionsHashMap::const_iterator collision_ptr =
2140 collision_map.find(file_name);
2141 bool collision = (collision_ptr != collision_map.end());
2142 bool file_path_is_same =
false;
2143 bool file_time_is_same =
false;
2145 wxString table_file_name;
2148 if (b_found_cm93) collision =
false;
2151 pEntry = &active_chartTable[collision_ptr->second];
2152 table_file_name = pEntry->GetFullSystemPath();
2154 bthis_dir_in_dB && full_name.IsSameAs(table_file_name);
2158 if (file_path_is_same) {
2162 time_t t_oldFile = pEntry->GetFileTime();
2163 time_t t_newFile = file.GetModificationTime().GetTicks();
2165 if (t_newFile <= t_oldFile) {
2166 file_time_is_same =
true;
2168 pEntry->SetValid(
true);
2171 pEntry->SetValid(
false);
2176 wxString msg_fn(full_name);
2177 msg_fn.Replace(_T(
"%"), _T(
"%%"));
2178 if (file_time_is_same) {
2182 wxString::Format(_T(
"Loading chart data for %s"), msg_fn.c_str()));
2184 pnewChart = CreateChartTableEntry(full_name, utf8_path, chart_desc);
2187 wxLogMessage(wxString::Format(
2188 _T(
" CreateChartTableEntry() failed for file: %s"),
2193 if (!collision || !pnewChart) {
2195 }
else if (file_path_is_same) {
2197 wxString::Format(_T(
" Replacing older chart file of same path: %s"),
2199 }
else if (!file_time_is_same) {
2205 if (pnewChart->IsEarlierThan(*pEntry)) {
2206 wxFileName table_file(table_file_name);
2208 if (table_file.IsFileReadable()) {
2209 pEntry->SetValid(
true);
2211 wxLogMessage(wxString::Format(
2212 _T(
" Retaining newer chart file of same name: %s"),
2215 }
else if (pnewChart->IsEqualTo(*pEntry)) {
2223 pEntry->SetValid(
false);
2225 wxLogMessage(wxString::Format(
2226 _T(
" Replacing older chart file of same name: %s"),
2232 if (0 == b_add_msg) {
2234 wxString::Format(_T(
" Adding chart file: %s"), msg_fn.c_str()));
2236 collision_map[file_name] = active_chartTable.GetCount();
2237 active_chartTable.Add(pnewChart);
2240 if (pnewChart)
delete pnewChart;
2246 m_nentries = active_chartTable.GetCount();
2251bool ChartDatabase::AddChart(wxString &chartfilename,
2253 wxGenericProgressDialog *pprog,
int isearch,
2254 bool bthis_dir_in_dB) {
2256 wxFileName file(chartfilename);
2257 wxString full_name = file.GetFullPath();
2258 wxString file_name = file.GetFullName();
2268 pprog->Update(wxMin((m_pdifile * 100) / m_pdnFile, 100), full_name);
2271 bool bAddFinal =
true;
2273 wxString msg_fn(full_name);
2274 msg_fn.Replace(_T(
"%"), _T(
"%%"));
2276 pnewChart = CreateChartTableEntry(full_name, full_name, chart_desc);
2279 wxLogMessage(wxString::Format(
2280 _T(
" CreateChartTableEntry() failed for file: %s"), msg_fn.c_str()));
2285 int nEntry = active_chartTable.GetCount();
2286 for (
int i = 0; i < nEntry; i++) {
2287 wxString *ptable_file_name = active_chartTable[isearch].GetpsFullPath();
2291 if (bthis_dir_in_dB && full_name.IsSameAs(*ptable_file_name)) {
2295 time_t t_oldFile = active_chartTable[isearch].GetFileTime();
2296 time_t t_newFile = file.GetModificationTime().GetTicks();
2298 if (t_newFile <= t_oldFile) {
2300 active_chartTable[isearch].SetValid(
true);
2303 active_chartTable[isearch].SetValid(
false);
2304 wxLogMessage(wxString::Format(
2305 _T(
" Replacing older chart file of same path: %s"),
2315 wxFileName table_file(*ptable_file_name);
2317 if (table_file.GetFullName() == file_name) {
2320 if (pnewChart->IsEarlierThan(active_chartTable[isearch])) {
2322 if (table_file.IsFileReadable()) {
2323 active_chartTable[isearch].SetValid(
true);
2325 wxLogMessage(wxString::Format(
2326 _T(
" Retaining newer chart file of same name: %s"),
2329 }
else if (pnewChart->IsEqualTo(active_chartTable[isearch])) {
2339 active_chartTable[isearch].SetValid(
false);
2341 wxLogMessage(wxString::Format(
2342 _T(
" Replacing older chart file of same name: %s"),
2352 if (nEntry == isearch) isearch = 0;
2357 if (0 == b_add_msg) {
2359 wxString::Format(_T(
" Adding chart file: %s"), msg_fn.c_str()));
2362 active_chartTable.Add(pnewChart);
2372 m_nentries = active_chartTable.GetCount();
2377bool ChartDatabase::AddSingleChart(wxString &ChartFullPath,
2378 bool b_force_full_search) {
2380 wxFileName fn(ChartFullPath);
2381 wxString ext = fn.GetExt();
2382 ext.Prepend(_T(
"*."));
2383 wxString ext_upper = ext.MakeUpper();
2384 wxString ext_lower = ext.MakeLower();
2385 wxString dir_name = fn.GetPath();
2391 for (
auto &cd : m_ChartClassDescriptorArray) {
2392 if (cd.m_descriptor_type == PLUGIN_DESCRIPTOR) {
2393 if (cd.m_search_mask == ext_upper) {
2397 if (cd.m_search_mask == ext_lower) {
2406 bool b_recurse =
true;
2407 if (!b_force_full_search) b_recurse = IsChartDirUsed(dir_name);
2409 bool rv = AddChart(ChartFullPath, desc, NULL, 0, b_recurse);
2413 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
2414 if (!active_chartTable[i].GetbValid()) {
2415 active_chartTable.RemoveAt(i);
2421 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++)
2422 active_chartTable[i].SetEntryOffset(i);
2426 DetectDirChange(dir_name, _T(
""), _T(
""), new_magic, 0);
2429 bool bcfound =
false;
2430 ArrayOfCDI NewChartDirArray;
2432 ArrayOfCDI ChartDirArray = GetChartDirArray();
2433 for (
unsigned int i = 0; i < ChartDirArray.GetCount(); i++) {
2439 if (newcdi.fullpath == dir_name) {
2440 newcdi.magic_number = new_magic;
2444 NewChartDirArray.Add(newcdi);
2449 cdi.fullpath = dir_name;
2450 cdi.magic_number = new_magic;
2451 NewChartDirArray.Add(cdi);
2455 SetChartDirArray(NewChartDirArray);
2458 m_chartDirs.Clear();
2460 for (
unsigned int i = 0; i < GetChartDirArray().GetCount(); i++) {
2462 m_chartDirs.Add(cdi.fullpath);
2465 m_nentries = active_chartTable.GetCount();
2470bool ChartDatabase::RemoveSingleChart(wxString &ChartFullPath) {
2474 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
2475 if (ChartFullPath.IsSameAs(GetChartTableEntry(i).GetFullSystemPath())) {
2476 active_chartTable.RemoveAt(i);
2484 for (
unsigned int i = 0; i < active_chartTable.GetCount(); i++) {
2485 pcte = GetpChartTableEntry(i);
2486 pcte->SetEntryOffset(i);
2490 wxFileName fn(ChartFullPath);
2491 wxString fd = fn.GetPath();
2492 if (!IsChartDirUsed(fd)) {
2494 ArrayOfCDI NewChartDirArray;
2496 ArrayOfCDI ChartDirArray = GetChartDirArray();
2497 for (
unsigned int i = 0; i < ChartDirArray.GetCount(); i++) {
2502 if (newcdi.fullpath != fd) NewChartDirArray.Add(newcdi);
2505 SetChartDirArray(NewChartDirArray);
2509 m_chartDirs.Clear();
2510 for (
unsigned int i = 0; i < GetChartDirArray().GetCount(); i++) {
2512 m_chartDirs.Add(cdi.fullpath);
2515 m_nentries = active_chartTable.GetCount();
2524ChartBase *ChartDatabase::GetChart(
const wxChar *theFilePath,
2535 const wxString &filePath, wxString &utf8Path,
2537 wxString msg_fn(filePath);
2538 msg_fn.Replace(_T(
"%"), _T(
"%%"));
2540 wxString::Format(_T(
"Loading chart data for %s"), msg_fn.c_str()));
2542 ChartBase *pch = GetChart(filePath, chart_desc);
2545 wxString::Format(_T(
" ...creation failed for %s"), msg_fn.c_str()));
2549 InitReturn rc = pch->Init(filePath, HEADER_ONLY);
2550 if (rc != INIT_OK) {
2552 wxLogMessage(wxString::Format(_T(
" ...initialization failed for %s"),
2558 ret_val->SetValid(
true);
2565bool ChartDatabase::GetCentroidOfLargestScaleChart(
double *clat,
double *clon,
2566 ChartFamilyEnum family) {
2568 int cur_max_scale = 0;
2570 int nEntry = active_chartTable.GetCount();
2572 for (
int i = 0; i < nEntry; i++) {
2573 if (GetChartFamily(active_chartTable[i].GetChartType()) == family) {
2574 if (active_chartTable[i].GetScale() > cur_max_scale) {
2575 cur_max_scale = active_chartTable[i].GetScale();
2581 if (cur_max_i == -1)
2584 *clat = (active_chartTable[cur_max_i].GetLatMax() +
2585 active_chartTable[cur_max_i].GetLatMin()) /
2587 *clon = (active_chartTable[cur_max_i].GetLonMin() +
2588 active_chartTable[cur_max_i].GetLonMax()) /
2597int ChartDatabase::GetDBChartProj(
int dbIndex) {
2598 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size()))
2599 return active_chartTable[dbIndex].GetChartProjectionType();
2601 return PROJECTION_UNKNOWN;
2607int ChartDatabase::GetDBChartFamily(
int dbIndex) {
2608 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size()))
2609 return active_chartTable[dbIndex].GetChartFamily();
2611 return CHART_FAMILY_UNKNOWN;
2617wxString ChartDatabase::GetDBChartFileName(
int dbIndex) {
2618 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2619 return wxString(active_chartTable[dbIndex].GetFullSystemPath());
2627int ChartDatabase::GetDBChartType(
int dbIndex) {
2628 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size()))
2629 return active_chartTable[dbIndex].GetChartType();
2637float ChartDatabase::GetDBChartSkew(
int dbIndex) {
2638 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size()))
2639 return active_chartTable[dbIndex].GetChartSkew();
2647int ChartDatabase::GetDBChartScale(
int dbIndex) {
2648 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size()))
2649 return active_chartTable[dbIndex].GetScale();
2657bool ChartDatabase::GetDBBoundingBox(
int dbIndex, LLBBox &box) {
2658 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2660 box.Set(entry.GetLatMin(), entry.GetLonMin(), entry.GetLatMax(),
2667const LLBBox &ChartDatabase::GetDBBoundingBox(
int dbIndex) {
2668 if ((bValid) && (dbIndex >= 0)) {
2670 return entry.GetBBox();
2672 return m_dummy_bbox;
2679int ChartDatabase::GetDBPlyPoint(
int dbIndex,
int plyindex,
float *lat,
2681 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2683 if (entry.GetnPlyEntries()) {
2684 float *fp = entry.GetpPlyTable();
2686 if (lat) *lat = *fp;
2688 if (lon) *lon = *fp;
2690 return entry.GetnPlyEntries();
2698int ChartDatabase::GetDBAuxPlyPoint(
int dbIndex,
int plyindex,
int ply,
2699 float *lat,
float *lon) {
2700 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2702 if (entry.GetnAuxPlyEntries()) {
2703 float *fp = entry.GetpAuxPlyTableEntry(ply);
2706 if (lat) *lat = *fp;
2708 if (lon) *lon = *fp;
2711 return entry.GetAuxCntTableEntry(ply);
2716int ChartDatabase::GetnAuxPlyEntries(
int dbIndex) {
2717 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2719 return entry.GetnAuxPlyEntries();
2727std::vector<float> ChartDatabase::GetReducedPlyPoints(
int dbIndex) {
2728 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2730 if (pentry)
return pentry->GetReducedPlyPoints();
2733 std::vector<float> dummy;
2740std::vector<float> ChartDatabase::GetReducedAuxPlyPoints(
int dbIndex,
2742 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2744 if (pentry)
return pentry->GetReducedAuxPlyPoints(iTable);
2747 std::vector<float> dummy;
2751bool ChartDatabase::IsChartAvailable(
int dbIndex) {
2752 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2756 if (pentry->GetChartType() != CHART_TYPE_PLUGIN)
return true;
2758 wxString *path = pentry->GetpsFullPath();
2759 wxFileName fn(*path);
2760 wxString ext = fn.GetExt();
2761 ext.Prepend(_T(
"*."));
2762 wxString ext_upper = ext.MakeUpper();
2763 wxString ext_lower = ext.MakeLower();
2768 for (
auto &cd : m_ChartClassDescriptorArray) {
2769 if (cd.m_descriptor_type == PLUGIN_DESCRIPTOR) {
2770 wxString search_mask = cd.m_search_mask;
2772 if (search_mask == ext_upper) {
2775 if (search_mask == ext_lower) {
2778 if (path->Matches(search_mask)) {
2788void ChartDatabase::ApplyGroupArray(ChartGroupArray *pGroupArray) {
2789 wxString separator(wxFileName::GetPathSeparator());
2791 for (
unsigned int ic = 0; ic < active_chartTable.GetCount(); ic++) {
2794 pcte->ClearGroupArray();
2796 wxString *chart_full_path = pcte->GetpsFullPath();
2798 for (
unsigned int igroup = 0; igroup < pGroupArray->GetCount(); igroup++) {
2799 ChartGroup *pGroup = pGroupArray->Item(igroup);
2800 for (
const auto &elem : pGroup->m_element_array) {
2801 wxString element_root = elem.m_element_name;
2807 if (!chart_full_path->IsSameAs(element_root))
2808 element_root.Append(
2810 if (chart_full_path->StartsWith(element_root)) {
2812 for (
unsigned int k = 0; k < elem.m_missing_name_array.size(); k++) {
2813 const wxString &missing_item = elem.m_missing_name_array[k];
2814 if (chart_full_path->StartsWith(missing_item)) {
2815 if (chart_full_path->IsSameAs(
2821 if (wxDir::Exists(missing_item))
2830 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.
PlugInManager and helper classes – Mostly gui parts (dialogs) and plugin API stuff.
Represents an entry in the chart table, containing information about a single chart.