├── .gitignore ├── LICENSE ├── README.md ├── __init__.py ├── add_connection_dialog.py ├── add_connection_dialog_base.ui ├── browser_mapitem.py ├── browser_root_collection.py ├── configure_dialog.py ├── configure_dialog_base.ui ├── data ├── background.geojson ├── icons │ ├── bright │ │ ├── road_1.svg │ │ ├── road_2.svg │ │ ├── road_3.svg │ │ ├── road_4.svg │ │ ├── road_5.svg │ │ ├── road_6.svg │ │ ├── us-highway_1.svg │ │ ├── us-highway_2.svg │ │ ├── us-highway_3.svg │ │ ├── us-interstate_1.svg │ │ ├── us-interstate_2.svg │ │ ├── us-interstate_3.svg │ │ ├── us-interstate_4.svg │ │ ├── us-interstate_5.svg │ │ ├── us-state_1.svg │ │ ├── us-state_2.svg │ │ ├── us-state_3.svg │ │ ├── us-state_4.svg │ │ ├── us-state_5.svg │ │ └── us-state_6.svg │ ├── default_1.svg │ ├── default_2.svg │ ├── default_3.svg │ ├── default_4.svg │ ├── default_5.svg │ ├── default_6.svg │ ├── road_1.svg │ ├── road_2.svg │ ├── road_3.svg │ ├── road_4.svg │ ├── road_5.svg │ ├── road_6.svg │ ├── road_motorway.svg │ ├── road_primary.svg │ ├── road_secondary.svg │ ├── road_tertiary.svg │ ├── us-highway_1.svg │ ├── us-highway_2.svg │ ├── us-highway_3.svg │ ├── us-interstate_1.svg │ ├── us-interstate_2.svg │ ├── us-interstate_3.svg │ ├── us-interstate_4.svg │ ├── us-interstate_5.svg │ ├── us-state_1.svg │ ├── us-state_2.svg │ ├── us-state_3.svg │ ├── us-state_4.svg │ ├── us-state_5.svg │ └── us-state_6.svg ├── ocean-color-ramp.txt └── terrain-color-ramp.txt ├── edit_connection_dialog.py ├── edit_connection_dialog_base.ui ├── geocoder.py ├── gl2qgis ├── __init__.py ├── converter.py └── gl2qgis.py ├── imgs ├── icon_account_dark.svg ├── icon_account_light.svg ├── icon_maps_dark.svg ├── icon_maps_light.svg ├── icon_maptiler.svg ├── logo_maptiler_dark.svg ├── logo_maptiler_light.svg ├── readme_01.png ├── readme_02.png ├── readme_03.png ├── readme_04.png ├── readme_06.png ├── readme_07.png ├── readme_08.png ├── readme_09.png ├── readme_10.gif ├── readme_11.png ├── readme_12.png ├── readme_13.gif └── readme_14.png ├── mapdatasets.py ├── maptiler.py ├── metadata.txt ├── settings_manager.py └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | .DS_Store 3 | gl2qgis/sprites/ -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | /*************************************************************************** 4 | MapTiler 5 | A QGIS plugin 6 | Show MapTiler cloud maps. 7 | Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/ 8 | ------------------- 9 | begin : 2020-04-02 10 | copyright : (C) 2020 by MapTiler AG 11 | email : sales@maptiler.com 12 | git sha : $Format:%H$ 13 | ***************************************************************************/ 14 | 15 | /*************************************************************************** 16 | * * 17 | * This program is free software; you can redistribute it and/or modify * 18 | * it under the terms of the GNU General Public License as published by * 19 | * the Free Software Foundation; either version 2 of the License, or * 20 | * (at your option) any later version. * 21 | * * 22 | ***************************************************************************/ 23 | This script initializes the plugin, making it known to QGIS. 24 | """ 25 | 26 | 27 | # noinspection PyPep8Naming 28 | def classFactory(iface): # pylint: disable=invalid-name 29 | """Load MapTiler class from file MapTiler. 30 | 31 | :param iface: A QGIS interface instance. 32 | :type iface: QgsInterface 33 | """ 34 | # 35 | from .maptiler import MapTiler 36 | return MapTiler(iface) 37 | -------------------------------------------------------------------------------- /add_connection_dialog.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from qgis.PyQt import uic, QtWidgets, QtCore 4 | import os 5 | 6 | from .settings_manager import SettingsManager 7 | from . import mapdatasets 8 | 9 | 10 | class AddConnectionDialog(QtWidgets.QDialog): 11 | 12 | STANDARD_DATASET = mapdatasets.STANDARD_DATASET 13 | LOCAL_JP_DATASET = mapdatasets.LOCAL_JP_DATASET 14 | LOCAL_NL_DATASET = mapdatasets.LOCAL_NL_DATASET 15 | LOCAL_UK_DATASET = mapdatasets.LOCAL_UK_DATASET 16 | 17 | def __init__(self): 18 | super().__init__() 19 | self.ui = uic.loadUi(os.path.join(os.path.dirname( 20 | __file__), 'add_connection_dialog_base.ui'), self) 21 | 22 | self._init_list() 23 | self.ui.button_box.accepted.connect(self._accepted) 24 | self.ui.button_box.rejected.connect(self._rejected) 25 | 26 | def _init_list(self): 27 | # support multiple selection 28 | self.ui.listWidget.setSelectionMode( 29 | QtWidgets.QAbstractItemView.ExtendedSelection) 30 | 31 | DATASETS = dict(**self.STANDARD_DATASET, 32 | **self.LOCAL_JP_DATASET, 33 | **self.LOCAL_NL_DATASET, 34 | **self.LOCAL_UK_DATASET 35 | ) 36 | 37 | smanager = SettingsManager() 38 | selectedmaps = smanager.get_setting('selectedmaps') 39 | for key in DATASETS: 40 | if key in selectedmaps: 41 | continue 42 | self.ui.listWidget.addItem(key) 43 | 44 | def _maptiler_tab_action(self): 45 | selecteditems = self.ui.listWidget.selectedItems() 46 | 47 | smanager = SettingsManager() 48 | selectedmaps = smanager.get_setting('selectedmaps') 49 | 50 | for item in selecteditems: 51 | # this lines is not runned in normal 52 | if item.text() in selectedmaps: 53 | print('Selected map already exist in Browser') 54 | return 55 | 56 | selectedmaps.append(item.text()) 57 | smanager.store_setting('selectedmaps', selectedmaps) 58 | 59 | def _custom_tab_action(self): 60 | name = self.ui.nameLineEdit.text() 61 | json_url = self.ui.jsonLineEdit.text() 62 | 63 | smanager = SettingsManager() 64 | if self._has_error(): 65 | return 66 | 67 | custommaps = smanager.get_setting('custommaps') 68 | custommaps[name] = { 69 | 'custom': json_url 70 | } 71 | smanager.store_setting('custommaps', custommaps) 72 | 73 | def _accepted(self): 74 | if self.ui.tabWidget.currentIndex() == 0: 75 | self._maptiler_tab_action() 76 | else: 77 | self._custom_tab_action() 78 | self.close() 79 | 80 | def _rejected(self): 81 | self.close() 82 | 83 | def _has_error(self): 84 | name = self.ui.nameLineEdit.text() 85 | json_url = self.ui.jsonLineEdit.text() 86 | 87 | is_empty_name = False 88 | is_empty_url = False 89 | is_existing_name = False 90 | error_message = '' 91 | 92 | if name == '': 93 | is_empty_name = True 94 | error_message += 'Name is empty, please input.\n' 95 | 96 | if json_url == '': 97 | is_empty_url = True 98 | error_message += 'Url to map is empty, please input.\n' 99 | 100 | smanager = SettingsManager() 101 | custommaps = smanager.get_setting('custommaps') 102 | if name in custommaps: 103 | is_existing_name = True 104 | error_message += str('"' + name + '"' + 105 | ' already exists, please input other name.\n') 106 | 107 | if is_empty_name or is_empty_url or is_existing_name: 108 | QtWidgets.QMessageBox.warning(None, 'Error', error_message) 109 | return True 110 | -------------------------------------------------------------------------------- /add_connection_dialog_base.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | newConnectionDialogBase 4 | 5 | 6 | 7 | 0 8 | 0 9 | 500 10 | 500 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | 21 | 500 22 | 500 23 | 24 | 25 | 26 | 27 | 500 28 | 500 29 | 30 | 31 | 32 | Add a new map... 33 | 34 | 35 | 36 | 37 | 38 | 39 | 0 40 | 0 41 | 42 | 43 | 44 | 45 | 0 46 | 0 47 | 48 | 49 | 50 | 0 51 | 52 | 53 | 54 | MapTiler Cloud 55 | 56 | 57 | 58 | 59 | 60 | 61 | 0 62 | 0 63 | 64 | 65 | 66 | 67 | 0 68 | 0 69 | 70 | 71 | 72 | 73 | 50 74 | false 75 | 76 | 77 | 78 | Select a map from MapTiler Cloud. 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | From URL 90 | 91 | 92 | 93 | 94 | 95 | Name 96 | 97 | 98 | 99 | 100 | 101 | 102 | 50 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 0 111 | 0 112 | 113 | 114 | 115 | 116 | 0 117 | 0 118 | 119 | 120 | 121 | Add a link to vector style (GL JSON format) or raster TileJSON. 122 | 123 | 124 | true 125 | 126 | 127 | 128 | 129 | 130 | 131 | 200 132 | 133 | 134 | 135 | 136 | 137 | 138 | JSON URL 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | Qt::Horizontal 150 | 151 | 152 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /browser_root_collection.py: -------------------------------------------------------------------------------- 1 | import os 2 | from qgis.PyQt import sip 3 | from qgis.PyQt.QtGui import QIcon 4 | from qgis.PyQt.QtWidgets import QAction 5 | from qgis.core import * 6 | 7 | from .browser_mapitem import MapDataItem 8 | from .add_connection_dialog import AddConnectionDialog 9 | from .configure_dialog import ConfigureDialog 10 | from .settings_manager import SettingsManager 11 | from . import mapdatasets 12 | 13 | IMGS_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "imgs") 14 | 15 | 16 | class DataItemProvider(QgsDataItemProvider): 17 | def __init__(self): 18 | QgsDataItemProvider.__init__(self) 19 | 20 | def name(self): 21 | return "MapTilerProvider" 22 | 23 | def capabilities(self): 24 | return QgsDataProvider.Net 25 | 26 | def createDataItem(self, path, parentItem): 27 | root = RootCollection() 28 | sip.transferto(root, None) 29 | return root 30 | 31 | 32 | class RootCollection(QgsDataCollectionItem): 33 | STANDARD_DATASET = mapdatasets.STANDARD_DATASET 34 | LOCAL_JP_DATASET = mapdatasets.LOCAL_JP_DATASET 35 | LOCAL_NL_DATASET = mapdatasets.LOCAL_NL_DATASET 36 | LOCAL_UK_DATASET = mapdatasets.LOCAL_UK_DATASET 37 | 38 | def __init__(self): 39 | QgsDataCollectionItem.__init__(self, None, "MapTiler", "/MapTiler") 40 | 41 | self.setIcon(QIcon(os.path.join(IMGS_PATH, "icon_maptiler.svg"))) 42 | 43 | def createChildren(self): 44 | children = [] 45 | 46 | DATASETS = dict(**self.STANDARD_DATASET, 47 | **self.LOCAL_JP_DATASET, 48 | **self.LOCAL_NL_DATASET, 49 | **self.LOCAL_UK_DATASET, 50 | ) 51 | smanager = SettingsManager() 52 | selectedmaps = smanager.get_setting('selectedmaps') 53 | 54 | for key in DATASETS: 55 | if not key in selectedmaps: 56 | continue 57 | md_item = MapDataItem(self, key, DATASETS[key]) 58 | sip.transferto(md_item, self) 59 | children.append(md_item) 60 | 61 | custommaps = smanager.get_setting('custommaps') 62 | for key in custommaps: 63 | md_item = MapDataItem(self, key, 64 | custommaps[key], editable=True) 65 | sip.transferto(md_item, self) 66 | children.append(md_item) 67 | 68 | return children 69 | 70 | def actions(self, parent): 71 | actions = [] 72 | 73 | add_action = QAction(QIcon(), 'Add a new map...', parent) 74 | add_action.triggered.connect(self._open_add_dialog) 75 | actions.append(add_action) 76 | 77 | configure_action = QAction(QIcon(), 'Account...', parent) 78 | configure_action.triggered.connect(self._open_configure_dialog) 79 | actions.append(configure_action) 80 | 81 | return actions 82 | 83 | def _open_add_dialog(self): 84 | add_dialog = AddConnectionDialog() 85 | add_dialog.exec() 86 | self.refreshConnections() 87 | 88 | def _open_configure_dialog(self): 89 | configure_dialog = ConfigureDialog() 90 | configure_dialog.exec() 91 | self.refreshConnections() 92 | -------------------------------------------------------------------------------- /configure_dialog.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | 5 | from qgis.PyQt import uic, QtWidgets, QtGui 6 | from qgis.core import Qgis, QgsAuthMethodConfig, QgsApplication 7 | from qgis.PyQt.QtWidgets import QMessageBox 8 | 9 | from .settings_manager import SettingsManager 10 | from . import utils 11 | 12 | 13 | def _token_format_valid(token): 14 | if "_" in token: 15 | parts = token.split("_") 16 | if len(parts) == 2: 17 | if len(parts[0]) == 32 and len(parts[1]) >= 64: 18 | return True 19 | return False 20 | 21 | 22 | class ConfigureDialog(QtWidgets.QDialog): 23 | def __init__(self): 24 | super().__init__() 25 | 26 | self.ui = uic.loadUi(os.path.join(os.path.dirname( 27 | __file__), 'configure_dialog_base.ui'), self) 28 | 29 | self.ui.button_box.accepted.connect(self._accepted) 30 | self.ui.button_box.rejected.connect(self._rejected) 31 | 32 | # Load saved token 33 | smanager = SettingsManager() 34 | auth_cfg_id = smanager.get_setting('auth_cfg_id') 35 | if auth_cfg_id: 36 | am = QgsApplication.authManager() 37 | cfg = QgsAuthMethodConfig() 38 | am.loadAuthenticationConfig(auth_cfg_id, cfg, True) 39 | token = cfg.configMap().get("token") 40 | self.ui.token_txt.setText(token) 41 | 42 | # e.g. QGIS3.10.4 -> 31004 43 | qgis_version_str = str(Qgis.QGIS_VERSION_INT) 44 | #major_ver = int(qgis_version_str[0]) 45 | minor_ver = int(qgis_version_str[1:3]) 46 | #micro_ver = int(qgis_version_str[3:]) 47 | 48 | # Checkbox are available only when on QGIS version having feature of Vectortile 49 | if minor_ver > 12: 50 | self.ui.vtileCheckBox.setEnabled(True) 51 | prefervector = bool(int(smanager.get_setting('prefervector'))) 52 | self.ui.vtileCheckBox.setChecked(prefervector) 53 | 54 | # when OS in darkmode change icon to darkmode one 55 | if utils.is_in_darkmode(): 56 | darkmode_icon_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 57 | "imgs", 58 | "logo_maptiler_dark.svg") 59 | pixmap = QtGui.QPixmap(darkmode_icon_path) 60 | self.ui.label_2.setPixmap(pixmap) 61 | 62 | def _accepted(self): 63 | # get and store UI values 64 | token = self.ui.token_txt.text() 65 | if token: 66 | cfg = QgsAuthMethodConfig('MapTilerHmacSha256') 67 | if _token_format_valid(token): 68 | smanager = SettingsManager() 69 | am = QgsApplication.authManager() 70 | auth_cfg_id = smanager.get_setting('auth_cfg_id') 71 | if auth_cfg_id: 72 | (res, cfg) = am.loadAuthenticationConfig(auth_cfg_id, cfg, True) 73 | if res: 74 | saved_token = cfg.configMap().get("token") 75 | if not saved_token == token: 76 | cfg.setConfigMap({'token': token}) 77 | (res, cfg) = am.storeAuthenticationConfig(cfg, True) 78 | if res: 79 | smanager.store_setting('auth_cfg_id', cfg.id()) 80 | else: 81 | cfg.setName('qgis-maptiler-plugin') 82 | cfg.setConfigMap({'token': token}) 83 | (res, cfg) = am.storeAuthenticationConfig(cfg, True) 84 | if res: 85 | smanager.store_setting('auth_cfg_id', cfg.id()) 86 | else: 87 | cfg.setName('qgis-maptiler-plugin') 88 | cfg.setConfigMap({'token': token}) 89 | (res, cfg) = am.storeAuthenticationConfig(cfg, True) 90 | if res: 91 | smanager.store_setting('auth_cfg_id', cfg.id()) 92 | prefervector = str(int(self.ui.vtileCheckBox.isChecked())) 93 | smanager.store_setting('prefervector', prefervector) 94 | self.close() 95 | else: 96 | self.ui.label_6.setText(f"Not a valid token format. (Use token, not API key.)") 97 | else: 98 | self.ui.label_6.setText(f"Token is required.") 99 | 100 | def _rejected(self): 101 | self.close() 102 | -------------------------------------------------------------------------------- /configure_dialog_base.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MapTilerConfigureDialogBase 4 | 5 | 6 | 7 | 0 8 | 0 9 | 300 10 | 226 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | 21 | 0 22 | 0 23 | 24 | 25 | 26 | 27 | 300 28 | 226 29 | 30 | 31 | 32 | MapTiler Account 33 | 34 | 35 | 36 | 37 | 38 | 39 | 0 40 | 0 41 | 42 | 43 | 44 | 45 | 300 46 | 80 47 | 48 | 49 | 50 | 51 | 52 | 53 | Qt::RichText 54 | 55 | 56 | imgs/logo_maptiler_light.svg 57 | 58 | 59 | true 60 | 61 | 62 | 63 | 64 | 65 | 66 | Account Settings 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Token 76 | 77 | 78 | 79 | 80 | 81 | 82 | true 83 | 84 | 85 | 100 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | Get FREE credentials at: 102 | 103 | 104 | 105 | 106 | 107 | 108 | <a href="https://maptiler.link/qgiscredentials"><span style=" text-decoration: underline; color:#419cff;">https://cloud.maptiler.com/account/credentials/</span></a></p></body></html> 109 | 110 | 111 | Qt::RichText 112 | 113 | 114 | true 115 | 116 | 117 | 118 | 119 | 120 | 121 | false 122 | 123 | 124 | Use vector tiles by default (requires QGIS 3.14+) 125 | 126 | 127 | 128 | 129 | 130 | 131 | <html><head/><body><p>Read more <a href="https://maptiler.link/qgisplugin"><span style=" text-decoration: underline; color:#419cff;">about this plugin.</span></a></p></body></html> 132 | 133 | 134 | true 135 | 136 | 137 | 138 | 139 | 140 | 141 | Qt::Horizontal 142 | 143 | 144 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /data/background.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "name": "background", 4 | "crs": { 5 | "type": "name", 6 | "properties": { 7 | "name": "urn:ogc:def:crs:EPSG::3857" 8 | } 9 | }, 10 | "features": [ 11 | { 12 | "type": "Feature", 13 | "properties": { 14 | "fid": 1 15 | }, 16 | "geometry": { 17 | "type": "Polygon", 18 | "coordinates": [ 19 | [ 20 | [ 21 | -20037508.342789243906736, 22 | 20048966.104014594107866 23 | ], 24 | [ 25 | -20037508.342789243906736, 26 | -20048966.104014601558447 27 | ], 28 | [ 29 | 20037508.342789243906736, 30 | -20048966.104014601558447 31 | ], 32 | [ 33 | 20037508.342789243906736, 34 | 20048966.104014594107866 35 | ], 36 | [ 37 | -20037508.342789243906736, 38 | 20048966.104014594107866 39 | ] 40 | ] 41 | ] 42 | } 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /data/icons/bright/road_1.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 41 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 58 | 61 | 71 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /data/icons/bright/road_2.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 41 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 58 | 61 | 71 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /data/icons/bright/road_3.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 41 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 58 | 61 | 70 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /data/icons/bright/road_4.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 41 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 58 | 61 | 70 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /data/icons/bright/road_5.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 41 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 58 | 61 | 70 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /data/icons/bright/road_6.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 41 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 58 | 61 | 63 | 72 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /data/icons/bright/us-highway_1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 44 | 55 | 56 | 58 | 59 | 61 | image/svg+xml 62 | 64 | 65 | 66 | 67 | 68 | 73 | 79 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /data/icons/bright/us-highway_2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 44 | 55 | 56 | 58 | 59 | 61 | image/svg+xml 62 | 64 | 65 | 66 | 67 | 68 | 73 | 79 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /data/icons/bright/us-highway_3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 44 | 55 | 56 | 58 | 59 | 61 | image/svg+xml 62 | 64 | 65 | 66 | 67 | 68 | 73 | 81 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /data/icons/bright/us-interstate_1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 45 | 56 | 57 | 59 | 61 | 62 | 64 | image/svg+xml 65 | 67 | 68 | 69 | 70 | 71 | 77 | 83 | 90 | 91 | -------------------------------------------------------------------------------- /data/icons/bright/us-interstate_2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 45 | 56 | 57 | 59 | 61 | 62 | 64 | image/svg+xml 65 | 67 | 68 | 69 | 70 | 71 | 77 | 83 | 90 | 91 | -------------------------------------------------------------------------------- /data/icons/bright/us-interstate_3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 49 | 60 | 64 | 68 | 69 | 71 | 73 | 74 | 76 | image/svg+xml 77 | 79 | 80 | 81 | 82 | 83 | 89 | 95 | 102 | 103 | -------------------------------------------------------------------------------- /data/icons/bright/us-interstate_4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 49 | 60 | 64 | 68 | 69 | 71 | 73 | 74 | 76 | image/svg+xml 77 | 79 | 80 | 81 | 82 | 83 | 89 | 95 | 102 | 103 | -------------------------------------------------------------------------------- /data/icons/bright/us-interstate_5.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 49 | 60 | 64 | 68 | 69 | 71 | 73 | 74 | 76 | image/svg+xml 77 | 79 | 80 | 81 | 82 | 83 | 89 | 95 | 102 | 103 | -------------------------------------------------------------------------------- /data/icons/bright/us-state_1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 42 | 44 | 45 | 47 | image/svg+xml 48 | 50 | 51 | 52 | 53 | 54 | 59 | 63 | 73 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /data/icons/bright/us-state_2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 42 | 44 | 45 | 47 | image/svg+xml 48 | 50 | 51 | 52 | 53 | 54 | 59 | 63 | 73 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /data/icons/bright/us-state_3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 42 | 44 | 45 | 47 | image/svg+xml 48 | 50 | 51 | 52 | 53 | 54 | 59 | 63 | 73 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /data/icons/bright/us-state_4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 43 | 52 | 53 | 55 | 56 | 58 | image/svg+xml 59 | 61 | 62 | 63 | 64 | 65 | 70 | 74 | 84 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /data/icons/bright/us-state_5.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 43 | 52 | 53 | 55 | 56 | 58 | image/svg+xml 59 | 61 | 62 | 63 | 64 | 65 | 70 | 74 | 84 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /data/icons/bright/us-state_6.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 43 | 52 | 53 | 55 | 56 | 58 | image/svg+xml 59 | 61 | 62 | 63 | 64 | 65 | 70 | 74 | 84 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /data/icons/default_1.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 41 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 58 | 61 | 71 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /data/icons/default_2.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 41 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 58 | 61 | 71 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /data/icons/default_3.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 41 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 58 | 61 | 70 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /data/icons/default_4.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 41 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 58 | 61 | 70 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /data/icons/default_5.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 41 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 58 | 61 | 70 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /data/icons/default_6.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 41 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 58 | 61 | 63 | 72 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /data/icons/road_1.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 41 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 58 | 61 | 71 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /data/icons/road_2.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 41 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 58 | 61 | 71 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /data/icons/road_3.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 41 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 58 | 61 | 70 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /data/icons/road_4.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 41 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 58 | 61 | 70 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /data/icons/road_5.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 41 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 58 | 61 | 70 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /data/icons/road_6.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 41 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 58 | 61 | 63 | 72 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /data/icons/road_motorway.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 25 | 50 | 51 | 57 | 63 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /data/icons/us-highway_1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 44 | 55 | 56 | 58 | 59 | 61 | image/svg+xml 62 | 64 | 65 | 66 | 67 | 68 | 73 | 79 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /data/icons/us-highway_2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 44 | 55 | 56 | 58 | 59 | 61 | image/svg+xml 62 | 64 | 65 | 66 | 67 | 68 | 73 | 79 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /data/icons/us-highway_3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 44 | 55 | 56 | 58 | 59 | 61 | image/svg+xml 62 | 64 | 65 | 66 | 67 | 68 | 73 | 81 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /data/icons/us-interstate_1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 21 | 22 | 23 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /data/icons/us-interstate_2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 21 | 22 | 23 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /data/icons/us-interstate_3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 21 | 22 | 23 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /data/icons/us-interstate_4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 21 | 22 | 23 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /data/icons/us-interstate_5.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 21 | 22 | 23 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /data/icons/us-state_1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 42 | 44 | 45 | 47 | image/svg+xml 48 | 50 | 51 | 52 | 53 | 54 | 59 | 63 | 73 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /data/icons/us-state_2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 42 | 44 | 45 | 47 | image/svg+xml 48 | 50 | 51 | 52 | 53 | 54 | 59 | 63 | 73 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /data/icons/us-state_3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 42 | 44 | 45 | 47 | image/svg+xml 48 | 50 | 51 | 52 | 53 | 54 | 59 | 63 | 73 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /data/icons/us-state_4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 43 | 52 | 53 | 55 | 56 | 58 | image/svg+xml 59 | 61 | 62 | 63 | 64 | 65 | 70 | 74 | 84 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /data/icons/us-state_5.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 43 | 52 | 53 | 55 | 56 | 58 | image/svg+xml 59 | 61 | 62 | 63 | 64 | 65 | 70 | 74 | 84 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /data/icons/us-state_6.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 43 | 52 | 53 | 55 | 56 | 58 | image/svg+xml 59 | 61 | 62 | 63 | 64 | 65 | 70 | 74 | 84 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /data/ocean-color-ramp.txt: -------------------------------------------------------------------------------- 1 | # QGIS Generated Color Map Export File 2 | INTERPOLATION:INTERPOLATED 3 | -12000,8,48,107,255,-12000 4 | -11500,8,62,128,255,-11500 5 | -11000,8,76,148,255,-11000 6 | -10500,13,88,161,255,-10500 7 | -10000,22,98,170,255,-10000 8 | -9500,30,110,178,255,-9500 9 | -9000,40,120,185,255,-9000 10 | -8500,51,131,190,255,-8500 11 | -8000,62,142,196,255,-8000 12 | -7500,74,151,201,255,-7500 13 | -7000,87,161,206,255,-7000 14 | -6500,100,169,211,255,-6500 15 | -6000,115,179,216,255,-6000 16 | -5500,131,187,219,255,-5500 17 | -5000,148,196,223,255,-5000 18 | -4500,159,203,226,255,-4500 19 | -4000,162,207,230,255,-4000 20 | -3500,164,211,234,255,-3500 21 | -3000,169,214,237,255,-3000 22 | -2500,177,220,240,255,-2500 23 | -2000,185,225,243,255,-2000 24 | -1500,193,231,247,255,-1500 25 | -1000,199,235,250,255,-1000 26 | -500,200,238,252,255,-500 27 | -0.1,214,246,255,255,-0 28 | 0,214,246,255,1,0 29 | -------------------------------------------------------------------------------- /data/terrain-color-ramp.txt: -------------------------------------------------------------------------------- 1 | # QGIS Generated Color Map Export File 2 | INTERPOLATION:INTERPOLATED 3 | 0,86,162,21,0,0 4 | 0.1,86,162,21,255,0 5 | 100,106,173,30,255,100 6 | 200,126,183,40,255,200 7 | 300,146,194,50,255,300 8 | 400,166,204,60,255,400 9 | 500,186,215,70,255,500 10 | 600,206,225,80,255,600 11 | 700,226,236,90,255,700 12 | 800,246,245,99,255,800 13 | 900,255,247,101,255,900 14 | 1000,255,242,96,255,1000 15 | 1100,255,237,91,255,1100 16 | 1200,255,232,86,255,1200 17 | 1300,255,226,81,255,1300 18 | 1400,255,221,76,255,1400 19 | 1500,255,217,72,255,1500 20 | 1600,255,211,68,255,1600 21 | 1700,255,206,63,255,1700 22 | 1800,255,201,58,255,1800 23 | 1900,255,195,53,255,1900 24 | 2000,255,190,48,255,2000 25 | 2100,255,185,43,255,2100 26 | 2200,254,180,39,255,2200 27 | 2300,251,176,37,255,2300 28 | 2400,246,173,37,255,2400 29 | 2500,241,169,37,255,2500 30 | 2600,237,165,36,255,2600 31 | 2700,232,162,36,255,2700 32 | 2800,227,158,36,255,2800 33 | 2900,222,155,35,255,2900 34 | 3000,217,152,35,255,3000 35 | 3100,212,148,35,255,3100 36 | 3200,207,144,34,255,3200 37 | 3300,203,141,34,255,3300 38 | 3400,198,137,33,255,3400 39 | 3500,193,134,33,255,3500 40 | 3600,188,131,33,255,3600 41 | 3700,183,127,32,255,3700 42 | 3800,178,123,32,255,3800 43 | 3900,173,120,32,255,3900 44 | 4000,168,117,31,255,4000 45 | 4100,164,113,31,255,4100 46 | 4200,159,109,30,255,4200 47 | 4300,154,106,30,255,4300 48 | 4400,149,102,30,255,4400 49 | 4500,147,101,31,255,4500 50 | 4600,149,104,36,255,4600 51 | 4700,151,108,41,255,4700 52 | 4800,154,111,46,255,4800 53 | 4900,156,114,51,255,4900 54 | 5000,159,118,56,255,5000 55 | 5100,161,122,61,255,5100 56 | 5200,164,125,66,255,5200 57 | 5300,166,128,71,255,5300 58 | 5400,168,132,76,255,5400 59 | 5500,171,135,81,255,5500 60 | 5600,173,138,86,255,5600 61 | 5700,176,142,91,255,5700 62 | 5800,178,146,96,255,5800 63 | 5900,181,149,101,255,5900 64 | 6000,183,153,106,255,6000 65 | 6100,185,156,111,255,6100 66 | 6200,188,159,116,255,6200 67 | 6300,190,163,121,255,6300 68 | 6400,193,166,126,255,6400 69 | 6500,195,169,131,255,6500 70 | 6600,198,174,136,255,6600 71 | 6700,200,177,141,255,6700 72 | 6800,203,180,146,255,6800 73 | 6900,204,183,151,255,6900 74 | 7000,207,187,156,255,7000 75 | 7100,210,190,161,255,7100 76 | 7200,212,193,166,255,7200 77 | 7300,215,197,171,255,7300 78 | 7400,217,200,176,255,7400 79 | 7500,219,204,181,255,7500 80 | 7600,222,208,185,255,7600 81 | 7700,224,211,190,255,7700 82 | 7800,226,214,195,255,7800 83 | 7900,229,218,200,255,7900 84 | 8000,231,221,205,255,8000 85 | 8100,234,224,210,255,8100 86 | 8200,236,228,215,255,8200 87 | 8300,238,232,220,255,8300 88 | 8400,241,235,225,255,8400 89 | 8500,243,238,230,255,8500 90 | 8600,246,242,235,255,8600 91 | 8700,248,245,240,255,8700 92 | 8800,251,248,245,255,8800 93 | 8900,253,252,250,255,8900 94 | 9000,255,255,255,255,9000 95 | -------------------------------------------------------------------------------- /edit_connection_dialog.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from qgis.PyQt import uic, QtWidgets 4 | import os 5 | 6 | from .settings_manager import SettingsManager 7 | 8 | 9 | class EditConnectionDialog(QtWidgets.QDialog): 10 | def __init__(self, current_name): 11 | super().__init__() 12 | self.ui = uic.loadUi(os.path.join(os.path.dirname( 13 | __file__), 'edit_connection_dialog_base.ui'), self) 14 | self.ui.button_box.accepted.connect(self._accepted) 15 | self.ui.button_box.rejected.connect(self._rejected) 16 | 17 | self._current_name = current_name 18 | smanager = SettingsManager() 19 | custommaps = smanager.get_setting('custommaps') 20 | self._current_data = custommaps[self._current_name] 21 | 22 | self.ui.nameLineEdit.setText(self._current_name) 23 | self.ui.jsonLineEdit.setText(self._current_data['custom']) 24 | 25 | def _accepted(self): 26 | name = self.ui.nameLineEdit.text() 27 | json_url = self.ui.jsonLineEdit.text() 28 | smanager = SettingsManager() 29 | 30 | if self._has_error(): 31 | return 32 | 33 | custommaps = smanager.get_setting('custommaps') 34 | del custommaps[self._current_name] 35 | custommaps[name] = { 36 | 'custom': json_url, 37 | } 38 | smanager.store_setting('custommaps', custommaps) 39 | self.close() 40 | 41 | def _rejected(self): 42 | self.close() 43 | 44 | def _has_error(self): 45 | name = self.ui.nameLineEdit.text() 46 | json_url = self.ui.jsonLineEdit.text() 47 | 48 | is_empty_name = False 49 | is_empty_url = False 50 | is_existing_name = False 51 | error_message = '' 52 | 53 | if name == '': 54 | is_empty_name = True 55 | error_message += 'Name is empty, please input.\n' 56 | 57 | if json_url == '': 58 | is_empty_url = True 59 | error_message += 'Url to map is empty, please input.\n' 60 | 61 | smanager = SettingsManager() 62 | custommaps = smanager.get_setting('custommaps') 63 | if not name == self._current_name and name in custommaps: 64 | is_existing_name = True 65 | error_message += str('"' + name + '"' + 66 | ' already exists, please input other name.\n') 67 | 68 | if is_empty_name or is_empty_url or is_existing_name: 69 | QtWidgets.QMessageBox.warning(None, 'Error', error_message) 70 | return True 71 | -------------------------------------------------------------------------------- /edit_connection_dialog_base.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | editConnectionDialogBase 4 | 5 | 6 | 7 | 0 8 | 0 9 | 506 10 | 191 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | 21 | 0 22 | 0 23 | 24 | 25 | 26 | 27 | 16777215 28 | 16777215 29 | 30 | 31 | 32 | Edit Connection 33 | 34 | 35 | 36 | 37 | 38 | 39 | 0 40 | 0 41 | 42 | 43 | 44 | 45 | 0 46 | 25 47 | 48 | 49 | 50 | Load map from MapTiler Cloud. Add url to style for map or TileJSON url for raster mode of a plugin. 51 | 52 | 53 | true 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 0 64 | 0 65 | 66 | 67 | 68 | Name 69 | 70 | 71 | 72 | 73 | 74 | 75 | true 76 | 77 | 78 | 79 | 0 80 | 0 81 | 82 | 83 | 84 | URL to map 85 | 86 | 87 | 88 | 89 | 90 | 91 | true 92 | 93 | 94 | 95 | 0 96 | 0 97 | 98 | 99 | 100 | 101 | 400 102 | 0 103 | 104 | 105 | 106 | 107 | 16777215 108 | 16777215 109 | 110 | 111 | 112 | 200 113 | 114 | 115 | 116 | 117 | 118 | 119 | true 120 | 121 | 122 | 123 | 0 124 | 0 125 | 126 | 127 | 128 | 129 | 400 130 | 0 131 | 132 | 133 | 134 | 135 | 16777215 136 | 16777215 137 | 138 | 139 | 140 | 141 | 142 | 143 | 200 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | Qt::Horizontal 153 | 154 | 155 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /geocoder.py: -------------------------------------------------------------------------------- 1 | import json 2 | import urllib 3 | 4 | 5 | from qgis.PyQt.QtCore import Qt, QModelIndex, QSettings 6 | from qgis.PyQt.QtWidgets import QCompleter, QLineEdit, QMessageBox 7 | from qgis.core import * 8 | 9 | from .configure_dialog import ConfigureDialog 10 | from .settings_manager import SettingsManager 11 | from . import utils 12 | 13 | 14 | class MapTilerGeocoder: 15 | def __init__(self, language='en'): 16 | self._language = language 17 | 18 | def geocoding(self, searchword, center_lonlat): 19 | if not utils.validate_credentials(): 20 | QMessageBox.warning(None, 'Access Error', '\nAccess error occurred. \nPlease Confirm your Credentials.') 21 | self._openConfigureDialog() 22 | return 23 | 24 | proximity = '%s,%s' % (str(center_lonlat[0]), str(center_lonlat[1])) 25 | params = { 26 | 'proximity': proximity, 27 | 'language': self._language 28 | } 29 | p = urllib.parse.urlencode(params, safe=',') 30 | query = urllib.parse.quote(searchword) 31 | url = f"https://api.maptiler.com/geocoding/{query}.json?{p}" 32 | geojson_dict = utils.qgis_request_json(url) 33 | 34 | return geojson_dict 35 | 36 | def _openConfigureDialog(self): 37 | configure_dialog = ConfigureDialog() 38 | configure_dialog.exec() 39 | 40 | 41 | class MapTilerGeocoderToolbar: 42 | def __init__(self, iface): 43 | # TODO: We are going to let the user set this up in a future iteration 44 | self.iface = iface 45 | self.proj = QgsProject.instance() 46 | self.toolbar = self.iface.addToolBar(u'MapTiler') 47 | self.toolbar.setObjectName(u'MapTiler') 48 | 49 | # init QCompleter 50 | self.completer = QCompleter([]) 51 | self.completer.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive) 52 | self.completer.setMaxVisibleItems(30) 53 | self.completer.setModelSorting(QCompleter.ModelSorting.UnsortedModel) 54 | self.completer.setCompletionMode(QCompleter.CompletionMode.UnfilteredPopupCompletion) 55 | self.completer.activated[QModelIndex].connect(self.on_result_clicked) 56 | 57 | # init LineEdit of searchword 58 | self.search_line_edit = QLineEdit() 59 | self.search_line_edit.setPlaceholderText('MapTiler Geocoding API') 60 | self.search_line_edit.setMaximumWidth(300) 61 | self.search_line_edit.setClearButtonEnabled(True) 62 | self.search_line_edit.setCompleter(self.completer) 63 | self.search_line_edit.textEdited.connect(self.on_searchword_edited) 64 | self.search_line_edit.returnPressed.connect( 65 | self.on_searchword_returned) 66 | self.toolbar.addWidget(self.search_line_edit) 67 | 68 | # LineEdit edited event 69 | def on_searchword_edited(self): 70 | model = self.completer.model() 71 | model.setStringList([]) 72 | self.completer.complete() 73 | 74 | # LineEdit returned event 75 | def on_searchword_returned(self): 76 | searchword = self.search_line_edit.text() 77 | geojson_dict = self._fetch_geocoding_api(searchword) 78 | 79 | # always dict is None when apikey invalid 80 | if geojson_dict is None: 81 | return 82 | 83 | self.result_features = geojson_dict['features'] 84 | 85 | result_list = [] 86 | for feature in self.result_features: 87 | result_list.append('%s:%s' % 88 | (feature['text'], feature['place_name'])) 89 | 90 | model = self.completer.model() 91 | model.setStringList(result_list) 92 | self.completer.complete() 93 | 94 | def _fetch_geocoding_api(self, searchword): 95 | # get a center point of MapCanvas 96 | center = self.iface.mapCanvas().center() 97 | center_as_qgspoint = QgsPoint(center.x(), center.y()) 98 | 99 | # transform the center point to EPSG:4326 100 | target_crs = QgsCoordinateReferenceSystem('EPSG:4326') 101 | transform = QgsCoordinateTransform( 102 | self.proj.crs(), target_crs, self.proj) 103 | center_as_qgspoint.transform(transform) 104 | center_lonlat = [center_as_qgspoint.x(), center_as_qgspoint.y()] 105 | 106 | # start Geocoding 107 | locale = 'en' 108 | global_locale = QSettings().value('locale/globalLocale') 109 | if global_locale: 110 | locale = global_locale[0:2] 111 | else: 112 | user_locale = QSettings().value('locale/userLocale') 113 | if user_locale: 114 | locale = user_locale[0:2] 115 | 116 | geocoder = MapTilerGeocoder(locale) 117 | geojson_dict = geocoder.geocoding(searchword, center_lonlat) 118 | return geojson_dict 119 | 120 | def on_result_clicked(self, result_index): 121 | # add selected feature to Project 122 | selected_feature = self.result_features[result_index.row()] 123 | 124 | extent_rect = QgsRectangle() 125 | current_crs = QgsCoordinateReferenceSystem() 126 | 127 | bbox = selected_feature.get("bbox") 128 | geometry_type = selected_feature.get("geometry", {}).get("type") 129 | 130 | if bbox: 131 | extent_rect = QgsRectangle(bbox[0], bbox[1], bbox[2], bbox[3]) 132 | current_crs = QgsCoordinateReferenceSystem("EPSG:4326") 133 | elif geometry_type == "GeometryCollection": 134 | geometries = selected_feature.get("geometries") 135 | if geometries is None: 136 | print("invalid GeoJSON") 137 | return 138 | tmp_geojson = { 139 | "type": "Feature", 140 | "geometry": geometries[0] 141 | } 142 | geojson_str = json.dumps(tmp_geojson) 143 | vlayer = QgsVectorLayer(geojson_str, 'tmp', 'ogr') 144 | extent_rect = vlayer.extent() 145 | current_crs = vlayer.sourceCrs() 146 | else: 147 | geojson_str = json.dumps(selected_feature) 148 | vlayer = QgsVectorLayer(geojson_str, 'tmp', 'ogr') 149 | extent_rect = vlayer.extent() 150 | current_crs = vlayer.sourceCrs() 151 | 152 | extent_leftbottom = QgsPoint( 153 | extent_rect.xMinimum(), extent_rect.yMinimum()) 154 | extent_righttop = QgsPoint( 155 | extent_rect.xMaximum(), extent_rect.yMaximum()) 156 | 157 | # transform 2points to project CRS 158 | target_crs = self.proj.crs() 159 | transform = QgsCoordinateTransform(current_crs, target_crs, self.proj) 160 | extent_leftbottom.transform(transform) 161 | extent_righttop.transform(transform) 162 | 163 | # make rectangle same to new extent by transformed 2points 164 | extent_rect = QgsRectangle(extent_leftbottom.x(), extent_leftbottom.y(), 165 | extent_righttop.x(), extent_righttop.y()) 166 | 167 | self.iface.mapCanvas().zoomToFeatureExtent(extent_rect) 168 | -------------------------------------------------------------------------------- /gl2qgis/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maptiler/qgis-maptiler-plugin/db79cf1b6cb496a3586f2d40b489f446ed19af79/gl2qgis/__init__.py -------------------------------------------------------------------------------- /imgs/icon_account_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /imgs/icon_account_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /imgs/icon_maps_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /imgs/icon_maps_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /imgs/icon_maptiler.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /imgs/logo_maptiler_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /imgs/logo_maptiler_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /imgs/readme_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maptiler/qgis-maptiler-plugin/db79cf1b6cb496a3586f2d40b489f446ed19af79/imgs/readme_01.png -------------------------------------------------------------------------------- /imgs/readme_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maptiler/qgis-maptiler-plugin/db79cf1b6cb496a3586f2d40b489f446ed19af79/imgs/readme_02.png -------------------------------------------------------------------------------- /imgs/readme_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maptiler/qgis-maptiler-plugin/db79cf1b6cb496a3586f2d40b489f446ed19af79/imgs/readme_03.png -------------------------------------------------------------------------------- /imgs/readme_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maptiler/qgis-maptiler-plugin/db79cf1b6cb496a3586f2d40b489f446ed19af79/imgs/readme_04.png -------------------------------------------------------------------------------- /imgs/readme_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maptiler/qgis-maptiler-plugin/db79cf1b6cb496a3586f2d40b489f446ed19af79/imgs/readme_06.png -------------------------------------------------------------------------------- /imgs/readme_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maptiler/qgis-maptiler-plugin/db79cf1b6cb496a3586f2d40b489f446ed19af79/imgs/readme_07.png -------------------------------------------------------------------------------- /imgs/readme_08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maptiler/qgis-maptiler-plugin/db79cf1b6cb496a3586f2d40b489f446ed19af79/imgs/readme_08.png -------------------------------------------------------------------------------- /imgs/readme_09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maptiler/qgis-maptiler-plugin/db79cf1b6cb496a3586f2d40b489f446ed19af79/imgs/readme_09.png -------------------------------------------------------------------------------- /imgs/readme_10.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maptiler/qgis-maptiler-plugin/db79cf1b6cb496a3586f2d40b489f446ed19af79/imgs/readme_10.gif -------------------------------------------------------------------------------- /imgs/readme_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maptiler/qgis-maptiler-plugin/db79cf1b6cb496a3586f2d40b489f446ed19af79/imgs/readme_11.png -------------------------------------------------------------------------------- /imgs/readme_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maptiler/qgis-maptiler-plugin/db79cf1b6cb496a3586f2d40b489f446ed19af79/imgs/readme_12.png -------------------------------------------------------------------------------- /imgs/readme_13.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maptiler/qgis-maptiler-plugin/db79cf1b6cb496a3586f2d40b489f446ed19af79/imgs/readme_13.gif -------------------------------------------------------------------------------- /imgs/readme_14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maptiler/qgis-maptiler-plugin/db79cf1b6cb496a3586f2d40b489f446ed19af79/imgs/readme_14.png -------------------------------------------------------------------------------- /mapdatasets.py: -------------------------------------------------------------------------------- 1 | STANDARD_DATASET = { 2 | 'Basic': { 3 | 'raster': r'https://api.maptiler.com/maps/basic/256/tiles.json', 4 | 'vector': r'https://api.maptiler.com/maps/basic/style.json', 5 | 'customize_url': r'https://maptiler.link/qgisbasic' 6 | }, 7 | 'Bright': { 8 | 'raster': r'https://api.maptiler.com/maps/bright/256/tiles.json', 9 | 'vector': r'https://api.maptiler.com/maps/bright/style.json', 10 | 'customize_url': r'https://maptiler.link/qgisbright' 11 | }, 12 | 'Cadastre': { 13 | 'raster': r'https://api.maptiler.com/maps/cadastre/256/tiles.json', 14 | 'vector': r'https://api.maptiler.com/maps/cadastre/style.json', 15 | 'customize_url': r'https://maptiler.link/qgiscadastre' 16 | }, 17 | 'Cadastre Satellite': { 18 | 'raster': r'https://api.maptiler.com/maps/cadastre-satellite/256/tiles.json', 19 | 'vector': r'https://api.maptiler.com/maps/cadastre-satellite/style.json', 20 | 'customize_url': r'https://maptiler.link/qgiscadsatellite' 21 | }, 22 | 'Dark Matter': { 23 | 'raster': r'https://api.maptiler.com/maps/darkmatter/256/tiles.json', 24 | 'vector': r'https://api.maptiler.com/maps/darkmatter/style.json', 25 | 'customize_url': r'https://maptiler.link/qgisdarkmatter' 26 | }, 27 | 'Dataviz': { 28 | 'raster': r'https://api.maptiler.com/maps/dataviz/256/tiles.json', 29 | 'vector': r'https://api.maptiler.com/maps/dataviz/style.json', 30 | 'customize_url': r'https://maptiler.link/qgisdataviz' 31 | }, 32 | 'Dataviz Dark': { 33 | 'raster': r'https://api.maptiler.com/maps/dataviz-dark/256/tiles.json', 34 | 'vector': r'https://api.maptiler.com/maps/dataviz-dark/style.json', 35 | 'customize_url': r'https://maptiler.link/qgisdatavizdark' 36 | }, 37 | 'Dataviz Light': { 38 | 'raster': r'https://api.maptiler.com/maps/dataviz-light/256/tiles.json', 39 | 'vector': r'https://api.maptiler.com/maps/dataviz-light/style.json', 40 | 'customize_url': r'https://maptiler.link/qgisdatavizlight' 41 | }, 42 | 'Ocean RGB': { 43 | 'raster-dem': r'https://api.maptiler.com/tiles/ocean-rgb/tiles.json' 44 | }, 45 | 'OpenStreetMap': { 46 | 'raster': r'https://api.maptiler.com/maps/openstreetmap/256/tiles.json', 47 | 'vector': r'https://api.maptiler.com/maps/openstreetmap/style.json', 48 | 'customize_url': r'https://maptiler.link/qgisosm' 49 | }, 50 | 'Outdoor': { 51 | 'raster': r'https://api.maptiler.com/maps/outdoor/256/tiles.json', 52 | 'vector': r'https://api.maptiler.com/maps/outdoor/style.json', 53 | 'customize_url': r'https://maptiler.link/qgisoutdoor' 54 | }, 55 | 'Pastel': { 56 | 'raster': r'https://api.maptiler.com/maps/pastel/256/tiles.json', 57 | 'vector': r'https://api.maptiler.com/maps/pastel/style.json', 58 | 'customize_url': r'https://maptiler.link/qgispastel' 59 | }, 60 | 'Positron': { 61 | 'raster': r'https://api.maptiler.com/maps/positron/256/tiles.json', 62 | 'vector': r'https://api.maptiler.com/maps/positron/style.json', 63 | 'customize_url': r'https://maptiler.link/qgispositron' 64 | }, 65 | 'Satellite': { 66 | 'raster': r'https://api.maptiler.com/tiles/satellite-v2/tiles.json' 67 | }, 68 | 'Satellite Hybrid': { 69 | 'raster': r'https://api.maptiler.com/maps/hybrid/256/tiles.json', 70 | 'vector': r'https://api.maptiler.com/maps/hybrid/style.json', 71 | 'customize_url': r'https://maptiler.link/qgishybrid' 72 | }, 73 | 'Streets': { 74 | 'raster': r'https://api.maptiler.com/maps/streets/256/tiles.json', 75 | 'vector': r'https://api.maptiler.com/maps/streets/style.json', 76 | 'customize_url': r'https://maptiler.link/qgisstreets' 77 | }, 78 | 'Terrain': { 79 | 'terrain-group': [ 80 | r'https://api.maptiler.com/tiles/terrain-rgb-v2/tiles.json', 81 | r'https://api.maptiler.com/tiles/ocean-rgb/tiles.json' 82 | ] 83 | }, 84 | 'Terrain RGB': { 85 | 'raster-dem': r'https://api.maptiler.com/tiles/terrain-rgb-v2/tiles.json' 86 | }, 87 | 'Toner': { 88 | 'raster': r'https://api.maptiler.com/maps/toner/256/tiles.json', 89 | 'vector': r'https://api.maptiler.com/maps/toner/style.json', 90 | 'customize_url': r'https://maptiler.link/qgistoner' 91 | }, 92 | 'Topo': { 93 | 'raster': r'https://api.maptiler.com/maps/topo/256/tiles.json', 94 | 'vector': r'https://api.maptiler.com/maps/topo/style.json', 95 | 'customize_url': r'https://maptiler.link/qgistopo' 96 | }, 97 | 'Topographique': { 98 | 'raster': r'https://api.maptiler.com/maps/topographique/256/tiles.json', 99 | 'vector': r'https://api.maptiler.com/maps/topographique/style.json', 100 | 'customize_url': r'https://maptiler.link/qgistopographique' 101 | }, 102 | 'Voyager': { 103 | 'raster': r'https://api.maptiler.com/maps/voyager/256/tiles.json', 104 | 'vector': r'https://api.maptiler.com/maps/voyager/style.json', 105 | 'customize_url': r'https://maptiler.link/qgisvoyager' 106 | }, 107 | 'Winter': { 108 | 'raster': r'https://api.maptiler.com/maps/winter/256/tiles.json', 109 | 'vector': r'https://api.maptiler.com/maps/winter/style.json', 110 | 'customize_url': r'https://maptiler.link/qgiswinter' 111 | } 112 | } 113 | 114 | LOCAL_JP_DATASET = { 115 | 'JP MIERUNE Streets': { 116 | 'raster': r'https://api.maptiler.com/maps/jp-mierune-streets/256/tiles.json', 117 | 'vector': r'https://api.maptiler.com/maps/jp-mierune-streets/style.json', 118 | 'customize_url': r'https://maptiler.link/qgisjpstreets' 119 | }, 120 | 'JP MIERUNE Dark': { 121 | 'raster': r'https://api.maptiler.com/maps/jp-mierune-dark/256/tiles.json', 122 | 'vector': r'https://api.maptiler.com/maps/jp-mierune-dark/style.json', 123 | 'customize_url': r'https://maptiler.link/qgisjpdark' 124 | }, 125 | 'JP MIERUNE Gray': { 126 | 'raster': r'https://api.maptiler.com/maps/jp-mierune-gray/256/tiles.json', 127 | 'vector': r'https://api.maptiler.com/maps/jp-mierune-gray/style.json', 128 | 'customize_url': r'https://maptiler.link/qgisjpgray' 129 | } 130 | } 131 | 132 | LOCAL_NL_DATASET = { 133 | 'NL Cartiqo Dark': { 134 | 'raster': r'https://api.maptiler.com/maps/nl-cartiqo-dark/256/tiles.json', 135 | 'vector': r'https://api.maptiler.com/maps/nl-cartiqo-dark/style.json', 136 | 'customize_url': r'https://maptiler.link/qgisnldark' 137 | }, 138 | 'NL Cartiqo Light': { 139 | 'raster': r'https://api.maptiler.com/maps/nl-cartiqo-light/256/tiles.json', 140 | 'vector': r'https://api.maptiler.com/maps/nl-cartiqo-light/style.json', 141 | 'customize_url': r'https://maptiler.link/qgisnllight' 142 | }, 143 | 'NL Cartiqo Topo': { 144 | 'raster': r'https://api.maptiler.com/maps/nl-cartiqo-topo/256/tiles.json', 145 | 'vector': r'https://api.maptiler.com/maps/nl-cartiqo-topo/style.json', 146 | 'customize_url': r'https://maptiler.link/qgisnltopo' 147 | } 148 | } 149 | 150 | LOCAL_UK_DATASET = { 151 | 'UK OS Open Zoomstack Light': { 152 | 'raster': r'https://api.maptiler.com/maps/uk-openzoomstack-light/256/tiles.json', 153 | 'vector': r'https://api.maptiler.com/maps/uk-openzoomstack-light/style.json', 154 | 'customize_url': r'https://maptiler.link/qgislight' 155 | }, 156 | 'UK OS Open Zoomstack Night': { 157 | 'raster': r'https://api.maptiler.com/maps/uk-openzoomstack-night/256/tiles.json', 158 | 'vector': r'https://api.maptiler.com/maps/uk-openzoomstack-night/style.json', 159 | 'customize_url': r'https://maptiler.link/qgisosnight' 160 | }, 161 | 'UK OS Open Zoomstack Outdoor': { 162 | 'raster': r'https://api.maptiler.com/maps/uk-openzoomstack-outdoor/256/tiles.json', 163 | 'vector': r'https://api.maptiler.com/maps/uk-openzoomstack-outdoor/style.json', 164 | 'customize_url': r'https://maptiler.link/qgisosoutdoor' 165 | }, 166 | 'UK OS Open Zoomstack Road': { 167 | 'raster': r'https://api.maptiler.com/maps/uk-openzoomstack-road/256/tiles.json', 168 | 'vector': r'https://api.maptiler.com/maps/uk-openzoomstack-road/style.json', 169 | 'customize_url': r'https://maptiler.link/qgisosroad' 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /metadata.txt: -------------------------------------------------------------------------------- 1 | # This file contains metadata for your plugin. 2 | 3 | # This file should be included when you package your plugin.# Mandatory items: 4 | 5 | [general] 6 | name=MapTiler 7 | qgisMinimumVersion=3.24 8 | supportsQt6=yes 9 | description=Street and satellite base maps with vector tiles 10 | version=3.4.2 11 | author=MapTiler 12 | email=info@maptiler.com 13 | 14 | about=- Beautiful base maps using OpenStreetMap data powered by vector tiles - served from MapTiler Cloud. 15 | 16 | - Satellite map of the entire world, outdoor map, hillshading and landcover, Ordnance Survey Open ZoomStack, Dutch Kadaster via Cartiqo, or GSI data in Japan - or your own datasets! 17 | 18 | - Global digital elevation model (DEM) for the entire planet. 19 | 20 | - Customize language & colors of your map with a few clicks! 21 | 22 | - Import your own Mapbox GL JSON style or TileJSON from any URL - even hosted on your own server. 23 | 24 | - Print the maps using vector PDF. (It might cause spikes in Cloud export requests.) 25 | 26 | tracker=https://github.com/maptiler/qgis-maptiler-plugin/issues 27 | repository=https://github.com/maptiler/qgis-maptiler-plugin 28 | # End of mandatory metadata 29 | 30 | # Recommended items: 31 | 32 | hasProcessingProvider=no 33 | # Uncomment the following line and add your changelog: 34 | # changelog= 35 | 36 | # Tags are comma separated with spaces allowed 37 | tags=maptiler, open, map, maps, tiles, openmaptiles, osm, openstreetmap, raster, vector, wmts, tilejson, mapbox, gl, style, styles, geocoding, basemaps, quickmapservices, vector-tiles, geojson, reader, xyz, print, ordnance survey, zoomstack, kadaster, cartiqo, gsi, japan, outdoor, carto, dem, digital, elevation, model, cadastre, cadastral, hillshade, terrain, ocean, rgb, dataviz, customize 38 | 39 | homepage=https://maptiler.link/qgisplugin 40 | category=Web 41 | icon=./imgs/icon_maptiler.svg 42 | # experimental flag 43 | experimental=False 44 | -------------------------------------------------------------------------------- /settings_manager.py: -------------------------------------------------------------------------------- 1 | from qgis.PyQt.QtCore import QSettings 2 | 3 | # QSettings holds variables as list or dict or str. 4 | # if int or bool value is set, they are converted to str in the Class. 5 | # In particular, isVectorEnabled is treated as bool by cast str '0' or '1' to int(bool). 6 | 7 | 8 | class SettingsManager: 9 | SETTING_GROUP = '/maptiler' 10 | 11 | def __init__(self): 12 | self._settings = { 13 | 'selectedmaps': ['Basic', 'Bright', 'Dataviz', 'Outdoor', 'OpenStreetMap', 'Satellite', 'Streets', 14 | 'Terrain', 'Toner', 'Topo', 'Voyager'], 15 | 'prefervector': '1', 16 | 'custommaps': {}, 17 | 'auth_cfg_id': '' 18 | } 19 | self.load_settings() 20 | 21 | def load_setting(self, key): 22 | qsettings = QSettings() 23 | qsettings.beginGroup(self.SETTING_GROUP) 24 | value = qsettings.value(key) 25 | qsettings.endGroup() 26 | if value: 27 | self._settings[key] = value 28 | 29 | def load_settings(self): 30 | for key in self._settings: 31 | self.load_setting(key) 32 | 33 | def store_setting(self, key, value): 34 | if key == "auth_cfg_id": 35 | value = value.strip() 36 | qsettings = QSettings() 37 | qsettings.beginGroup(self.SETTING_GROUP) 38 | qsettings.setValue(key, value) 39 | qsettings.endGroup() 40 | 41 | self.load_settings() 42 | 43 | def get_setting(self, key): 44 | return self._settings[key] 45 | 46 | def get_settings(self): 47 | return self._settings 48 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | from qgis.PyQt.QtWidgets import QMessageBox 2 | from qgis.core import Qgis, QgsColorRampShader, QgsNetworkAccessManager, QgsApplication, QgsAuthMethodConfig 3 | from qgis.PyQt.QtGui import QColor 4 | from qgis.PyQt.QtNetwork import QNetworkRequest, QNetworkReply 5 | from qgis.PyQt.QtCore import QUrl 6 | 7 | import ssl 8 | import json 9 | 10 | from .settings_manager import SettingsManager 11 | ssl._create_default_https_context = ssl._create_unverified_context 12 | 13 | 14 | def validate_credentials() -> bool: 15 | testurl = 'https://api.maptiler.com/maps/basic/style.json' 16 | smanager = SettingsManager() 17 | auth_cfg_id = smanager.get_setting('auth_cfg_id') 18 | if auth_cfg_id: 19 | am = QgsApplication.authManager() 20 | cfg = QgsAuthMethodConfig() 21 | (res, cfg) = am.loadAuthenticationConfig(auth_cfg_id, cfg, True) 22 | if res: 23 | token = cfg.configMap().get("token") 24 | if token and len(token) > 33 and "_" in token: 25 | request = QNetworkRequest(QUrl(testurl)) 26 | reply_content = QgsNetworkAccessManager.instance().blockingGet(request, auth_cfg_id) 27 | if reply_content.error() == QNetworkReply.NetworkError.NoError: 28 | return True 29 | return False 30 | 31 | 32 | def is_qgs_vectortile_api_enable(): 33 | # judge vtile is available or not 34 | # e.g. QGIS3.10.4 -> 31004 35 | qgis_version_str = str(Qgis.QGIS_VERSION_INT) 36 | minor_ver = int(qgis_version_str[1:3]) 37 | return minor_ver >= 13 38 | 39 | 40 | def is_qgs_early_resampling_enabled(): 41 | qgis_version_str = str(Qgis.QGIS_VERSION_INT) 42 | minor_ver = int(qgis_version_str[1:3]) 43 | return minor_ver >= 25 44 | 45 | 46 | def is_in_darkmode(threshold=383): 47 | """detect the Qt in Darkmode or not 48 | 49 | This function has a dependancy on PyQt, QMessageBox. 50 | Although Qt has no API to detect running in Darkmode or not, 51 | it is able to get RGB value of widgets, including UI parts of them. 52 | This function detect Darkmode by evaluating a sum of RGB value of the widget with threshold. 53 | 54 | Args: 55 | threshold (int, optional): a sum of RGB value (each 0-255, sum 0-765). Default to 383, is just median. 56 | Returns: 57 | bool: True means in Darkmode, False in not. 58 | """ 59 | # generate empty QMessageBox to detect 60 | # generated widgets has default color palette in the OS 61 | empty_mbox = QMessageBox() 62 | 63 | # get a background color of the widget 64 | red = empty_mbox.palette().window().color().red() 65 | green = empty_mbox.palette().window().color().green() 66 | blue = empty_mbox.palette().window().color().blue() 67 | 68 | sum_rgb_value = red + green + blue 69 | return sum_rgb_value < threshold 70 | 71 | 72 | def load_color_ramp_from_file(fp: str) -> list: 73 | with open(fp, 'r') as f: 74 | lines = f.readlines()[2:] # get rid of header 75 | ramp_items = [[float(line.rstrip("\n").split(',')[0])] + list(map(int, line.rstrip("\n").split(',')[1:5])) for line in lines] 76 | ramp_lst = [QgsColorRampShader.ColorRampItem(ramp_item[0], QColor(ramp_item[1], ramp_item[2], ramp_item[3], ramp_item[4])) for ramp_item in ramp_items] 77 | min_ramp_value = ramp_items[0][0] 78 | max_ramp_value = ramp_items[-1][0] 79 | return min_ramp_value, max_ramp_value, ramp_lst 80 | 81 | 82 | class MapTilerApiException(Exception): 83 | def __init__(self, message, content): 84 | self.message = message 85 | self.content = content 86 | super().__init__(self.message) 87 | 88 | 89 | def _qgis_request(url: str): 90 | smanager = SettingsManager() 91 | auth_cfg_id = smanager.get_setting('auth_cfg_id') 92 | if "https://api.maptiler.com" in url: 93 | if "key=" in url: 94 | url = url.split("key=")[0] 95 | request = QNetworkRequest(QUrl(url)) 96 | reply_content = QgsNetworkAccessManager.instance().blockingGet(request, auth_cfg_id) 97 | else: 98 | request = QNetworkRequest(QUrl(url)) 99 | reply_content = QgsNetworkAccessManager.instance().blockingGet(request) 100 | 101 | if not reply_content.error(): 102 | return reply_content 103 | else: 104 | error_msg = "" 105 | error_content = "" 106 | if reply_content.errorString(): 107 | error_msg = f"{reply_content.errorString()}" 108 | if reply_content.content(): 109 | error_content = f"{str(reply_content.content(), 'utf-8')}" 110 | raise MapTilerApiException(error_msg, error_content) 111 | 112 | 113 | def qgis_request_json(url: str) -> dict: 114 | reply_content = _qgis_request(url) 115 | if not reply_content.error(): 116 | json_data = json.loads(reply_content.content().data().decode()) 117 | return json_data 118 | else: 119 | return None 120 | 121 | 122 | def qgis_request_data(url: str) -> bytes: 123 | reply_content = _qgis_request(url) 124 | return reply_content.content().data() 125 | 126 | 127 | if __name__ == "__main__": 128 | validate_credentials() 129 | --------------------------------------------------------------------------------