From 8b043c87ab9f9dbf585ad05e18e02c755e9ded6b Mon Sep 17 00:00:00 2001 From: Xun Li Date: Mon, 9 Oct 2023 22:45:37 -0700 Subject: [PATCH] Fix: Migrate Stamen basemap to Stadia (#2463) --- .vscode/settings.json | 6 + BuildTools/CommonDistFiles/cache.sqlite | Bin 9216 -> 9216 bytes BuildTools/macosx/code_sign.py | 34 +- BuildTools/macosx/install_name.py | 18 + BuildTools/windows/GeoDa.vs2019.vcxproj | 16 +- DialogTools/BasemapConfDlg.cpp | 123 +- DialogTools/BasemapConfDlg.h | 50 +- Explore/Basemap.cpp | 1522 +++++++++++------------ Explore/Basemap.h | 796 ++++++------ GdaConst.cpp | 6 +- rc/GdaAppResources.cpp | 296 +++-- rc/dialogs.xrc | 17 +- 12 files changed, 1443 insertions(+), 1441 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index d849728e7..9a6156f38 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -23,5 +23,11 @@ }, "python.formatting.provider": "none", "activestate.promptRuntimeCreation": false, + "files.associations": { + "cmath": "cpp", + "typeindex": "cpp", + "typeinfo": "cpp", + "algorithm": "cpp" + }, "C_Cpp.default.compilerPath": "/usr/bin/clang++" } diff --git a/BuildTools/CommonDistFiles/cache.sqlite b/BuildTools/CommonDistFiles/cache.sqlite index 273d75cb87cf3a591aa69415f0e986e162592b36..ad143fc4b7e084f9ce8d2c236f61cd60ef9c04e5 100644 GIT binary patch delta 354 zcmZqhXz-XI&B@2Wz`zN@6IHAk`8Fmj5#DU8Aj_mwl9`iQtXEu;n39>8n^;h+mzJ4smqxbd2|QjpPaj%j<>Y=cN|;WR|4rWR#Q? z6kF*-wBt3i1f(ZE2dHUsqnNNT7Skuc6_X~$aa!V1*tBnU5kJ7<&cqC?(vl1fj10|EEK<$;4Ov-?=Q(a} dk*Q#wd{@Mh2WSi%*f1b5`Jsr}W;=yL%m6Y&ZQ1|; delta 163 zcmZqhXz-XI&B@Eaz`zN_Y!g+i8F@D*ED>e_@)$PDi@asD_b*7z3oa>2O)c?FEYNex zNleam%u5N+$Sg_K$tWo)D7MnqFUibFE!Hc~P0LA4D%Q&{O4m;+*Ue2V$ji*0{99CQ zbCl9D#!V6|ENqMnwG0fk6B|`onJ*j9%b)Bm Disabled temp\OpenCL\sdk\include;temp\boost;temp\wxWidgets\lib\vc_dll\mswud;temp\wxWidgets\include;temp\json_spirit_v4.08;temp\eigen3;temp\spectra\include;libraries\include;%(AdditionalIncludeDirectories) - WIN32;DEBUG;_DEBUG;_WINDOWS;__WXMSW__;__WXDEBUG__;WXUSINGDLL;_UNICODE;%(PreprocessorDefinitions) + WIN32;DEBUG;_DEBUG;_WINDOWS;NOMINMAX;__WXMSW__;__WXDEBUG__;WXUSINGDLL;_UNICODE;%(PreprocessorDefinitions) false EnableFastChecks MultiThreadedDebugDLL @@ -103,7 +103,7 @@ true - __WXMSW__;_UNICODE;_WINDOWS;NOPCH;WXUSINGDLL;%(PreprocessorDefinitions) + NOMINMAX;__WXMSW__;_UNICODE;_WINDOWS;NOPCH;WXUSINGDLL;%(PreprocessorDefinitions) 0x0409 temp\wxWidgets\include;%(AdditionalIncludeDirectories) @@ -122,7 +122,7 @@ Disabled temp\OpenCL\sdk\include;temp\wxWidgets\lib\vc_dll\mswud;temp\wxWidgets\include;temp\boost\include;temp\json_spirit_v4.08;temp\eigen3;temp\spectra\include;libraries\include;%(AdditionalIncludeDirectories) - WIN32;DEBUG;_DEBUG;_WINDOWS;__WXMSW__;__WXDEBUG__;WXUSINGDLL;_UNICODE;__PROJ6__%(PreprocessorDefinitions) + WIN32;DEBUG;_DEBUG;_WINDOWS;NOMINMAX;__WXMSW__;__WXDEBUG__;WXUSINGDLL;_UNICODE;__PROJ6__%(PreprocessorDefinitions) false EnableFastChecks MultiThreadedDebugDLL @@ -139,7 +139,7 @@ Default - __WXMSW__;_UNICODE;_WINDOWS;NOPCH;WXUSINGDLL;%(PreprocessorDefinitions) + NOMINMAX;__WXMSW__;_UNICODE;_WINDOWS;NOPCH;WXUSINGDLL;%(PreprocessorDefinitions) 0x0409 temp\wxWidgets\include;%(AdditionalIncludeDirectories) @@ -160,7 +160,7 @@ temp\OpenCL\sdk\include;temp\wxWidgets\lib\vc_dll\mswu;temp\wxWidgets\include;temp\boost\include;temp\json_spirit_v4.08;temp\eigen3;temp\spectra\include;libraries\include;%(AdditionalIncludeDirectories) - WIN32;_WINDOWS;__WXMSW__;__NO_VC_CRTDBG__;WXUSINGDLL;_UNICODE;__PROJ6__;BOOST_BIND_NO_PLACEHOLDERS;%(PreprocessorDefinitions) + WIN32;_WINDOWS;NOMINMAX;__WXMSW__;__NO_VC_CRTDBG__;WXUSINGDLL;_UNICODE;__PROJ6__;BOOST_BIND_NO_PLACEHOLDERS;%(PreprocessorDefinitions) MultiThreadedDLL @@ -170,7 +170,7 @@ /MP %(AdditionalOptions) - __WXMSW__;_UNICODE;_WINDOWS;NOPCH;WXUSINGDLL;%(PreprocessorDefinitions) + NOMINMAX;__WXMSW__;_UNICODE;_WINDOWS;NOPCH;WXUSINGDLL;%(PreprocessorDefinitions) 0x0409 temp\wxWidgets\include;%(AdditionalIncludeDirectories) @@ -187,7 +187,7 @@ temp\OpenCL\sdk\include;temp\wxWidgets\lib\vc_x64_dll\mswu;temp\wxWidgets\include;temp\boost\include;temp\json_spirit_v4.08;temp\eigen3;temp\spectra\include;libraries\include;%(AdditionalIncludeDirectories) - WIN32;_WINDOWS;__WXMSW__;__NO_VC_CRTDBG__;WXUSINGDLL;_UNICODE;__PROJ6__;BOOST_BIND_NO_PLACEHOLDERS;%(PreprocessorDefinitions) + WIN32;_WINDOWS;NOMINMAX;__WXMSW__;__NO_VC_CRTDBG__;WXUSINGDLL;_UNICODE;__PROJ6__;BOOST_BIND_NO_PLACEHOLDERS;%(PreprocessorDefinitions) MultiThreadedDLL @@ -198,7 +198,7 @@ true - __WXMSW__;_UNICODE;_WINDOWS;NOPCH;WXUSINGDLL;UNICODE;%(PreprocessorDefinitions) + NOMINMAX;__WXMSW__;_UNICODE;_WINDOWS;NOPCH;WXUSINGDLL;UNICODE;%(PreprocessorDefinitions) 0x0409 temp\wxWidgets\include;%(AdditionalIncludeDirectories) diff --git a/DialogTools/BasemapConfDlg.cpp b/DialogTools/BasemapConfDlg.cpp index 18e77535a..88b3bca4a 100644 --- a/DialogTools/BasemapConfDlg.cpp +++ b/DialogTools/BasemapConfDlg.cpp @@ -17,84 +17,81 @@ * along with this program. If not, see . */ +#include "BasemapConfDlg.h" - -#include -#include -#include #include +#include #include #include +#include #include +#include + +#include "../GdaConst.h" #include "../Project.h" #include "../ShapeOperations/OGRDataAdapter.h" -#include "../GdaConst.h" -#include "BasemapConfDlg.h" - -BEGIN_EVENT_TABLE( BasemapConfDlg, wxDialog ) -EVT_BUTTON( wxID_OK, BasemapConfDlg::OnOkClick ) -EVT_BUTTON( XRCID("ID_NOKIA_RESET"), BasemapConfDlg::OnResetClick ) +BEGIN_EVENT_TABLE(BasemapConfDlg, wxDialog) +EVT_BUTTON(wxID_OK, BasemapConfDlg::OnOkClick) +EVT_BUTTON(XRCID("ID_NOKIA_RESET"), BasemapConfDlg::OnResetClick) END_EVENT_TABLE() -BasemapConfDlg::BasemapConfDlg(wxWindow* parent, Project* _p, - wxWindowID id, - const wxString& title, - const wxPoint& pos, - const wxSize& size ) -{ - - wxLogMessage("Open BasemapConfDlg."); - p = _p; - - basemap_resources = wxString::FromUTF8(GdaConst::gda_basemap_sources.mb_str()); - std::vector items = OGRDataAdapter::GetInstance().GetHistory("gda_basemap_sources"); - if (items.size()>0) { - basemap_resources = items[0]; - } - - wxString encoded_str= wxString::FromUTF8((const char*)basemap_resources.mb_str()); - if (encoded_str.IsEmpty() == false) { - basemap_resources = encoded_str; - } - - wxXmlResource::Get()->LoadDialog(this, GetParent(), "IDD_BASEMAP_CONF_DLG"); - FindWindow(XRCID("wxID_OK"))->Enable(true); - m_txt_nokia_uname = XRCCTRL(*this, "IDC_NOKIA_USERNAME",wxTextCtrl); - m_txt_nokia_key = XRCCTRL(*this, "IDC_NOKIA_KEY",wxTextCtrl); - m_txt_basemap = XRCCTRL(*this, "IDC_BASEMAP_SOURCE",wxTextCtrl); - m_txt_basemap->SetValue(basemap_resources); - - SetParent(parent); - SetPosition(pos); - Centre(); +BasemapConfDlg::BasemapConfDlg(wxWindow* parent, Project* _p, wxWindowID id, const wxString& title, const wxPoint& pos, + const wxSize& size) { + wxLogMessage("Open BasemapConfDlg."); + p = _p; + + basemap_resources = wxString::FromUTF8(GdaConst::gda_basemap_sources.mb_str()); + std::vector items = OGRDataAdapter::GetInstance().GetHistory("gda_basemap_sources"); + if (items.size() > 0) { + basemap_resources = items[0]; + } + + wxString encoded_str = wxString::FromUTF8((const char*)basemap_resources.mb_str()); + if (encoded_str.IsEmpty() == false) { + basemap_resources = encoded_str; + } + + wxXmlResource::Get()->LoadDialog(this, GetParent(), "IDD_BASEMAP_CONF_DLG"); + FindWindow(XRCID("wxID_OK"))->Enable(true); + m_txt_stadia_key = XRCCTRL(*this, "IDC_STADIA_KEY", wxTextCtrl); + m_txt_nokia_uname = XRCCTRL(*this, "IDC_NOKIA_USERNAME", wxTextCtrl); + m_txt_nokia_key = XRCCTRL(*this, "IDC_NOKIA_KEY", wxTextCtrl); + m_txt_basemap = XRCCTRL(*this, "IDC_BASEMAP_SOURCE", wxTextCtrl); + m_txt_basemap->SetValue(basemap_resources); + + SetParent(parent); + SetPosition(pos); + Centre(); } +void BasemapConfDlg::OnOkClick(wxCommandEvent& event) { + wxLogMessage("BasemapConfDlg: Click OK Button."); + + wxString stadie_key(m_txt_stadia_key->GetValue().Trim()); + if (!stadie_key.empty()) { + OGRDataAdapter::GetInstance().AddEntry("stadia_key", stadie_key); + } -void BasemapConfDlg::OnOkClick( wxCommandEvent& event ) -{ - wxLogMessage("BasemapConfDlg: Click OK Button."); - - wxString nokia_uname(m_txt_nokia_uname->GetValue().Trim()); - wxString nokia_key(m_txt_nokia_key->GetValue().Trim()); - - if (!nokia_uname.empty() && !nokia_key.empty()) { - OGRDataAdapter::GetInstance().AddEntry("nokia_user", nokia_uname); - OGRDataAdapter::GetInstance().AddEntry("nokia_key", nokia_key); - } - if (m_txt_basemap->GetValue() != basemap_resources) { - OGRDataAdapter::GetInstance().AddEntry("gda_basemap_sources", m_txt_basemap->GetValue()); - } - EndDialog(wxID_OK); + wxString nokia_uname(m_txt_nokia_uname->GetValue().Trim()); + wxString nokia_key(m_txt_nokia_key->GetValue().Trim()); + if (!nokia_uname.empty() && !nokia_key.empty()) { + OGRDataAdapter::GetInstance().AddEntry("nokia_user", nokia_uname); + OGRDataAdapter::GetInstance().AddEntry("nokia_key", nokia_key); + } + + if (m_txt_basemap->GetValue() != basemap_resources) { + OGRDataAdapter::GetInstance().AddEntry("gda_basemap_sources", m_txt_basemap->GetValue()); + } + EndDialog(wxID_OK); } -void BasemapConfDlg::OnResetClick( wxCommandEvent& event ) -{ - wxLogMessage("BasemapConfDlg: Click Reset Button."); +void BasemapConfDlg::OnResetClick(wxCommandEvent& event) { + wxLogMessage("BasemapConfDlg: Click Reset Button."); + + m_txt_basemap->SetValue(GdaConst::gda_basemap_sources); + OGRDataAdapter::GetInstance().AddEntry("gda_basemap_sources", GdaConst::gda_basemap_sources); - m_txt_basemap->SetValue(GdaConst::gda_basemap_sources); - OGRDataAdapter::GetInstance().AddEntry("gda_basemap_sources", GdaConst::gda_basemap_sources); - - EndDialog(wxID_OK); + EndDialog(wxID_OK); } diff --git a/DialogTools/BasemapConfDlg.h b/DialogTools/BasemapConfDlg.h index 65fbe94e8..3be4e7b5f 100644 --- a/DialogTools/BasemapConfDlg.h +++ b/DialogTools/BasemapConfDlg.h @@ -19,40 +19,38 @@ #ifndef __GEODA_CENTER_BASEMAP_CONF_DLG_H__ #define __GEODA_CENTER_BASEMAP_CONF_DLG_H__ - -#include -#include #include +#include #include -#include +#include #include -#include +#include + +#include #include "../Project.h" class Project; -class BasemapConfDlg: public wxDialog -{ -public: - BasemapConfDlg(wxWindow* parent, Project* p, - wxWindowID id = wxID_ANY, - const wxString& title = _("Basemap Configuration Dialog"), - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize ); - -private: - Project* p; - wxString basemap_resources; - - wxTextCtrl* m_txt_nokia_uname; - wxTextCtrl* m_txt_nokia_key; - wxTextCtrl* m_txt_basemap; - - void OnOkClick( wxCommandEvent& event ); - void OnResetClick( wxCommandEvent& event ); - - DECLARE_EVENT_TABLE() +class BasemapConfDlg : public wxDialog { + public: + BasemapConfDlg(wxWindow* parent, Project* p, wxWindowID id = wxID_ANY, + const wxString& title = _("Basemap Configuration Dialog"), const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize); + + private: + Project* p; + wxString basemap_resources; + + wxTextCtrl* m_txt_stadia_key; + wxTextCtrl* m_txt_nokia_uname; + wxTextCtrl* m_txt_nokia_key; + wxTextCtrl* m_txt_basemap; + + void OnOkClick(wxCommandEvent& event); + void OnResetClick(wxCommandEvent& event); + + DECLARE_EVENT_TABLE() }; #endif diff --git a/Explore/Basemap.cpp b/Explore/Basemap.cpp index a40d29b06..969b5da68 100644 --- a/Explore/Basemap.cpp +++ b/Explore/Basemap.cpp @@ -2,7 +2,7 @@ * GeoDa TM, Copyright (C) 2011-2015 by Luc Anselin - all rights reserved * * This file is part of GeoDa. - * + * * GeoDa is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -17,31 +17,35 @@ * along with this program. If not, see . */ -#include -#include -#include #include #include -#define BOOST_PHOENIX_STL_TUPLE_H_ -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include -#include -#include -#include +#define BOOST_PHOENIX_STL_TUPLE_H_ +#include +#include #include +#include #include #include #include +#include #include -#include +#include #include -#include +#include + +#include +#include +#include +#include -#include #include "../GdaConst.h" #include "../GeneralWxUtils.h" #include "../ShapeOperations/OGRDataAdapter.h" @@ -49,839 +53,789 @@ using namespace Gda; -BasemapItem Gda::GetBasemapSelection(int idx, wxString basemap_sources) -{ - BasemapItem basemap_item; - - idx = idx - 1; // first item [0] is choice "no basemap" - wxString encoded_str= wxString::FromUTF8((const char*)basemap_sources.mb_str()); - if (encoded_str.IsEmpty() == false) { - basemap_sources = encoded_str; - } - std::vector keys; - wxString newline; - if (basemap_sources.Find("\r\n") != wxNOT_FOUND) { - newline = "\r\n"; - } else if (basemap_sources.Find("\r") != wxNOT_FOUND) { - newline = "\r"; - } else if (basemap_sources.Find("\n") != wxNOT_FOUND) { - newline = "\n"; - } - if (newline.IsEmpty() == false) { - wxStringTokenizer tokenizer(basemap_sources, newline); - while ( tokenizer.HasMoreTokens() ) { - wxString token = tokenizer.GetNextToken(); - keys.push_back(token.Trim()); - } - if (idx >= 0 && idx < keys.size()) { - wxString basemap_source = keys[idx]; - wxUniChar comma = ','; - int comma_pos = basemap_source.Find(comma); - if ( comma_pos != wxNOT_FOUND ) { - // group.name,url - wxString group_n_name = basemap_source.BeforeFirst(comma); - wxString url = basemap_source.AfterFirst(comma); - wxUniChar dot = '.'; - wxString group = group_n_name.Before(dot); - wxString name = group_n_name.After(dot); - if (!group.IsEmpty() && !name.IsEmpty()) { - basemap_item.group = group; - basemap_item.name = name; - basemap_item.url = url; - } - } +BasemapItem Gda::GetBasemapSelection(int idx, wxString basemap_sources) { + BasemapItem basemap_item; + + idx = idx - 1; // first item [0] is choice "no basemap" + wxString encoded_str = wxString::FromUTF8((const char*)basemap_sources.mb_str()); + if (encoded_str.IsEmpty() == false) { + basemap_sources = encoded_str; + } + std::vector keys; + wxString newline; + if (basemap_sources.Find("\r\n") != wxNOT_FOUND) { + newline = "\r\n"; + } else if (basemap_sources.Find("\r") != wxNOT_FOUND) { + newline = "\r"; + } else if (basemap_sources.Find("\n") != wxNOT_FOUND) { + newline = "\n"; + } + if (newline.IsEmpty() == false) { + wxStringTokenizer tokenizer(basemap_sources, newline); + while (tokenizer.HasMoreTokens()) { + wxString token = tokenizer.GetNextToken(); + keys.push_back(token.Trim()); + } + if (idx >= 0 && idx < keys.size()) { + wxString basemap_source = keys[idx]; + wxUniChar comma = ','; + int comma_pos = basemap_source.Find(comma); + if (comma_pos != wxNOT_FOUND) { + // group.name,url + wxString group_n_name = basemap_source.BeforeFirst(comma); + wxString url = basemap_source.AfterFirst(comma); + wxUniChar dot = '.'; + wxString group = group_n_name.Before(dot); + wxString name = group_n_name.After(dot); + if (!group.IsEmpty() && !name.IsEmpty()) { + basemap_item.group = group; + basemap_item.name = name; + basemap_item.url = url; } + } } - return basemap_item; + } + return basemap_item; } std::vector Gda::ExtractBasemapResources(wxString basemap_sources) { - std::vector group_names; - std::map group_dict; - - wxString encoded_str= wxString::FromUTF8((const char*)basemap_sources.mb_str()); - if (encoded_str.IsEmpty() == false) { - basemap_sources = encoded_str; - } - std::vector keys; - wxString newline; - if (basemap_sources.Find("\r\n") != wxNOT_FOUND) { - newline = "\r\n"; - } else if (basemap_sources.Find("\r") != wxNOT_FOUND) { - newline = "\r"; - } else if (basemap_sources.Find("\n") != wxNOT_FOUND) { - newline = "\n"; - } - if (newline.IsEmpty() == false) { - wxStringTokenizer tokenizer(basemap_sources, newline); - while ( tokenizer.HasMoreTokens() ) { - wxString token = tokenizer.GetNextToken(); - keys.push_back(token.Trim()); + std::vector group_names; + std::map group_dict; + + wxString encoded_str = wxString::FromUTF8((const char*)basemap_sources.mb_str()); + if (encoded_str.IsEmpty() == false) { + basemap_sources = encoded_str; + } + std::vector keys; + wxString newline; + if (basemap_sources.Find("\r\n") != wxNOT_FOUND) { + newline = "\r\n"; + } else if (basemap_sources.Find("\r") != wxNOT_FOUND) { + newline = "\r"; + } else if (basemap_sources.Find("\n") != wxNOT_FOUND) { + newline = "\n"; + } + if (newline.IsEmpty() == false) { + wxStringTokenizer tokenizer(basemap_sources, newline); + while (tokenizer.HasMoreTokens()) { + wxString token = tokenizer.GetNextToken(); + keys.push_back(token.Trim()); + } + for (int i = 0; i < keys.size(); i++) { + wxString basemap_source = keys[i]; + wxUniChar comma = ','; + int comma_pos = basemap_source.Find(comma); + if (comma_pos != wxNOT_FOUND) { + // group.name,url + wxString group_n_name = basemap_source.BeforeFirst(comma); + wxString url = basemap_source.AfterFirst(comma); + wxUniChar dot = '.'; + wxString group = group_n_name.Before(dot); + wxString name = group_n_name.After(dot); + if (group.IsEmpty() || name.IsEmpty()) { + continue; } - for (int i=0; i groups; - for (int i=0; i groups; + for (int i = 0; i < group_names.size(); i++) { + groups.push_back(group_dict[group_names[i]]); + } + return groups; } -Basemap::~Basemap() { - //done = true; - if (screen) { - delete screen; - screen = 0; - } - if (map) { - delete map; - map = 0; - } - if (origMap) { - delete origMap; - origMap = 0; - } - if (poCT) { - delete poCT; - poCT = 0; - } +XYFraction::XYFraction(double _x, double _y) { + x = _x; + y = _y; + xfrac = modf(_x, &xint); + yfrac = modf(_y, &yint); } -void Basemap::CleanCache() -{ - wxString filename; - filename << cachePath << separator(); - wxDir dir(filename); - if (dir.IsOpened() ) { - wxString file; - bool cont = dir.GetFirst(&file); - while ( cont ) { - file = filename + wxFileName::GetPathSeparator()+ file; - if(wxFileName::FileExists(file)) wxRemoveFile(file); - cont = dir.GetNext(&file); - } - } -} +Basemap::Basemap(BasemapItem& _basemap_item, Screen* _screen, MapLayer* _map, MapLayer* _origMap, wxString _cachePath, + OGRCoordinateTransformation* _poCT, double _scale_factor) { + basemap_item = _basemap_item; + screen = _screen; + map = _map; + origMap = _origMap; + cachePath = _cachePath; + poCT = _poCT; + scale_factor = _scale_factor; -void Basemap::SetupMapType(BasemapItem& _basemap_item) -{ - basemap_item = _basemap_item; - basemapName = basemap_item.group + "." + basemap_item.name; - basemapUrl = basemap_item.url; - - wxString content_type = GetContentType(); - if (content_type.IsEmpty()) content_type = GetContentType(); - - content_type.MakeUpper(); - if (content_type.Find("PNG") != wxNOT_FOUND) { - imageSuffix = ".png"; - } else if (content_type.Find("JPG") != wxNOT_FOUND) { - imageSuffix = ".jpg"; - } else if (content_type.Find("JPEG") != wxNOT_FOUND) { - imageSuffix = ".jpeg"; - } else if (content_type.Find("GIF") != wxNOT_FOUND) { - imageSuffix = ".gif"; - } else { - imageSuffix = ".png"; - } - - // get a latest HERE account - std::vector nokia_user = OGRDataAdapter::GetInstance().GetHistory("nokia_user"); - if (!nokia_user.empty()) { - wxString user = nokia_user[0]; - if (!user.empty()) { - nokia_id = user; - } - } - - std::vector nokia_key = OGRDataAdapter::GetInstance().GetHistory("nokia_key"); - if (!nokia_key.empty()) { - wxString key = nokia_key[0]; - if (!key.empty()) { - nokia_code = key; - } - } - - GetTiles(); + isPan = false; + panX = 0; + panY = 0; + + start_download = false; + n_tasks = 0; + complete_tasks = 0; + isTileReady = false; + isTileDrawn = false; + + wxInitAllImageHandlers(); + curl_global_init(CURL_GLOBAL_ALL); + + GetEasyZoomLevel(); + SetupMapType(basemap_item); } -void Basemap::Reset() -{ - map->north = origMap->north; - map->south= origMap->south; - map->west= origMap->west; - map->east= origMap->east; +Basemap::~Basemap() { + // done = true; + if (screen) { + delete screen; + screen = 0; + } + if (map) { + delete map; + map = 0; + } + if (origMap) { + delete origMap; + origMap = 0; + } + if (poCT) { + delete poCT; + poCT = 0; + } +} + +void Basemap::CleanCache() { + wxString filename; + filename << cachePath << separator(); + wxDir dir(filename); + if (dir.IsOpened()) { + wxString file; + bool cont = dir.GetFirst(&file); + while (cont) { + file = filename + wxFileName::GetPathSeparator() + file; + if (wxFileName::FileExists(file)) wxRemoveFile(file); + cont = dir.GetNext(&file); + } + } +} + +void Basemap::SetupMapType(BasemapItem& _basemap_item) { + basemap_item = _basemap_item; + basemapName = basemap_item.group + "." + basemap_item.name; + basemapUrl = basemap_item.url; + + wxString content_type = GetContentType(); + if (content_type.IsEmpty()) content_type = GetContentType(); + + content_type.MakeUpper(); + if (content_type.Find("PNG") != wxNOT_FOUND) { + imageSuffix = ".png"; + } else if (content_type.Find("JPG") != wxNOT_FOUND) { + imageSuffix = ".jpg"; + } else if (content_type.Find("JPEG") != wxNOT_FOUND) { + imageSuffix = ".jpeg"; + } else if (content_type.Find("GIF") != wxNOT_FOUND) { + imageSuffix = ".gif"; + } else { + imageSuffix = ".png"; + } + + // get a latest Stadia key + std::vector stadia_user = OGRDataAdapter::GetInstance().GetHistory("stadia_key"); + if (!stadia_user.empty()) { + wxString key = stadia_user[0]; + if (!key.empty()) { + stadia_key = key; + } + } + + // get a latest HERE account + std::vector nokia_user = OGRDataAdapter::GetInstance().GetHistory("nokia_user"); + if (!nokia_user.empty()) { + wxString user = nokia_user[0]; + if (!user.empty()) { + nokia_id = user; + } + } + + std::vector nokia_key = OGRDataAdapter::GetInstance().GetHistory("nokia_key"); + if (!nokia_key.empty()) { + wxString key = nokia_key[0]; + if (!key.empty()) { + nokia_code = key; + } + } + + GetTiles(); +} + +void Basemap::Reset() { + map->north = origMap->north; + map->south = origMap->south; + map->west = origMap->west; + map->east = origMap->east; + GetEasyZoomLevel(); + // SetupMapType(basemap_item); + GetTiles(); +} + +void Basemap::Reset(int map_type) { + map->north = origMap->north; + map->south = origMap->south; + map->west = origMap->west; + map->east = origMap->east; + GetEasyZoomLevel(); + SetupMapType(basemap_item); +} + +void Basemap::Extent(double _n, double _w, double _s, double _e, OGRCoordinateTransformation* _poCT) { + if (poCT != NULL) { + poCT->Transform(1, &_w, &_n); + poCT->Transform(1, &_e, &_s); + } + map->north = _n; + map->south = _s; + map->west = _w; + map->east = _e; + origMap->north = _n; + origMap->south = _s; + origMap->west = _w; + origMap->east = _e; + GetEasyZoomLevel(); + // SetupMapType(basemap_item); + GetTiles(); +} + +void Basemap::ResizeScreen(int _width, int _height) { + if (screen) { + screen->width = _width; + screen->height = _height; + + // isTileDrawn = false; GetEasyZoomLevel(); - //SetupMapType(basemap_item); + + // SetupMapType(basemap_item); GetTiles(); + } } -void Basemap::Reset(int map_type) -{ - map->north = origMap->north; - map->south= origMap->south; - map->west= origMap->west; - map->east= origMap->east; - GetEasyZoomLevel(); - SetupMapType(basemap_item); -} +void Basemap::Pan(int x0, int y0, int x1, int y1) { + XYFraction origXY((x0 + leftP + offsetX) / 256.0, (y0 + topP + offsetY) / 256.0); + XYFraction newXY((x1 + leftP + offsetX) / 256.0, (y1 + topP + offsetY) / 256.0); -void Basemap::Extent(double _n, double _w, double _s, double _e, - OGRCoordinateTransformation *_poCT) -{ - if (poCT!= NULL) { - poCT->Transform(1, &_w, &_n); - poCT->Transform(1, &_e, &_s); - } - map->north = _n; - map->south= _s; - map->west= _w; - map->east= _e; - origMap->north = _n; - origMap->south= _s; - origMap->west= _w; - origMap->east= _e; - GetEasyZoomLevel(); - //SetupMapType(basemap_item); + LatLng* p0 = XYToLatLng(origXY, true); + LatLng* p1 = XYToLatLng(newXY, true); + + double offsetLat = p1->lat - p0->lat; + double offsetLon = p1->lng - p0->lng; + + delete p0; + delete p1; + + if (map->Pan(-offsetLat, -offsetLon)) { + // isTileDrawn = false; + // isTileReady = false; GetTiles(); + } } -void Basemap::ResizeScreen(int _width, int _height) -{ - if (screen) { - screen->width = _width; - screen->height = _height; +bool Basemap::Zoom(bool is_zoomin, int x0, int y0, int x1, int y1) { + if (is_zoomin == false && zoom <= 1) return false; - //isTileDrawn = false; - GetEasyZoomLevel(); + int left = x0 < x1 ? x0 : x1; + int right = x0 < x1 ? x1 : x0; + int top = y0 > y1 ? y1 : y0; + int bottom = y0 > y1 ? y0 : y1; - //SetupMapType(basemap_item); - GetTiles(); - } -} + if (is_zoomin == false) { + left = 0 - left; + top = 0 - top; + right = screen->width * 2 - right; + bottom = screen->height * 2 - bottom; + } -void Basemap::Pan(int x0, int y0, int x1, int y1) -{ - XYFraction origXY((x0 + leftP + offsetX)/256.0, (y0 + topP + offsetY)/256.0); - XYFraction newXY((x1 + leftP + offsetX)/256.0, (y1 + topP + offsetY)/256.0); - - LatLng* p0 = XYToLatLng(origXY, true); - LatLng* p1 = XYToLatLng(newXY, true); - - double offsetLat = p1->lat - p0->lat; - double offsetLon = p1->lng - p0->lng; - - delete p0; - delete p1; - - if (map->Pan(-offsetLat, -offsetLon)) { - //isTileDrawn = false; - //isTileReady = false; - GetTiles(); - } -} + XYFraction origXY((left + leftP + offsetX) / 256.0, (top + topP + offsetY) / 256.0); + XYFraction newXY((right + leftP + offsetX) / 256.0, (bottom + topP + offsetY) / 256.0); -bool Basemap::Zoom(bool is_zoomin, int x0, int y0, int x1, int y1) -{ - if (is_zoomin == false && zoom <= 1) - return false; - - int left = x0 < x1 ? x0 : x1; - int right = x0 < x1 ? x1 : x0; - int top = y0 > y1 ? y1 : y0; - int bottom = y0 > y1 ? y0 : y1; - - if (is_zoomin == false) { - left = 0 - left; - top = 0 - top; - right = screen->width * 2 - right; - bottom = screen->height * 2 - bottom; - } - - XYFraction origXY((left + leftP + offsetX)/256.0, (top + topP + offsetY)/256.0); - XYFraction newXY((right + leftP + offsetX)/256.0, (bottom + topP + offsetY)/256.0); - - LatLng* p0 = XYToLatLng(origXY, true); - LatLng* p1 = XYToLatLng(newXY, true); - - double north = p0->lat; - double west = p0->lng; - double south = p1->lat; - double east = p1->lng; - - delete p0; - delete p1; - - map->UpdateExtent(west, south, east, north); - - //isTileDrawn = false; - //isTileReady = false; - GetEasyZoomLevel(); - GetTiles(); - return true; + LatLng* p0 = XYToLatLng(origXY, true); + LatLng* p1 = XYToLatLng(newXY, true); + + double north = p0->lat; + double west = p0->lng; + double south = p1->lat; + double east = p1->lng; + + delete p0; + delete p1; + + map->UpdateExtent(west, south, east, north); + + // isTileDrawn = false; + // isTileReady = false; + GetEasyZoomLevel(); + GetTiles(); + return true; } -bool Basemap::IsExtentChanged() -{ - bool no_change = (origMap->north == map->north && - origMap->south == map->south && - origMap->east == map->east && - origMap->west == map->west); - return !no_change; +bool Basemap::IsExtentChanged() { + bool no_change = (origMap->north == map->north && origMap->south == map->south && origMap->east == map->east && + origMap->west == map->west); + return !no_change; } -void Basemap::ZoomIn(int mouseX, int mouseY) -{ - // 2X by default - map->ZoomIn(); - GetEasyZoomLevel(); - - int x0 = screen->width / 2.0; - int y0 = screen->height / 2.0; - - //isTileDrawn = false; - //isTileReady = false; - Pan(mouseX, mouseY, x0, y0); - -} - -void Basemap::ZoomOut(int mouseX, int mouseY) -{ - // 2X by default - map->ZoomOut(); - GetEasyZoomLevel(); - - int x0 = screen->width / 2.0; - int y0 = screen->height / 2.0; - - //isTileDrawn = false; - //isTileReady = false; - Pan(mouseX, mouseY, x0, y0); -} - -int Basemap::GetOptimalZoomLevel(double paddingFactor) -{ - double ry1 = log(sin(Deg2Rad(map->south)) + 1) / cos(Deg2Rad(map->south)); - double ry2 = log(sin(Deg2Rad(map->north)) + 1) / cos(Deg2Rad(map->north)); - double ryc = (ry1 + ry2) / 2.0; - double centerY = Rad2Deg(atan(sinh(ryc))); - double resolutionHorizontal = map->GetWidth() / screen->width; - - double vy0 = log(tan(M_PI * (0.25 + centerY / 360.0))); - double vy1 = log(tan(M_PI * (0.25 + map->north / 360.0))); - double viewHeightHalf = screen->height / 2.0; - - double zoomFactorPowered = viewHeightHalf / (40.7436654315252*(vy1-vy0)); - double resolutionVertical = 360.0 / (zoomFactorPowered * 256); - - double resolution = std::max(resolutionHorizontal, resolutionVertical) * paddingFactor; - - zoom = log2(360.0 / (resolution * 256)); - - return zoom; -} - -int Basemap::GetEasyZoomLevel() -{ - double degreeRatio = 360.0 / map->GetWidth(); - double zoomH = (int)ceil(log2(degreeRatio * screen->width / 256.0)); - - degreeRatio = 85.0511 * 2.0 / map->GetHeight(); - double zoomV = (int)ceil(log2(degreeRatio * screen->height / 256.0)); - - if (zoomH > 0 && zoomV > 0) { - zoom = std::min(zoomH, zoomV); - } else { - if (zoomH > 0) - zoom = zoomH; - if (zoomV > 0) - zoom = zoomV; - } - - if (zoom > 18) - zoom = 18; - - nn = pow(2.0, zoom); - - return zoom; +void Basemap::ZoomIn(int mouseX, int mouseY) { + // 2X by default + map->ZoomIn(); + GetEasyZoomLevel(); + + int x0 = screen->width / 2.0; + int y0 = screen->height / 2.0; + + // isTileDrawn = false; + // isTileReady = false; + Pan(mouseX, mouseY, x0, y0); } -void Basemap::Refresh() -{ - GetTiles(); +void Basemap::ZoomOut(int mouseX, int mouseY) { + // 2X by default + map->ZoomOut(); + GetEasyZoomLevel(); + + int x0 = screen->width / 2.0; + int y0 = screen->height / 2.0; + + // isTileDrawn = false; + // isTileReady = false; + Pan(mouseX, mouseY, x0, y0); } -void Basemap::GetTiles() -{ - if (zoom < 1) - return; - - // following: http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames - // top-left / north-west - LatLng nw(map->north, map->west); - XYFraction* topleft = LatLngToRawXY(nw); - - // bottom-right / south-east - LatLng se(map->south, map->east); - XYFraction* bottomright = LatLngToRawXY(se); - - startX = topleft->GetXInt(); - startY = topleft->GetYInt(); - - leftP = startX * 256; - topP = startY * 256; - - endX = bottomright->GetXInt(); - endY = bottomright->GetYInt(); - - // crosss 180 border - if (endX < startX) { - endX = nn + endX; - } - widthP = (endX - startX + 1) * 256; - heightP = (endY - startY + 1) * 256; - - if (widthP < screen->width) { - int x_addition = (int)ceil((screen->width - widthP)/ 256.0); - endX += x_addition; - widthP = (endX - startX + 1) * 256; - } - if (heightP < screen->height) { - int y_addition = (int)ceil((screen->height - heightP)/ 256.0); - endY += y_addition; - heightP = (endY - startY + 1) * 256; - } - - // position of first tile (top-left) - double map_wp = 0; - if (endX <= nn) { - map_wp = (bottomright->x - topleft->x) * 255; - } else { - map_wp = (nn - topleft->x + bottomright->x) * 255; - } - int map_offx = (int)(ceil) ((screen->width - map_wp) / 2.0); - - // if offset to left, need to zoom out - if (map_offx < 0 && zoom > 0) { - if (zoom > 1) { - zoom = zoom -1; - nn = pow(2.0, zoom); - GetTiles(); - return; - } - } - - offsetX = topleft->GetXFrac() * 255 - map_offx; - // if offset to right, need to patch empty tiles - if (offsetX < 0) { - while (offsetX < 0) { - offsetX += 256; - startX = startX -1; - widthP = widthP + 256; - leftP = startX * 256; - } - } - - double map_hp = (bottomright->y - topleft->y) * 255; - int map_offy = (int) ((screen->height - map_hp) / 2.0); - offsetY = topleft->GetYFrac() * 255 - map_offy; - - // if offset down, need to patch empty tiles - if (offsetY < 0 ) { - while (offsetY < 0) { - startY = startY -1; - offsetY = offsetY + 256; - heightP = heightP + 256; - topP = startY * 256; - } - } - - // check tiles again after offset - if (widthP - offsetX < screen->width) { - endX += 1; - widthP = (endX - startX + 1) * 256; - } - if (heightP - offsetY < screen->height) { - endY += 1; - heightP = (endY - startY + 1) * 256; - } - - offsetX = offsetX - panX; - offsetY = offsetY - panY; +int Basemap::GetOptimalZoomLevel(double paddingFactor) { + double ry1 = log(sin(Deg2Rad(map->south)) + 1) / cos(Deg2Rad(map->south)); + double ry2 = log(sin(Deg2Rad(map->north)) + 1) / cos(Deg2Rad(map->north)); + double ryc = (ry1 + ry2) / 2.0; + double centerY = Rad2Deg(atan(sinh(ryc))); + double resolutionHorizontal = map->GetWidth() / screen->width; - //SetReady(true); - //isTileDrawn = false; + double vy0 = log(tan(M_PI * (0.25 + centerY / 360.0))); + double vy1 = log(tan(M_PI * (0.25 + map->north / 360.0))); + double viewHeightHalf = screen->height / 2.0; - start_download = true; - n_tasks = (endX - startX + 1) * (endY - startY + 1); - complete_tasks = 0; + double zoomFactorPowered = viewHeightHalf / (40.7436654315252 * (vy1 - vy0)); + double resolutionVertical = 360.0 / (zoomFactorPowered * 256); - for (int i=startX; i<=endX; i++) { - for (int j=startY; j<=endY; j++) { - int idx_x = i < 0 ? nn + i : i; - int idx_y = j < 0 ? nn + j : j; - if (idx_x > nn) - idx_x = idx_x - nn; - pool.enqueue(boost::bind(&Basemap::DownloadTile, this, idx_x, idx_y)); - } - } + double resolution = std::max(resolutionHorizontal, resolutionVertical) * paddingFactor; - delete topleft; - delete bottomright; -} + zoom = log2(360.0 / (resolution * 256)); -bool Basemap::IsDownloading() -{ - return start_download; + return zoom; } -size_t curlCallback(void *ptr, size_t size, size_t nmemb, void* userdata) -{ - FILE* stream = (FILE*)userdata; - if (!stream) { - //printf("!!! No stream\n"); - return 0; - } - - size_t written = fwrite((FILE*)ptr, size, nmemb, stream); - return written; -} +int Basemap::GetEasyZoomLevel() { + double degreeRatio = 360.0 / map->GetWidth(); + double zoomH = static_cast(ceil(log2(degreeRatio * screen->width / 256.0))); -wxString Basemap::GetUserAgent(const wxString& url) -{ - if (url.Find("openstreetmap") != wxNOT_FOUND) { - return GdaConst::gda_basemap_osm_useragent; - } - if (GeneralWxUtils::isWindows()) { - return GdaConst::gda_basemap_win_useragent; - } else if (GeneralWxUtils::isMac()) { - return GdaConst::gda_basemap_mac_useragent; - } else { - return GdaConst::gda_basemap_linux_useragent; - } - -} - -wxString Basemap::GetContentType() -{ - wxString url = GetTileUrl(16, 11); // guerry - wxString content_type; - CURL *curl = curl_easy_init(); - CURLcode res; - if(curl) { - curl_easy_setopt(curl, CURLOPT_URL, url.ToUTF8().data()); - curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); - curl_easy_setopt(curl, CURLOPT_USERAGENT, GetUserAgent(url).ToUTF8().data()); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); - curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 1L); - curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1L); - curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); - res = curl_easy_perform(curl); - - if(!res) { - /* extract the content-type */ - char *ct = NULL; - res = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct); - if(!res && ct) { - content_type = ct; - } - } - curl_easy_cleanup(curl); - } - //std::cout << "ContentType():" << content_type << "," << url.c_str() << "," << url.ToUTF8().data() << std::endl; - return content_type; + degreeRatio = 85.0511 * 2.0 / map->GetHeight(); + double zoomV = static_cast(ceil(log2(degreeRatio * screen->height / 256.0))); + + if (zoomH > 0 && zoomV > 0) { + zoom = std::min(zoomH, zoomV); + } else { + if (zoomH > 0) zoom = zoomH; + if (zoomV > 0) zoom = zoomV; + } + + if (zoom > 18) zoom = 18; + + nn = pow(2.0, zoom); + + return zoom; } -void Basemap::DownloadTile(int x, int y) -{ - if (x < 0 || y < 0) - return; - - // detect if file exists in temp/ directory - wxString filepathStr = GetTilePath(x, y); - std::string filepath = GET_ENCODED_FILENAME(filepathStr); +void Basemap::Refresh() { GetTiles(); } + +void Basemap::GetTiles() { + if (zoom < 1) return; + + // following: http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames + // top-left / north-west + LatLng nw(map->north, map->west); + XYFraction* topleft = LatLngToRawXY(nw); + + // bottom-right / south-east + LatLng se(map->south, map->east); + XYFraction* bottomright = LatLngToRawXY(se); + + startX = topleft->GetXInt(); + startY = topleft->GetYInt(); - if (!wxFileExists(filepathStr) || wxFileName::GetSize(filepathStr) == 0) { - // otherwise, download the image - wxString url = GetTileUrl(x, y); + leftP = startX * 256; + topP = startY * 256; - FILE* fp; - CURL* image; - CURLcode imgResult; + endX = bottomright->GetXInt(); + endY = bottomright->GetYInt(); - image = curl_easy_init(); - if (image) { + // crosss 180 border + if (endX < startX) { + endX = nn + endX; + } + widthP = (endX - startX + 1) * 256; + heightP = (endY - startY + 1) * 256; + + if (widthP < screen->width) { + int x_addition = static_cast(ceil((screen->width - widthP) / 256.0)); + endX += x_addition; + widthP = (endX - startX + 1) * 256; + } + if (heightP < screen->height) { + int y_addition = static_cast(ceil((screen->height - heightP) / 256.0)); + endY += y_addition; + heightP = (endY - startY + 1) * 256; + } + + // position of first tile (top-left) + double map_wp = 0; + if (endX <= nn) { + map_wp = (bottomright->x - topleft->x) * 255; + } else { + map_wp = (nn - topleft->x + bottomright->x) * 255; + } + int map_offx = static_cast((ceil)((screen->width - map_wp) / 2.0)); + + // if offset to left, need to zoom out + if (map_offx < 0 && zoom > 0) { + if (zoom > 1) { + zoom = zoom - 1; + nn = pow(2.0, zoom); + GetTiles(); + return; + } + } + + offsetX = topleft->GetXFrac() * 255 - map_offx; + // if offset to right, need to patch empty tiles + if (offsetX < 0) { + while (offsetX < 0) { + offsetX += 256; + startX = startX - 1; + widthP = widthP + 256; + leftP = startX * 256; + } + } + + double map_hp = (bottomright->y - topleft->y) * 255; + int map_offy = static_cast(((screen->height - map_hp) / 2.0)); + offsetY = topleft->GetYFrac() * 255 - map_offy; + + // if offset down, need to patch empty tiles + if (offsetY < 0) { + while (offsetY < 0) { + startY = startY - 1; + offsetY = offsetY + 256; + heightP = heightP + 256; + topP = startY * 256; + } + } + + // check tiles again after offset + if (widthP - offsetX < screen->width) { + endX += 1; + widthP = (endX - startX + 1) * 256; + } + if (heightP - offsetY < screen->height) { + endY += 1; + heightP = (endY - startY + 1) * 256; + } + + offsetX = offsetX - panX; + offsetY = offsetY - panY; + + // SetReady(true); + // isTileDrawn = false; + + start_download = true; + n_tasks = (endX - startX + 1) * (endY - startY + 1); + complete_tasks = 0; + + for (int i = startX; i <= endX; i++) { + for (int j = startY; j <= endY; j++) { + int idx_x = i < 0 ? nn + i : i; + int idx_y = j < 0 ? nn + j : j; + if (idx_x > nn) idx_x = idx_x - nn; + pool.enqueue(boost::bind(&Basemap::DownloadTile, this, idx_x, idx_y)); + } + } + + delete topleft; + delete bottomright; +} + +bool Basemap::IsDownloading() { return start_download; } + +size_t curlCallback(void* ptr, size_t size, size_t nmemb, void* userdata) { + FILE* stream = reinterpret_cast(userdata); + if (!stream) { + // printf("!!! No stream\n"); + return 0; + } + + size_t written = fwrite(reinterpret_cast(ptr), size, nmemb, stream); + return written; +} + +wxString Basemap::GetUserAgent(const wxString& url) { + if (url.Find("openstreetmap") != wxNOT_FOUND) { + return GdaConst::gda_basemap_osm_useragent; + } + if (GeneralWxUtils::isWindows()) { + return GdaConst::gda_basemap_win_useragent; + } else if (GeneralWxUtils::isMac()) { + return GdaConst::gda_basemap_mac_useragent; + } else { + return GdaConst::gda_basemap_linux_useragent; + } +} + +wxString Basemap::GetContentType() { + wxString url = GetTileUrl(16, 11); // guerry + wxString content_type; + CURL* curl = curl_easy_init(); + CURLcode res; + if (curl) { + curl_easy_setopt(curl, CURLOPT_URL, url.ToUTF8().data()); + curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); + curl_easy_setopt(curl, CURLOPT_USERAGENT, GetUserAgent(url).ToUTF8().data()); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 1L); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1L); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); + res = curl_easy_perform(curl); + + if (!res) { + /* extract the content-type */ + char* ct = NULL; + res = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct); + if (!res && ct) { + content_type = ct; + } + } + curl_easy_cleanup(curl); + } + // std::cout << "ContentType():" << content_type << "," << url.c_str() << "," << url.ToUTF8().data() << std::endl; + return content_type; +} + +void Basemap::DownloadTile(int x, int y) { + if (x < 0 || y < 0) return; + + // detect if file exists in temp/ directory + wxString filepathStr = GetTilePath(x, y); + std::string filepath = GET_ENCODED_FILENAME(filepathStr); + + if (!wxFileExists(filepathStr) || wxFileName::GetSize(filepathStr) == 0) { + // otherwise, download the image + wxString url = GetTileUrl(x, y); + + FILE* fp; + CURL* image; + CURLcode imgResult; + + image = curl_easy_init(); + if (image) { #ifdef __WIN32__ - fp = _wfopen(filepathStr.wc_str(), L"wb"); + fp = _wfopen(filepathStr.wc_str(), L"wb"); #else - fp = fopen(GET_ENCODED_FILENAME(filepathStr), "wb"); + fp = fopen(GET_ENCODED_FILENAME(filepathStr), "wb"); #endif - if (fp) { - curl_easy_setopt(image, CURLOPT_URL, url.ToUTF8().data()); - curl_easy_setopt(image, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); - curl_easy_setopt(image, CURLOPT_USERAGENT, GetUserAgent(url).ToUTF8().data()); - curl_easy_setopt(image, CURLOPT_WRITEFUNCTION, curlCallback); - curl_easy_setopt(image, CURLOPT_WRITEDATA, fp); - curl_easy_setopt(image, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(image, CURLOPT_SSL_VERIFYHOST, 0); - curl_easy_setopt(image, CURLOPT_SSL_VERIFYPEER, 0); - curl_easy_setopt(image, CURLOPT_CONNECTTIMEOUT, 1L); - curl_easy_setopt(image, CURLOPT_TIMEOUT, 1L); - curl_easy_setopt(image, CURLOPT_NOSIGNAL, 1L); - - // Grab image - imgResult = curl_easy_perform(image); - if (imgResult == CURLE_OK) { - curl_easy_cleanup(image); - } - fclose(fp); - } + if (fp) { + curl_easy_setopt(image, CURLOPT_URL, url.ToUTF8().data()); + curl_easy_setopt(image, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); + curl_easy_setopt(image, CURLOPT_USERAGENT, GetUserAgent(url).ToUTF8().data()); + curl_easy_setopt(image, CURLOPT_WRITEFUNCTION, curlCallback); + curl_easy_setopt(image, CURLOPT_WRITEDATA, fp); + curl_easy_setopt(image, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(image, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt(image, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(image, CURLOPT_CONNECTTIMEOUT, 1L); + curl_easy_setopt(image, CURLOPT_TIMEOUT, 1L); + curl_easy_setopt(image, CURLOPT_NOSIGNAL, 1L); + + // Grab image + imgResult = curl_easy_perform(image); + if (imgResult == CURLE_OK) { + curl_easy_cleanup(image); } + fclose(fp); + } } + } - mutex.lock(); - complete_tasks += 1; - mutex.unlock(); -} - -void Basemap::SetReady(bool flag) -{ - mutex.lock(); - isTileReady = flag; - mutex.unlock(); -} - -LatLng* Basemap::XYToLatLng(XYFraction &xy, bool isLL) -{ - double x = xy.x; - if (x > nn) - x = x - nn; - double lng = x * 360.0 / nn - 180.0; - double n = M_PI - 2.0 * M_PI * xy.y / nn; - double lat = 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n))); - - if (!isLL && poCT) { - poCT->Transform(1, &lng, &lat); - } - return new LatLng(lat, lng); + mutex.lock(); + complete_tasks += 1; + mutex.unlock(); +} + +void Basemap::SetReady(bool flag) { + mutex.lock(); + isTileReady = flag; + mutex.unlock(); } -XYFraction* Basemap::LatLngToRawXY(LatLng &latlng) -{ - double lat_rad = latlng.GetLatRad(); - double x = (latlng.GetLngDeg() + 180.0 ) / 360.0 * nn; - double tan_lat = tan(lat_rad); - double cos_lat = cos(lat_rad); - double y = (1.0 - log(tan_lat + 1.0/cos_lat) / M_PI) / 2.0 * nn; - return new XYFraction(x, y); +LatLng* Basemap::XYToLatLng(XYFraction& xy, bool isLL) { + double x = xy.x; + if (x > nn) x = x - nn; + double lng = x * 360.0 / nn - 180.0; + double n = M_PI - 2.0 * M_PI * xy.y / nn; + double lat = 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n))); + + if (!isLL && poCT) { + poCT->Transform(1, &lng, &lat); + } + return new LatLng(lat, lng); +} + +XYFraction* Basemap::LatLngToRawXY(LatLng& latlng) { + double lat_rad = latlng.GetLatRad(); + double x = (latlng.GetLngDeg() + 180.0) / 360.0 * nn; + double tan_lat = tan(lat_rad); + double cos_lat = cos(lat_rad); + double y = (1.0 - log(tan_lat + 1.0 / cos_lat) / M_PI) / 2.0 * nn; + return new XYFraction(x, y); } -XYFraction* Basemap::LatLngToXY(LatLng &latlng) -{ - double lat_rad = latlng.GetLatRad(); - double x = (latlng.GetLngDeg() + 180.0 ) / 360.0 * nn; - double y = (1.0 - log(tan(lat_rad) + 1.0 / cos(lat_rad)) / M_PI) / 2.0 * nn; - int xp = (int)(x * 256 - leftP) - offsetX; - int yp = (int)(y * 256 - topP) - offsetY; - return new XYFraction(xp, yp); +XYFraction* Basemap::LatLngToXY(LatLng& latlng) { + double lat_rad = latlng.GetLatRad(); + double x = (latlng.GetLngDeg() + 180.0) / 360.0 * nn; + double y = (1.0 - log(tan(lat_rad) + 1.0 / cos(lat_rad)) / M_PI) / 2.0 * nn; + int xp = static_cast(x * 256 - leftP) - offsetX; + int yp = static_cast(y * 256 - topP) - offsetY; + return new XYFraction(xp, yp); } // This function is used to convert lng/lat to screen (x,y) // This function will be called by projectToBasemap() -void Basemap::LatLngToXY(double lng, double lat, int &x, int &y) -{ - if (poCT!= NULL) { - poCT->Transform(1, &lng, &lat); - } - if (lat > 85.0511) lat = 85.0511; - if (lat < -85.0511) lat = -85.0511; - double lat_rad = lat * M_PI / 180.0; - double yy = (1.0 - log(tan(lat_rad) + 1.0 / cos(lat_rad)) / M_PI) / 2.0 * nn; - y = (int)(yy * 256 - topP) - offsetY; - - double xx = (lng + 180.0 ) / 360.0 * nn; - x = (int)(xx * 256 - leftP) - offsetX; -} - -wxString Basemap::GetRandomSubdomain(wxString url) -{ - unsigned int initseed = (unsigned int) time(0); - srand(initseed); - - std::vector domains; - if (url.Find("openstreetmap") != wxNOT_FOUND || - url.Find("carto") != wxNOT_FOUND || - url.Find("wmflabs") != wxNOT_FOUND || - url.Find("fastly") != wxNOT_FOUND) - { - domains.push_back("a"); - domains.push_back("b"); - domains.push_back("c"); - } else if (url.Find("here") != wxNOT_FOUND || - url.Find("bdimg") != wxNOT_FOUND) { - domains.push_back("1"); - domains.push_back("2"); - domains.push_back("3"); - domains.push_back("4"); - } else if (url.Find("autonavi") != wxNOT_FOUND) { - domains.push_back("01"); - domains.push_back("02"); - domains.push_back("03"); - domains.push_back("04"); +void Basemap::LatLngToXY(double lng, double lat, int& x, int& y) { + if (poCT != NULL) { + poCT->Transform(1, &lng, &lat); + } + if (lat > 85.0511) lat = 85.0511; + if (lat < -85.0511) lat = -85.0511; + double lat_rad = lat * M_PI / 180.0; + double yy = (1.0 - log(tan(lat_rad) + 1.0 / cos(lat_rad)) / M_PI) / 2.0 * nn; + y = static_cast(yy * 256 - topP) - offsetY; + + double xx = (lng + 180.0) / 360.0 * nn; + x = static_cast(xx * 256 - leftP) - offsetX; +} + +wxString Basemap::GetRandomSubdomain(wxString url) { + unsigned int initseed = (unsigned int)time(0); + srand(initseed); + + std::vector domains; + if (url.Find("openstreetmap") != wxNOT_FOUND || url.Find("carto") != wxNOT_FOUND || + url.Find("wmflabs") != wxNOT_FOUND || url.Find("fastly") != wxNOT_FOUND) { + domains.push_back("a"); + domains.push_back("b"); + domains.push_back("c"); + } else if (url.Find("here") != wxNOT_FOUND || url.Find("bdimg") != wxNOT_FOUND) { + domains.push_back("1"); + domains.push_back("2"); + domains.push_back("3"); + domains.push_back("4"); + } else if (url.Find("autonavi") != wxNOT_FOUND) { + domains.push_back("01"); + domains.push_back("02"); + domains.push_back("03"); + domains.push_back("04"); + } else { + return wxEmptyString; + } + + int n_domains = domains.size(); + int idx = rand() % n_domains; + if (idx < 0 || idx >= n_domains) return wxEmptyString; + + return domains[idx]; +} + +wxString Basemap::GetTileUrl(int x, int y) { + wxString url = basemapUrl; + url.Replace("{z}", wxString::Format("%d", zoom)); + url.Replace("{x}", wxString::Format("%d", x)); + url.Replace("{y}", wxString::Format("%d", y)); + url.Replace("STADIA_KEY", stadia_key); + url.Replace("HERE_APP_ID", nokia_id); + url.Replace("HERE_APP_CODE", nokia_code); + url.Replace("{s}", GetRandomSubdomain(url)); + // std::cout << url.c_str() << std::endl; + return url; +} + +wxString Basemap::GetTilePath(int x, int y) { + // std::ostringstream filepathBuf; + wxString filepathBuf; + filepathBuf << cachePath << separator(); + filepathBuf << basemapName << "-"; + filepathBuf << zoom << "-" << x << "-" << y << imageSuffix; + + wxString newpath; + for (int i = 0; i < filepathBuf.length(); i++) { + if (filepathBuf[i] == '\\') { + newpath += filepathBuf[i]; + newpath += filepathBuf[i]; } else { - return wxEmptyString; + newpath += filepathBuf[i]; } - - int n_domains = domains.size(); - int idx = rand() % n_domains; - if (idx < 0 || idx >= n_domains) return wxEmptyString; - - return domains[idx]; -} - -wxString Basemap::GetTileUrl(int x, int y) -{ - wxString url = basemapUrl; - url.Replace("{z}", wxString::Format("%d", zoom)); - url.Replace("{x}", wxString::Format("%d", x)); - url.Replace("{y}", wxString::Format("%d", y)); - url.Replace("HERE_APP_ID", nokia_id); - url.Replace("HERE_APP_CODE", nokia_code); - url.Replace("{s}", GetRandomSubdomain(url)); - //std::cout << url.c_str() << std::endl; - return url; -} - -wxString Basemap::GetTilePath(int x, int y) -{ - //std::ostringstream filepathBuf; - wxString filepathBuf; - filepathBuf << cachePath << separator(); - filepathBuf << basemapName << "-"; - filepathBuf << zoom << "-" << x << "-" << y << imageSuffix; - - wxString newpath; - for (int i = 0; i < filepathBuf.length() ;i++) { - if(filepathBuf[i] == '\\') { - newpath += filepathBuf[i]; - newpath += filepathBuf[i]; - } else { - newpath += filepathBuf[i]; - } - } - return newpath; + } + return newpath; } -bool Basemap::Draw(wxBitmap* buffer) -{ - bool draw_complete = false; - if (n_tasks > 0 && n_tasks <= complete_tasks) { - draw_complete = true; - complete_tasks = 0; - } - // when tiles pngs are ready, draw them on a buffer - wxMemoryDC dc(*buffer); - dc.SetBackground(*wxWHITE); - dc.Clear(); - wxGraphicsContext* gc = wxGraphicsContext::Create(dc); - if (!gc) return false; - - int x0 = startX; - int x1 = endX; - for (int i=x0; i<=x1; i++) { - for (int j=startY; j<=endY; j++ ) { - int pos_x = (i-startX) * 256 - offsetX; - int pos_y = (j-startY) * 256 - offsetY; - int idx_x = i; - - if ( i >= nn) { - idx_x = i - nn; - } else if (i < 0) { - idx_x = nn + i; - } - - int idx_y = j; - wxString wxFilePath = GetTilePath(idx_x, idx_y); - wxFileName fp(wxFilePath); - wxBitmap bmp; - if (imageSuffix.CmpNoCase(".png") == 0) { - bmp.LoadFile(wxFilePath, wxBITMAP_TYPE_PNG); - } else if (imageSuffix.CmpNoCase(".jpeg") == 0 || - imageSuffix.CmpNoCase(".jpg") == 0 ) { - bmp.LoadFile(wxFilePath, wxBITMAP_TYPE_JPEG); - } - if (bmp.IsOk()) { - gc->DrawBitmap(bmp, pos_x, pos_y, 257,257); - //dc.DrawRectangle((i-startX) * 256 - offsetX, (j-startY) * 256 - offsetY, 256, 256); - } - } - } - delete gc; - return draw_complete; +bool Basemap::Draw(wxBitmap* buffer) { + bool draw_complete = false; + if (n_tasks > 0 && n_tasks <= complete_tasks) { + draw_complete = true; + complete_tasks = 0; + } + // when tiles pngs are ready, draw them on a buffer + wxMemoryDC dc(*buffer); + dc.SetBackground(*wxWHITE); + dc.Clear(); + wxGraphicsContext* gc = wxGraphicsContext::Create(dc); + if (!gc) return false; + + int x0 = startX; + int x1 = endX; + for (int i = x0; i <= x1; i++) { + for (int j = startY; j <= endY; j++) { + int pos_x = (i - startX) * 256 - offsetX; + int pos_y = (j - startY) * 256 - offsetY; + int idx_x = i; + + if (i >= nn) { + idx_x = i - nn; + } else if (i < 0) { + idx_x = nn + i; + } + + int idx_y = j; + wxString wxFilePath = GetTilePath(idx_x, idx_y); + wxFileName fp(wxFilePath); + wxBitmap bmp; + if (imageSuffix.CmpNoCase(".png") == 0) { + bmp.LoadFile(wxFilePath, wxBITMAP_TYPE_PNG); + } else if (imageSuffix.CmpNoCase(".jpeg") == 0 || imageSuffix.CmpNoCase(".jpg") == 0) { + bmp.LoadFile(wxFilePath, wxBITMAP_TYPE_JPEG); + } + if (bmp.IsOk()) { + gc->DrawBitmap(bmp, pos_x, pos_y, 257, 257); + // dc.DrawRectangle((i-startX) * 256 - offsetX, (j-startY) * 256 - offsetY, 256, 256); + } + } + } + delete gc; + return draw_complete; } diff --git a/Explore/Basemap.h b/Explore/Basemap.h index e5cf9e980..bfb8c436c 100644 --- a/Explore/Basemap.h +++ b/Explore/Basemap.h @@ -2,7 +2,7 @@ * GeoDa TM, Copyright (C) 2011-2015 by Luc Anselin - all rights reserved * * This file is part of GeoDa. - * + * * GeoDa is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -20,423 +20,411 @@ #ifndef GeoDa_Basemap_h #define GeoDa_Basemap_h -#include -#include +#include #include -#include -#include +#include +#include + #include -#include +#include +#include +#include #include "../Algorithms/threadpool.h" namespace Gda { - /** - * BasemapItem is for "Basemap Source Configuration" dialog - * Each basemap source can be represented in the form of: - * GroupName.Name,url - * For example: - * Carto.Light,https://a.basemaps.cartocdn.com/light_all/{z}/{x}/{y}@2x.png - * Carto.Dark,https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}@2x.png - * GeoDa will create a menu from a collection of BasemapItems - */ - class BasemapItem { - public: - BasemapItem() {} - - BasemapItem(wxString _group, wxString _name, wxString _url) { - group = _group; - name = _name; - url = _url; - } - - ~BasemapItem() {} - - BasemapItem& operator=(const BasemapItem& other) { - group = other.group; - name = other.name; - url = other.url; - return *this; - } - - bool operator==(const BasemapItem& other) { - return (group == other.group && name == other.name && url == other.url); - } - - void Reset() { - group = ""; - name = ""; - url = ""; - } - - wxString group; - wxString name; - wxString url; - }; - - /** - * BasemapGroup is a collection of BasemapItems. - * GeoDa will create a menu from an instance of BasemapGroup - * For example: - * Carto - * |__Light (https://a.basemaps.cartocdn.com/light_all/{z}/{x}/{y}@2x.png) - * |__Dark (https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}@2x.png) - */ - class BasemapGroup { - public: - BasemapGroup() {} - BasemapGroup(wxString _name) { - name = _name; - } - ~BasemapGroup() {} - BasemapGroup& operator=(const BasemapGroup& other) { - name = other.name; - items = other.items; - return *this; - } - void AddItem(BasemapItem item) { - items.push_back(item); - } - wxString name; - std::vector items; - }; - - // Return an instance of BasemapItem based on the basemap_source, which is - // at idx-th row of basemap_sources (e.g. GdaConst::gda_basemap_sources) - BasemapItem GetBasemapSelection(int idx, wxString basemap_sources); - - // Construct a std::vector of BasemapGroup (which is a collection of - // BasemapItems) using basemap_sources (e.g. GdaConst::gda_basemap_sources - // or the value in "Basemap Sources:" TextCtrl in Basemap Configuration Dialog - std::vector ExtractBasemapResources(wxString basemap_sources) ; - - // inline function return separator for local file path - inline char separator() { +/** + * BasemapItem is for "Basemap Source Configuration" dialog + * Each basemap source can be represented in the form of: + * GroupName.Name,url + * For example: + * Carto.Light,https://a.basemaps.cartocdn.com/light_all/{z}/{x}/{y}@2x.png + * Carto.Dark,https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}@2x.png + * GeoDa will create a menu from a collection of BasemapItems + */ +class BasemapItem { + public: + BasemapItem() {} + + BasemapItem(wxString _group, wxString _name, wxString _url) { + group = _group; + name = _name; + url = _url; + } + + ~BasemapItem() {} + + BasemapItem& operator=(const BasemapItem& other) { + group = other.group; + name = other.name; + url = other.url; + return *this; + } + + bool operator==(const BasemapItem& other) { return (group == other.group && name == other.name && url == other.url); } + + void Reset() { + group = ""; + name = ""; + url = ""; + } + + wxString group; + wxString name; + wxString url; +}; + +/** + * BasemapGroup is a collection of BasemapItems. + * GeoDa will create a menu from an instance of BasemapGroup + * For example: + * Carto + * |__Light (https://a.basemaps.cartocdn.com/light_all/{z}/{x}/{y}@2x.png) + * |__Dark (https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}@2x.png) + */ +class BasemapGroup { + public: + BasemapGroup() {} + explicit BasemapGroup(wxString _name) { name = _name; } + ~BasemapGroup() {} + BasemapGroup& operator=(const BasemapGroup& other) { + name = other.name; + items = other.items; + return *this; + } + void AddItem(BasemapItem item) { items.push_back(item); } + wxString name; + std::vector items; +}; + +// Return an instance of BasemapItem based on the basemap_source, which is +// at idx-th row of basemap_sources (e.g. GdaConst::gda_basemap_sources) +BasemapItem GetBasemapSelection(int idx, wxString basemap_sources); + +// Construct a std::vector of BasemapGroup (which is a collection of +// BasemapItems) using basemap_sources (e.g. GdaConst::gda_basemap_sources +// or the value in "Basemap Sources:" TextCtrl in Basemap Configuration Dialog +std::vector ExtractBasemapResources(wxString basemap_sources); + +// inline function return separator for local file path +inline char separator() { #ifdef __WIN32__ - return '\\'; + return '\\'; #else - return '/'; + return '/'; #endif - } - - inline bool is_file_exist(const char *fileName) { - std::ifstream infile(fileName); - return infile.good() && (infile.peek() != std::ifstream::traits_type::eof()); - } - - inline double log2(double n) { - return log(n) / log(2.0); - } - - class LatLng { - public: - LatLng() {} - LatLng(double _lat, double _lng) { - lat = _lat; - lng = _lng; - if (lat > 85.0511) lat = 85.0511; - if (lat < -85.0511) lat = -85.0511; - } - ~LatLng(){} - - double lat; - double lng; - - double GetLatDeg() {return lat;} - double GetLngDeg() {return lng;} - double GetLatRad() {return lat * M_PI / 180.0;} - double GetLngRad() {return lng * M_PI / 180.0;} - }; - - class XYFraction { - public: - XYFraction(){} - XYFraction(int _x, int _y) { - x = _x; - y = _y; - xint = _x; - yint = _y; - xfrac = .0; - yfrac = .0; - } - XYFraction(double _x, double _y); - ~XYFraction(){} - - double x; - double y; - double xfrac; - double yfrac; - double xint; - double yint; - - int GetXInt() { return (int)xint;} - int GetYInt() { return (int)yint;} - double GetXFrac() { return xfrac;} - double GetYFrac() { return yfrac;} - }; - - class Screen { - public: - Screen(){} - Screen(int _w, int _h) { width=_w; height= _h;} - ~Screen(){} - - int width; - int height; - }; - - class MapLayer { - public: - double north; - double south; - double west; - double east; - OGRCoordinateTransformation *poCT; - OGRCoordinateTransformation *poCT_rev; - - MapLayer(){ - poCT = NULL; - poCT_rev = NULL; - } - MapLayer(MapLayer* _map) { - north = _map->north; - south = _map->south; - west = _map->west; - east = _map->east; - poCT = NULL; - poCT_rev = NULL; - } - MapLayer(double _n, double _w, double _s, double _e){ - north = _n; - south = _s; - west = _w; - east = _e; - poCT = NULL; - poCT_rev = NULL; - } - MapLayer(double _n, double _w, double _s, double _e, - OGRCoordinateTransformation *_poCT){ - north = _n; - south = _s; - west = _w; - east = _e; - poCT = _poCT; - poCT_rev = NULL; - if (poCT!= NULL) { - if (poCT->Transform(1, &_w, &_n)) { - west = _w; - north = _n; - } - if (poCT->Transform(1, &_e, &_s)) { - east = _e; - south = _s; - } - const OGRSpatialReference* s1 = poCT->GetTargetCS(); - const OGRSpatialReference* s2 = poCT->GetSourceCS(); +} + +inline bool is_file_exist(const char* fileName) { + std::ifstream infile(fileName); + return infile.good() && (infile.peek() != std::ifstream::traits_type::eof()); +} + +inline double log2(double n) { return log(n) / log(2.0); } + +class LatLng { + public: + LatLng() {} + LatLng(double _lat, double _lng) { + lat = _lat; + lng = _lng; + if (lat > 85.0511) lat = 85.0511; + if (lat < -85.0511) lat = -85.0511; + } + ~LatLng() {} + + double lat; + double lng; + + double GetLatDeg() { return lat; } + double GetLngDeg() { return lng; } + double GetLatRad() { return lat * M_PI / 180.0; } + double GetLngRad() { return lng * M_PI / 180.0; } +}; + +class XYFraction { + public: + XYFraction() {} + XYFraction(int _x, int _y) { + x = _x; + y = _y; + xint = _x; + yint = _y; + xfrac = .0; + yfrac = .0; + } + XYFraction(double _x, double _y); + ~XYFraction() {} + + double x; + double y; + double xfrac; + double yfrac; + double xint; + double yint; + + int GetXInt() { return static_cast(xint); } + int GetYInt() { return static_cast(yint); } + double GetXFrac() { return xfrac; } + double GetYFrac() { return yfrac; } +}; + +class Screen { + public: + Screen() {} + Screen(int _w, int _h) { + width = _w; + height = _h; + } + ~Screen() {} + + int width; + int height; +}; + +class MapLayer { + public: + double north; + double south; + double west; + double east; + OGRCoordinateTransformation* poCT; + OGRCoordinateTransformation* poCT_rev; + + MapLayer() { + poCT = NULL; + poCT_rev = NULL; + } + explicit MapLayer(MapLayer* _map) { + north = _map->north; + south = _map->south; + west = _map->west; + east = _map->east; + poCT = NULL; + poCT_rev = NULL; + } + MapLayer(double _n, double _w, double _s, double _e) { + north = _n; + south = _s; + west = _w; + east = _e; + poCT = NULL; + poCT_rev = NULL; + } + MapLayer(double _n, double _w, double _s, double _e, OGRCoordinateTransformation* _poCT) { + north = _n; + south = _s; + west = _w; + east = _e; + poCT = _poCT; + poCT_rev = NULL; + if (poCT != NULL) { + if (poCT->Transform(1, &_w, &_n)) { + west = _w; + north = _n; + } + if (poCT->Transform(1, &_e, &_s)) { + east = _e; + south = _s; + } + const OGRSpatialReference* s1 = poCT->GetTargetCS(); + const OGRSpatialReference* s2 = poCT->GetSourceCS(); #ifdef __PROJ6__ - const_cast(s1)->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); - const_cast(s2)->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); + const_cast(s1)->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); + const_cast(s2)->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); #endif - poCT_rev = OGRCreateCoordinateTransformation(s1, s2); - } - } - MapLayer(LatLng& nw, LatLng& se){ - north = nw.lat; - west = nw.lng; - south = se.lat; - east = se.lng; - poCT = NULL; - poCT_rev = NULL; - } - ~MapLayer(){ - - } - - void GetWestNorthEastSouth(double& w, double& n, double& e, double& s) { - w = west; - n = north; - e = east; - s = south; - if (poCT_rev) { - poCT_rev->Transform(1, &w, &n); - poCT_rev->Transform(1, &e, &s); - } - } - - double GetWidth() { - if (east >= west) - return east - west; - else - return 180 - east + 180 + west; - } - - double GetHeight() { return north - south; } - - bool IsWGS84Valid() { return north < 90 && south > -90 && east > -180 && west < 180;} - - bool Pan(double lat, double lng) { - north += lat; - south += lat; - west += lng; - east += lng; - if (north > 90 || south < -90 || east > 180 || west < -180) - return false; - return true; - } - - void UpdateExtent(double _w, double _s, double _e, double _n) { - north = _n; - south = _s; - east = _e; - west = _w; - } - - void ZoomIn() { - // 2X by default - double w = east - west; - double h = north - south; - double offsetW = w / 4.0; - double offsetH = h / 4.0; - west = west + offsetW; - east = east - offsetW; - north = north - offsetH; - south = south + offsetH; - } - - void ZoomOut() { - // 2X by default - double w = east - west; - double h = north - south; - double offsetW = w / 2.0; - double offsetH = h / 2.0; - west = west - offsetW; - east = east + offsetW; - north = north + offsetH; - south = south - offsetH; - } - MapLayer* operator=(const MapLayer* other) { - north = other->north; - south = other->south; - west = other->west; - east = other->east; - poCT = NULL; - poCT_rev = NULL; - if (other) { - if (other->poCT) { - const OGRSpatialReference* s1 = other->poCT->GetSourceCS(); - const OGRSpatialReference* s2 = other->poCT->GetTargetCS(); + poCT_rev = OGRCreateCoordinateTransformation(s1, s2); + } + } + MapLayer(LatLng& nw, LatLng& se) { + north = nw.lat; + west = nw.lng; + south = se.lat; + east = se.lng; + poCT = NULL; + poCT_rev = NULL; + } + ~MapLayer() {} + + void GetWestNorthEastSouth(double& w, double& n, double& e, double& s) { + w = west; + n = north; + e = east; + s = south; + if (poCT_rev) { + poCT_rev->Transform(1, &w, &n); + poCT_rev->Transform(1, &e, &s); + } + } + + double GetWidth() { + if (east >= west) + return east - west; + else + return 180 - east + 180 + west; + } + + double GetHeight() { return north - south; } + + bool IsWGS84Valid() { return north < 90 && south > -90 && east > -180 && west < 180; } + + bool Pan(double lat, double lng) { + north += lat; + south += lat; + west += lng; + east += lng; + if (north > 90 || south < -90 || east > 180 || west < -180) return false; + return true; + } + + void UpdateExtent(double _w, double _s, double _e, double _n) { + north = _n; + south = _s; + east = _e; + west = _w; + } + + void ZoomIn() { + // 2X by default + double w = east - west; + double h = north - south; + double offsetW = w / 4.0; + double offsetH = h / 4.0; + west = west + offsetW; + east = east - offsetW; + north = north - offsetH; + south = south + offsetH; + } + + void ZoomOut() { + // 2X by default + double w = east - west; + double h = north - south; + double offsetW = w / 2.0; + double offsetH = h / 2.0; + west = west - offsetW; + east = east + offsetW; + north = north + offsetH; + south = south - offsetH; + } + MapLayer* operator=(const MapLayer* other) { + north = other->north; + south = other->south; + west = other->west; + east = other->east; + poCT = NULL; + poCT_rev = NULL; + if (other) { + if (other->poCT) { + const OGRSpatialReference* s1 = other->poCT->GetSourceCS(); + const OGRSpatialReference* s2 = other->poCT->GetTargetCS(); #ifdef __PROJ6__ - const_cast(s1)->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); - const_cast(s2)->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); + const_cast(s1)->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); + const_cast(s2)->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); #endif - poCT = OGRCreateCoordinateTransformation(s1, s2); - } - if (other->poCT_rev) { - const OGRSpatialReference* s1 = other->poCT_rev->GetSourceCS(); - const OGRSpatialReference* s2 = other->poCT_rev->GetTargetCS(); + poCT = OGRCreateCoordinateTransformation(s1, s2); + } + if (other->poCT_rev) { + const OGRSpatialReference* s1 = other->poCT_rev->GetSourceCS(); + const OGRSpatialReference* s2 = other->poCT_rev->GetTargetCS(); #ifdef __PROJ6__ - const_cast(s1)->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); - const_cast(s2)->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); + const_cast(s1)->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); + const_cast(s2)->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); #endif - poCT_rev = OGRCreateCoordinateTransformation(s1, s2); - } - } - return this; - } - }; - - // only for Web mercator projection - class Basemap { - int nn; // pow(2.0, zoom) - - thread_pool pool; - boost::mutex mutex; - - bool start_download; - int n_tasks; - int complete_tasks; - - wxString GetRandomSubdomain(wxString url); - int GetOptimalZoomLevel(double paddingFactor=1.2); - int GetEasyZoomLevel(); - void GetTiles(); - void DownloadTile(int x, int y); - - public: - Basemap() {} - Basemap(BasemapItem& basemap_item, - Screen* _screen, - MapLayer* _map, - MapLayer* _origMap, - wxString _cachePath, - OGRCoordinateTransformation* _poCT, - double scale_factor = 1.0); - ~Basemap(); - - OGRCoordinateTransformation *poCT; - BasemapItem basemap_item; - wxString basemapName; - wxString basemapUrl; - wxString imageSuffix; - wxString cachePath; - wxString nokia_id; - wxString nokia_code; - - int startX; - int startY; - int endX; - int endY; - int offsetX; - int offsetY; - int widthP; // width of all tiles in pixel - int heightP; // height of all tiles in pixel - int leftP; - int topP; - double scale_factor; - - bool isTileDrawn; - bool isTileReady; - - bool isPan; - int panX; - int panY; - - int zoom; - Screen* screen; - MapLayer* map; - MapLayer* origMap; - - double Deg2Rad (double degree) { return degree * M_PI / 180.0; } - double Rad2Deg (double radians) { return radians * 180.0 / M_PI;} - XYFraction* LatLngToXY(LatLng &latlng); - XYFraction* LatLngToRawXY(LatLng &latlng); - LatLng* XYToLatLng(XYFraction &xy, bool isLL=false); - void LatLngToXY(double lng, double lat, int &x, int &y); - - wxString GetTileUrl(int x, int y); - wxString GetTilePath(int x, int y); - - bool Draw(wxBitmap* buffer); - void Extent(double _n, double _w, double _s, double _e, - OGRCoordinateTransformation *_poCT); - void ResizeScreen(int _width, int _height); - void ZoomIn(int mouseX, int mouseY); - void ZoomOut(int mouseX, int mouseY); - bool Zoom(bool is_zoomin, int x0, int y0, int x1, int y1); - void Pan(int x0, int y0, int x1, int y1); - void Reset(int map_type); - void Reset(); - void Refresh(); - bool IsDownloading(); - void SetReady(bool flag); - bool IsExtentChanged(); - void SetupMapType(BasemapItem& basemap_item); - - void CleanCache(); - wxString GetContentType(); - wxString GetUserAgent(const wxString& url); - }; - -} + poCT_rev = OGRCreateCoordinateTransformation(s1, s2); + } + } + return this; + } +}; + +// only for Web mercator projection +class Basemap { + int nn; // pow(2.0, zoom) + + thread_pool pool; + boost::mutex mutex; + + bool start_download; + int n_tasks; + int complete_tasks; + + wxString GetRandomSubdomain(wxString url); + int GetOptimalZoomLevel(double paddingFactor = 1.2); + int GetEasyZoomLevel(); + void GetTiles(); + void DownloadTile(int x, int y); + + public: + Basemap() {} + Basemap(BasemapItem& basemap_item, Screen* _screen, MapLayer* _map, MapLayer* _origMap, wxString _cachePath, + OGRCoordinateTransformation* _poCT, double scale_factor = 1.0); + ~Basemap(); + + OGRCoordinateTransformation* poCT; + BasemapItem basemap_item; + wxString basemapName; + wxString basemapUrl; + wxString imageSuffix; + wxString cachePath; + wxString nokia_id; + wxString nokia_code; + wxString stadia_key; + + int startX; + int startY; + int endX; + int endY; + int offsetX; + int offsetY; + int widthP; // width of all tiles in pixel + int heightP; // height of all tiles in pixel + int leftP; + int topP; + double scale_factor; + + bool isTileDrawn; + bool isTileReady; + + bool isPan; + int panX; + int panY; + + int zoom; + Screen* screen; + MapLayer* map; + MapLayer* origMap; + + double Deg2Rad(double degree) { return degree * M_PI / 180.0; } + double Rad2Deg(double radians) { return radians * 180.0 / M_PI; } + XYFraction* LatLngToXY(LatLng& latlng); + XYFraction* LatLngToRawXY(LatLng& latlng); + LatLng* XYToLatLng(XYFraction& xy, bool isLL = false); + void LatLngToXY(double lng, double lat, int& x, int& y); + + wxString GetTileUrl(int x, int y); + wxString GetTilePath(int x, int y); + + bool Draw(wxBitmap* buffer); + void Extent(double _n, double _w, double _s, double _e, OGRCoordinateTransformation* _poCT); + void ResizeScreen(int _width, int _height); + void ZoomIn(int mouseX, int mouseY); + void ZoomOut(int mouseX, int mouseY); + bool Zoom(bool is_zoomin, int x0, int y0, int x1, int y1); + void Pan(int x0, int y0, int x1, int y1); + void Reset(int map_type); + void Reset(); + void Refresh(); + bool IsDownloading(); + void SetReady(bool flag); + bool IsExtentChanged(); + void SetupMapType(BasemapItem& basemap_item); + + void CleanCache(); + wxString GetContentType(); + wxString GetUserAgent(const wxString& url); +}; + +} // namespace Gda #endif diff --git a/GdaConst.cpp b/GdaConst.cpp index 6891e51ad..41eab87f3 100644 --- a/GdaConst.cpp +++ b/GdaConst.cpp @@ -391,9 +391,9 @@ wxString GdaConst::gda_basemap_sources = "\nHERE.Hybrid,http://{s}.aerial.maps.api.here.com/maptile/2.1/maptile/newest/hybrid.day/{z}/{x}/{y}/256/png8?app_id=HERE_APP_ID&app_code=HERE_APP_CODE" "\nHERE.Satellite,http://{s}.aerial.maps.api.here.com/maptile/2.1/maptile/newest/satellite.day/{z}/{x}/{y}/256/png8?app_id=HERE_APP_ID&app_code=HERE_APP_CODE" "\nOpenStreetMap.Mapnik,https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" -"\nStamen.Toner,https://stamen-tiles-{s}.a.ssl.fastly.net/toner/{z}/{x}/{y}@2x.png" -"\nStamen.TonerLite,https://stamen-tiles-{s}.a.ssl.fastly.net/toner-lite/{z}/{x}/{y}@2x.png" -"\nStamen.Watercolor,https://stamen-tiles-{s}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg" +"\nStamen.Toner,https://tiles.stadiamaps.com/tiles/stamen_toner/{z}/{x}/{y}@2x.png?api_key=STADIA_KEY" +"\nStamen.TonerLite,https://tiles.stadiamaps.com/tiles/stamen_toner_lite/{z}/{x}/{y}@2x.png?api_key=STADIA_KEY" +"\nStamen.Watercolor,https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.jpg?api_key=STADIA_KEY" "\nOther (China).GaoDe,http://webst{s}.is.autonavi.com/appmaptile?style=8&x={x}&y={y}&z={z}" "\nOther (China).GaoDe(Satellite),http://webst{s}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}" ; diff --git a/rc/GdaAppResources.cpp b/rc/GdaAppResources.cpp index 4baaf6bac..9218fdcb4 100644 --- a/rc/GdaAppResources.cpp +++ b/rc/GdaAppResources.cpp @@ -4,10 +4,6 @@ #include -#ifdef __BORLANDC__ - #pragma hdrstop -#endif - #include #include #include @@ -15580,7 +15576,7 @@ static unsigned char xml_res_file_8[] = { 22,138,34,50,137,8,19,192,47,251,204,72,98,41,90,148,202,0,0,0,0,73,69, 78,68,174,66,96,130}; -static size_t xml_res_size_9 = 401583; +static size_t xml_res_size_9 = 402128; static unsigned char xml_res_file_9[] = { 60,63,120,109,108,32,118,101,114,115,105,111,110,61,34,49,46,48,34,32,101, 110,99,111,100,105,110,103,61,34,117,116,102,45,56,34,63,62,10,60,114,101, @@ -33468,7 +33464,7 @@ static unsigned char xml_res_file_9[] = { 101,109,34,62,10,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99, 108,97,115,115,61,34,119,120,80,97,110,101,108,34,32,110,97,109,101,61, 34,34,62,10,32,32,32,32,32,32,32,32,32,32,60,115,105,122,101,62,54,54,48, -44,52,52,48,60,47,115,105,122,101,62,10,32,32,32,32,32,32,32,32,32,32,60, +44,52,56,48,60,47,115,105,122,101,62,10,32,32,32,32,32,32,32,32,32,32,60, 111,98,106,101,99,116,32,99,108,97,115,115,61,34,119,120,66,111,120,83, 105,122,101,114,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,60,111,114, 105,101,110,116,62,119,120,86,69,82,84,73,67,65,76,60,47,111,114,105,101, @@ -33539,156 +33535,180 @@ static unsigned char xml_res_file_9[] = { 34,115,105,122,101,114,105,116,101,109,34,62,10,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108,97,115, 115,61,34,119,120,83,116,97,116,105,99,84,101,120,116,34,32,110,97,109, -101,61,34,73,68,95,83,84,65,84,73,67,84,69,88,84,34,62,10,32,32,32,32,32, -32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,108,97,98,101,108,62,72, -69,82,69,32,65,112,112,32,73,68,60,47,108,97,98,101,108,62,10,32,32,32, -32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99,116, -62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,98,111,114, -100,101,114,62,53,60,47,98,111,114,100,101,114,62,10,32,32,32,32,32,32, -32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32, -32,32,32,32,32,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108, -97,115,115,61,34,115,105,122,101,114,105,116,101,109,34,62,10,32,32,32, -32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32, -99,108,97,115,115,61,34,119,120,84,101,120,116,67,116,114,108,34,32,110, -97,109,101,61,34,73,68,67,95,78,79,75,73,65,95,85,83,69,82,78,65,77,69, -34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60, -115,105,122,101,62,49,51,48,44,45,49,60,47,115,105,122,101,62,10,32,32, -32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99, -116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98, -106,101,99,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60, -111,98,106,101,99,116,32,99,108,97,115,115,61,34,115,105,122,101,114,105, -116,101,109,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, -32,60,111,98,106,101,99,116,32,99,108,97,115,115,61,34,119,120,83,116,97, -116,105,99,84,101,120,116,34,47,62,10,32,32,32,32,32,32,32,32,32,32,32, -32,32,32,32,32,32,32,60,102,108,97,103,62,119,120,65,76,73,71,78,95,67, -69,78,84,82,69,95,72,79,82,73,90,79,78,84,65,76,60,47,102,108,97,103,62, -10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,98,111,114, -100,101,114,62,53,60,47,98,111,114,100,101,114,62,10,32,32,32,32,32,32, -32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32, -32,32,32,32,32,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108, +101,61,34,73,68,67,95,83,84,65,84,73,67,95,83,84,65,68,73,65,34,62,10,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,108,97,98,101, +108,62,83,116,97,100,105,97,32,77,97,112,115,32,75,101,121,60,47,108,97, +98,101,108,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108,97,115,115,61,34,115, +105,122,101,114,105,116,101,109,34,62,10,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108,97,115,115,61, +34,119,120,84,101,120,116,67,116,114,108,34,32,110,97,109,101,61,34,73, +68,67,95,83,84,65,68,73,65,95,75,69,89,34,62,10,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,60,115,105,122,101,62,49,51,48,44,45, +49,100,60,47,115,105,122,101,62,10,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108, 97,115,115,61,34,115,105,122,101,114,105,116,101,109,34,62,10,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32, 99,108,97,115,115,61,34,119,120,83,116,97,116,105,99,84,101,120,116,34, -32,110,97,109,101,61,34,73,68,67,95,83,84,65,84,73,67,34,62,10,32,32,32, -32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,108,97,98,101,108, -62,72,69,82,69,32,65,112,112,32,67,111,100,101,60,47,108,97,98,101,108, -62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98, -106,101,99,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60, +47,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106, +101,99,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,111, +98,106,101,99,116,32,99,108,97,115,115,61,34,115,105,122,101,114,105,116, +101,109,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +60,111,98,106,101,99,116,32,99,108,97,115,115,61,34,119,120,83,116,97,116, +105,99,84,101,120,116,34,32,110,97,109,101,61,34,73,68,95,83,84,65,84,73, +67,84,69,88,84,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,60,108,97,98,101,108,62,72,69,82,69,32,65,112,112,32,73,68, +60,47,108,97,98,101,108,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,60,98,111,114,100,101,114,62,53,60,47,98,111, +114,100,101,114,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60, 47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,60,111,98,106,101,99,116,32,99,108,97,115,115,61,34,115,105,122,101, 114,105,116,101,109,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,32,32,60,111,98,106,101,99,116,32,99,108,97,115,115,61,34,119,120, 84,101,120,116,67,116,114,108,34,32,110,97,109,101,61,34,73,68,67,95,78, -79,75,73,65,95,75,69,89,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32, -32,32,32,32,32,32,32,60,115,105,122,101,62,49,51,48,44,45,49,100,60,47, -115,105,122,101,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, -32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32, +79,75,73,65,95,85,83,69,82,78,65,77,69,34,62,10,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,60,115,105,122,101,62,49,51,48,44,45, +49,60,47,115,105,122,101,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,32,32,32, -32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108,97,115,115,61,34, -115,105,122,101,114,105,116,101,109,34,62,10,32,32,32,32,32,32,32,32,32, -32,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108,97,115,115, -61,34,119,120,83,116,97,116,105,99,84,101,120,116,34,47,62,10,32,32,32, -32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10, -32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,99,111,108,115,62,51, -60,47,99,111,108,115,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, -32,60,114,111,119,115,62,50,60,47,114,111,119,115,62,10,32,32,32,32,32, -32,32,32,32,32,32,32,32,32,32,32,60,118,103,97,112,62,50,60,47,118,103, -97,112,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,104,103, -97,112,62,49,48,60,47,104,103,97,112,62,10,32,32,32,32,32,32,32,32,32,32, -32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,32,32,32, -32,32,32,32,32,60,102,108,97,103,62,119,120,76,69,70,84,124,119,120,65, -76,76,124,119,120,65,76,73,71,78,95,67,69,78,84,82,69,95,72,79,82,73,90, -79,78,84,65,76,60,47,102,108,97,103,62,10,32,32,32,32,32,32,32,32,32,32, -32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,32,32,32,32,32, -32,60,111,98,106,101,99,116,32,99,108,97,115,115,61,34,115,105,122,101, -114,105,116,101,109,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32, -60,111,98,106,101,99,116,32,99,108,97,115,115,61,34,119,120,66,111,120, -83,105,122,101,114,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, -32,60,111,98,106,101,99,116,32,99,108,97,115,115,61,34,115,112,97,99,101, -114,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,115, -105,122,101,62,50,44,49,48,60,47,115,105,122,101,62,10,32,32,32,32,32,32, +32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108,97,115, +115,61,34,115,105,122,101,114,105,116,101,109,34,62,10,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108, +97,115,115,61,34,119,120,83,116,97,116,105,99,84,101,120,116,34,47,62,10, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,102,108,97,103, +62,119,120,65,76,73,71,78,95,67,69,78,84,82,69,95,72,79,82,73,90,79,78, +84,65,76,60,47,102,108,97,103,62,10,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,60,98,111,114,100,101,114,62,53,60,47,98,111,114,100, +101,114,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111, +98,106,101,99,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +60,111,98,106,101,99,116,32,99,108,97,115,115,61,34,115,105,122,101,114, +105,116,101,109,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,60,111,98,106,101,99,116,32,99,108,97,115,115,61,34,119,120,83,116, +97,116,105,99,84,101,120,116,34,32,110,97,109,101,61,34,73,68,67,95,83, +84,65,84,73,67,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,60,108,97,98,101,108,62,72,69,82,69,32,65,112,112,32,67,111, +100,101,60,47,108,97,98,101,108,62,10,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108, 97,115,115,61,34,115,105,122,101,114,105,116,101,109,34,62,10,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32, -99,108,97,115,115,61,34,119,120,83,116,97,116,105,99,84,101,120,116,34, -62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,108, -97,98,101,108,62,71,101,116,32,97,32,102,114,101,101,32,72,69,82,69,32, -97,99,99,111,117,110,116,58,32,60,47,108,97,98,101,108,62,10,32,32,32,32, -32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62, -10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101, -99,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,111,114, -105,101,110,116,62,119,120,72,79,82,73,90,79,78,84,65,76,60,47,111,114, -105,101,110,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60, -111,98,106,101,99,116,32,99,108,97,115,115,61,34,115,105,122,101,114,105, -116,101,109,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, -32,60,111,98,106,101,99,116,32,99,108,97,115,115,61,34,119,120,72,121,112, -101,114,108,105,110,107,67,116,114,108,34,62,10,32,32,32,32,32,32,32,32, -32,32,32,32,32,32,32,32,32,32,32,32,60,117,114,108,62,104,116,116,112,115, -58,47,47,100,101,118,101,108,111,112,101,114,46,104,101,114,101,46,99,111, -109,47,60,47,117,114,108,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32, -32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,32,32,32, -32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32, -32,32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32, -32,32,32,32,32,32,32,32,32,60,102,108,97,103,62,119,120,76,69,70,84,124, -119,120,65,76,76,124,119,120,65,76,73,71,78,95,67,69,78,84,82,69,95,72, -79,82,73,90,79,78,84,65,76,60,47,102,108,97,103,62,10,32,32,32,32,32,32, -32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,32, -32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,32,32,60,47, -111,98,106,101,99,116,62,10,32,32,32,32,32,32,60,47,111,98,106,101,99,116, -62,10,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108,97,115,115,61, -34,115,105,122,101,114,105,116,101,109,34,62,10,32,32,32,32,32,32,32,32, -60,111,98,106,101,99,116,32,99,108,97,115,115,61,34,119,120,66,111,120, -83,105,122,101,114,34,62,10,32,32,32,32,32,32,32,32,32,32,60,111,98,106, -101,99,116,32,99,108,97,115,115,61,34,115,105,122,101,114,105,116,101,109, -34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32, -99,108,97,115,115,61,34,119,120,66,117,116,116,111,110,34,32,110,97,109, -101,61,34,73,68,95,78,79,75,73,65,95,82,69,83,69,84,34,62,10,32,32,32,32, -32,32,32,32,32,32,32,32,32,32,60,108,97,98,101,108,62,82,101,115,101,116, -60,47,108,97,98,101,108,62,10,32,32,32,32,32,32,32,32,32,32,32,32,60,47, -111,98,106,101,99,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,60,102, -108,97,103,62,119,120,65,76,73,71,78,95,67,69,78,84,69,82,95,86,69,82,84, -73,67,65,76,124,119,120,65,76,76,60,47,102,108,97,103,62,10,32,32,32,32, -32,32,32,32,32,32,32,32,60,98,111,114,100,101,114,62,53,60,47,98,111,114, -100,101,114,62,10,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99, -116,62,10,32,32,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99, -108,97,115,115,61,34,115,105,122,101,114,105,116,101,109,34,62,10,32,32, +99,108,97,115,115,61,34,119,120,84,101,120,116,67,116,114,108,34,32,110, +97,109,101,61,34,73,68,67,95,78,79,75,73,65,95,75,69,89,34,62,10,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,115,105,122,101, +62,49,51,48,44,45,49,100,60,47,115,105,122,101,62,10,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99,116, +62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,111,98,106,101, +99,116,32,99,108,97,115,115,61,34,115,105,122,101,114,105,116,101,109,34, +62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,111,98,106, +101,99,116,32,99,108,97,115,115,61,34,119,120,83,116,97,116,105,99,84,101, +120,116,34,47,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60, +47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,60,99,111,108,115,62,51,60,47,99,111,108,115,62,10,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,60,114,111,119,115,62,51,60,47,114,111, +119,115,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,118,103, +97,112,62,50,60,47,118,103,97,112,62,10,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,60,104,103,97,112,62,49,48,60,47,104,103,97,112,62,10,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,102,108,97,103,62,119,120, +76,69,70,84,124,119,120,65,76,76,124,119,120,65,76,73,71,78,95,67,69,78, +84,82,69,95,72,79,82,73,90,79,78,84,65,76,60,47,102,108,97,103,62,10,32, +32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32, 32,32,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108,97,115, -115,61,34,119,120,66,117,116,116,111,110,34,32,110,97,109,101,61,34,119, -120,73,68,95,79,75,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60, -108,97,98,101,108,62,79,75,60,47,108,97,98,101,108,62,10,32,32,32,32,32, -32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32, -32,32,32,32,32,32,60,102,108,97,103,62,119,120,65,76,73,71,78,95,67,69, -78,84,69,82,95,86,69,82,84,73,67,65,76,124,119,120,65,76,76,60,47,102,108, -97,103,62,10,32,32,32,32,32,32,32,32,32,32,32,32,60,98,111,114,100,101, -114,62,53,60,47,98,111,114,100,101,114,62,10,32,32,32,32,32,32,32,32,32, -32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,32,32,32,32,60,111, +115,61,34,115,105,122,101,114,105,116,101,109,34,62,10,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108,97,115,115,61, +34,119,120,66,111,120,83,105,122,101,114,34,62,10,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108,97,115,115, +61,34,115,112,97,99,101,114,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,60,115,105,122,101,62,50,44,49,48,60,47,115,105,122,101, +62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106, +101,99,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,111, 98,106,101,99,116,32,99,108,97,115,115,61,34,115,105,122,101,114,105,116, -101,109,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,60,111,98,106,101, -99,116,32,99,108,97,115,115,61,34,119,120,66,117,116,116,111,110,34,32, -110,97,109,101,61,34,119,120,73,68,95,67,65,78,67,69,76,34,62,10,32,32, -32,32,32,32,32,32,32,32,32,32,32,32,60,108,97,98,101,108,62,67,97,110,99, -101,108,60,47,108,97,98,101,108,62,10,32,32,32,32,32,32,32,32,32,32,32, -32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32, -60,102,108,97,103,62,119,120,71,82,79,87,124,119,120,65,76,76,60,47,102, +101,109,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +60,111,98,106,101,99,116,32,99,108,97,115,115,61,34,119,120,83,116,97,116, +105,99,84,101,120,116,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,60,108,97,98,101,108,62,71,101,116,32,97,32,102,114,101, +101,32,72,69,82,69,32,97,99,99,111,117,110,116,58,32,60,47,108,97,98,101, +108,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111, +98,106,101,99,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,60,111,114,105,101,110,116,62,119,120,72,79,82,73,90,79,78,84, +65,76,60,47,111,114,105,101,110,116,62,10,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108,97,115,115,61,34,115, +105,122,101,114,105,116,101,109,34,62,10,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108,97,115,115,61, +34,119,120,72,121,112,101,114,108,105,110,107,67,116,114,108,34,62,10,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,117,114,108, +62,104,116,116,112,115,58,47,47,100,101,118,101,108,111,112,101,114,46, +104,101,114,101,46,99,111,109,47,60,47,117,114,108,62,10,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99, +116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101, +99,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,102,108,97,103, +62,119,120,76,69,70,84,124,119,120,65,76,76,124,119,120,65,76,73,71,78, +95,67,69,78,84,82,69,95,72,79,82,73,90,79,78,84,65,76,60,47,102,108,97, +103,62,10,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99,116, +62,10,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32, +32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32, +60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,60,111,98,106,101,99, +116,32,99,108,97,115,115,61,34,115,105,122,101,114,105,116,101,109,34,62, +10,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108,97,115,115, +61,34,119,120,66,111,120,83,105,122,101,114,34,62,10,32,32,32,32,32,32, +32,32,32,32,60,111,98,106,101,99,116,32,99,108,97,115,115,61,34,115,105, +122,101,114,105,116,101,109,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32, +60,111,98,106,101,99,116,32,99,108,97,115,115,61,34,119,120,66,117,116, +116,111,110,34,32,110,97,109,101,61,34,73,68,95,78,79,75,73,65,95,82,69, +83,69,84,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,108,97,98, +101,108,62,82,101,115,101,116,60,47,108,97,98,101,108,62,10,32,32,32,32, +32,32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32, +32,32,32,32,32,32,32,60,102,108,97,103,62,119,120,65,76,73,71,78,95,67, +69,78,84,69,82,95,86,69,82,84,73,67,65,76,124,119,120,65,76,76,60,47,102, 108,97,103,62,10,32,32,32,32,32,32,32,32,32,32,32,32,60,98,111,114,100, 101,114,62,53,60,47,98,111,114,100,101,114,62,10,32,32,32,32,32,32,32,32, 32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,32,32,32,32,60, -111,114,105,101,110,116,62,119,120,72,79,82,73,90,79,78,84,65,76,60,47, -111,114,105,101,110,116,62,10,32,32,32,32,32,32,32,32,60,47,111,98,106, -101,99,116,62,10,32,32,32,32,32,32,32,32,60,102,108,97,103,62,119,120,65, -76,73,71,78,95,67,69,78,84,82,69,95,72,79,82,73,90,79,78,84,65,76,60,47, -102,108,97,103,62,10,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10, -32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,60,116,105,116, -108,101,62,66,97,115,101,109,97,112,32,67,111,110,102,105,103,117,114,97, -116,105,111,110,32,68,105,97,108,111,103,60,47,116,105,116,108,101,62,10, -32,32,32,32,60,99,101,110,116,101,114,101,100,62,49,60,47,99,101,110,116, -101,114,101,100,62,10,32,32,32,32,60,115,116,121,108,101,62,119,120,67, -65,80,84,73,79,78,124,119,120,83,89,83,84,69,77,95,77,69,78,85,124,119, -120,67,76,79,83,69,95,66,79,88,60,47,115,116,121,108,101,62,10,32,32,32, -32,60,115,105,122,101,62,53,54,48,44,50,52,48,60,47,115,105,122,101,62, -10,32,32,60,47,111,98,106,101,99,116,62,10,60,47,114,101,115,111,117,114, -99,101,62,10}; +111,98,106,101,99,116,32,99,108,97,115,115,61,34,115,105,122,101,114,105, +116,101,109,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,60,111,98,106, +101,99,116,32,99,108,97,115,115,61,34,119,120,66,117,116,116,111,110,34, +32,110,97,109,101,61,34,119,120,73,68,95,79,75,34,62,10,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,60,108,97,98,101,108,62,79,75,60,47,108,97,98, +101,108,62,10,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101, +99,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,60,102,108,97,103,62,119, +120,65,76,73,71,78,95,67,69,78,84,69,82,95,86,69,82,84,73,67,65,76,124, +119,120,65,76,76,60,47,102,108,97,103,62,10,32,32,32,32,32,32,32,32,32, +32,32,32,60,98,111,114,100,101,114,62,53,60,47,98,111,114,100,101,114,62, +10,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32, +32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108,97,115,115,61, +34,115,105,122,101,114,105,116,101,109,34,62,10,32,32,32,32,32,32,32,32, +32,32,32,32,60,111,98,106,101,99,116,32,99,108,97,115,115,61,34,119,120, +66,117,116,116,111,110,34,32,110,97,109,101,61,34,119,120,73,68,95,67,65, +78,67,69,76,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,108,97, +98,101,108,62,67,97,110,99,101,108,60,47,108,97,98,101,108,62,10,32,32, +32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32, +32,32,32,32,32,32,32,32,32,60,102,108,97,103,62,119,120,71,82,79,87,124, +119,120,65,76,76,60,47,102,108,97,103,62,10,32,32,32,32,32,32,32,32,32, +32,32,32,60,98,111,114,100,101,114,62,53,60,47,98,111,114,100,101,114,62, +10,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32, +32,32,32,32,32,32,32,32,60,111,114,105,101,110,116,62,119,120,72,79,82, +73,90,79,78,84,65,76,60,47,111,114,105,101,110,116,62,10,32,32,32,32,32, +32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,32,32,60,102, +108,97,103,62,119,120,65,76,73,71,78,95,67,69,78,84,82,69,95,72,79,82,73, +90,79,78,84,65,76,60,47,102,108,97,103,62,10,32,32,32,32,32,32,60,47,111, +98,106,101,99,116,62,10,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32, +32,32,32,60,116,105,116,108,101,62,66,97,115,101,109,97,112,32,67,111,110, +102,105,103,117,114,97,116,105,111,110,32,68,105,97,108,111,103,60,47,116, +105,116,108,101,62,10,32,32,32,32,60,99,101,110,116,101,114,101,100,62, +49,60,47,99,101,110,116,101,114,101,100,62,10,32,32,32,32,60,115,116,121, +108,101,62,119,120,67,65,80,84,73,79,78,124,119,120,83,89,83,84,69,77,95, +77,69,78,85,124,119,120,67,76,79,83,69,95,66,79,88,60,47,115,116,121,108, +101,62,10,32,32,32,32,60,115,105,122,101,62,53,54,48,44,50,52,48,60,47, +115,105,122,101,62,10,32,32,60,47,111,98,106,101,99,116,62,10,60,47,114, +101,115,111,117,114,99,101,62,10}; static size_t xml_res_size_10 = 936; static unsigned char xml_res_file_10[] = { diff --git a/rc/dialogs.xrc b/rc/dialogs.xrc index c5757ecf0..1afdedb33 100644 --- a/rc/dialogs.xrc +++ b/rc/dialogs.xrc @@ -9387,7 +9387,7 @@ wxVERTICAL - 660,440 + 660,480 wxVERTICAL @@ -9422,6 +9422,19 @@ + + + + + + + + 130,-1d + + + + + @@ -9452,7 +9465,7 @@ 3 - 2 + 3 2 10