33#include <wx/arrimpl.cpp>
35#include <wx/encconv.h>
36#include <wx/progdlg.h>
38#include <wx/tokenzr.h>
39#include <wx/evtloop.h>
46#include "LOD_reduce.h"
54#define UINT32 unsigned int
58#include "androidUTIL.h"
63static int s_dbVersion;
69static ChartFamilyEnum GetChartFamily(
int charttype) {
74 cf = CHART_FAMILY_RASTER;
77 cf = CHART_FAMILY_RASTER;
80 cf = CHART_FAMILY_VECTOR;
83 cf = CHART_FAMILY_VECTOR;
85 case CHART_TYPE_CM93COMP:
86 cf = CHART_FAMILY_VECTOR;
88 case CHART_TYPE_DUMMY:
89 cf = CHART_FAMILY_RASTER;
91 case CHART_TYPE_UNKNOWN:
92 cf = CHART_FAMILY_UNKNOWN;
95 cf = CHART_FAMILY_UNKNOWN;
105void ChartTableHeader::Read(wxInputStream &is) {
109void ChartTableHeader::Write(wxOutputStream &os) {
111 sprintf(vb,
"V%03d", DB_VERSION_CURRENT);
113 memcpy(dbVersion, vb, 4);
117bool ChartTableHeader::CheckValid() {
119 sprintf(vb,
"V%03d", DB_VERSION_CURRENT);
120 if (strncmp(vb, dbVersion,
sizeof(dbVersion))) {
123 memcpy(vbo, dbVersion, 4);
125 msg.Append(wxString(vbo, wxConvUTF8));
126 msg.Prepend(
" Warning: found incorrect chart db version: ");
132 sprintf(vb,
"V%03d", DB_VERSION_PREVIOUS);
133 if (strncmp(vb, dbVersion,
sizeof(dbVersion)))
137 " Scheduling db upgrade to current db version on "
138 "Options->Charts page visit...");
145 memcpy(vbo, dbVersion, 4);
147 msg.Append(wxString(vbo, wxConvUTF8));
148 msg.Prepend(
"Loading chart db version: ");
159void ChartTableEntry::SetScale(
int scale) {
163 if (Scale >= 1000) rounding = 5 * pow(10, log10(Scale) - 2);
166ChartTableEntry::ChartTableEntry(
ChartBase &theChart, wxString &utf8Path) {
169 char *pt = (
char *)malloc(strlen(utf8Path.mb_str(wxConvUTF8)) + 1);
170 strcpy(pt, utf8Path.mb_str(wxConvUTF8));
173 SetScale(theChart.GetNativeScale());
175 ChartType = theChart.GetChartType();
176 ChartFamily = theChart.GetChartFamily();
178 Skew = theChart.GetChartSkew();
179 ProjectionType = theChart.GetChartProjectionType();
181 wxDateTime ed = theChart.GetEditionDate();
182 if (theChart.GetEditionDate().IsValid())
183 edition_date = theChart.GetEditionDate().GetTicks();
185 wxFileName fn(theChart.GetFullPath());
186 if (fn.GetModificationTime().IsValid())
187 file_date = fn.GetModificationTime().GetTicks();
189 m_pfilename =
new wxString;
190 *m_pfilename = fn.GetFullName();
191 m_psFullPath =
new wxString;
192 *m_psFullPath = utf8Path;
193 m_fullSystemPath = utf8Path;
194 m_FullPath = std::string(pFullPath);
197 m_fullSystemPath = wxString(utf8Path.mb_str(wxConvUTF8));
201 theChart.GetChartExtent(&ext);
207 m_bbox.Set(LatMin, LonMin, LatMax, LonMax);
212 double scale_max_zoom = Scale / 4;
214 double display_ppm = 1 / .00025;
215 double meters_per_pixel_max_scale = scale_max_zoom / display_ppm;
216 double LOD_meters = meters_per_pixel_max_scale * LOD_pixels;
221 if (theChart.GetCOVREntries() == 1) {
222 nPlyEntries = theChart.GetCOVRTablePoints(0);
224 if (nPlyEntries > 5 && (LOD_meters > .01)) {
225 std::vector<int> index_keep{0, nPlyEntries - 1, 1, nPlyEntries - 2};
227 double *DPbuffer = (
double *)malloc(2 * nPlyEntries *
sizeof(
double));
229 double *pfed = DPbuffer;
232 for (
int i = 0; i < nPlyEntries; i++) {
238 DouglasPeucker(DPbuffer, 1, nPlyEntries - 2, LOD_meters / (1852 * 60),
244 for (
unsigned int i = 0; i < index_keep.size(); i++) {
245 DPbuffer[2 * index_keep[i]] += 2000.;
248 float *pf = (
float *)malloc(2 * index_keep.size() *
sizeof(float));
251 for (
int i = 0; i < nPlyEntries; i++) {
252 if (DPbuffer[2 * i] > 1000.) {
253 *pfe++ = DPbuffer[2 * i] - 2000.;
254 *pfe++ = DPbuffer[(2 * i) + 1];
259 nPlyEntries = index_keep.size();
262 float *pf = (
float *)malloc(2 * nPlyEntries *
sizeof(
float));
267 for (
int i = 0; i < nPlyEntries; i++) {
279 float *pf1 = (
float *)malloc(2 * 4 *
sizeof(
float));
283 theChart.GetChartExtent(&fext);
299 nAuxPlyEntries = theChart.GetCOVREntries();
300 wxASSERT(nAuxPlyEntries);
301 float **pfp = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
303 int *pip = (
int *)malloc(nAuxPlyEntries *
sizeof(
int));
305 for (
int j = 0; j < nAuxPlyEntries; j++) {
306 int nPE = theChart.GetCOVRTablePoints(j);
308 if (nPE > 5 && (LOD_meters > .01)) {
309 std::vector<int> index_keep{0, nPE - 1, 1, nPE - 2};
311 double *DPbuffer = (
double *)malloc(2 * nPE *
sizeof(
double));
313 double *pfed = DPbuffer;
316 for (
int i = 0; i < nPE; i++) {
322 DouglasPeucker(DPbuffer, 1, nPE - 2, LOD_meters / (1852 * 60),
328 for (
unsigned int i = 0; i < index_keep.size(); i++) {
329 DPbuffer[2 * index_keep[i]] += 2000.;
332 float *pf = (
float *)malloc(2 * index_keep.size() *
sizeof(float));
335 for (
int i = 0; i < nPE; i++) {
336 if (DPbuffer[2 * i] > 1000.) {
337 *pfe1++ = DPbuffer[2 * i] - 2000.;
338 *pfe1++ = DPbuffer[(2 * i) + 1];
343 pip[j] = index_keep.size();
347 (
float *)malloc(theChart.GetCOVRTablePoints(j) * 2 *
sizeof(float));
348 memcpy(pf_entry, theChart.GetCOVRTableHead(j),
349 theChart.GetCOVRTablePoints(j) * 2 *
sizeof(
float));
351 pip[j] = theChart.GetCOVRTablePoints(j);
361 nNoCovrPlyEntries = theChart.GetNoCOVREntries();
362 if (nNoCovrPlyEntries == 0)
return;
364 float **pfpnc = (
float **)malloc(nNoCovrPlyEntries *
sizeof(
float *));
365 float **pft0nc = pfpnc;
366 int *pipnc = (
int *)malloc(nNoCovrPlyEntries *
sizeof(
int));
368 for (
int j = 0; j < nNoCovrPlyEntries; j++) {
370 (
float *)malloc(theChart.GetNoCOVRTablePoints(j) * 2 *
sizeof(float));
371 memcpy(pf_entry, theChart.GetNoCOVRTableHead(j),
372 theChart.GetNoCOVRTablePoints(j) * 2 *
sizeof(
float));
373 pft0nc[j] = pf_entry;
374 pipnc[j] = theChart.GetNoCOVRTablePoints(j);
377 pNoCovrPlyTable = pfpnc;
378 pNoCovrCntTable = pipnc;
383ChartTableEntry::~ChartTableEntry() {
387 for (
int i = 0; i < nAuxPlyEntries; i++) free(pAuxPlyTable[i]);
391 if (nNoCovrPlyEntries) {
392 for (
int i = 0; i < nNoCovrPlyEntries; i++) free(pNoCovrPlyTable[i]);
393 free(pNoCovrPlyTable);
394 free(pNoCovrCntTable);
406 wxDateTime mine(edition_date);
407 wxDateTime theirs(cte.edition_date);
409 if (!mine.IsValid() || !theirs.IsValid())
412 return (mine.IsEarlierThan(theirs));
416 wxDateTime mine(edition_date);
417 wxDateTime theirs(cte.edition_date);
419 if (!mine.IsValid() || !theirs.IsValid())
422 return (mine.IsEqualTo(theirs));
427static int convertChartType(
int charttype) {
430 if (s_dbVersion == 14) {
433 return CHART_TYPE_KAP;
435 return CHART_TYPE_GEO;
437 return CHART_TYPE_S57;
439 return CHART_TYPE_CM93;
441 return CHART_TYPE_CM93COMP;
443 return CHART_TYPE_UNKNOWN;
445 return CHART_TYPE_DONTCARE;
447 return CHART_TYPE_DUMMY;
449 return CHART_TYPE_UNKNOWN;
455static int convertChartFamily(
int charttype,
int chartfamily) {
456 if (s_dbVersion < 18) {
460 return CHART_FAMILY_RASTER;
463 case CHART_TYPE_CM93:
464 case CHART_TYPE_CM93COMP:
465 return CHART_FAMILY_VECTOR;
468 return CHART_FAMILY_UNKNOWN;
474bool ChartTableEntry::Read(
const ChartDatabase *pDb, wxInputStream &is) {
475 char path[4096], *cp;
481 int db_version = pD->GetVersion();
483 if (db_version == 18) {
485 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
486 pFullPath = (
char *)malloc(cp - path + 1);
487 strncpy(pFullPath, path, cp - path + 1);
488 wxLogVerbose(
" Chart %s", pFullPath);
491 m_pfilename =
new wxString;
492 wxString fullfilename(pFullPath, wxConvUTF8);
493 wxFileName fn(fullfilename);
494 *m_pfilename = fn.GetFullName();
495 m_psFullPath =
new wxString;
496 *m_psFullPath = fullfilename;
497 m_fullSystemPath = fullfilename;
498 m_FullPath = std::string(pFullPath);
501 m_fullSystemPath = wxString(fullfilename.mb_str(wxConvUTF8));
508 EntryOffset = cte.EntryOffset;
509 ChartType = cte.ChartType;
510 ChartFamily = cte.ChartFamily;
516 m_bbox.Set(LatMin, LonMin, LatMax, LonMax);
519 ProjectionType = cte.ProjectionType;
522 edition_date = cte.edition_date;
523 file_date = cte.file_date;
525 nPlyEntries = cte.nPlyEntries;
526 nAuxPlyEntries = cte.nAuxPlyEntries;
528 nNoCovrPlyEntries = cte.nNoCovrPlyEntries;
533 int npeSize = nPlyEntries * 2 *
sizeof(float);
534 pPlyTable = (
float *)malloc(npeSize);
535 is.Read(pPlyTable, npeSize);
538 if (nAuxPlyEntries) {
539 int napeSize = nAuxPlyEntries *
sizeof(int);
540 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
541 pAuxCntTable = (
int *)malloc(napeSize);
542 is.Read(pAuxCntTable, napeSize);
544 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
546 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
547 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
548 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
552 if (nNoCovrPlyEntries) {
553 int napeSize = nNoCovrPlyEntries *
sizeof(int);
554 pNoCovrCntTable = (
int *)malloc(napeSize);
555 is.Read(pNoCovrCntTable, napeSize);
557 pNoCovrPlyTable = (
float **)malloc(nNoCovrPlyEntries *
sizeof(
float *));
558 for (
int i = 0; i < nNoCovrPlyEntries; i++) {
559 int nfSize = pNoCovrCntTable[i] * 2 *
sizeof(float);
560 pNoCovrPlyTable[i] = (
float *)malloc(nfSize);
561 is.Read(pNoCovrPlyTable[i], nfSize);
566 else if (db_version == 17) {
568 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
569 pFullPath = (
char *)malloc(cp - path + 1);
570 strncpy(pFullPath, path, cp - path + 1);
571 wxLogVerbose(
" Chart %s", pFullPath);
574 m_pfilename =
new wxString;
575 wxString fullfilename(pFullPath, wxConvUTF8);
576 wxFileName fn(fullfilename);
577 *m_pfilename = fn.GetFullName();
578 m_psFullPath =
new wxString;
579 *m_psFullPath = fullfilename;
580 m_FullPath = std::string(pFullPath);
587 EntryOffset = cte.EntryOffset;
588 ChartType = cte.ChartType;
594 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
597 ProjectionType = cte.ProjectionType;
600 edition_date = cte.edition_date;
601 file_date = cte.file_date;
603 nPlyEntries = cte.nPlyEntries;
604 nAuxPlyEntries = cte.nAuxPlyEntries;
606 nNoCovrPlyEntries = cte.nNoCovrPlyEntries;
611 int npeSize = nPlyEntries * 2 *
sizeof(float);
612 pPlyTable = (
float *)malloc(npeSize);
613 is.Read(pPlyTable, npeSize);
616 if (nAuxPlyEntries) {
617 int napeSize = nAuxPlyEntries *
sizeof(int);
618 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
619 pAuxCntTable = (
int *)malloc(napeSize);
620 is.Read(pAuxCntTable, napeSize);
622 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
624 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
625 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
626 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
630 if (nNoCovrPlyEntries) {
631 int napeSize = nNoCovrPlyEntries *
sizeof(int);
632 pNoCovrCntTable = (
int *)malloc(napeSize);
633 is.Read(pNoCovrCntTable, napeSize);
635 pNoCovrPlyTable = (
float **)malloc(nNoCovrPlyEntries *
sizeof(
float *));
636 for (
int i = 0; i < nNoCovrPlyEntries; i++) {
637 int nfSize = pNoCovrCntTable[i] * 2 *
sizeof(float);
638 pNoCovrPlyTable[i] = (
float *)malloc(nfSize);
639 is.Read(pNoCovrPlyTable[i], nfSize);
644 else if (db_version == 16) {
646 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
648 pFullPath = (
char *)malloc(cp - path + 1);
649 strncpy(pFullPath, path, cp - path + 1);
650 wxLogVerbose(
" Chart %s", pFullPath);
653 m_pfilename =
new wxString;
654 wxString fullfilename(pFullPath, wxConvUTF8);
655 wxFileName fn(fullfilename);
656 *m_pfilename = fn.GetFullName();
657 m_psFullPath =
new wxString;
658 *m_psFullPath = fullfilename;
659 m_FullPath = std::string(pFullPath);
666 EntryOffset = cte.EntryOffset;
667 ChartType = cte.ChartType;
673 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
676 ProjectionType = cte.ProjectionType;
679 edition_date = cte.edition_date;
680 file_date = cte.file_date;
682 nPlyEntries = cte.nPlyEntries;
683 nAuxPlyEntries = cte.nAuxPlyEntries;
688 int npeSize = nPlyEntries * 2 *
sizeof(float);
689 pPlyTable = (
float *)malloc(npeSize);
690 is.Read(pPlyTable, npeSize);
693 if (nAuxPlyEntries) {
694 int napeSize = nAuxPlyEntries *
sizeof(int);
695 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
696 pAuxCntTable = (
int *)malloc(napeSize);
697 is.Read(pAuxCntTable, napeSize);
699 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
701 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
702 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
703 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
708 else if (db_version == 15) {
710 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
712 pFullPath = (
char *)malloc(cp - path + 1);
713 strncpy(pFullPath, path, cp - path + 1);
714 wxLogVerbose(
" Chart %s", pFullPath);
721 EntryOffset = cte.EntryOffset;
722 ChartType = cte.ChartType;
728 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
731 edition_date = cte.edition_date;
732 file_date = cte.file_date;
734 nPlyEntries = cte.nPlyEntries;
735 nAuxPlyEntries = cte.nAuxPlyEntries;
740 int npeSize = nPlyEntries * 2 *
sizeof(float);
741 pPlyTable = (
float *)malloc(npeSize);
742 is.Read(pPlyTable, npeSize);
745 if (nAuxPlyEntries) {
746 int napeSize = nAuxPlyEntries *
sizeof(int);
747 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
748 pAuxCntTable = (
int *)malloc(napeSize);
749 is.Read(pAuxCntTable, napeSize);
751 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
753 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
754 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
755 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
758 }
else if (db_version == 14) {
760 for (cp = path; (*cp = (char)is.GetC()) != 0; cp++);
761 pFullPath = (
char *)malloc(cp - path + 1);
762 strncpy(pFullPath, path, cp - path + 1);
763 wxLogVerbose(
" Chart %s", pFullPath);
770 EntryOffset = cte.EntryOffset;
771 ChartType = cte.ChartType;
777 m_bbox.Set(LatMin, LatMax, LonMin, LonMax);
780 edition_date = cte.edition_date;
782 nPlyEntries = cte.nPlyEntries;
783 nAuxPlyEntries = cte.nAuxPlyEntries;
787 int npeSize = nPlyEntries * 2 *
sizeof(float);
788 pPlyTable = (
float *)malloc(npeSize);
789 is.Read(pPlyTable, npeSize);
792 if (nAuxPlyEntries) {
793 int napeSize = nAuxPlyEntries *
sizeof(int);
794 pAuxPlyTable = (
float **)malloc(nAuxPlyEntries *
sizeof(
float *));
795 pAuxCntTable = (
int *)malloc(napeSize);
796 is.Read(pAuxCntTable, napeSize);
798 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries;
800 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
801 pAuxPlyTable[nAuxPlyEntry] = (
float *)malloc(nfSize);
802 is.Read(pAuxPlyTable[nAuxPlyEntry], nfSize);
806 ChartFamily = convertChartFamily(ChartType, ChartFamily);
807 ChartType = convertChartType(ChartType);
814bool ChartTableEntry::Write(
const ChartDatabase *pDb, wxOutputStream &os) {
815 os.Write(pFullPath, strlen(pFullPath) + 1);
822 cte.EntryOffset = EntryOffset;
823 cte.ChartType = ChartType;
824 cte.ChartFamily = ChartFamily;
831 cte.edition_date = edition_date;
832 cte.file_date = file_date;
834 cte.nPlyEntries = nPlyEntries;
835 cte.nAuxPlyEntries = nAuxPlyEntries;
838 cte.ProjectionType = ProjectionType;
842 cte.nNoCovrPlyEntries = nNoCovrPlyEntries;
845 wxLogVerbose(
" Wrote Chart %s", pFullPath);
849 int npeSize = nPlyEntries * 2 *
sizeof(float);
850 os.Write(pPlyTable, npeSize);
853 if (nAuxPlyEntries) {
854 int napeSize = nAuxPlyEntries *
sizeof(int);
855 os.Write(pAuxCntTable, napeSize);
857 for (
int nAuxPlyEntry = 0; nAuxPlyEntry < nAuxPlyEntries; nAuxPlyEntry++) {
858 int nfSize = pAuxCntTable[nAuxPlyEntry] * 2 *
sizeof(float);
859 os.Write(pAuxPlyTable[nAuxPlyEntry], nfSize);
863 if (nNoCovrPlyEntries) {
864 int ncSize = nNoCovrPlyEntries *
sizeof(int);
865 os.Write(pNoCovrCntTable, ncSize);
867 for (
int i = 0; i < nNoCovrPlyEntries; i++) {
868 int nctSize = pNoCovrCntTable[i] * 2 *
sizeof(float);
869 os.Write(pNoCovrPlyTable[i], nctSize);
878void ChartTableEntry::Clear() {
885 pNoCovrCntTable = NULL;
886 pNoCovrPlyTable = NULL;
888 nNoCovrPlyEntries = 0;
898void ChartTableEntry::Disable() {
902 LatMax += (float)1000.;
903 LatMin += (float)1000.;
906void ChartTableEntry::ReEnable() {
908 LatMax -= (float)1000.;
909 LatMin -= (float)1000.;
912bool ChartTableEntry::IsBasemap()
const {
913 wxFileName fn(GetFullPath());
914 return (fn.GetPath().Lower().Contains(
"basemap"));
917std::vector<float> ChartTableEntry::GetReducedPlyPoints() {
918 if (m_reducedPlyPoints.size())
return m_reducedPlyPoints;
921 float LOD_meters = 1;
923 float plylat, plylon;
924 const int nPoints = GetnPlyEntries();
926 float *fpo = GetpPlyTable();
928 double *ppd =
new double[nPoints * 2];
929 double *ppsm =
new double[nPoints * 2];
932 for (
int i = 0; i < nPoints; i++) {
934 plylon = fpo[i * 2 + 1];
937 toSM(plylat, plylon, fpo[0], fpo[1], &x, &y);
945 std::vector<int> index_keep;
947 index_keep.push_back(0);
948 index_keep.push_back(nPoints - 1);
949 index_keep.push_back(1);
950 index_keep.push_back(nPoints - 2);
952 DouglasPeuckerM(ppsm, 1, nPoints - 2, LOD_meters, &index_keep);
955 index_keep.resize(nPoints);
956 for (
int i = 0; i < nPoints; i++) index_keep[i] = i;
960 for (
int ip = 0; ip < nPoints; ip++) {
964 for (
unsigned int j = 0; j < index_keep.size(); j++) {
965 if (index_keep[j] == ip) {
966 m_reducedPlyPoints.push_back(x);
967 m_reducedPlyPoints.push_back(y);
976 int nprr = m_reducedPlyPoints.size() / 2;
978 return m_reducedPlyPoints;
981std::vector<float> ChartTableEntry::GetReducedAuxPlyPoints(
int iTable) {
983 if (!m_reducedAuxPlyPointsVector.size()) {
984 std::vector<float> vec;
985 for (
int i = 0; i < GetnAuxPlyEntries(); i++) {
986 m_reducedAuxPlyPointsVector.push_back(vec);
990 std::vector<float> vec;
993 if ((
unsigned int)iTable >= m_reducedAuxPlyPointsVector.size())
return vec;
995 if (m_reducedAuxPlyPointsVector.at(iTable).size())
996 return m_reducedAuxPlyPointsVector.at(iTable);
999 float LOD_meters = 1.0;
1001 const int nPoints = GetAuxCntTableEntry(iTable);
1002 float *fpo = GetpAuxPlyTableEntry(iTable);
1004 double *ppd =
new double[nPoints * 2];
1005 double *ppsm =
new double[nPoints * 2];
1007 double *npsm = ppsm;
1008 float plylat, plylon;
1010 for (
int i = 0; i < nPoints; i++) {
1011 plylat = fpo[i * 2];
1012 plylon = fpo[i * 2 + 1];
1015 toSM(plylat, plylon, fpo[0], fpo[1], &x, &y);
1023 std::vector<int> index_keep;
1025 index_keep.push_back(0);
1026 index_keep.push_back(nPoints - 1);
1027 index_keep.push_back(1);
1028 index_keep.push_back(nPoints - 2);
1030 DouglasPeuckerM(ppsm, 1, nPoints - 2, LOD_meters, &index_keep);
1033 index_keep.resize(nPoints);
1034 for (
int i = 0; i < nPoints; i++) index_keep[i] = i;
1037 int nnn = index_keep.size();
1040 for (
int ip = 0; ip < nPoints; ip++) {
1044 for (
unsigned int j = 0; j < index_keep.size(); j++) {
1045 if (index_keep[j] == ip) {
1056 m_reducedAuxPlyPointsVector[iTable] = vec;
1058 int nprr = vec.size() / 2;
1066const int ID_DBS_PROGRESS_UPDATE = wxNewId();
1068WX_DEFINE_OBJARRAY(ChartTable);
1070ChartDatabase::ChartDatabase() {
1074 m_ChartTableEntryDummy.Clear();
1076 Bind(wxEVT_OCPN_CHARTTABLEENTRYTHREAD, &ChartDatabase::OnEvtThread,
this);
1077 Bind(wxEVT_COMMAND_MENU_SELECTED, &ChartDatabase::OnDBSProgressUpdate,
this,
1078 ID_DBS_PROGRESS_UPDATE);
1080 m_progress_timer.SetOwner(
this, ID_TIMER_UPDATE_PROGRESS);
1081 Bind(wxEVT_TIMER, &ChartDatabase::OnProgessTimer,
this,
1082 ID_TIMER_UPDATE_PROGRESS);
1084 m_update_competion_timer.SetOwner(
this, ID_TIMER_UPDATE_COMPLETE);
1085 Bind(wxEVT_TIMER, &ChartDatabase::OnUpdateComplete,
this,
1086 ID_TIMER_UPDATE_COMPLETE);
1088 UpdateChartClassDescriptorArray();
1091void ChartDatabase::OnDBSProgressUpdate(wxCommandEvent &evt) {}
1093void ChartDatabase::OnUpdateComplete(wxTimerEvent &event) {
1094 ProcessThreadQueueEmpty();
1097void ChartDatabase::OnProgessTimer(wxTimerEvent &event) {
1098 if (m_pprog && m_progress_timer.IsRunning() && (m_jobsRemaining > 1)) {
1099 if (m_progress_dirty.exchange(
false)) {
1101 qDebug() <<
" ***Update()" << m_jobsRemaining;
1103 m_pprog->Update(m_progress_value, m_progress_message);
1105 qDebug() <<
" ---Update() out" << m_jobsRemaining;
1111void ChartDatabase::ProcessThreadQueueEmpty() {
1113 qDebug() <<
"ProcessThreadQueueEmpty";
1115 if (m_deferred_ticket_vector.size()) {
1119 for (
auto &ticket_d : m_deferred_ticket_vector) {
1120 ticket_d->m_ticket_type = 0;
1121 m_pool_deferred.Push(ticket_d);
1124 m_deferred_ticket_vector.clear();
1129 m_ticketcount = m_jobsRemaining;
1130 m_nFileProgressQuantum = 1;
1131 m_progress_message = _(
"Processing charts");
1132 m_progress_timer.Start(50, wxTIMER_CONTINUOUS);
1135 if (m_jobsRemaining) {
1136 const int workerCount = 1;
1137 if (m_pool_deferred.GetWorkerCount() < workerCount) {
1138 int threads_needed = workerCount - m_pool_deferred.GetWorkerCount();
1139 for (
int i = 0; i < threads_needed; ++i) {
1141 m_pool_deferred.AddWorker();
1148 m_progress_timer.Stop();
1149 if (m_pprog) m_pprog->Hide();
1158 for (
auto &ticket_valid : m_ticket_vector) {
1159 if (ticket_valid->m_chart_table_entry)
1160 active_chartTable.push_back(ticket_valid->m_chart_table_entry);
1164 m_ticket_vector.clear();
1167 qDebug() <<
"FinalizeChartUpdate";
1170 FinalizeChartUpdate();
1175 qDebug() <<
"Remaining: " << m_jobsRemaining;
1178 auto ticket =
event.GetTicket();
1184 if (m_pprog && (m_jobsRemaining > 1)) {
1186 double ratio = 100. * (double)m_progcount / m_ticketcount;
1188 if (((m_progcount % m_nFileProgressQuantum) == 0)) {
1189 if (val != m_progint) {
1191 m_progress_value = val;
1192 m_progress_dirty =
true;
1197 if (!ticket->m_ticket_type) {
1198 bool collision_found =
false;
1199 if (ticket->b_thread_safe) {
1200 wxFileName fn(ticket->m_ChartPath);
1201 ChartCollisionsHashMap::iterator it;
1202 for (it = m_full_collision_map.begin(); it != m_full_collision_map.end();
1204 if (it->first.IsSameAs(fn.GetFullName())) {
1208 collision_found =
true;
1212 if (!collision_found) {
1213 m_full_collision_map[fn.GetFullName()] = 1;
1218 m_ticket_vector.push_back(ticket);
1221 int remaining = --m_jobsRemaining;
1224 m_pool.pending_events--;
1225 m_pool_deferred.pending_events--;
1227 if (remaining == 0) {
1229 m_pool_deferred.Shutdown();
1230 m_progress_timer.Stop();
1233 qDebug() <<
"Fire completion Oneshot";
1235 m_update_competion_timer.Start(100, wxTIMER_ONE_SHOT);
1239void ChartDatabase::FinalizeChartUpdate() {
1242 active_chartTable.erase(
1243 std::remove_if(active_chartTable.begin(), active_chartTable.end(),
1244 [](
const auto &cte) { return !cte->GetbValid(); }),
1245 active_chartTable.end());
1247 size_t d = active_chartTable.size();
1250 active_chartTable_pathindex.clear();
1252 for (
auto cte : active_chartTable) {
1253 active_chartTable_pathindex[cte->GetFullSystemPath()] = i;
1254 cte->SetEntryOffset(i);
1258 m_nentries = active_chartTable.size();
1264 ChartData->SaveBinary(ChartListFileName);
1265 wxLogMessage(
"Finished chart database Update");
1268 if (m_pprog) m_pprog->Destroy();
1273 if (gWorldMapLocation.empty()) {
1277 gWorldMapLocation = gDefaultWorldMapLocation;
1278 m_gshhg_chart_loc = wxEmptyString;
1282 if (gWorldMapLocation != m_gshhg_chart_loc) {
1291 gshhsCrossesLandReset();
1304bool ChartDatabase::ScrubGroupArray() {
1309 bool b_change =
false;
1310 unsigned int igroup = 0;
1311 while (igroup < g_pGroupArray->GetCount()) {
1312 bool b_chart_in_element =
false;
1315 for (
unsigned int j = 0; j < pGroup->m_element_array.size(); j++) {
1316 const wxString &element_root = pGroup->m_element_array[j].m_element_name;
1318 for (
unsigned int ic = 0;
1319 ic < (
unsigned int)
ChartData->GetChartTableEntries(); ic++) {
1320 auto &cte =
ChartData->GetChartTableEntry(ic);
1321 wxString chart_full_path = cte.GetFullSystemPath();
1323 if (chart_full_path.StartsWith(element_root)) {
1324 b_chart_in_element =
true;
1330 if (!b_chart_in_element) {
1331 wxString test_string =
"GSHH";
1332 if (element_root.Upper().Contains(test_string))
1333 b_chart_in_element =
true;
1336 if (!b_chart_in_element)
1338 pGroup->m_element_array.erase(pGroup->m_element_array.begin() + j);
1350void ChartDatabase::UpdateChartClassDescriptorArray() {
1351 if (m_ChartClassDescriptorArray.empty()) {
1352 m_ChartClassDescriptorArray.push_back(
1354 m_ChartClassDescriptorArray.push_back(
1356 m_ChartClassDescriptorArray.push_back(
1358 m_ChartClassDescriptorArray.push_back(
1361 "cm93compchart",
"00300000.a",
nullptr, BUILTIN_DESCRIPTOR));
1363 "ChartMbTiles",
"*.mbtiles",
nullptr, BUILTIN_DESCRIPTOR));
1368 m_ChartClassDescriptorArray.erase(
1369 std::remove_if(m_ChartClassDescriptorArray.begin(),
1370 m_ChartClassDescriptorArray.end(),
1372 return cd.m_descriptor_type == PLUGIN_DESCRIPTOR;
1374 m_ChartClassDescriptorArray.end());
1376 wxArrayString array =
g_pi_manager->GetPlugInChartClassNameArray();
1377 for (
unsigned int j = 0; j < array.GetCount(); j++) {
1380 wxString class_name = array[j];
1383 wxString mask = cpiw->GetFileSearchMask();
1386 auto plugin_t =
g_pi_manager->GetProvidingPlugin(class_name);
1389 m_ChartClassDescriptorArray.push_back(
1398 if (index < GetChartTableEntries())
1399 return *active_chartTable[index];
1401 return m_ChartTableEntryDummy;
1404bool ChartDatabase::CompareChartDirArray(ArrayOfCDI &test_array) {
1408 if (test_array.GetCount() != m_dir_array.GetCount())
return false;
1411 unsigned int nfound_outer = 0;
1413 for (
unsigned int i = 0; i < test_array.GetCount(); i++) {
1415 bfound_inner =
false;
1416 for (
unsigned int j = 0; j < m_dir_array.GetCount(); j++) {
1419 if (p.fullpath.IsSameAs(q.fullpath)) {
1420 bfound_inner =
true;
1424 if (bfound_inner) nfound_outer++;
1427 return (nfound_outer == test_array.GetCount());
1430wxString ChartDatabase::GetMagicNumberCached(wxString dir) {
1431 for (
unsigned int j = 0; j < m_dir_array.GetCount(); j++) {
1433 if (dir.IsSameAs(q.fullpath))
return q.magic_number;
1439bool ChartDatabase::Read(
const wxString &filePath) {
1445 wxFileName file(filePath);
1446 if (!file.FileExists())
return false;
1448 m_DBFileName = filePath;
1450 wxFFileInputStream ifs(filePath);
1451 if (!ifs.Ok())
return false;
1455 if (!cth.CheckValid())
return false;
1459 memcpy(vbo, cth.GetDBVersionString(), 4);
1461 m_dbversion = atoi(&vbo[1]);
1462 s_dbVersion = m_dbversion;
1464 wxLogVerbose(
"Chartdb:Reading %d directory entries, %d table entries",
1465 cth.GetDirEntries(), cth.GetTableEntries());
1466 wxLogMessage(
"Chartdb: Chart directory list follows");
1467 if (0 == cth.GetDirEntries()) wxLogMessage(
" Nil");
1470 for (
int iDir = 0; iDir < cth.GetDirEntries(); iDir++) {
1473 ifs.Read(&dirlen,
sizeof(
int));
1474 while (dirlen > 0) {
1476 int alen = dirlen > 1023 ? 1023 : dirlen;
1477 if (ifs.Read(&dirbuf, alen).Eof())
goto read_error;
1480 dir.Append(wxString(dirbuf, wxConvUTF8));
1483 msg.Printf(
" Chart directory #%d: ", iDir);
1486 m_chartDirs.Add(dir);
1489 entries = cth.GetTableEntries();
1491 active_chartTable_pathindex.clear();
1492 while (entries-- && entry.Read(
this, ifs)) {
1493 active_chartTable_pathindex[entry.GetFullSystemPath()] = ind++;
1494 auto sharedPtr = std::make_shared<ChartTableEntry>(entry);
1495 active_chartTable.push_back(sharedPtr);
1500 entry.SetAvailable(
true);
1502 m_nentries = active_chartTable.size();
1507 m_nentries = active_chartTable.size();
1513bool ChartDatabase::Write(
const wxString &filePath) {
1514 wxFileName file(filePath);
1516 file.GetPath(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME, wxPATH_NATIVE));
1518 if (!dir.DirExists() && !dir.Mkdir())
return false;
1520 wxFFileOutputStream ofs(filePath);
1521 if (!ofs.Ok())
return false;
1526 for (
int iDir = 0; iDir < cth.GetDirEntries(); iDir++) {
1527 wxString dir1 = m_chartDirs[iDir];
1528 int dirlen = dir1.length();
1530 strncpy(s, dir1.mb_str(wxConvUTF8), 199);
1533 ofs.Write(&dirlen,
sizeof(
int));
1535 ofs.Write(s, dirlen);
1538 for (UINT32 iTable = 0; iTable < active_chartTable.size(); iTable++) {
1539 auto &cte = GetChartTableEntry(iTable);
1540 cte.Write(
this, ofs);
1544 m_dbversion = DB_VERSION_CURRENT;
1550wxString SplitPath(wxString s, wxString tkd,
int nchar,
int offset,
1556 wxStringTokenizer tkz(s, tkd);
1557 while (tkz.HasMoreTokens()) {
1558 wxString token = tkz.GetNextToken();
1559 if ((rlen + (
int)token.Len() + 1) < nchar) {
1562 rlen += token.Len() + 1;
1566 for (
int i = 0; i < offset; i++) {
1571 rlen = offset + token.Len() + 1;
1575 if (pn_split) *pn_split = ncr;
1577 return r.Mid(0, r.Len() - 1);
1580wxString ChartDatabase::GetFullChartInfo(
ChartBase *pc,
int dbIndex,
1581 int *char_width,
int *line_count) {
1584 unsigned int max_width = 0;
1586 unsigned int target_width = 60;
1592 line = _(
" Name: ");
1593 wxString longline = pc->GetName();
1596 if (longline.Find(
' ') != wxNOT_FOUND)
1601 if (longline.Len() > target_width) {
1602 line += SplitPath(pc->GetName(), tkz, target_width, 12, &ncr);
1603 max_width = wxMax(max_width, target_width + 4);
1607 max_width = wxMax(max_width, line.Len() + 4);
1616 line.Printf(
" %s: 1:%d", _(
"Scale"), pc->GetNativeScale());
1618 line.Printf(
" %s: 1:%d", _(
"Scale"), cte.GetScale());
1621 max_width = wxMax(max_width, line.Len());
1625 wxDateTime ed = pc->GetEditionDate();
1627 line = _(
" Updated: ") + ed.FormatISODate() +
"\n";
1628 max_width = wxMax(max_width, line.Len());
1633 line = _(
" Source Edition: ") + pc->GetSE() +
"\n";
1634 max_width = wxMax(max_width, line.Len());
1640 line = _(
" Depth Units: ") + pc->GetDepthUnits() +
"\n";
1641 max_width = wxMax(max_width, line.Len());
1645 line = _(
" Soundings: ") + pc->GetSoundingsDatum() +
"\n";
1646 max_width = wxMax(max_width, line.Len());
1650 line = _(
" Datum: ") + pc->GetDatumString() +
"\n";
1651 max_width = wxMax(max_width, line.Len());
1656 line = _(
" Projection: ");
1657 if (PROJECTION_UNKNOWN == cte.GetChartProjectionType())
1658 line += _(
"Unknown");
1659 else if (PROJECTION_MERCATOR == cte.GetChartProjectionType())
1660 line += _(
"Mercator");
1661 else if (PROJECTION_TRANSVERSE_MERCATOR == cte.GetChartProjectionType())
1662 line += _(
"Transverse Mercator");
1663 else if (PROJECTION_POLYCONIC == cte.GetChartProjectionType())
1664 line += _(
"Polyconic");
1665 else if (PROJECTION_WEB_MERCATOR == cte.GetChartProjectionType())
1666 line += _(
"Web Mercator (EPSG:3857)");
1668 max_width = wxMax(max_width, line.Len());
1677 if (pc && pc->GetExtraInfo().Len()) {
1678 line += pc->GetExtraInfo();
1680 max_width = wxMax(max_width, line.Len());
1687 line += pc->GetID();
1689 max_width = wxMax(max_width, line.Len());
1694 line = _(
" ChartFile: ");
1695 wxString longline = *(cte.GetpsFullPath());
1696 if (longline.Len() > target_width) {
1697 line += SplitPath(longline,
"/,\\", target_width, 15, &ncr);
1698 max_width = wxMax(max_width, target_width + 4);
1702 max_width = wxMax(max_width, line.Len() + 4);
1708 if (line_count) *line_count = lc;
1710 if (char_width) *char_width = max_width;
1720 wxGenericProgressDialog *pprog) {
1721 m_dir_array = dir_array;
1725 m_chartDirs.Clear();
1726 active_chartTable.clear();
1727 active_chartTable_pathindex.clear();
1729 Update(dir_array,
true, pprog);
1734 m_dbversion = DB_VERSION_CURRENT;
1746 virtual wxDirTraverseResult OnFile(
const wxString &filename)
override {
1747 wxFileName fn(filename);
1748 wxFileName dir(fn.GetPath());
1749 if (fn.GetFullName().Matches(
"poly-*-1.dat") &&
1750 dir.GetFullName().IsSameAs(
"GSHHG",
false)) {
1751 parent_dir = fn.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
1754 return wxDIR_CONTINUE;
1756 virtual wxDirTraverseResult OnDir(
const wxString &dirname)
override {
1758 return wxDIR_CONTINUE;
1760 wxString GetGshhsDir()
const {
return parent_dir; }
1763 wxString parent_dir;
1770wxString findGshhgDirectory(
const wxString &directory) {
1771 wxDir dir(directory);
1772 if (!dir.IsOpened()) {
1773 return wxEmptyString;
1776 dir.Traverse(traverser, wxEmptyString, wxDIR_FILES | wxDIR_DIRS);
1777 return traverser.GetGshhsDir();
1780bool ChartDatabase::UpdateChartDatabaseInplace(ArrayOfCDI &DirArray,
1782 wxGenericProgressDialog *_prog) {
1788 wxLogMessage(
"Starting chart database Update...");
1792 m_gshhg_chart_loc = gWorldMapLocation;
1793 gWorldMapLocation = wxEmptyString;
1795 Update(DirArray, b_force, m_pprog);
1806 wxGenericProgressDialog *pprog) {
1808 m_ticket_vector.clear();
1809 m_full_collision_map.clear();
1810 m_jobsRemaining = 0;
1811 m_deferred_ticket_vector.clear();
1812 SetChartDirArray(dir_array);
1819 for (
unsigned int i = 0; i < active_chartTable.size(); i++) {
1820 auto &cte = GetChartTableEntry(i);
1821 cte.SetValid(
false);
1824 m_chartDirs.Clear();
1826 if (bForce) active_chartTable.clear();
1828 bool lbForce = bForce;
1831 if (s_dbVersion != DB_VERSION_CURRENT) {
1832 active_chartTable.clear();
1834 s_dbVersion = DB_VERSION_CURRENT;
1835 m_dbversion = DB_VERSION_CURRENT;
1840 for (
unsigned int j = 0; j < dir_array.GetCount(); j++) {
1847 if (!androidIsDirWritable(dir_info.fullpath))
continue;
1853 wxString gshhg_dir = findGshhgDirectory(dir_info.fullpath);
1854 if (!gshhg_dir.empty()) {
1859 wxLogMessage(
"Updating GSHHG directory: %s", gshhg_dir.c_str());
1860 gWorldMapLocation = gshhg_dir;
1865 if (dir_info.fullpath.Find(
"OSMSHP") != wxNOT_FOUND) {
1866 if (!wxDir::FindFirst(dir_info.fullpath,
"basemap_*.shp").empty()) {
1867 wxLogMessage(
"Updating OSMSHP directory: %s", dir_info.fullpath);
1868 gWorldShapefileLocation =
1869 dir_info.fullpath + wxFileName::GetPathSeparator();
1874 TraverseDirAndAddCharts(dir_info, pprog, dir_magic, lbForce);
1877 dir_info.magic_number = dir_magic;
1878 dir_array.RemoveAt(j);
1879 dir_array.Insert(dir_info, j);
1881 m_chartDirs.Add(dir_info.fullpath);
1885 if (m_chartDirs.IsEmpty() || !m_jobsRemaining) {
1886 if (!m_deferred_ticket_vector.size()) {
1887 FinalizeChartUpdate();
1892 auto ticket_deferred = m_deferred_ticket_vector.back();
1893 ticket_deferred->m_ticket_type = 1;
1895 m_jobsRemaining = 1;
1896 evt->SetTicket(ticket_deferred);
1897 wxQueueEvent(
this, evt);
1904 m_ticketcount = m_jobsRemaining;
1905 m_nFileProgressQuantum = 1;
1906 m_progress_message = _(
"Processing charts");
1908 m_progress_timer.Start(50, wxTIMER_CONTINUOUS);
1911 if (m_jobsRemaining) {
1912 int workerCount = 4;
1916 if (m_pool.GetWorkerCount() < workerCount) {
1917 int threads_needed = workerCount - m_pool.GetWorkerCount();
1918 for (
int i = 0; i < threads_needed; ++i) {
1932int ChartDatabase::FinddbIndex(wxString PathToFind) {
1933 if (active_chartTable_pathindex.find(PathToFind) !=
1934 active_chartTable_pathindex.end())
1935 return active_chartTable_pathindex[PathToFind];
1944int ChartDatabase::DisableChart(wxString &PathToDisable) {
1945 int index = FinddbIndex(PathToDisable);
1947 auto &entry = GetChartTableEntry(index);
1961int ChartDatabase::TraverseDirAndAddCharts(
ChartDirInfo &dir_info,
1962 wxGenericProgressDialog *pprog,
1963 wxString &dir_magic,
bool bForce) {
1965 wxString dir_path = dir_info.fullpath;
1967 dir_path = wxString(dir_info.fullpath.mb_str(wxConvUTF8));
1970 wxString old_magic = dir_info.magic_number;
1971 wxString new_magic = old_magic;
1972 dir_magic = old_magic;
1976 bool b_skipDetectDirChange =
false;
1977 bool b_dirchange =
false;
1980 if (!wxDir::Exists(dir_path))
return 0;
1986 bool b_cm93 = Check_CM93_Structure(dir_path);
1988 b_skipDetectDirChange =
true;
1994 if (!b_skipDetectDirChange)
1995 b_dirchange = DetectDirChange(dir_path, dir_info.fullpath, old_magic,
1998 if (!bForce && !b_dirchange) {
1999 wxString msg(
" No change detected on directory ");
2000 msg.Append(dir_path);
2006 wxFileName fn_dir(dir_path,
"stuff");
2007 unsigned int dir_path_count = fn_dir.GetDirCount();
2009 if (pprog) pprog->SetTitle(_(
"OpenCPN Chart Scan...."));
2011 int nEntries = active_chartTable.size();
2013 for (
int ic = 0; ic < nEntries; ic++) {
2014 auto &cte = GetChartTableEntry(ic);
2015 wxFileName fn(cte.GetFullSystemPath());
2017 while (fn.GetDirCount() >= dir_path_count) {
2018 if (fn.GetPath() == dir_path) {
2019 auto &cte_a = GetChartTableEntry(ic);
2020 cte_a.SetValid(
true);
2036 dir_magic = new_magic;
2039 for (
auto &cd : m_ChartClassDescriptorArray) {
2040 nAdd += SearchDirAndAddCharts(dir_info.fullpath, cd, pprog);
2046bool ChartDatabase::DetectDirChange(
const wxString &dir_path,
2047 const wxString &prog_label,
2048 const wxString &magic, wxString &new_magic,
2049 wxGenericProgressDialog *pprog) {
2050 if (pprog) pprog->SetTitle(_(
"OpenCPN Directory Scan...."));
2053 long long unsigned int nmagic;
2054 wxULongLong nacc = 0;
2056 magic.ToULongLong(&nmagic, 10);
2059 wxArrayString FileList;
2060 wxDir dir(dir_path);
2061 int n_files = dir.GetAllFiles(dir_path, &FileList);
2067 if (pprog) pprog->Update(0, prog_label);
2071 for (
int ifile = 0; ifile < n_files; ifile++) {
2072 wxFileName file(FileList[ifile]);
2077 wxString fileNameNative = file.GetFullPath();
2078 wxScopedCharBuffer fileNameUTF8 = fileNameNative.ToUTF8();
2079 hash.Update(fileNameUTF8.data(), fileNameUTF8.length());
2082 wxULongLong size = file.GetSize();
2083 wxULongLong fileSize = ((size != wxInvalidSize) ? size : 0);
2084 hash.Update(&fileSize, (
sizeof fileSize));
2087 wxDateTime t = file.GetModificationTime();
2088 wxULongLong fileTime = t.GetTicks();
2089 hash.Update(&fileTime, (
sizeof fileTime));
2093 hash.Receive(&nacc);
2096 new_magic = nacc.ToString();
2099 if (new_magic != magic)
2105bool ChartDatabase::IsChartDirUsed(
const wxString &theDir) {
2106 wxString dir(theDir);
2107 if (dir.Last() ==
'/' || dir.Last() == wxFileName::GetPathSeparator())
2111 for (UINT32 i = 0; i < active_chartTable.size(); i++) {
2112 auto &cte_u = GetChartTableEntry(i);
2113 if (cte_u.GetpsFullPath()->Matches(dir))
return true;
2122bool ChartDatabase::Check_CM93_Structure(wxString dir_name) {
2125 wxRegEx test(
"[0-9]+");
2127 wxDir dirt(dir_name);
2130 if (dirt.IsOpened())
2131 wxLogMessage(
"check_cm93 opened dir OK: " + dir_name);
2133 wxLogMessage(
"check_cm93 NOT OPENED OK: " + dir_name);
2134 wxLogMessage(
"check_cm93 returns false." + dir_name);
2138 bool b_maybe_found_cm93 =
false;
2139 bool b_cont = dirt.GetFirst(&candidate);
2142 if (test.Matches(candidate) && (candidate.Len() == 8)) {
2143 b_maybe_found_cm93 =
true;
2147 b_cont = dirt.GetNext(&candidate);
2150 if (b_maybe_found_cm93) {
2151 wxString dir_next = dir_name;
2153 dir_next += candidate;
2154 if (wxDir::Exists(dir_next)) {
2155 wxDir dir_n(dir_next);
2156 if (dirt.IsOpened()) {
2157 wxString candidate_n;
2159 wxRegEx test_n(
"^[A-Ga-g]");
2160 bool b_probably_found_cm93 =
false;
2161 bool b_cont_n = dir_n.IsOpened() && dir_n.GetFirst(&candidate_n);
2163 if (test_n.Matches(candidate_n) && (candidate_n.Len() == 1)) {
2164 b_probably_found_cm93 =
true;
2167 b_cont_n = dir_n.GetNext(&candidate_n);
2170 if (b_probably_found_cm93)
2175 wxString dir_luk = dir_next;
2177 dir_luk += candidate_n;
2178 if (wxDir::Exists(dir_luk))
return true;
2291int ChartDatabase::SearchDirAndAddCharts(wxString &dir_name_base,
2293 wxGenericProgressDialog *pprog) {
2294 wxString msg(
"Searching directory: ");
2295 msg += dir_name_base;
2297 msg += chart_desc.m_search_mask;
2300 wxString dir_name = dir_name_base;
2303 dir_name = wxString(dir_name_base.mb_str(wxConvUTF8));
2306 if (!wxDir::Exists(dir_name))
return 0;
2308 wxString filespec = chart_desc.m_search_mask.Upper();
2309 wxString lowerFileSpec = chart_desc.m_search_mask.Lower();
2310 wxString filespecXZ = filespec +
".xz";
2311 wxString lowerFileSpecXZ = lowerFileSpec +
".xz";
2315 wxArrayString FileList;
2316 int gaf_flags = wxDIR_DEFAULT;
2323 bool b_found_cm93 =
false;
2324 bool b_cm93 = Check_CM93_Structure(dir_name);
2326 if (filespec !=
"00300000.A")
2329 filespec = dir_name;
2330 b_found_cm93 =
true;
2334 if (!b_found_cm93) {
2335 wxDir dir(dir_name);
2336 dir.GetAllFiles(dir_name, &FileList, filespec, gaf_flags);
2339 if (!FileList.GetCount()) {
2340 wxArrayString afl = androidTraverseDir(dir_name, filespec);
2341 for (wxArrayString::const_iterator item = afl.begin(); item != afl.end();
2343 FileList.Add(*item);
2348 if (filespec != lowerFileSpec) {
2350 wxArrayString lowerFileList;
2351 dir.GetAllFiles(dir_name, &lowerFileList, lowerFileSpec, gaf_flags);
2354 if (!lowerFileList.GetCount()) {
2355 wxArrayString afl = androidTraverseDir(dir_name, lowerFileSpec);
2356 for (wxArrayString::const_iterator item = afl.begin();
2357 item != afl.end(); item++)
2358 lowerFileList.Add(*item);
2362 for (wxArrayString::const_iterator item = lowerFileList.begin();
2363 item != lowerFileList.end(); item++)
2364 FileList.Add(*item);
2370 dir.GetAllFiles(dir_name, &FileList, filespecXZ, gaf_flags);
2371 dir.GetAllFiles(dir_name, &FileList, lowerFileSpecXZ, gaf_flags);
2377 wxString dir_plus = dir_name;
2378 dir_plus += wxFileName::GetPathSeparator();
2379 FileList.Add(dir_plus);
2382 int nFile = FileList.GetCount();
2384 if (!nFile)
return false;
2393 if (pprog) pprog->SetTitle(_(
"OpenCPN Chart Add...."));
2397 ChartCollisionsHashMap collision_map;
2406 std::vector<std::shared_ptr<ChartTableEntryJobTicket>> ticket_vector;
2408 for (
int ifile = 0; ifile < nFile; ifile++) {
2409 wxFileName file(FileList[ifile]);
2410 wxString full_path = file.GetFullPath();
2411 wxString file_name = file.GetFullName();
2412 wxString utf8_path = full_path;
2420 wxFileName fnbase(dir_name_base);
2421 int nDirs = fnbase.GetDirCount();
2423 wxFileName file_target(FileList[ifile]);
2425 for (
int i = 0; i < nDirs + 1;
2427 file_target.RemoveDir(0);
2429 wxString leftover_path = file_target.GetFullPath();
2431 dir_name_base + leftover_path;
2437 if (!file_name.Matches(lowerFileSpec) && !file_name.Matches(filespec) &&
2438 !file_name.Matches(lowerFileSpecXZ) && !file_name.Matches(filespecXZ) &&
2445 bool bAddFinal =
true;
2461 if (b_found_cm93) collision =
false;
2464 pEntry = &active_chartTable[collision_ptr->second];
2465 table_file_name = pEntry->GetFullSystemPath();
2467 bthis_dir_in_dB && full_name.IsSameAs(table_file_name);
2471 if (file_path_is_same) {
2475 time_t t_oldFile = pEntry->GetFileTime();
2476 time_t t_newFile = file.GetModificationTime().GetTicks();
2478 if (t_newFile <= t_oldFile) {
2479 file_time_is_same =
true;
2481 pEntry->SetValid(
true);
2484 pEntry->SetValid(
false);
2491 bool collision_found =
false;
2492 ChartCollisionsHashMap::iterator it;
2493 for (it = collision_map.begin(); it != collision_map.end(); ++it) {
2494 if (it->first.IsSameAs(file_name)) {
2498 collision_found =
true;
2503 if (!collision_found) {
2505 auto ticket = std::make_shared<ChartTableEntryJobTicket>();
2506 ticket->m_ChartPath = full_path;
2507 ticket->m_ChartPathUTF8 = full_path;
2508 ticket->chart_desc = chart_desc;
2510 ticket_vector.push_back(ticket);
2511 collision_map[file_name] = ifile;
2518 bool is_kap =
false;
2519 if (chart_desc.m_descriptor_type == PLUGIN_DESCRIPTOR || is_kap) {
2525 if (!provider_plugin118 || is_kap) {
2529 for (
auto &ticket : ticket_vector) {
2530 ticket->b_thread_safe =
false;
2531 ticket->m_provider_type = 1;
2532 ticket->provider_class_name = chart_desc.m_class_name;
2533 m_deferred_ticket_vector.push_back(ticket);
2542 for (
auto &ticket : ticket_vector) {
2543 m_pool.Push(ticket);
2551bool ChartDatabase::AddChart(wxString &chartfilename,
2553 wxGenericProgressDialog *pprog,
int isearch,
2554 bool bthis_dir_in_dB) {
2556 wxFileName file(chartfilename);
2557 wxString full_name = file.GetFullPath();
2558 wxString file_name = file.GetFullName();
2568 pprog->Update(wxMin((m_pdifile * 100) / m_pdnFile, 100), full_name);
2571 bool bAddFinal =
true;
2573 wxString msg_fn(full_name);
2574 msg_fn.Replace(
"%",
"%%");
2576 pnewChart = CreateChartTableEntry(full_name, full_name, chart_desc);
2579 wxLogMessage(wxString::Format(
2580 " CreateChartTableEntry() failed for file: %s", msg_fn.c_str()));
2585 int nEntry = active_chartTable.size();
2586 for (
int i = 0; i < nEntry; i++) {
2587 auto &cte_a = GetChartTableEntry(i);
2588 wxString *ptable_file_name = cte_a.GetpsFullPath();
2592 if (bthis_dir_in_dB && full_name.IsSameAs(*ptable_file_name)) {
2596 auto &cte_search = GetChartTableEntry(isearch);
2597 time_t t_oldFile = cte_search.GetFileTime();
2598 time_t t_newFile = file.GetModificationTime().GetTicks();
2600 if (t_newFile <= t_oldFile) {
2602 cte_search.SetValid(
true);
2605 cte_search.SetValid(
false);
2607 wxString::Format(
" Replacing older chart file of same path: %s",
2617 wxFileName table_file(*ptable_file_name);
2619 if (table_file.GetFullName() == file_name) {
2624 if (pnewChart->IsEarlierThan(active_chartTable[isearch])) {
2626 if (table_file.IsFileReadable()) {
2627 active_chartTable[isearch].SetValid(
true);
2629 wxLogMessage(wxString::Format(
2630 " Retaining newer chart file of same name: %s",
2633 }
else if (pnewChart->IsEqualTo(active_chartTable[isearch])) {
2643 active_chartTable[isearch].SetValid(
false);
2646 wxString::Format(
" Replacing older chart file of same name: %s",
2656 if (nEntry == isearch) isearch = 0;
2661 if (0 == b_add_msg) {
2663 wxString::Format(
" Adding chart file: %s", msg_fn.c_str()));
2665 std::shared_ptr<ChartTableEntry> sharedPtr(pnewChart);
2666 active_chartTable.push_back(sharedPtr);
2675 m_nentries = active_chartTable.size();
2680bool ChartDatabase::AddSingleChart(wxString &ChartFullPath,
2681 bool b_force_full_search) {
2683 wxFileName fn(ChartFullPath);
2684 wxString ext = fn.GetExt();
2686 wxString ext_upper = ext.MakeUpper();
2687 wxString ext_lower = ext.MakeLower();
2688 wxString dir_name = fn.GetPath();
2694 for (
auto &cd : m_ChartClassDescriptorArray) {
2695 if (cd.m_descriptor_type == PLUGIN_DESCRIPTOR) {
2696 if (cd.m_search_mask == ext_upper) {
2700 if (cd.m_search_mask == ext_lower) {
2709 bool b_recurse =
true;
2710 if (!b_force_full_search) b_recurse = IsChartDirUsed(dir_name);
2712 bool rv = AddChart(ChartFullPath, desc, NULL, 0, b_recurse);
2716 for (
unsigned int i = 0; i < active_chartTable.size(); i++) {
2717 auto &cte_r = GetChartTableEntry(i);
2718 if (!cte_r.GetbValid()) {
2726 for (
unsigned int i = 0; i < active_chartTable.size(); i++) {
2727 auto &cte_ef = GetChartTableEntry(i);
2728 cte_ef.SetEntryOffset(i);
2733 DetectDirChange(dir_name,
"",
"", new_magic, 0);
2736 bool bcfound =
false;
2737 ArrayOfCDI NewChartDirArray;
2739 ArrayOfCDI ChartDirArray = GetChartDirArray();
2740 for (
unsigned int i = 0; i < ChartDirArray.GetCount(); i++) {
2746 if (newcdi.fullpath == dir_name) {
2747 newcdi.magic_number = new_magic;
2751 NewChartDirArray.Add(newcdi);
2756 cdi.fullpath = dir_name;
2757 cdi.magic_number = new_magic;
2758 NewChartDirArray.Add(cdi);
2762 SetChartDirArray(NewChartDirArray);
2765 m_chartDirs.Clear();
2767 for (
unsigned int i = 0; i < GetChartDirArray().GetCount(); i++) {
2769 m_chartDirs.Add(cdi.fullpath);
2772 m_nentries = active_chartTable.size();
2774 ChartData->UpdateChartDatabaseInplace(NewChartDirArray,
false,
nullptr);
2778bool ChartDatabase::RemoveSingleChart(wxString &ChartFullPath) {
2782 for (
unsigned int i = 0; i < active_chartTable.size(); i++) {
2783 auto &cte = GetChartTableEntry(i);
2784 if (ChartFullPath.IsSameAs(cte.GetFullSystemPath())) {
2786 std::swap(active_chartTable[i], active_chartTable.back());
2787 active_chartTable.pop_back();
2793 for (
unsigned int i = 0; i < active_chartTable.size(); i++) {
2794 auto &pcte = GetChartTableEntry(i);
2795 pcte.SetEntryOffset(i);
2799 wxFileName fn(ChartFullPath);
2800 wxString fd = fn.GetPath();
2801 if (!IsChartDirUsed(fd)) {
2803 ArrayOfCDI NewChartDirArray;
2805 ArrayOfCDI ChartDirArray = GetChartDirArray();
2806 for (
unsigned int i = 0; i < ChartDirArray.GetCount(); i++) {
2811 if (newcdi.fullpath != fd) NewChartDirArray.Add(newcdi);
2814 SetChartDirArray(NewChartDirArray);
2818 m_chartDirs.Clear();
2819 for (
unsigned int i = 0; i < GetChartDirArray().GetCount(); i++) {
2821 m_chartDirs.Add(cdi.fullpath);
2824 m_nentries = active_chartTable.size();
2825 ChartData->UpdateChartDatabaseInplace(m_dir_array,
false,
nullptr);
2834ChartBase *ChartDatabase::GetChart(
const wxChar *theFilePath,
2845 const wxString &filePath, wxString &utf8Path,
2847 wxString msg_fn(filePath);
2848 msg_fn.Replace(
"%",
"%%");
2849 wxLogMessage(wxString::Format(
"Loading chart data for %s", msg_fn.c_str()));
2852 qDebug() <<
" CCTE: " << filePath.ToStdString().c_str();
2854 ChartBase *pch = GetChart(filePath, chart_desc);
2857 wxString::Format(
" ...creation failed for %s", msg_fn.c_str()));
2861 InitReturn rc = pch->Init(filePath, HEADER_ONLY);
2862 if (rc != INIT_OK) {
2865 wxString::Format(
" ...initialization failed for %s", msg_fn.c_str()));
2870 ret_val->SetValid(
true);
2877bool ChartDatabase::GetCentroidOfLargestScaleChart(
double *clat,
double *clon,
2878 ChartFamilyEnum family) {
2880 int cur_max_scale = 0;
2882 int nEntry = active_chartTable.size();
2884 for (
int i = 0; i < nEntry; i++) {
2885 auto &cte_fam = GetChartTableEntry(i);
2886 if (GetChartFamily(cte_fam.GetChartType()) == family) {
2887 if (cte_fam.GetScale() > cur_max_scale) {
2888 cur_max_scale = cte_fam.GetScale();
2894 if (cur_max_i == -1)
2897 auto &cte_sel = GetChartTableEntry(cur_max_i);
2898 *clat = (cte_sel.GetLatMax() + cte_sel.GetLatMin()) / 2.;
2899 *clon = (cte_sel.GetLonMin() + cte_sel.GetLonMax()) / 2.;
2907int ChartDatabase::GetDBChartProj(
int dbIndex) {
2908 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2909 auto &cte = GetChartTableEntry(dbIndex);
2910 return cte.GetChartProjectionType();
2912 return PROJECTION_UNKNOWN;
2918int ChartDatabase::GetDBChartFamily(
int dbIndex) {
2919 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2920 auto &cte = GetChartTableEntry(dbIndex);
2921 return cte.GetChartFamily();
2923 return CHART_FAMILY_UNKNOWN;
2929wxString ChartDatabase::GetDBChartFileName(
int dbIndex) {
2930 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2931 auto &cte = GetChartTableEntry(dbIndex);
2932 return wxString(cte.GetFullSystemPath());
2940int ChartDatabase::GetDBChartType(
int dbIndex) {
2941 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2942 auto &cte = GetChartTableEntry(dbIndex);
2943 return cte.GetChartType();
2951float ChartDatabase::GetDBChartSkew(
int dbIndex) {
2952 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2953 auto &cte = GetChartTableEntry(dbIndex);
2954 return cte.GetChartSkew();
2962int ChartDatabase::GetDBChartScale(
int dbIndex) {
2963 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2964 auto &cte = GetChartTableEntry(dbIndex);
2965 return cte.GetScale();
2973bool ChartDatabase::GetDBBoundingBox(
int dbIndex, LLBBox &box) {
2974 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2975 auto &entry = GetChartTableEntry(dbIndex);
2976 box.Set(entry.GetLatMin(), entry.GetLonMin(), entry.GetLatMax(),
2983const LLBBox &ChartDatabase::GetDBBoundingBox(
int dbIndex) {
2984 if ((bValid) && (dbIndex >= 0)) {
2985 auto &entry = GetChartTableEntry(dbIndex);
2986 return entry.GetBBox();
2988 return m_dummy_bbox;
2995int ChartDatabase::GetDBPlyPoint(
int dbIndex,
int plyindex,
float *lat,
2997 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
2999 if (entry.GetnPlyEntries()) {
3000 float *fp = entry.GetpPlyTable();
3002 if (lat) *lat = *fp;
3004 if (lon) *lon = *fp;
3006 return entry.GetnPlyEntries();
3014int ChartDatabase::GetDBAuxPlyPoint(
int dbIndex,
int plyindex,
int ply,
3015 float *lat,
float *lon) {
3016 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
3018 if (entry.GetnAuxPlyEntries()) {
3019 float *fp = entry.GetpAuxPlyTableEntry(ply);
3022 if (lat) *lat = *fp;
3024 if (lon) *lon = *fp;
3027 return entry.GetAuxCntTableEntry(ply);
3032int ChartDatabase::GetnAuxPlyEntries(
int dbIndex) {
3033 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
3035 return entry.GetnAuxPlyEntries();
3043std::vector<float> ChartDatabase::GetReducedPlyPoints(
int dbIndex) {
3044 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
3046 return cte.GetReducedPlyPoints();
3049 std::vector<float> dummy;
3056std::vector<float> ChartDatabase::GetReducedAuxPlyPoints(
int dbIndex,
3058 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
3059 auto &cte = GetChartTableEntry(dbIndex);
3060 return cte.GetReducedAuxPlyPoints(iTable);
3063 std::vector<float> dummy;
3067bool ChartDatabase::IsChartAvailable(
int dbIndex) {
3068 if ((bValid) && (dbIndex >= 0) && (dbIndex < (
int)active_chartTable.size())) {
3069 auto &cte = GetChartTableEntry(dbIndex);
3072 if (cte.GetChartType() != CHART_TYPE_PLUGIN)
return true;
3074 wxString *path = cte.GetpsFullPath();
3075 wxFileName fn(*path);
3076 wxString ext = fn.GetExt();
3078 wxString ext_upper = ext.MakeUpper();
3079 wxString ext_lower = ext.MakeLower();
3084 for (
auto &cd : m_ChartClassDescriptorArray) {
3085 if (cd.m_descriptor_type == PLUGIN_DESCRIPTOR) {
3086 wxString search_mask = cd.m_search_mask;
3088 if (search_mask == ext_upper) {
3091 if (search_mask == ext_lower) {
3094 if (path->Matches(search_mask)) {
3104void ChartDatabase::ApplyGroupArray(ChartGroupArray *pGroupArray) {
3105 wxString separator(wxFileName::GetPathSeparator());
3107 for (
unsigned int ic = 0; ic < active_chartTable.size(); ic++) {
3108 auto &cte = GetChartTableEntry(ic);
3110 cte.ClearGroupArray();
3112 wxString *chart_full_path = cte.GetpsFullPath();
3114 for (
unsigned int igroup = 0; igroup < pGroupArray->GetCount(); igroup++) {
3115 ChartGroup *pGroup = pGroupArray->Item(igroup);
3116 for (
const auto &elem : pGroup->m_element_array) {
3117 wxString element_root = elem.m_element_name;
3123 if (!chart_full_path->IsSameAs(element_root))
3124 element_root.Append(
3126 if (chart_full_path->StartsWith(element_root)) {
3128 for (
unsigned int k = 0; k < elem.m_missing_name_array.size(); k++) {
3129 const wxString &missing_item = elem.m_missing_name_array[k];
3130 if (chart_full_path->StartsWith(missing_item)) {
3131 if (chart_full_path->IsSameAs(
3137 if (wxDir::Exists(missing_item))
3146 if (b_add) cte.AddIntToGroupArray(igroup + 1);
General chart base definitions.
ChartDB * ChartData
Global instance.
Define threaded chart database classes.
ChartGroupArray * g_pGroupArray
Global instance.
Basic chart info storage.
ChartGroupArray * g_pGroupArray
Global instance.
Base class for all chart types.
Manages a database of charts, including reading, writing, and querying chart information.
bool Create(ArrayOfCDI &dir_array, wxGenericProgressDialog *pprog)
Creates a new chart database from a list of directories.
bool Update(ArrayOfCDI &dir_array, bool bForce, wxGenericProgressDialog *pprog)
Updates the chart database.
Represents a user-defined collection of logically related charts.
Wrapper class for plugin-based charts.
void Notify() override
Notify all listeners, no data supplied.
Class for computing hash of arbitrary length.
EventVar options_on_finalize_chartdbs
Notified when chartdbs async operations complete, to finalize settings.
EventVar on_finalize_chartdbs
Notified when chartdbs async operations complete, to reload charts.
Base class for OpenCPN plugins.
Hash of arbitrary length.
Misc GUI event vars, a singleton.
PlugInManager * g_pi_manager
Global instance.
PlugInManager and helper classes – Mostly gui parts (dialogs) and plugin API stuff.
ShapeBaseChartSet gShapeBasemap
global instance
Represents an entry in the chart table, containing information about a single chart.