37#include <wx/progdlg.h>
39#include <wx/stopwatch.h>
40#include <wx/tokenzr.h>
56#include "user_colors.h"
59#include "androidUTIL.h"
66extern ColorScheme GetColorScheme();
69extern s52plib *ps52plib;
75bool G_FloatPtInPolygon(
MyFlPoint *rgpts,
int wnumpts,
float x,
float y);
77static bool IsSingleChart(
ChartBase *chart) {
78 if (chart ==
nullptr)
return false;
83 if (cc && cc->m_singleChart == chart) {
93int ChartStack::GetCurrentEntrydbIndex() {
94 if (nEntry && (CurrentStackEntry >= 0) )
95 return DBIndex[CurrentStackEntry];
100void ChartStack::SetCurrentEntryFromdbIndex(
int current_db_index) {
101 for (
int i = 0; i < nEntry; i++) {
102 if (current_db_index == DBIndex[i]) CurrentStackEntry = i;
106int ChartStack::GetDBIndex(
int stack_index) {
107 if ((stack_index >= 0) && (stack_index < nEntry) && (stack_index < MAXSTACK))
108 return DBIndex[stack_index];
113void ChartStack::SetDBIndex(
int stack_index,
int db_index) {
114 if ((stack_index >= 0) && (stack_index < nEntry) && (stack_index < MAXSTACK))
115 DBIndex[stack_index] = db_index;
118bool ChartStack::DoesStackContaindbIndex(
int db_index) {
119 for (
int i = 0; i < nEntry; i++) {
120 if (db_index == DBIndex[i])
return true;
126void ChartStack::AddChart(
int db_add) {
131 int db_index = db_add;
138 SetDBIndex(j - 1, db_index);
148 for (
int id = 0;
id < j - 1;
id++) {
149 if (GetDBIndex(
id) != -1) {
150 auto &pm =
ChartData->GetChartTableEntry(GetDBIndex(
id));
152 for (
int jd =
id + 1; jd < j; jd++) {
153 if (GetDBIndex(jd) != -1) {
154 auto &pn =
ChartData->GetChartTableEntry(GetDBIndex(jd));
155 if (pm.GetFileTime() && pn.GetFileTime()) {
156 if (labs(pm.GetFileTime() - pn.GetFileTime()) <
158 if (pn.GetpFileName()->IsSameAs(*(pm.GetpFileName())))
169 if (GetDBIndex(
id) == -1) {
172 int db_index = GetDBIndex(jd);
173 SetDBIndex(jd - 1, db_index);
190 for (
int i = 0; i < j - 1; i++) {
193 ChartData->GetChartTableEntry(GetDBIndex(i + 1));
195 if (n.GetScale() < m.GetScale()) {
197 SetDBIndex(i, GetDBIndex(i + 1));
198 SetDBIndex(i + 1, ti);
210 pChartCache =
new wxArrayPtrVoid;
219 if (g_memCacheLimit) {
221 msg.Printf(
"ChartDB Cache policy: Application target is %d MBytes",
222 g_memCacheLimit / 1024);
226 msg.Printf(
"ChartDB Cache policy: Max open chart limit is %d.",
231 m_checkGroupIndex[0] = m_checkGroupIndex[1] = -1;
232 m_checkedTileOnly[0] = m_checkedTileOnly[1] =
false;
243 ArrayOfCDI &dir_array_check) {
244 m_dir_array = dir_array_check;
245 return ChartDatabase::Read(filename);
250void ChartDB::DeleteCacheEntry(
CacheEntry *pce,
bool bDelTexture,
251 const wxString &msg) {
254 if (msg != wxEmptyString) {
255 wxLogMessage(
"%s%s", msg.c_str(), ch->GetFullPath().c_str());
269 pChartCache->Remove(pce);
274void ChartDB::DeleteCacheEntry(
int i,
bool bDelTexture,
const wxString &msg) {
276 if (pce) DeleteCacheEntry(pce, bDelTexture, msg);
279void ChartDB::PurgeCache() {
283 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
284 unsigned int nCache = pChartCache->GetCount();
285 for (
unsigned int i = 0; i < nCache; i++) {
286 DeleteCacheEntry(0,
true);
288 pChartCache->Clear();
290 m_cache_mutex.Unlock();
294void ChartDB::PurgeCachePlugins() {
296 wxLogMessage(
"Chart cache PlugIn purge");
298 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
299 unsigned int nCache = pChartCache->GetCount();
305 if (CHART_TYPE_PLUGIN == Ch->GetChartType()) {
306 DeleteCacheEntry(pce,
true);
308 nCache = pChartCache->GetCount();
315 m_cache_mutex.Unlock();
319void ChartDB::ClearCacheInUseFlags() {
320 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
321 unsigned int nCache = pChartCache->GetCount();
322 for (
unsigned int i = 0; i < nCache; i++) {
324 pce->b_in_use =
false;
326 m_cache_mutex.Unlock();
333void ChartDB::PurgeCacheUnusedCharts(
double factor) {
335 if (g_memCacheLimit) {
336 if (wxMUTEX_NO_ERROR == m_cache_mutex.TryLock()) {
340 int mem_limit = g_memCacheLimit * factor;
342 int nl = pChartCache->GetCount();
344 wxString msg(
"Purging unused chart from cache: ");
346 while ((mem_used > mem_limit) && (nl > 0)) {
347 if (pChartCache->GetCount() < 2) {
352 CacheEntry *pce = FindOldestDeleteCandidate(
false);
355 DeleteCacheEntry(pce,
false , msg);
366 m_cache_mutex.Unlock();
370 else if (g_nCacheLimit) {
371 if (wxMUTEX_NO_ERROR == m_cache_mutex.TryLock()) {
373 double fac10 = factor * 10;
374 int chart_limit = g_nCacheLimit * fac10 / 10;
376 int nl = pChartCache->GetCount();
378 wxString msg(
"Purging unused chart from cache: ");
379 while ((nl > chart_limit) && (nl > 0)) {
380 if (pChartCache->GetCount() < 2) {
385 CacheEntry *pce = FindOldestDeleteCandidate(
false);
388 DeleteCacheEntry(pce,
false , msg);
393 nl = pChartCache->GetCount();
396 m_cache_mutex.Unlock();
405ChartBase *ChartDB::GetChart(
const wxChar *theFilePath,
407 wxFileName fn(theFilePath);
409 if (!fn.FileExists()) {
411 if (!wxDir::Exists(theFilePath)) {
412 wxLogMessage(
" ...file does not exist: %s", theFilePath);
418 wxString chartExt = fn.GetExt().Upper();
420 if (chartExt ==
"XZ") {
421 wxString npath = theFilePath;
422 npath = npath.Left(npath.length() - 3);
423 wxFileName fn(npath);
424 chartExt = fn.GetExt().Upper();
427 if (chartExt ==
"KAP") {
429 }
else if (chartExt ==
"GEO") {
431 }
else if (chartExt ==
"MBTILES") {
433 }
else if (chartExt ==
"000" || chartExt ==
"S57") {
436 }
else if (chart_desc.m_descriptor_type == PLUGIN_DESCRIPTOR) {
443 wxRegEx rxName(
"[0-9]+");
444 wxRegEx rxExt(
"[A-G]");
445 if (rxName.Matches(fn.GetName()) && rxExt.Matches(chartExt))
456bool ChartDB::IsChartDirectoryExcluded(
const std::string &chart_file) {
466int ChartDB::BuildChartStack(
ChartStack *cstk,
float lat,
float lon,
int db_add,
468 BuildChartStack(cstk, lat, lon, groupIndex);
470 if (db_add >= 0) cstk->AddChart(db_add);
475int ChartDB::BuildChartStack(
ChartStack *cstk,
float lat,
float lon,
480 if (!IsValid())
return 0;
484 int nEntry = GetChartTableEntries();
486 for (
int db_index = 0; db_index < nEntry; db_index++) {
490 if (IsChartDirectoryExcluded(cte.GetFullPath()))
continue;
493 bool b_group_add =
false;
494 if (groupIndex > 0) {
495 const int ng = cte.GetGroupArray().size();
496 for (
int ig = 0; ig < ng; ig++) {
497 if (groupIndex == cte.GetGroupArray()[ig]) {
505 bool b_writable_add =
true;
509 wxFileName fn(cte.GetFullSystemPath());
510 if (!androidIsDirWritable(fn.GetPath())) b_writable_add =
false;
513 bool b_pos_add =
false;
514 if (b_group_add && b_writable_add) {
518 if (cte.GetChartType() == CHART_TYPE_PLUGIN) {
523 if (CheckPositionWithinChart(db_index, lat, lon) && (j < MAXSTACK))
527 else if ((cte.GetLonMax() > 180.) && (cte.GetLonMin() < 180.)) {
528 if (CheckPositionWithinChart(db_index, lat, lon + 360.) &&
533 else if ((cte.GetLonMax() > 180.) && (cte.GetLonMin() > 180.)) {
534 if (CheckPositionWithinChart(db_index, lat, lon + 360.) &&
540 bool b_available =
true;
542 if (b_group_add && b_pos_add && (cte.GetChartType() == CHART_TYPE_PLUGIN)) {
544 if (!IsChartAvailable(db_index)) {
545 pcte->SetAvailable(
false);
548 pcte->SetAvailable(
true);
553 if (b_group_add && b_pos_add && b_available) {
556 cstk->SetDBIndex(j - 1, db_index);
572 for (
int id = 0;
id < j - 1;
id++) {
573 if (cstk->GetDBIndex(
id) != -1) {
574 const ChartTableEntry &ctem = GetChartTableEntry(cstk->GetDBIndex(
id));
576 for (
int jd =
id + 1; jd < j; jd++) {
577 if (cstk->GetDBIndex(jd) != -1) {
579 GetChartTableEntry(cstk->GetDBIndex(jd));
580 bool bsameTime =
false;
581 if (ctem.GetFileTime() && cten.GetFileTime()) {
582 if (labs(ctem.GetFileTime() - cten.GetFileTime()) < 60)
585 if (ctem.GetChartEditionDate() == cten.GetChartEditionDate())
589 if (cten.GetpFileName()->IsSameAs(*(ctem.GetpFileName())))
590 cstk->SetDBIndex(jd, -1);
599 if (cstk->GetDBIndex(
id) == -1) {
602 int db_index = cstk->GetDBIndex(jd);
603 cstk->SetDBIndex(jd - 1, db_index);
620 for (i = 0; i < j - 1; i++) {
622 const ChartTableEntry &n = GetChartTableEntry(cstk->GetDBIndex(i + 1));
624 if (n.GetScale() < m.GetScale()) {
625 ti = cstk->GetDBIndex(i);
626 cstk->SetDBIndex(i, cstk->GetDBIndex(i + 1));
627 cstk->SetDBIndex(i + 1, ti);
633 cstk->b_valid =
true;
638bool ChartDB::IsChartInGroup(
const int db_index,
const int group) {
642 bool b_in_group =
false;
644 for (
unsigned int ig = 0; ig < pt->GetGroupArray().size(); ig++) {
645 if (group == pt->GetGroupArray()[ig]) {
656bool ChartDB::IsENCInGroup(
const int groupIndex) {
660 for (
int db_index = 0; db_index < GetChartTableEntries(); db_index++) {
664 bool b_group_add =
false;
665 if (groupIndex > 0) {
666 const int ng = cte.GetGroupArray().size();
667 for (
int ig = 0; ig < ng; ig++) {
668 if (groupIndex == cte.GetGroupArray()[ig]) {
677 if (cte.GetChartFamily() == CHART_FAMILY_VECTOR) {
687bool ChartDB::IsNonMBTileInGroup(
const int groupIndex) {
692 for (
int db_index = 0; db_index < GetChartTableEntries(); db_index++) {
696 bool b_group_add =
false;
697 if (groupIndex > 0) {
698 const int ng = cte.GetGroupArray().size();
699 for (
int ig = 0; ig < ng; ig++) {
700 if (groupIndex == cte.GetGroupArray()[ig]) {
709 if (cte.GetChartType() != CHART_TYPE_MBTILES) {
722bool ChartDB::CheckPositionWithinChart(
int index,
float lat,
float lon) {
727 if ((lat <= pt->GetLatMax()) && (lat >= pt->GetLatMin()) &&
728 (lon >= pt->GetLonMin()) && (lon <= pt->GetLonMax())) {
731 bool bInside = G_FloatPtInPolygon((
MyFlPoint *)pt->GetpPlyTable(),
732 pt->GetnPlyEntries(), lon, lat);
735 if (pt->GetnAuxPlyEntries()) {
736 for (
int k = 0; k < pt->GetnAuxPlyEntries(); k++) {
738 G_FloatPtInPolygon((
MyFlPoint *)pt->GetpAuxPlyTableEntry(k),
739 pt->GetAuxCntTableEntry(k), lon, lat);
740 if (bAuxInside)
return true;
756 if ((pa == 0) || (pb == 0))
return false;
757 if ((!pa->b_valid) || (!pb->b_valid))
return false;
758 if (pa->nEntry != pb->nEntry)
return false;
760 for (
int i = 0; i < pa->nEntry; i++) {
761 if (pa->GetDBIndex(i) != pb->GetDBIndex(i))
return false;
771 if ((pa == 0) || (pb == 0))
return false;
772 pa->nEntry = pb->nEntry;
774 for (
int i = 0; i < pa->nEntry; i++) pa->SetDBIndex(i, pb->GetDBIndex(i));
776 pa->CurrentStackEntry = pb->CurrentStackEntry;
778 pa->b_valid = pb->b_valid;
783wxString ChartDB::GetFullPath(
ChartStack *ps,
int stackindex) {
784 int dbIndex = ps->GetDBIndex(stackindex);
785 return GetChartTableEntry(dbIndex).GetFullSystemPath();
792int ChartDB::GetCSPlyPoint(
ChartStack *ps,
int stackindex,
int plyindex,
793 float *lat,
float *lon) {
794 int dbIndex = ps->GetDBIndex(stackindex);
795 wxASSERT(dbIndex >= 0);
798 if (entry.GetnPlyEntries()) {
799 float *fp = entry.GetpPlyTable();
806 return entry.GetnPlyEntries();
812int ChartDB::GetStackChartScale(
ChartStack *ps,
int stackindex,
char *buf,
814 int dbindex = ps->GetDBIndex(stackindex);
815 wxASSERT(dbindex >= 0);
818 int sc = entry.GetScale();
819 if (buf) sprintf(buf,
"%d", sc);
827int ChartDB::GetStackEntry(
ChartStack *ps, wxString fp) {
828 for (
int i = 0; i < ps->nEntry; i++) {
830 if (fp.IsSameAs(entry.GetFullSystemPath()))
return i;
839ChartTypeEnum ChartDB::GetCSChartType(
ChartStack *ps,
int stackindex) {
841 int dbindex = ps->GetDBIndex(stackindex);
843 return (ChartTypeEnum)GetChartTableEntry(dbindex).GetChartType();
845 return CHART_TYPE_UNKNOWN;
848ChartFamilyEnum ChartDB::GetCSChartFamily(
ChartStack *ps,
int stackindex) {
850 int dbindex = ps->GetDBIndex(stackindex);
854 ChartTypeEnum type = (ChartTypeEnum)entry.GetChartType();
857 return CHART_FAMILY_RASTER;
859 return CHART_FAMILY_RASTER;
861 return CHART_FAMILY_VECTOR;
862 case CHART_TYPE_CM93:
863 return CHART_FAMILY_VECTOR;
864 case CHART_TYPE_CM93COMP:
865 return CHART_FAMILY_VECTOR;
866 case CHART_TYPE_DUMMY:
867 return CHART_FAMILY_RASTER;
869 return CHART_FAMILY_UNKNOWN;
873 return CHART_FAMILY_UNKNOWN;
876std::vector<int> ChartDB::GetCSArray(
ChartStack *ps) {
877 std::vector<int> ret;
880 ret.reserve(ps->nEntry);
881 for (
int i = 0; i < ps->nEntry; i++) {
882 ret.push_back(ps->GetDBIndex(i));
889bool ChartDB::IsChartInCache(
int dbindex) {
890 bool bInCache =
false;
893 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
894 unsigned int nCache = pChartCache->GetCount();
895 for (
unsigned int i = 0; i < nCache; i++) {
897 if (pce->dbIndex == dbindex) {
898 if (pce->pChart != 0 && ((
ChartBase *)pce->pChart)->IsReadyToRender())
903 m_cache_mutex.Unlock();
909bool ChartDB::IsChartInCache(wxString path) {
910 bool bInCache =
false;
911 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
913 unsigned int nCache = pChartCache->GetCount();
914 for (
unsigned int i = 0; i < nCache; i++) {
916 if (pce->FullPath == path) {
917 if (pce->pChart != 0 && ((
ChartBase *)pce->pChart)->IsReadyToRender())
923 m_cache_mutex.Unlock();
928bool ChartDB::IsChartLocked(
int index) {
929 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
930 unsigned int nCache = pChartCache->GetCount();
931 for (
unsigned int i = 0; i < nCache; i++) {
933 if (pce->dbIndex == index) {
934 bool ret = pce->n_lock > 0;
935 m_cache_mutex.Unlock();
939 m_cache_mutex.Unlock();
945bool ChartDB::LockCacheChart(
int index) {
948 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
949 unsigned int nCache = pChartCache->GetCount();
950 for (
unsigned int i = 0; i < nCache; i++) {
952 if (pce->dbIndex == index) {
958 m_cache_mutex.Unlock();
963void ChartDB::UnLockCacheChart(
int index) {
965 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
966 unsigned int nCache = pChartCache->GetCount();
967 for (
unsigned int i = 0; i < nCache; i++) {
969 if (pce->dbIndex == index) {
970 if (pce->n_lock > 0) pce->n_lock--;
974 m_cache_mutex.Unlock();
978void ChartDB::UnLockAllCacheCharts() {
980 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
981 unsigned int nCache = pChartCache->GetCount();
982 for (
unsigned int i = 0; i < nCache; i++) {
984 if (pce->n_lock > 0) pce->n_lock--;
986 m_cache_mutex.Unlock();
993ChartBase *ChartDB::OpenChartFromDB(
int index, ChartInitFlag init_flag) {
994 return OpenChartUsingCache(index, init_flag);
997ChartBase *ChartDB::OpenChartFromDB(wxString chart_path,
998 ChartInitFlag init_flag) {
999 int dbii = FinddbIndex(chart_path);
1000 return OpenChartUsingCache(dbii, init_flag);
1004 ChartInitFlag init_flag) {
1005 return OpenChartUsingCache(pStack->GetDBIndex(StackEntry), init_flag);
1008ChartBase *ChartDB::OpenChartFromDBAndLock(
int index, ChartInitFlag init_flag,
1010 wxCriticalSectionLocker locker(m_critSect);
1011 ChartBase *pret = OpenChartUsingCache(index, init_flag);
1012 if (lock && pret) LockCacheChart(index);
1016ChartBase *ChartDB::OpenChartFromDBAndLock(wxString chart_path,
1017 ChartInitFlag init_flag) {
1018 int dbii = FinddbIndex(chart_path);
1019 return OpenChartFromDBAndLock(dbii, init_flag);
1022CacheEntry *ChartDB::FindOldestDeleteCandidate(
bool blog) {
1025 unsigned int nCache = pChartCache->GetCount();
1027 if (blog) wxLogMessage(
"Searching chart cache for oldest entry");
1028 int LRUTime = m_ticks;
1030 for (
unsigned int i = 0; i < nCache; i++) {
1032 if (pce->RecentTime < LRUTime && !pce->n_lock) {
1033 if (!IsSingleChart((
ChartBase *)(pce->pChart))) {
1036 if (pChart && pChart->GetChartType() == CHART_TYPE_MBTILES) {
1037 wxFileName fn(pChart->GetFullPath());
1038 if (fn.GetPath().Lower().Contains(
"basemap"))
continue;
1040 LRUTime = pce->RecentTime;
1045 int dt = m_ticks - LRUTime;
1050 if (!pce->n_lock && !IsSingleChart(pDeleteCandidate)) {
1052 wxLogMessage(
"Oldest unlocked cache index is %d, delta t is %d",
1057 wxLogMessage(
"All chart in cache locked, size: %d", nCache);
1063ChartBase *ChartDB::OpenChartUsingCache(
int dbindex, ChartInitFlag init_flag) {
1064 if ((dbindex < 0) || (dbindex > GetChartTableEntries() - 1))
return NULL;
1069 wxString ChartFullPath = cte.GetFullSystemPath();
1070 ChartTypeEnum chart_type = (ChartTypeEnum)cte.GetChartType();
1071 ChartFamilyEnum chart_family = (ChartFamilyEnum)cte.GetChartFamily();
1074 msg1.Printf(
"OpenChartUsingCache: type %d ", chart_type);
1077 if (cte.GetLatMax() > 90.0)
1084 bool bInCache =
false;
1088 wxMutexLocker lock(m_cache_mutex);
1090 unsigned int nCache = pChartCache->GetCount();
1092 for (
unsigned int i = 0; i < nCache; i++) {
1094 if (pce->FullPath == ChartFullPath) {
1103 msg.Printf(
"OpenChartUsingCache, IN cache: cache size: %d\n",
1104 (
int)pChartCache->GetCount());
1106 if (FULL_INIT == init_flag)
1108 if (Ch->IsReadyToRender()) {
1110 pce->RecentTime = m_ticks;
1111 pce->b_in_use =
true;
1118 old_lock = pce->n_lock;
1119 pChartCache->Remove(pce);
1127 pce->RecentTime = m_ticks;
1128 pce->b_in_use =
true;
1139 if (g_memCacheLimit) {
1145 msg.Printf(
"OpenChartUsingCache, NOT in cache: cache size: %d\n",
1146 (
int)pChartCache->GetCount());
1149 msg1.Printf(
" OpenChartUsingCache: type %d ", chart_type);
1150 wxLogMessage(msg1 + ChartFullPath);
1152 if ((mem_used > g_memCacheLimit * 8 / 10) &&
1153 (pChartCache->GetCount() > 2)) {
1154 wxString msg(
"Removing oldest chart from cache: ");
1156 CacheEntry *pce = FindOldestDeleteCandidate(
true);
1157 if (pce == 0)
break;
1160 DeleteCacheEntry(pce,
true, msg);
1163 if ((mem_used < g_memCacheLimit * 8 / 10) ||
1164 (pChartCache->GetCount() <= 2))
1175 unsigned int nCache = pChartCache->GetCount();
1176 if (nCache > (
unsigned int)g_nCacheLimit && nCache > 2) {
1177 wxString msg(
"Removing oldest chart from cache: ");
1178 while (nCache > (
unsigned int)g_nCacheLimit) {
1179 CacheEntry *pce = FindOldestDeleteCandidate(
true);
1180 if (pce == 0)
break;
1182 DeleteCacheEntry(pce,
true, msg);
1193 wxLogMessage(
"Creating new chart");
1195 if (chart_type == CHART_TYPE_KAP)
1198 else if (chart_type == CHART_TYPE_GEO)
1201 else if (chart_type == CHART_TYPE_MBTILES)
1204 else if (chart_type == CHART_TYPE_S57) {
1209 Chs57->SetNativeScale(cte.GetScale());
1214 ext.NLAT = cte.GetLatMax();
1215 ext.SLAT = cte.GetLatMin();
1216 ext.WLON = cte.GetLonMin();
1217 ext.ELON = cte.GetLonMax();
1218 Chs57->SetFullExtent(ext);
1221 else if (chart_type == CHART_TYPE_CM93) {
1226 Chcm93->SetNativeScale(cte.GetScale());
1231 ext.NLAT = cte.GetLatMax();
1232 ext.SLAT = cte.GetLatMin();
1233 ext.WLON = cte.GetLonMin();
1234 ext.ELON = cte.GetLonMax();
1235 Chcm93->SetFullExtent(ext);
1238 else if (chart_type == CHART_TYPE_CM93COMP) {
1244 Chcm93->SetNativeScale(cte.GetScale());
1249 ext.NLAT = cte.GetLatMax();
1250 ext.SLAT = cte.GetLatMin();
1251 ext.WLON = cte.GetLonMin();
1252 ext.ELON = cte.GetLonMax();
1253 Chcm93->SetFullExtent(ext);
1256 else if (chart_type == CHART_TYPE_PLUGIN) {
1257 wxFileName fn(ChartFullPath);
1258 wxString ext = fn.GetExt();
1260 wxString ext_upper = ext.MakeUpper();
1261 wxString ext_lower = ext.MakeLower();
1262 wxString chart_class_name;
1267 for (
auto &cd : m_ChartClassDescriptorArray) {
1268 if (cd.m_descriptor_type == PLUGIN_DESCRIPTOR) {
1269 if (cd.m_search_mask == ext_upper) {
1270 chart_class_name = cd.m_class_name;
1273 if (cd.m_search_mask == ext_lower) {
1274 chart_class_name = cd.m_class_name;
1277 if (ChartFullPath.Matches(cd.m_search_mask)) {
1278 chart_class_name = cd.m_class_name;
1285 if (chart_class_name.Len()) {
1288 if (chart_family == CHART_FAMILY_VECTOR) LoadS57();
1294 wxLogMessage(
"Unknown chart type");
1300 s52plib *plib = ps52plib;
1301 wxString msg_fn(ChartFullPath);
1302 msg_fn.Replace(
"%",
"%%");
1305 if ((chart_family != CHART_FAMILY_VECTOR) ||
1306 ((chart_family == CHART_FAMILY_VECTOR) && plib)) {
1307 wxLogMessage(wxString::Format(
"Initializing Chart %s", msg_fn.c_str()));
1309 ir = Ch->Init(ChartFullPath, init_flag);
1310 Ch->SetColorScheme(user_colors::GetColorScheme());
1312 wxLogMessage(wxString::Format(
" No PLIB, Skipping vector chart %s",
1315 ir = INIT_FAIL_REMOVE;
1318 if (INIT_OK == ir) {
1324 pce->FullPath = ChartFullPath;
1326 pce->dbIndex = dbindex;
1329 pce->RecentTime = m_ticks;
1330 pce->n_lock = old_lock;
1332 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
1333 pChartCache->Add((
void *)pce);
1334 m_cache_mutex.Unlock();
1349 if (chart_type == CHART_TYPE_MBTILES) {
1350 wxFileName tileFile(ChartFullPath);
1352 wxULongLong tileSizeMB = tileFile.GetSize() >> 20;
1355 bool isBasemap = tileFile.GetPath().Lower().Contains(
"basemap");
1357 auto &config_array = ConfigMgr::Get().GetCanvasConfigArray();
1359 if (!isBasemap && (!CheckAnyCanvasExclusiveTileGroup() ||
1360 (tileSizeMB.GetLo() > 5000))) {
1363 bool b_clicked =
false;
1367 switch (g_canvasConfig) {
1369 cc = config_array.Item(0);
1373 b_clicked |= canvas->IsTileOverlayIndexInYesShow(dbindex);
1375 cc = config_array.Item(1);
1379 b_clicked |= canvas->IsTileOverlayIndexInYesShow(dbindex);
1383 cc = config_array.Item(0);
1387 b_clicked |= canvas->IsTileOverlayIndexInYesShow(dbindex);
1394 switch (g_canvasConfig) {
1396 cc = config_array.Item(0);
1399 if (canvas) canvas->AddTileOverlayIndexToNoShow(dbindex);
1401 cc = config_array.Item(1);
1404 if (canvas) canvas->AddTileOverlayIndexToNoShow(dbindex);
1408 cc = config_array.Item(0);
1411 if (canvas) canvas->AddTileOverlayIndexToNoShow(dbindex);
1418 }
else if (INIT_FAIL_REMOVE == ir)
1421 wxString::Format(
"Problem initializing Chart %s", msg_fn.c_str()));
1428 DisableChart(ChartFullPath);
1429 }
else if ((INIT_FAIL_RETRY == ir) ||
1430 (INIT_FAIL_NOERROR ==
1433 wxLogMessage(wxString::Format(
1434 "Recoverable problem initializing Chart %s", msg_fn.c_str()));
1439 if (INIT_OK != ir) {
1442 wxString::Format(
" OpenChartFromStack... Error opening "
1443 "chart %s ... return code %d",
1444 msg_fn.c_str(), ir));
1458bool ChartDB::DeleteCacheChart(
ChartBase *pDeleteCandidate) {
1459 bool retval =
false;
1461 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
1462 if (!IsSingleChart(pDeleteCandidate)) {
1465 for (
unsigned int i = 0; i < pChartCache->GetCount(); i++) {
1467 if ((
ChartBase *)(pce->pChart) == pDeleteCandidate) {
1473 if (pce->n_lock > 0) pce->n_lock--;
1475 if (pce->n_lock == 0) {
1476 DeleteCacheEntry(pce);
1481 m_cache_mutex.Unlock();
1489void ChartDB::ApplyColorSchemeToCachedCharts(ColorScheme cs) {
1498 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
1499 unsigned int nCache = pChartCache->GetCount();
1500 for (
unsigned int i = 0; i < nCache; i++) {
1503 if (Ch) Ch->SetColorScheme(cs,
true);
1506 m_cache_mutex.Unlock();
1516ChartBase *ChartDB::OpenStackChartConditional(
1517 ChartStack *ps,
int index_start,
bool bSearchDir, ChartTypeEnum New_Type,
1518 ChartFamilyEnum New_Family_Fallback) {
1524 if (bSearchDir == 1)
1530 index = index_start;
1532 while ((index >= 0) && (index < ps->nEntry)) {
1533 ChartTypeEnum chart_type = (ChartTypeEnum)GetCSChartType(ps, index);
1534 if ((chart_type == New_Type) || (New_Type == CHART_TYPE_DONTCARE)) {
1535 ptc = OpenChartFromStack(ps, index);
1536 if (NULL != ptc)
break;
1538 index += delta_index;
1544 index = index_start;
1546 while ((index >= 0) && (index < ps->nEntry)) {
1547 ChartFamilyEnum chart_family = GetCSChartFamily(ps, index);
1548 if (chart_family == New_Family_Fallback) {
1549 ptc = OpenChartFromStack(ps, index);
1551 if (NULL != ptc)
break;
1553 index += delta_index;
1560wxXmlDocument ChartDB::GetXMLDescription(
int dbIndex,
bool b_getGeom) {
1562 if (!IsValid() || (dbIndex >= GetChartTableEntries()))
return doc;
1564 bool b_remove = !IsChartInCache(dbIndex);
1566 wxXmlNode *pcell_node = NULL;
1571 ChartBase *pc = OpenChartFromDB(dbIndex, HEADER_ONLY);
1572 b_remove = !IsChartInCache(dbIndex);
1575 if (CHART_FAMILY_RASTER == (ChartFamilyEnum)cte.GetChartFamily()) {
1576 pcell_node =
new wxXmlNode(wxXML_ELEMENT_NODE,
"chart");
1578 wxString path = GetDBChartFileName(dbIndex);
1579 node =
new wxXmlNode(wxXML_ELEMENT_NODE,
"path");
1580 pcell_node->AddChild(node);
1581 tnode =
new wxXmlNode(wxXML_TEXT_NODE,
"", path);
1582 node->AddChild(tnode);
1584 wxFileName name(path);
1585 node =
new wxXmlNode(wxXML_ELEMENT_NODE,
"name");
1586 pcell_node->AddChild(node);
1587 tnode =
new wxXmlNode(wxXML_TEXT_NODE,
"", name.GetName());
1588 node->AddChild(tnode);
1591 node =
new wxXmlNode(wxXML_ELEMENT_NODE,
"lname");
1592 pcell_node->AddChild(node);
1593 tnode =
new wxXmlNode(wxXML_TEXT_NODE,
"", pc->GetName());
1594 node->AddChild(tnode);
1598 scale.Printf(
"%d", cte.GetScale());
1599 node =
new wxXmlNode(wxXML_ELEMENT_NODE,
"cscale");
1600 pcell_node->AddChild(node);
1601 tnode =
new wxXmlNode(wxXML_TEXT_NODE,
"",
scale);
1602 node->AddChild(tnode);
1604 wxDateTime file_date(cte.GetFileTime());
1605 file_date.MakeUTC();
1606 wxString sfile_date = file_date.FormatISODate();
1608 sfile_date += file_date.FormatISOTime();
1610 node =
new wxXmlNode(wxXML_ELEMENT_NODE,
"local_file_datetime_iso8601");
1611 pcell_node->AddChild(node);
1612 tnode =
new wxXmlNode(wxXML_TEXT_NODE,
"", sfile_date);
1613 node->AddChild(tnode);
1616 node =
new wxXmlNode(wxXML_ELEMENT_NODE,
"source_edition");
1617 pcell_node->AddChild(node);
1618 tnode =
new wxXmlNode(wxXML_TEXT_NODE,
"", pc->GetSE());
1619 node->AddChild(tnode);
1621 wxDateTime sdt = pc->GetEditionDate();
1622 wxString ssdt =
"Unknown";
1623 if (sdt.IsValid()) ssdt = sdt.Format(
"%Y%m%d");
1625 node =
new wxXmlNode(wxXML_ELEMENT_NODE,
"source_date");
1626 pcell_node->AddChild(node);
1627 tnode =
new wxXmlNode(wxXML_TEXT_NODE,
"", ssdt);
1628 node->AddChild(tnode);
1634 if (s == "raster_edition")
1635 if (s == "ntm_edition")
1637 if (s == "ntm_date")
1638 if (s == "source_edition_last_correction")
1639 if (s == "raster_edition_last_correction")
1640 if (s == "ntm_edition_last_correction")
1644 else if (CHART_FAMILY_VECTOR == (ChartFamilyEnum)cte.GetChartFamily()) {
1645 pcell_node =
new wxXmlNode(wxXML_ELEMENT_NODE,
"cell");
1647 wxString path = GetDBChartFileName(dbIndex);
1648 node =
new wxXmlNode(wxXML_ELEMENT_NODE,
"path");
1649 pcell_node->AddChild(node);
1650 tnode =
new wxXmlNode(wxXML_TEXT_NODE,
"", path);
1651 node->AddChild(tnode);
1653 wxFileName name(path);
1654 node =
new wxXmlNode(wxXML_ELEMENT_NODE,
"name");
1655 pcell_node->AddChild(node);
1656 tnode =
new wxXmlNode(wxXML_TEXT_NODE,
"", name.GetName());
1657 node->AddChild(tnode);
1660 scale.Printf(
"%d", cte.GetScale());
1661 node =
new wxXmlNode(wxXML_ELEMENT_NODE,
"cscale");
1662 pcell_node->AddChild(node);
1663 tnode =
new wxXmlNode(wxXML_TEXT_NODE,
"",
scale);
1664 node->AddChild(tnode);
1666 wxDateTime file_date(cte.GetFileTime());
1667 file_date.MakeUTC();
1668 wxString sfile_date = file_date.FormatISODate();
1670 sfile_date += file_date.FormatISOTime();
1672 node =
new wxXmlNode(wxXML_ELEMENT_NODE,
"local_file_datetime_iso8601");
1673 pcell_node->AddChild(node);
1674 tnode =
new wxXmlNode(wxXML_TEXT_NODE,
"", sfile_date);
1675 node->AddChild(tnode);
1678 node =
new wxXmlNode(wxXML_ELEMENT_NODE,
"edtn");
1679 pcell_node->AddChild(node);
1680 tnode =
new wxXmlNode(wxXML_TEXT_NODE,
"", pc->GetSE());
1681 node->AddChild(tnode);
1686 node =
new wxXmlNode(wxXML_ELEMENT_NODE,
"isdt");
1687 pcell_node->AddChild(node);
1688 tnode =
new wxXmlNode(wxXML_TEXT_NODE,
"", pcs57->GetISDT());
1689 node->AddChild(tnode);
1691 wxString LastUpdateDate;
1693 pcs57->ValidateAndCountUpdates(path,
"", LastUpdateDate,
false);
1696 supdn.Printf(
"%d", updn);
1697 node =
new wxXmlNode(wxXML_ELEMENT_NODE,
"updn");
1698 pcell_node->AddChild(node);
1699 tnode =
new wxXmlNode(wxXML_TEXT_NODE,
"", supdn);
1700 node->AddChild(tnode);
1702 node =
new wxXmlNode(wxXML_ELEMENT_NODE,
"uadt");
1703 pcell_node->AddChild(node);
1704 tnode =
new wxXmlNode(wxXML_TEXT_NODE,
"", LastUpdateDate);
1705 node->AddChild(tnode);
1709 if (pcell_node && b_getGeom) {
1710 node =
new wxXmlNode(wxXML_ELEMENT_NODE,
"cov");
1711 pcell_node->AddChild(node);
1714 if (cte.GetnPlyEntries()) {
1715 wxXmlNode *panelnode =
new wxXmlNode(wxXML_ELEMENT_NODE,
"panel");
1716 node->AddChild(panelnode);
1719 panel_no.Printf(
"%d", 0);
1720 wxXmlNode *anode =
new wxXmlNode(wxXML_TEXT_NODE,
"", panel_no);
1721 panelnode->AddChild(anode);
1723 float *pf = cte.GetpPlyTable();
1724 for (
int j = 0; j < cte.GetnPlyEntries(); j++) {
1725 wxXmlNode *vnode =
new wxXmlNode(wxXML_ELEMENT_NODE,
"vertex");
1726 panelnode->AddChild(vnode);
1728 wxXmlNode *latnode =
new wxXmlNode(wxXML_ELEMENT_NODE,
"lat");
1729 vnode->AddChild(latnode);
1733 sl.Printf(
"%.5f", l);
1734 wxXmlNode *vtnode =
new wxXmlNode(wxXML_TEXT_NODE,
"", sl);
1735 latnode->AddChild(vtnode);
1737 wxXmlNode *lonnode =
new wxXmlNode(wxXML_ELEMENT_NODE,
"lon");
1738 vnode->AddChild(lonnode);
1742 sll.Printf(
"%.5f", ll);
1743 wxXmlNode *vtlnode =
new wxXmlNode(wxXML_TEXT_NODE,
"", sll);
1744 lonnode->AddChild(vtlnode);
1748 for (
int i = 0; i < cte.GetnAuxPlyEntries(); i++) {
1749 wxXmlNode *panelnode =
new wxXmlNode(wxXML_ELEMENT_NODE,
"panel");
1750 node->AddChild(panelnode);
1753 panel_no.Printf(
"%d", i + 1);
1754 wxXmlNode *anode =
new wxXmlNode(wxXML_TEXT_NODE,
"", panel_no);
1755 panelnode->AddChild(anode);
1757 float *pf = cte.GetpAuxPlyTableEntry(i);
1758 for (
int j = 0; j < cte.GetAuxCntTableEntry(i); j++) {
1759 wxXmlNode *vnode =
new wxXmlNode(wxXML_ELEMENT_NODE,
"vertex");
1760 panelnode->AddChild(vnode);
1762 wxXmlNode *latnode =
new wxXmlNode(wxXML_ELEMENT_NODE,
"lat");
1763 vnode->AddChild(latnode);
1767 sl.Printf(
"%.5f", l);
1768 wxXmlNode *vtnode =
new wxXmlNode(wxXML_TEXT_NODE,
"", sl);
1769 latnode->AddChild(vtnode);
1771 wxXmlNode *lonnode =
new wxXmlNode(wxXML_ELEMENT_NODE,
"lon");
1772 vnode->AddChild(lonnode);
1776 sll.Printf(
"%.5f", ll);
1777 wxXmlNode *vtlnode =
new wxXmlNode(wxXML_TEXT_NODE,
"", sll);
1778 lonnode->AddChild(vtlnode);
1783 doc.SetRoot(pcell_node);
1785 if (b_remove) DeleteCacheChart(pc);
1790bool ChartDB::CheckExclusiveTileGroup(
int canvasIndex) {
1798 auto &config_array = ConfigMgr::Get().GetCanvasConfigArray();
1800 switch (g_canvasConfig) {
1802 if (canvasIndex == 0) {
1803 cc = config_array.Item(0);
1804 if (cc) canvas = cc->
canvas;
1806 cc = config_array.Item(1);
1807 if (cc) canvas = cc->
canvas;
1812 cc = config_array.Item(0);
1813 if (cc) canvas = cc->
canvas;
1816 if (!canvas)
return false;
1819 if (canvas->m_groupIndex == m_checkGroupIndex[canvasIndex])
1820 return m_checkedTileOnly[canvasIndex];
1823 bool rv = IsNonMBTileInGroup(canvas->m_groupIndex);
1825 m_checkGroupIndex[canvasIndex] = canvas->m_groupIndex;
1826 m_checkedTileOnly[canvasIndex] = !rv;
1831bool ChartDB::CheckAnyCanvasExclusiveTileGroup() {
1839 auto &config_array = ConfigMgr::Get().GetCanvasConfigArray();
1841 switch (g_canvasConfig) {
1843 cc = config_array.Item(0);
1847 if (canvas->m_groupIndex == m_checkGroupIndex[0])
1848 rv |= m_checkedTileOnly[0];
1852 cc = config_array.Item(1);
1856 if (canvas->m_groupIndex == m_checkGroupIndex[1])
1857 rv |= m_checkedTileOnly[1];
1863 cc = config_array.Item(0);
1867 if (canvas->m_groupIndex == m_checkGroupIndex[0])
1868 rv |= m_checkedTileOnly[0];
1899bool G_FloatPtInPolygon(
MyFlPoint *rgpts,
int wnumpts,
float x,
float y)
1915 for (i = 0, ppt = rgpts; i < wnumpts - 1; i++, ppt++) {
1918 if (Intersect(pt0, pt2, *ppt, *(ppt1))) wnumintsct++;
1922 if (Intersect(pt0, pt2, *ppt, *rgpts)) wnumintsct++;
1928 if (!(wnumintsct & 1)) {
1929 for (i = 0, ppt = rgpts; i < wnumpts; i++, ppt++) {
1930 if (((*ppt).x == x) && ((*ppt).y == y))
return true;
1951 return (((CCW(p1, p2, p3) * CCW(p1, p2, p4)) <= 0) &&
1952 ((CCW(p3, p4, p1) * CCW(p3, p4, p2) <= 0)));
1982 return ((dx1 * dy2 > dy1 * dx2) ? 1 : -1);
Chart canvas configuration state
std::vector< std::string > ChartDirectoryExcludedVector
Global instance.
ChartDB * ChartData
Global instance.
Charts database management
std::vector< std::string > ChartDirectoryExcludedVector
Global instance.
ChartDB * ChartData
Global instance.
arrayofCanvasPtr g_canvasArray
Global instance.
Generic Chart canvas base.
Base class for all chart types.
ChartCanvas - Main chart display and interaction component.
Manages the chart database and provides access to chart data.
bool LoadBinary(const wxString &filename, ArrayOfCDI &dir_array_check)
Load the chart database from a binary file.
Represents a KAP format chart, derived from ChartBaseBSB.
Represents an MBTiles format chart.
Wrapper class for plugin-based charts.
Encapsulates persistent canvas configuration.
ChartCanvas * canvas
Pointer to associated chart canvas.
Represents a single CM93 chart at a specific scale.
Represents a composite CM93 chart covering multiple scales.
Represents an S57 format electronic navigational chart in OpenCPN.
Class cm93chart and helpers – CM93 chart state.
Config file user configuration interface.
OpenGL chart rendering canvas.
glTextureManager * g_glTextureManager
Global instance.
bool startswith(const std::string &str, const std::string &prefix)
Return true if s starts with given prefix.
Miscellaneous utilities, many of which string related.
Represents an entry in the chart table, containing information about a single chart.
ThumbWin * pthumbwin
Global instance.