27 #include <wx/wxprec.h>
33 #include <wx/stopwatch.h>
35 #include <wx/tokenzr.h>
45 #include "CanvasConfig.h"
46 #include "ConfigMgr.h"
47 #include "ocpn_frame.h"
48 #ifdef __OCPN__ANDROID__
49 #include "androidUTIL.h"
53 #include "glChartCanvas.h"
59 #include <wx/progdlg.h>
66 extern ColorScheme GetColorScheme();
71 extern int g_nCacheLimit;
72 extern int g_memCacheLimit;
73 extern s52plib *ps52plib;
75 extern unsigned int g_canvasConfig;
77 bool G_FloatPtInPolygon(
MyFlPoint *rgpts,
int wnumpts,
float x,
float y);
78 bool GetMemoryStatus(
int *mem_total,
int *mem_used);
84 int ChartStack::GetCurrentEntrydbIndex(
void) {
85 if (nEntry && (CurrentStackEntry >= 0) )
86 return DBIndex[CurrentStackEntry];
91 void ChartStack::SetCurrentEntryFromdbIndex(
int current_db_index) {
92 for (
int i = 0; i < nEntry; i++) {
93 if (current_db_index == DBIndex[i]) CurrentStackEntry = i;
97 int ChartStack::GetDBIndex(
int stack_index) {
98 if ((stack_index >= 0) && (stack_index < nEntry) && (stack_index < MAXSTACK))
99 return DBIndex[stack_index];
104 void ChartStack::SetDBIndex(
int stack_index,
int db_index) {
105 if ((stack_index >= 0) && (stack_index < nEntry) && (stack_index < MAXSTACK))
106 DBIndex[stack_index] = db_index;
109 bool ChartStack::DoesStackContaindbIndex(
int db_index) {
110 for (
int i = 0; i < nEntry; i++) {
111 if (db_index == DBIndex[i])
return true;
117 void ChartStack::AddChart(
int db_add) {
118 if (!ChartData)
return;
120 if (!ChartData->IsValid())
return;
122 int db_index = db_add;
129 SetDBIndex(j - 1, db_index);
139 for (
int id = 0;
id < j - 1;
id++) {
140 if (GetDBIndex(
id) != -1) {
143 for (
int jd =
id + 1; jd < j; jd++) {
144 if (GetDBIndex(jd) != -1) {
146 if (pm->GetFileTime() && pn->GetFileTime()) {
147 if (labs(pm->GetFileTime() - pn->GetFileTime()) <
149 if (pn->GetpFileName()->IsSameAs(*(pm->GetpFileName())))
160 if (GetDBIndex(
id) == -1) {
163 int db_index = GetDBIndex(jd);
164 SetDBIndex(jd - 1, db_index);
181 for (
int i = 0; i < j - 1; i++) {
182 const ChartTableEntry &m = ChartData->GetChartTableEntry(GetDBIndex(i));
184 ChartData->GetChartTableEntry(GetDBIndex(i + 1));
186 if (n.GetScale() < m.GetScale()) {
188 SetDBIndex(i, GetDBIndex(i + 1));
189 SetDBIndex(i + 1, ti);
201 pChartCache =
new wxArrayPtrVoid;
210 if (g_memCacheLimit) {
212 msg.Printf(_T(
"ChartDB Cache policy: Application target is %d MBytes"),
213 g_memCacheLimit / 1024);
217 msg.Printf(_T(
"ChartDB Cache policy: Max open chart limit is %d."),
222 m_checkGroupIndex[0] = m_checkGroupIndex[1] = -1;
223 m_checkedTileOnly[0] = m_checkedTileOnly[1] =
false;
226 ChartDB::~ChartDB() {
233 bool ChartDB::LoadBinary(
const wxString &filename,
234 ArrayOfCDI &dir_array_check) {
235 m_dir_array = dir_array_check;
236 return ChartDatabase::Read(filename);
241 void ChartDB::DeleteCacheEntry(
CacheEntry *pce,
bool bDelTexture,
242 const wxString &msg) {
245 if (msg != wxEmptyString) {
246 wxLogMessage(_T(
"%s%s"), msg.c_str(), ch->GetFullPath().c_str());
251 if (pthumbwin->pThumbChart == ch) pthumbwin->pThumbChart = NULL;
256 if (g_glTextureManager)
257 g_glTextureManager->PurgeChartTextures(ch, bDelTexture);
260 pChartCache->Remove(pce);
265 void ChartDB::DeleteCacheEntry(
int i,
bool bDelTexture,
const wxString &msg) {
267 if (pce) DeleteCacheEntry(pce, bDelTexture, msg);
270 void ChartDB::PurgeCache() {
274 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
275 unsigned int nCache = pChartCache->GetCount();
276 for (
unsigned int i = 0; i < nCache; i++) {
277 DeleteCacheEntry(0,
true);
279 pChartCache->Clear();
281 m_cache_mutex.Unlock();
285 void ChartDB::PurgeCachePlugins() {
287 wxLogMessage(_T(
"Chart cache PlugIn purge"));
289 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
290 unsigned int nCache = pChartCache->GetCount();
296 if (CHART_TYPE_PLUGIN == Ch->GetChartType()) {
297 DeleteCacheEntry(pce,
true);
299 nCache = pChartCache->GetCount();
306 m_cache_mutex.Unlock();
310 void ChartDB::ClearCacheInUseFlags(
void) {
311 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
312 unsigned int nCache = pChartCache->GetCount();
313 for (
unsigned int i = 0; i < nCache; i++) {
315 pce->b_in_use =
false;
317 m_cache_mutex.Unlock();
324 void ChartDB::PurgeCacheUnusedCharts(
double factor) {
326 if (g_memCacheLimit) {
327 if (wxMUTEX_NO_ERROR == m_cache_mutex.TryLock()) {
330 GetMemoryStatus(0, &mem_used);
331 int mem_limit = g_memCacheLimit * factor;
333 int nl = pChartCache->GetCount();
335 wxString msg(_T(
"Purging unused chart from cache: "));
337 while ((mem_used > mem_limit) && (nl > 0)) {
338 if (pChartCache->GetCount() < 2) {
343 CacheEntry *pce = FindOldestDeleteCandidate(
false);
346 DeleteCacheEntry(pce,
false , msg);
352 GetMemoryStatus(0, &mem_used);
357 m_cache_mutex.Unlock();
361 else if (g_nCacheLimit) {
362 if (wxMUTEX_NO_ERROR == m_cache_mutex.TryLock()) {
364 double fac10 = factor * 10;
365 int chart_limit = g_nCacheLimit * fac10 / 10;
367 int nl = pChartCache->GetCount();
369 wxString msg(_T(
"Purging unused chart from cache: "));
370 while ((nl > chart_limit) && (nl > 0)) {
371 if (pChartCache->GetCount() < 2) {
376 CacheEntry *pce = FindOldestDeleteCandidate(
false);
379 DeleteCacheEntry(pce,
false , msg);
384 nl = pChartCache->GetCount();
387 m_cache_mutex.Unlock();
396 ChartBase *ChartDB::GetChart(
const wxChar *theFilePath,
398 wxFileName fn(theFilePath);
400 if (!fn.FileExists()) {
402 if (!wxDir::Exists(theFilePath)) {
403 wxLogMessage(wxT(
" ...file does not exist: %s"), theFilePath);
409 wxString chartExt = fn.GetExt().Upper();
411 if (chartExt == wxT(
"XZ")) {
412 wxString npath = theFilePath;
413 npath = npath.Left(npath.length() - 3);
414 wxFileName fn(npath);
415 chartExt = fn.GetExt().Upper();
418 if (chartExt == wxT(
"KAP")) {
420 }
else if (chartExt == wxT(
"GEO")) {
422 }
else if (chartExt == wxT(
"MBTILES")) {
424 }
else if (chartExt == wxT(
"000") || chartExt == wxT(
"S57")) {
427 }
else if (chart_desc.m_descriptor_type == PLUGIN_DESCRIPTOR) {
434 wxRegEx rxName(wxT(
"[0-9]+"));
435 wxRegEx rxExt(wxT(
"[A-G]"));
436 if (rxName.Matches(fn.GetName()) && rxExt.Matches(chartExt))
450 int ChartDB::BuildChartStack(
ChartStack *cstk,
float lat,
float lon,
int db_add,
452 BuildChartStack(cstk, lat, lon, groupIndex);
454 if (db_add >= 0) cstk->AddChart(db_add);
459 int ChartDB::BuildChartStack(
ChartStack *cstk,
float lat,
float lon,
464 if (!IsValid())
return 0;
468 int nEntry = GetChartTableEntries();
470 for (
int db_index = 0; db_index < nEntry; db_index++) {
474 bool b_group_add =
false;
475 if (groupIndex > 0) {
476 const int ng = cte.GetGroupArray().size();
477 for (
int ig = 0; ig < ng; ig++) {
478 if (groupIndex == cte.GetGroupArray()[ig]) {
486 bool b_writable_add =
true;
489 #ifdef __OCPN__ANDROID__
490 wxFileName fn(cte.GetFullSystemPath());
491 if (!androidIsDirWritable( fn.GetPath()))
492 b_writable_add =
false;
495 bool b_pos_add =
false;
496 if (b_group_add && b_writable_add) {
500 if (cte.GetChartType() == CHART_TYPE_PLUGIN) {
505 if (CheckPositionWithinChart(db_index, lat, lon) && (j < MAXSTACK))
509 else if ((cte.GetLonMax() > 180.) && (cte.GetLonMin() < 180.)) {
510 if (CheckPositionWithinChart(db_index, lat, lon + 360.) &&
515 else if ((cte.GetLonMax() > 180.) && (cte.GetLonMin() > 180.)) {
516 if (CheckPositionWithinChart(db_index, lat, lon + 360.) &&
522 bool b_available =
true;
524 if (b_group_add && b_pos_add && (cte.GetChartType() == CHART_TYPE_PLUGIN)) {
526 if (!IsChartAvailable(db_index)) {
527 pcte->SetAvailable(
false);
530 pcte->SetAvailable(
true);
535 if (b_group_add && b_pos_add && b_available) {
538 cstk->SetDBIndex(j - 1, db_index);
553 for (
int id = 0;
id < j - 1;
id++) {
554 if (cstk->GetDBIndex(
id) != -1) {
555 const ChartTableEntry &ctem = GetChartTableEntry(cstk->GetDBIndex(
id));
557 for (
int jd =
id + 1; jd < j; jd++) {
558 if (cstk->GetDBIndex(jd) != -1) {
560 GetChartTableEntry(cstk->GetDBIndex(jd));
561 bool bsameTime =
false;
562 if (ctem.GetFileTime() && cten.GetFileTime()) {
563 if (labs(ctem.GetFileTime() - cten.GetFileTime()) < 60)
566 if (ctem.GetChartEditionDate() == cten.GetChartEditionDate() )
570 if (cten.GetpFileName()->IsSameAs(*(ctem.GetpFileName())))
571 cstk->SetDBIndex(jd, -1);
580 if (cstk->GetDBIndex(
id) == -1) {
583 int db_index = cstk->GetDBIndex(jd);
584 cstk->SetDBIndex(jd - 1, db_index);
601 for (i = 0; i < j - 1; i++) {
603 const ChartTableEntry &n = GetChartTableEntry(cstk->GetDBIndex(i + 1));
605 if (n.GetScale() < m.GetScale()) {
606 ti = cstk->GetDBIndex(i);
607 cstk->SetDBIndex(i, cstk->GetDBIndex(i + 1));
608 cstk->SetDBIndex(i + 1, ti);
614 cstk->b_valid =
true;
619 bool ChartDB::IsChartInGroup(
const int db_index,
const int group) {
623 bool b_in_group =
false;
625 for (
unsigned int ig = 0; ig < pt->GetGroupArray().size(); ig++) {
626 if (group == pt->GetGroupArray()[ig]) {
637 bool ChartDB::IsENCInGroup(
const int groupIndex) {
641 for (
int db_index = 0; db_index < GetChartTableEntries(); db_index++) {
645 bool b_group_add =
false;
646 if (groupIndex > 0) {
647 const int ng = cte.GetGroupArray().size();
648 for (
int ig = 0; ig < ng; ig++) {
649 if (groupIndex == cte.GetGroupArray()[ig]) {
658 if (cte.GetChartFamily() == CHART_FAMILY_VECTOR) {
668 bool ChartDB::IsNonMBTileInGroup(
const int groupIndex) {
673 for (
int db_index = 0; db_index < GetChartTableEntries(); db_index++) {
677 bool b_group_add =
false;
678 if (groupIndex > 0) {
679 const int ng = cte.GetGroupArray().size();
680 for (
int ig = 0; ig < ng; ig++) {
681 if (groupIndex == cte.GetGroupArray()[ig]) {
690 if (cte.GetChartType() != CHART_TYPE_MBTILES) {
703 bool ChartDB::CheckPositionWithinChart(
int index,
float lat,
float lon) {
708 if ((lat <= pt->GetLatMax()) && (lat >= pt->GetLatMin()) &&
709 (lon >= pt->GetLonMin()) && (lon <= pt->GetLonMax())) {
712 bool bInside = G_FloatPtInPolygon((
MyFlPoint *)pt->GetpPlyTable(),
713 pt->GetnPlyEntries(), lon, lat);
716 if (pt->GetnAuxPlyEntries()) {
717 for (
int k = 0; k < pt->GetnAuxPlyEntries(); k++) {
719 G_FloatPtInPolygon((
MyFlPoint *)pt->GetpAuxPlyTableEntry(k),
720 pt->GetAuxCntTableEntry(k), lon, lat);
721 if (bAuxInside)
return true;
737 if ((pa == 0) || (pb == 0))
return false;
738 if ((!pa->b_valid) || (!pb->b_valid))
return false;
739 if (pa->nEntry != pb->nEntry)
return false;
741 for (
int i = 0; i < pa->nEntry; i++) {
742 if (pa->GetDBIndex(i) != pb->GetDBIndex(i))
return false;
752 if ((pa == 0) || (pb == 0))
return false;
753 pa->nEntry = pb->nEntry;
755 for (
int i = 0; i < pa->nEntry; i++) pa->SetDBIndex(i, pb->GetDBIndex(i));
757 pa->CurrentStackEntry = pb->CurrentStackEntry;
759 pa->b_valid = pb->b_valid;
764 wxString ChartDB::GetFullPath(
ChartStack *ps,
int stackindex) {
765 int dbIndex = ps->GetDBIndex(stackindex);
766 return GetChartTableEntry(dbIndex).GetFullSystemPath();
773 int ChartDB::GetCSPlyPoint(
ChartStack *ps,
int stackindex,
int plyindex,
774 float *lat,
float *lon) {
775 int dbIndex = ps->GetDBIndex(stackindex);
776 wxASSERT(dbIndex >= 0);
779 if (entry.GetnPlyEntries()) {
780 float *fp = entry.GetpPlyTable();
787 return entry.GetnPlyEntries();
793 int ChartDB::GetStackChartScale(
ChartStack *ps,
int stackindex,
char *buf,
795 int dbindex = ps->GetDBIndex(stackindex);
796 wxASSERT(dbindex >= 0);
799 int sc = entry.GetScale();
800 if (buf) sprintf(buf,
"%d", sc);
808 int ChartDB::GetStackEntry(
ChartStack *ps, wxString fp) {
809 for (
int i = 0; i < ps->nEntry; i++) {
811 if (fp.IsSameAs(entry.GetFullSystemPath()))
return i;
820 ChartTypeEnum ChartDB::GetCSChartType(
ChartStack *ps,
int stackindex) {
822 int dbindex = ps->GetDBIndex(stackindex);
824 return (ChartTypeEnum)GetChartTableEntry(dbindex).GetChartType();
826 return CHART_TYPE_UNKNOWN;
829 ChartFamilyEnum ChartDB::GetCSChartFamily(
ChartStack *ps,
int stackindex) {
831 int dbindex = ps->GetDBIndex(stackindex);
835 ChartTypeEnum type = (ChartTypeEnum)entry.GetChartType();
838 return CHART_FAMILY_RASTER;
840 return CHART_FAMILY_RASTER;
842 return CHART_FAMILY_VECTOR;
843 case CHART_TYPE_CM93:
844 return CHART_FAMILY_VECTOR;
845 case CHART_TYPE_CM93COMP:
846 return CHART_FAMILY_VECTOR;
847 case CHART_TYPE_DUMMY:
848 return CHART_FAMILY_RASTER;
850 return CHART_FAMILY_UNKNOWN;
854 return CHART_FAMILY_UNKNOWN;
857 std::vector<int> ChartDB::GetCSArray(
ChartStack *ps) {
858 std::vector<int> ret;
861 ret.reserve(ps->nEntry);
862 for (
int i = 0; i < ps->nEntry; i++) {
863 ret.push_back(ps->GetDBIndex(i));
870 bool ChartDB::IsChartInCache(
int dbindex) {
871 bool bInCache =
false;
874 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
875 unsigned int nCache = pChartCache->GetCount();
876 for (
unsigned int i = 0; i < nCache; i++) {
878 if (pce->dbIndex == dbindex) {
879 if (pce->pChart != 0 && ((
ChartBase *)pce->pChart)->IsReadyToRender())
884 m_cache_mutex.Unlock();
890 bool ChartDB::IsChartInCache(wxString path) {
891 bool bInCache =
false;
892 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->FullPath == path) {
898 if (pce->pChart != 0 && ((
ChartBase *)pce->pChart)->IsReadyToRender())
904 m_cache_mutex.Unlock();
909 bool ChartDB::IsChartLocked(
int index) {
910 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
911 unsigned int nCache = pChartCache->GetCount();
912 for (
unsigned int i = 0; i < nCache; i++) {
914 if (pce->dbIndex == index) {
915 bool ret = pce->n_lock > 0;
916 m_cache_mutex.Unlock();
920 m_cache_mutex.Unlock();
926 bool ChartDB::LockCacheChart(
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) {
939 m_cache_mutex.Unlock();
944 void ChartDB::UnLockCacheChart(
int index) {
946 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
947 unsigned int nCache = pChartCache->GetCount();
948 for (
unsigned int i = 0; i < nCache; i++) {
950 if (pce->dbIndex == index) {
951 if (pce->n_lock > 0) pce->n_lock--;
955 m_cache_mutex.Unlock();
959 void ChartDB::UnLockAllCacheCharts() {
961 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
962 unsigned int nCache = pChartCache->GetCount();
963 for (
unsigned int i = 0; i < nCache; i++) {
965 if (pce->n_lock > 0) pce->n_lock--;
967 m_cache_mutex.Unlock();
974 ChartBase *ChartDB::OpenChartFromDB(
int index, ChartInitFlag init_flag) {
975 return OpenChartUsingCache(index, init_flag);
978 ChartBase *ChartDB::OpenChartFromDB(wxString chart_path,
979 ChartInitFlag init_flag) {
980 int dbii = FinddbIndex(chart_path);
981 return OpenChartUsingCache(dbii, init_flag);
985 ChartInitFlag init_flag) {
986 return OpenChartUsingCache(pStack->GetDBIndex(StackEntry), init_flag);
989 ChartBase *ChartDB::OpenChartFromDBAndLock(
int index, ChartInitFlag init_flag,
991 wxCriticalSectionLocker locker(m_critSect);
992 ChartBase *pret = OpenChartUsingCache(index, init_flag);
993 if (lock && pret) LockCacheChart(index);
997 ChartBase *ChartDB::OpenChartFromDBAndLock(wxString chart_path,
998 ChartInitFlag init_flag) {
999 int dbii = FinddbIndex(chart_path);
1000 return OpenChartFromDBAndLock(dbii, init_flag);
1003 CacheEntry *ChartDB::FindOldestDeleteCandidate(
bool blog) {
1006 unsigned int nCache = pChartCache->GetCount();
1008 if (blog) wxLogMessage(_T(
"Searching chart cache for oldest entry"));
1009 int LRUTime = m_ticks;
1011 for (
unsigned int i = 0; i < nCache; i++) {
1013 if (pce->RecentTime < LRUTime && !pce->n_lock) {
1014 if (!isSingleChart((
ChartBase *)(pce->pChart))) {
1015 LRUTime = pce->RecentTime;
1020 int dt = m_ticks - LRUTime;
1025 if (!pce->n_lock && !isSingleChart(pDeleteCandidate)) {
1027 wxLogMessage(_T(
"Oldest unlocked cache index is %d, delta t is %d"),
1032 wxLogMessage(_T(
"All chart in cache locked, size: %d"), nCache);
1038 ChartBase *ChartDB::OpenChartUsingCache(
int dbindex, ChartInitFlag init_flag) {
1039 if ((dbindex < 0) || (dbindex > GetChartTableEntries() - 1))
return NULL;
1044 wxString ChartFullPath = cte.GetFullSystemPath();
1045 ChartTypeEnum chart_type = (ChartTypeEnum)cte.GetChartType();
1046 ChartFamilyEnum chart_family = (ChartFamilyEnum)cte.GetChartFamily();
1049 msg1.Printf(_T(
"OpenChartUsingCache: type %d "), chart_type);
1052 if (cte.GetLatMax() > 90.0)
1059 bool bInCache =
false;
1063 wxMutexLocker lock(m_cache_mutex);
1065 unsigned int nCache = pChartCache->GetCount();
1067 for (
unsigned int i = 0; i < nCache; i++) {
1069 if (pce->FullPath == ChartFullPath) {
1078 msg.Printf(_T(
"OpenChartUsingCache, IN cache: cache size: %d\n"),
1079 (int)pChartCache->GetCount());
1081 if (FULL_INIT == init_flag)
1083 if (Ch->IsReadyToRender()) {
1085 pce->RecentTime = m_ticks;
1086 pce->b_in_use =
true;
1090 if (pthumbwin && pthumbwin->pThumbChart == Ch)
1091 pthumbwin->pThumbChart = NULL;
1093 old_lock = pce->n_lock;
1094 pChartCache->Remove(pce);
1102 pce->RecentTime = m_ticks;
1103 pce->b_in_use =
true;
1114 if (g_memCacheLimit) {
1117 GetMemoryStatus(0, &mem_used);
1121 _T(
"OpenChartUsingCache, NOT in cache: cache size: %d\n"),
1122 (
int)pChartCache->GetCount());
1125 msg1.Printf(_T(
" OpenChartUsingCache: type %d "), chart_type);
1126 wxLogMessage(msg1 + ChartFullPath);
1128 if ((mem_used > g_memCacheLimit * 8 / 10) &&
1129 (pChartCache->GetCount() > 2)) {
1130 wxString msg(_T(
"Removing oldest chart from cache: "));
1132 CacheEntry *pce = FindOldestDeleteCandidate(
true);
1133 if (pce == 0)
break;
1136 DeleteCacheEntry(pce,
true, msg);
1138 GetMemoryStatus(0, &mem_used);
1139 if ((mem_used < g_memCacheLimit * 8 / 10) ||
1140 (pChartCache->GetCount() <= 2))
1151 unsigned int nCache = pChartCache->GetCount();
1152 if (nCache > (
unsigned int)g_nCacheLimit && nCache > 2) {
1153 wxString msg(_T(
"Removing oldest chart from cache: "));
1154 while (nCache > (
unsigned int)g_nCacheLimit) {
1155 CacheEntry *pce = FindOldestDeleteCandidate(
true);
1156 if (pce == 0)
break;
1158 DeleteCacheEntry(pce,
true, msg);
1169 wxLogMessage(_T(
"Creating new chart"));
1171 if (chart_type == CHART_TYPE_KAP)
1174 else if (chart_type == CHART_TYPE_GEO)
1177 else if (chart_type == CHART_TYPE_MBTILES)
1180 else if (chart_type == CHART_TYPE_S57) {
1185 Chs57->SetNativeScale(cte.GetScale());
1190 ext.NLAT = cte.GetLatMax();
1191 ext.SLAT = cte.GetLatMin();
1192 ext.WLON = cte.GetLonMin();
1193 ext.ELON = cte.GetLonMax();
1194 Chs57->SetFullExtent(ext);
1197 else if (chart_type == CHART_TYPE_CM93) {
1202 Chcm93->SetNativeScale(cte.GetScale());
1207 ext.NLAT = cte.GetLatMax();
1208 ext.SLAT = cte.GetLatMin();
1209 ext.WLON = cte.GetLonMin();
1210 ext.ELON = cte.GetLonMax();
1211 Chcm93->SetFullExtent(ext);
1214 else if (chart_type == CHART_TYPE_CM93COMP) {
1220 Chcm93->SetNativeScale(cte.GetScale());
1225 ext.NLAT = cte.GetLatMax();
1226 ext.SLAT = cte.GetLatMin();
1227 ext.WLON = cte.GetLonMin();
1228 ext.ELON = cte.GetLonMax();
1229 Chcm93->SetFullExtent(ext);
1232 else if (chart_type == CHART_TYPE_PLUGIN) {
1233 wxFileName fn(ChartFullPath);
1234 wxString ext = fn.GetExt();
1235 ext.Prepend(_T(
"*."));
1236 wxString ext_upper = ext.MakeUpper();
1237 wxString ext_lower = ext.MakeLower();
1238 wxString chart_class_name;
1243 for (
auto &cd : m_ChartClassDescriptorArray) {
1244 if (cd.m_descriptor_type ==
1245 PLUGIN_DESCRIPTOR) {
1246 if (cd.m_search_mask == ext_upper) {
1247 chart_class_name = cd.m_class_name;
1250 if (cd.m_search_mask == ext_lower) {
1251 chart_class_name = cd.m_class_name;
1254 if (ChartFullPath.Matches(cd.m_search_mask)) {
1255 chart_class_name = cd.m_class_name;
1262 if (chart_class_name.Len()) {
1265 if (chart_family == CHART_FAMILY_VECTOR) LoadS57();
1271 wxLogMessage(_T(
"Unknown chart type"));
1277 s52plib *plib = ps52plib;
1278 wxString msg_fn(ChartFullPath);
1279 msg_fn.Replace(_T(
"%"), _T(
"%%"));
1282 if ((chart_family != CHART_FAMILY_VECTOR) ||
1283 ((chart_family == CHART_FAMILY_VECTOR) && plib)) {
1285 wxString::Format(_T(
"Initializing Chart %s"), msg_fn.c_str()));
1287 ir = Ch->Init(ChartFullPath, init_flag);
1288 Ch->SetColorScheme( GetColorScheme());
1290 wxLogMessage(wxString::Format(
1291 _T(
" No PLIB, Skipping vector chart %s"), msg_fn.c_str()));
1293 ir = INIT_FAIL_REMOVE;
1296 if (INIT_OK == ir) {
1302 pce->FullPath = ChartFullPath;
1304 pce->dbIndex = dbindex;
1307 pce->RecentTime = m_ticks;
1308 pce->n_lock = old_lock;
1310 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
1311 pChartCache->Add((
void *)pce);
1312 m_cache_mutex.Unlock();
1327 if (chart_type == CHART_TYPE_MBTILES) {
1328 wxFileName tileFile(ChartFullPath);
1330 wxULongLong tileSizeMB = tileFile.GetSize() >> 20;
1332 auto &config_array = ConfigMgr::Get().GetCanvasConfigArray();
1334 if (!CheckAnyCanvasExclusiveTileGroup() ||
1335 (tileSizeMB.GetLo() > 5000)) {
1338 bool b_clicked =
false;
1342 switch (g_canvasConfig) {
1344 cc = config_array.Item(0);
1348 b_clicked |= canvas->IsTileOverlayIndexInYesShow(dbindex);
1350 cc = config_array.Item(1);
1354 b_clicked |= canvas->IsTileOverlayIndexInYesShow(dbindex);
1358 cc = config_array.Item(0);
1362 b_clicked |= canvas->IsTileOverlayIndexInYesShow(dbindex);
1369 switch (g_canvasConfig) {
1371 cc = config_array.Item(0);
1374 if (canvas) canvas->AddTileOverlayIndexToNoShow(dbindex);
1376 cc = config_array.Item(1);
1379 if (canvas) canvas->AddTileOverlayIndexToNoShow(dbindex);
1383 cc = config_array.Item(0);
1386 if (canvas) canvas->AddTileOverlayIndexToNoShow(dbindex);
1393 }
else if (INIT_FAIL_REMOVE == ir)
1395 wxLogMessage(wxString::Format(_T(
"Problem initializing Chart %s"),
1403 DisableChart(ChartFullPath);
1404 }
else if ((INIT_FAIL_RETRY == ir) ||
1405 (INIT_FAIL_NOERROR ==
1408 wxLogMessage(wxString::Format(
1409 _T(
"Recoverable problem initializing Chart %s"), msg_fn.c_str()));
1414 if (INIT_OK != ir) {
1417 wxString::Format(_T(
" OpenChartFromStack... Error opening ")
1418 _T(
"chart %s ... return code %d"),
1419 msg_fn.c_str(), ir));
1433 bool ChartDB::DeleteCacheChart(
ChartBase *pDeleteCandidate) {
1434 bool retval =
false;
1436 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
1437 if (!isSingleChart(pDeleteCandidate)) {
1440 for (
unsigned int i = 0; i < pChartCache->GetCount(); i++) {
1442 if ((
ChartBase *)(pce->pChart) == pDeleteCandidate) {
1448 if (pce->n_lock > 0) pce->n_lock--;
1450 if (pce->n_lock == 0) {
1451 DeleteCacheEntry(pce);
1456 m_cache_mutex.Unlock();
1464 void ChartDB::ApplyColorSchemeToCachedCharts(ColorScheme cs) {
1469 if (wxMUTEX_NO_ERROR == m_cache_mutex.Lock()) {
1470 unsigned int nCache = pChartCache->GetCount();
1471 for (
unsigned int i = 0; i < nCache; i++) {
1474 if (Ch) Ch->SetColorScheme(cs,
true);
1477 m_cache_mutex.Unlock();
1487 ChartBase *ChartDB::OpenStackChartConditional(
1488 ChartStack *ps,
int index_start,
bool bSearchDir, ChartTypeEnum New_Type,
1489 ChartFamilyEnum New_Family_Fallback) {
1495 if (bSearchDir == 1)
1501 index = index_start;
1503 while ((index >= 0) && (index < ps->nEntry)) {
1504 ChartTypeEnum chart_type = (ChartTypeEnum)GetCSChartType(ps, index);
1505 if ((chart_type == New_Type) || (New_Type == CHART_TYPE_DONTCARE)) {
1506 ptc = OpenChartFromStack(ps, index);
1507 if (NULL != ptc)
break;
1509 index += delta_index;
1515 index = index_start;
1517 while ((index >= 0) && (index < ps->nEntry)) {
1518 ChartFamilyEnum chart_family = GetCSChartFamily(ps, index);
1519 if (chart_family == New_Family_Fallback) {
1520 ptc = OpenChartFromStack(ps, index);
1522 if (NULL != ptc)
break;
1524 index += delta_index;
1531 wxXmlDocument ChartDB::GetXMLDescription(
int dbIndex,
bool b_getGeom) {
1533 if (!IsValid() || (dbIndex >= GetChartTableEntries()))
return doc;
1535 bool b_remove = !IsChartInCache(dbIndex);
1537 wxXmlNode *pcell_node = NULL;
1542 ChartBase *pc = OpenChartFromDB(dbIndex, HEADER_ONLY);
1543 b_remove = !IsChartInCache(dbIndex);
1546 if (CHART_FAMILY_RASTER == (ChartFamilyEnum)cte.GetChartFamily()) {
1547 pcell_node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"chart" ));
1549 wxString path = GetDBChartFileName(dbIndex);
1550 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"path" ));
1551 pcell_node->AddChild(node);
1552 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), path);
1553 node->AddChild(tnode);
1555 wxFileName name(path);
1556 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"name" ));
1557 pcell_node->AddChild(node);
1558 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), name.GetName());
1559 node->AddChild(tnode);
1562 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"lname" ));
1563 pcell_node->AddChild(node);
1564 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), pc->GetName());
1565 node->AddChild(tnode);
1569 scale.Printf(_T(
"%d"), cte.GetScale());
1570 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"cscale" ));
1571 pcell_node->AddChild(node);
1572 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ),
scale);
1573 node->AddChild(tnode);
1575 wxDateTime file_date(cte.GetFileTime());
1576 file_date.MakeUTC();
1577 wxString sfile_date = file_date.FormatISODate();
1578 sfile_date += _T(
"T");
1579 sfile_date += file_date.FormatISOTime();
1580 sfile_date += _T(
"Z");
1582 new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"local_file_datetime_iso8601" ));
1583 pcell_node->AddChild(node);
1584 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), sfile_date);
1585 node->AddChild(tnode);
1588 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"source_edition" ));
1589 pcell_node->AddChild(node);
1590 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), pc->GetSE());
1591 node->AddChild(tnode);
1593 wxDateTime sdt = pc->GetEditionDate();
1594 wxString ssdt = _T(
"Unknown");
1595 if (sdt.IsValid()) ssdt = sdt.Format(_T(
"%Y%m%d"));
1597 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"source_date" ));
1598 pcell_node->AddChild(node);
1599 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), ssdt);
1600 node->AddChild(tnode);
1616 else if (CHART_FAMILY_VECTOR == (ChartFamilyEnum)cte.GetChartFamily()) {
1617 pcell_node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"cell" ));
1619 wxString path = GetDBChartFileName(dbIndex);
1620 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"path" ));
1621 pcell_node->AddChild(node);
1622 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), path);
1623 node->AddChild(tnode);
1625 wxFileName name(path);
1626 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"name" ));
1627 pcell_node->AddChild(node);
1628 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), name.GetName());
1629 node->AddChild(tnode);
1632 scale.Printf(_T(
"%d"), cte.GetScale());
1633 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"cscale" ));
1634 pcell_node->AddChild(node);
1635 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ),
scale);
1636 node->AddChild(tnode);
1638 wxDateTime file_date(cte.GetFileTime());
1639 file_date.MakeUTC();
1640 wxString sfile_date = file_date.FormatISODate();
1641 sfile_date += _T(
"T");
1642 sfile_date += file_date.FormatISOTime();
1643 sfile_date += _T(
"Z");
1645 new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"local_file_datetime_iso8601" ));
1646 pcell_node->AddChild(node);
1647 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), sfile_date);
1648 node->AddChild(tnode);
1651 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"edtn" ));
1652 pcell_node->AddChild(node);
1653 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), pc->GetSE());
1654 node->AddChild(tnode);
1659 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"isdt" ));
1660 pcell_node->AddChild(node);
1661 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), pcs57->GetISDT());
1662 node->AddChild(tnode);
1664 wxString LastUpdateDate;
1666 pcs57->ValidateAndCountUpdates(path, _T(
""), LastUpdateDate,
false);
1669 supdn.Printf(_T(
"%d"), updn);
1670 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"updn" ));
1671 pcell_node->AddChild(node);
1672 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), supdn);
1673 node->AddChild(tnode);
1675 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"uadt" ));
1676 pcell_node->AddChild(node);
1677 tnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), LastUpdateDate);
1678 node->AddChild(tnode);
1682 if (pcell_node && b_getGeom) {
1683 node =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"cov" ));
1684 pcell_node->AddChild(node);
1687 if (cte.GetnPlyEntries()) {
1688 wxXmlNode *panelnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"panel" ));
1689 node->AddChild(panelnode);
1692 panel_no.Printf(_T(
"%d"), 0);
1693 wxXmlNode *anode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), panel_no);
1694 panelnode->AddChild(anode);
1696 float *pf = cte.GetpPlyTable();
1697 for (
int j = 0; j < cte.GetnPlyEntries(); j++) {
1698 wxXmlNode *vnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"vertex" ));
1699 panelnode->AddChild(vnode);
1701 wxXmlNode *latnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"lat" ));
1702 vnode->AddChild(latnode);
1706 sl.Printf(_T(
"%.5f"), l);
1707 wxXmlNode *vtnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), sl);
1708 latnode->AddChild(vtnode);
1710 wxXmlNode *lonnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"lon" ));
1711 vnode->AddChild(lonnode);
1715 sll.Printf(_T(
"%.5f"), ll);
1716 wxXmlNode *vtlnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), sll);
1717 lonnode->AddChild(vtlnode);
1721 for (
int i = 0; i < cte.GetnAuxPlyEntries(); i++) {
1722 wxXmlNode *panelnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"panel" ));
1723 node->AddChild(panelnode);
1726 panel_no.Printf(_T(
"%d"), i + 1);
1727 wxXmlNode *anode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), panel_no);
1728 panelnode->AddChild(anode);
1730 float *pf = cte.GetpAuxPlyTableEntry(i);
1731 for (
int j = 0; j < cte.GetAuxCntTableEntry(i); j++) {
1732 wxXmlNode *vnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"vertex" ));
1733 panelnode->AddChild(vnode);
1735 wxXmlNode *latnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"lat" ));
1736 vnode->AddChild(latnode);
1740 sl.Printf(_T(
"%.5f"), l);
1741 wxXmlNode *vtnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), sl);
1742 latnode->AddChild(vtnode);
1744 wxXmlNode *lonnode =
new wxXmlNode(wxXML_ELEMENT_NODE, _T (
"lon" ));
1745 vnode->AddChild(lonnode);
1749 sll.Printf(_T(
"%.5f"), ll);
1750 wxXmlNode *vtlnode =
new wxXmlNode(wxXML_TEXT_NODE, _T (
"" ), sll);
1751 lonnode->AddChild(vtlnode);
1756 doc.SetRoot(pcell_node);
1758 if (b_remove) DeleteCacheChart(pc);
1763 bool ChartDB::CheckExclusiveTileGroup(
int canvasIndex) {
1771 auto &config_array = ConfigMgr::Get().GetCanvasConfigArray();
1773 switch (g_canvasConfig) {
1775 if (canvasIndex == 0) {
1776 cc = config_array.Item(0);
1777 if (cc) canvas = cc->canvas;
1779 cc = config_array.Item(1);
1780 if (cc) canvas = cc->canvas;
1785 cc = config_array.Item(0);
1786 if (cc) canvas = cc->canvas;
1789 if (!canvas)
return false;
1792 if (canvas->m_groupIndex == m_checkGroupIndex[canvasIndex])
1793 return m_checkedTileOnly[canvasIndex];
1796 bool rv = IsNonMBTileInGroup(canvas->m_groupIndex);
1798 m_checkGroupIndex[canvasIndex] = canvas->m_groupIndex;
1799 m_checkedTileOnly[canvasIndex] = !rv;
1804 bool ChartDB::CheckAnyCanvasExclusiveTileGroup() {
1812 auto &config_array = ConfigMgr::Get().GetCanvasConfigArray();
1814 switch (g_canvasConfig) {
1816 cc = config_array.Item(0);
1820 if (canvas->m_groupIndex == m_checkGroupIndex[0])
1821 rv |= m_checkedTileOnly[0];
1825 cc = config_array.Item(1);
1829 if (canvas->m_groupIndex == m_checkGroupIndex[1])
1830 rv |= m_checkedTileOnly[1];
1836 cc = config_array.Item(0);
1840 if (canvas->m_groupIndex == m_checkGroupIndex[0])
1841 rv |= m_checkedTileOnly[0];
1872 bool G_FloatPtInPolygon(
MyFlPoint *rgpts,
int wnumpts,
float x,
float y)
1888 for (i = 0, ppt = rgpts; i < wnumpts - 1; i++, ppt++) {
1891 if (Intersect(pt0, pt2, *ppt, *(ppt1))) wnumintsct++;
1895 if (Intersect(pt0, pt2, *ppt, *rgpts)) wnumintsct++;
1901 if (!(wnumintsct & 1)) {
1902 for (i = 0, ppt = rgpts; i < wnumpts; i++, ppt++) {
1903 if (((*ppt).x == x) && ((*ppt).y == y))
return true;
1924 return (((CCW(p1, p2, p3) * CCW(p1, p2, p4)) <= 0) &&
1925 ((CCW(p3, p4, p1) * CCW(p3, p4, p2) <= 0)));
1955 return ((dx1 * dy2 > dy1 * dx2) ? 1 : -1);