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.
Class for computing hash of arbitrary length.
obs::EventVar on_finalize_chartdbs
Notified when chartdbs async operations complete, to reload charts.
obs::EventVar options_on_finalize_chartdbs
Notified when chartdbs async operations complete, to finalize settings.
void Notify() override
Notify all listeners, no data supplied.
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.