From 9802fc4021a4f83163db28ddd836e67e1d18e10d Mon Sep 17 00:00:00 2001 From: ArtemL Date: Mon, 7 Jul 2025 18:29:19 +0300 Subject: [PATCH 1/2] Remove py_tiled_layer and all related legacy code --- COMPONENTS | 2 - pyproject.toml | 1 - .../i18n/QuickMapServices_ru.ts | 176 +-- .../py_tiled_layer/__init__.py | 3 - .../py_tiled_layer/downloader.py | 253 ---- .../py_tiled_layer/propertiesdialog.py | 90 -- .../py_tiled_layer/propertiesdialog_base.ui | 262 ---- .../py_tiled_layer/tilelayer.py | 1087 ----------------- .../py_tiled_layer/tiles.py | 271 ---- src/quick_map_services/qgis_map_helpers.py | 41 +- src/quick_map_services/quick_map_services.py | 7 - 11 files changed, 26 insertions(+), 2167 deletions(-) delete mode 100644 src/quick_map_services/py_tiled_layer/__init__.py delete mode 100644 src/quick_map_services/py_tiled_layer/downloader.py delete mode 100644 src/quick_map_services/py_tiled_layer/propertiesdialog.py delete mode 100644 src/quick_map_services/py_tiled_layer/propertiesdialog_base.ui delete mode 100644 src/quick_map_services/py_tiled_layer/tilelayer.py delete mode 100644 src/quick_map_services/py_tiled_layer/tiles.py diff --git a/COMPONENTS b/COMPONENTS index 25160dd..14a5e2a 100644 --- a/COMPONENTS +++ b/COMPONENTS @@ -1,3 +1 @@ - -

Python tile layer: TileLayer Plugin by Minoru Akagi

Some icons from QGIS: QGIS GitHub

\ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 66dd2b2..4477f82 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,6 @@ ui-files = [ "src/quick_map_services/gui/*.ui", "src/quick_map_services/scales.xml", "src/quick_map_services/*.ui", - "src/quick_map_services/py_tiled_layer/*.ui", ] compile = false diff --git a/src/quick_map_services/i18n/QuickMapServices_ru.ts b/src/quick_map_services/i18n/QuickMapServices_ru.ts index 41185bd..462ab6b 100644 --- a/src/quick_map_services/i18n/QuickMapServices_ru.ts +++ b/src/quick_map_services/i18n/QuickMapServices_ru.ts @@ -4,12 +4,12 @@ @default - + Layer %s can't be added to the map! Слой %s невозможно добавить на карту! - + Error Ошибка @@ -150,77 +150,27 @@ p, li { white-space: pre-wrap; } Лицензия - - Properties - Свойства - - - - Style - Стиль - - - - Transparency - Прозрачность - - - - Brightness - Яркость - - - - Blending mode - Режим наложения - - - - (Default: SourceOver) - (По умолчанию: SourceOver) - - - - Contrast - Контраст - - - - Grayscale - Оттенки серого - - - - Smoothing - Сглаживание - - - - Place the credit on the bottom right corner - Поместить авторство в нижнем правом углу - - - + Dialog Диалог - + General Общие - + ID ID - + Alias Алиас - + Icon Иконка @@ -255,7 +205,7 @@ p, li { white-space: pre-wrap; } Условия использования - + Choose Выбрать @@ -454,7 +404,7 @@ p, li { white-space: pre-wrap; } Form - + Form Форма @@ -464,7 +414,7 @@ p, li { white-space: pre-wrap; } Файл GDAL - + URL URL @@ -669,14 +619,6 @@ p, li { white-space: pre-wrap; } (QGIS 2.18.8 и выше): - - PropertiesDialog - - - Layer Properties - Свойства слоя - - QmsSearchResultItemWidget @@ -766,52 +708,52 @@ p, li { white-space: pre-wrap; } QuickMapServices - + QuickMapServices QuickMapServices - + Set proper scale Ближайший масштаб - + Set SlippyMap scales Установить масштабы SlippyMap - + Settings Настройки - + About QMS О QMS - + Error Ошибка - + Extra dirs for %s can't be created: %s %s Невозможно создать дополнительные директории для %s: %s %s - + Search QMS Поиск в QMS - + Add to Search Добавить в Поиск - + Set SlippyMap scales for current project? The previous settings will be overwritten! Установить масштабы мозаичной карты для текущего проекта? @@ -863,84 +805,6 @@ The previous settings will be overwritten! Ошибка при получении источников данных: %s - - TileLayer - - - Current zoom level ({0}) is smaller than zmin ({1}): {2} - Текущий уровень масштабирования ({0}) меньше чем zmin ({1}): {2} - - - - Tile count is over limit ({0}, max={1}) - Количество тайлов превысило лимит ({0}, max={1}) - - - - Download Timeout - {} - Время ожидания скачивания - {} - - - - {} files failed. - {} файла(ов) не скачано. - - - - Failed to download all {0} files. - {1} - Не удалось загрузить все {0} файла(ов). - {1} - - - - Reprojection requires python-gdal - Перепроецирование требует наличие python-gdal - - - - {0} of {1} files downloaded. - {0} из {1} файлов скачано. - - - - Title - Заголовок - - - - Credit - Авторство - - - - URL - URL - - - - yOrigin - yOrigin - - - - Not set - Не установлено - - - - Zoom range - Диапазон масштабирования - - - - Layer Extent - Границы слоя - - - - {0} files downloaded. {1} cache hits. - {0} файлов скачано. {1} попаданий кэша. - - UserGroupsBox diff --git a/src/quick_map_services/py_tiled_layer/__init__.py b/src/quick_map_services/py_tiled_layer/__init__.py deleted file mode 100644 index 906f671..0000000 --- a/src/quick_map_services/py_tiled_layer/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -__author__ = "yellow" -__license__ = "" -__date__ = "2014" diff --git a/src/quick_map_services/py_tiled_layer/downloader.py b/src/quick_map_services/py_tiled_layer/downloader.py deleted file mode 100644 index 8b8d1c0..0000000 --- a/src/quick_map_services/py_tiled_layer/downloader.py +++ /dev/null @@ -1,253 +0,0 @@ -# -*- coding: utf-8 -*- -""" -/*************************************************************************** - TileLayer Plugin - A QGIS plugin - Plugin layer for Tile Maps - ------------------- - begin : 2012-12-16 - copyright : (C) 2013 by Minoru Akagi - email : akaginch@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program 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 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ -""" - -import threading - -from qgis.core import QgsNetworkAccessManager -from qgis.PyQt.QtCore import ( - QDateTime, - QEventLoop, - QObject, - QTimer, - QUrl, - pyqtSignal, - qDebug, -) -from qgis.PyQt.QtNetwork import QNetworkReply, QNetworkRequest - -debug_mode = 0 - - -class Downloader(QObject): - NOT_FOUND = 0 - NO_ERROR = 0 - TIMEOUT_ERROR = 4 - UNKNOWN_ERROR = -1 - - replyFinished = pyqtSignal(str, int, int) - - def __init__(self, parent=None): - QObject.__init__(self, parent) - self.queue = [] - self.redirected_urls = {} - self.requestingUrls = [] - self.replies = [] - - self.eventLoop = QEventLoop() - self.sync = False - self.fetchedFiles = {} - self.clearCounts() - - self.timer = QTimer() - self.timer.setSingleShot(True) - self.timer.timeout.connect(self.fetchTimedOut) - - # network settings - self.userAgent = "QuickMapServices tile layer (+https://github.com/nextgis/quickmapservices)" - self.max_connection = 4 - self.default_cache_expiration = 24 - self.errorStatus = Downloader.NO_ERROR - - def clearCounts(self): - self.fetchSuccesses = 0 - self.fetchErrors = 0 - self.cacheHits = 0 - - def fetchTimedOut(self): - self.log("Downloader.timeOut()") - self.abort() - self.errorStatus = Downloader.TIMEOUT_ERROR - - def abort(self): - # clear queue and abort sent requests - self.queue = [] - self.timer.stop() - for reply in self.replies: - reply.abort() - self.errorStatus = Downloader.UNKNOWN_ERROR - - def replyFinishedSlot(self): - reply = self.sender() - url = reply.request().url().toString() - self.log("replyFinishedSlot: %s" % url) - if not url in self.fetchedFiles: - self.fetchedFiles[url] = None - self.requestingUrls.remove(url) - self.replies.remove(reply) - isFromCache = 0 - httpStatusCode = reply.attribute( - QNetworkRequest.Attribute.HttpStatusCodeAttribute - ) - if reply.error() == QNetworkReply.NetworkError.NoError: - if httpStatusCode == 301: - new_url = str(reply.rawHeader("Location")) - self.addToQueue(new_url, url) - else: - self.fetchSuccesses += 1 - if reply.attribute( - QNetworkRequest.Attribute.SourceIsFromCacheAttribute - ): - self.cacheHits += 1 - isFromCache = 1 - elif not reply.hasRawHeader("Cache-Control"): - cache = QgsNetworkAccessManager.instance().cache() - if cache: - metadata = cache.metaData(reply.request().url()) - # self.log("Expiration date: " + metadata.expirationDate().toString().encode("utf-8")) - if metadata.expirationDate().isNull(): - metadata.setExpirationDate( - QDateTime.currentDateTime().addSecs( - self.default_cache_expiration * 60 * 60 - ) - ) - cache.updateMetaData(metadata) - self.log( - "Default expiration date has been set: %s (%d h)" - % (url, self.default_cache_expiration) - ) - - if reply.isReadable(): - data = reply.readAll() - if url in self.redirected_urls: - url = self.redirected_urls[url] - - self.fetchedFiles[url] = data - else: - qDebug("http status code: " + str(httpStatusCode)) - - # self.emit(SIGNAL('replyFinished(QString, int, int)'), url, reply.error(), isFromCache) - self.replyFinished.emit(url, reply.error(), isFromCache) - else: - if self.sync and httpStatusCode == 404: - self.fetchedFiles[url] = self.NOT_FOUND - self.fetchErrors += 1 - if self.errorStatus == self.NO_ERROR: - self.errorStatus = self.UNKNOWN_ERROR - - reply.deleteLater() - - if debug_mode: - qDebug( - "queue: %d, requesting: %d" - % (len(self.queue), len(self.requestingUrls)) - ) - - if len(self.queue) + len(self.requestingUrls) == 0: - # all replies have been received - if self.sync: - self.logT("eventLoop.quit()") - self.eventLoop.quit() - else: - self.timer.stop() - elif len(self.queue) > 0: - # start fetching the next file - self.fetchNext() - self.log("replyFinishedSlot End: %s" % url) - - def fetchNext(self): - if len(self.queue) == 0: - return - url = self.queue.pop(0) - self.log("fetchNext: %s" % url) - - request = QNetworkRequest(QUrl(url)) - request.setRawHeader("User-Agent", self.userAgent) - reply = QgsNetworkAccessManager.instance().get(request) - reply.finished.connect(self.replyFinishedSlot) - self.requestingUrls.append(url) - self.replies.append(reply) - return reply - - def fetchFiles(self, urlList, timeout_ms=0): - self.log("fetchFiles()") - self.sync = True - self.queue = [] - self.redirected_urls = {} - self.clearCounts() - self.errorStatus = Downloader.NO_ERROR - self.fetchedFiles = {} - - if len(urlList) == 0: - return self.fetchedFiles - - for url in urlList: - self.addToQueue(url) - - for i in range(self.max_connection): - self.fetchNext() - - if timeout_ms > 0: - self.timer.setInterval(timeout_ms) - self.timer.start() - - self.logT("eventLoop.exec(): " + str(self.eventLoop)) - self.eventLoop.exec() - self.log("fetchFiles() End: %d" % self.errorStatus) - if timeout_ms > 0: - self.timer.stop() - return self.fetchedFiles - - def addToQueue(self, url, redirected_from=None): - if url in self.queue: - return False - self.queue.append(url) - if redirected_from is not None: - self.redirected_urls[url] = redirected_from - return True - - def queueCount(self): - return len(self.queue) - - def finishedCount(self): - return len(self.fetchedFiles) - - def unfinishedCount(self): - return len(self.queue) + len(self.requestingUrls) - - def log(self, msg): - if debug_mode: - qDebug(msg) - - def logT(self, msg): - if debug_mode: - qDebug("%s: %s" % (str(threading.current_thread()), msg)) - - def fetchFilesAsync(self, urlList, timeout_ms=0): - self.log("fetchFilesAsync()") - self.sync = False - self.queue = [] - self.clearCounts() - self.errorStatus = Downloader.NO_ERROR - self.fetchedFiles = {} - - if len(urlList) == 0: - return self.fetchedFiles - - for url in urlList: - self.addToQueue(url) - - for i in range(self.max_connection): - self.fetchNext() - - if timeout_ms > 0: - self.timer.setInterval(timeout_ms) - self.timer.start() diff --git a/src/quick_map_services/py_tiled_layer/propertiesdialog.py b/src/quick_map_services/py_tiled_layer/propertiesdialog.py deleted file mode 100644 index 052e8a7..0000000 --- a/src/quick_map_services/py_tiled_layer/propertiesdialog.py +++ /dev/null @@ -1,90 +0,0 @@ -# -*- coding: utf-8 -*- -""" -/*************************************************************************** - TileLayer Plugin - A QGIS plugin - Plugin layer for Tile Maps - ------------------- - begin : 2012-12-16 - copyright : (C) 2013 by Minoru Akagi - email : akaginch@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program 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 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ -""" - -import os - -from qgis.PyQt import uic -from qgis.PyQt.QtCore import QObject, pyqtSignal -from qgis.PyQt.QtGui import QDialog, QDialogButtonBox, QPainter - -CURR_PATH = os.path.dirname(__file__) -FORM_CLASS, _ = uic.loadUiType( - os.path.join(CURR_PATH, "propertiesdialog_base.ui") -) - - -class PropertiesDialog(QDialog): - applyClicked = pyqtSignal() - - def __init__(self, layer): - QDialog.__init__(self) - # set up the user interface - self.ui = FORM_CLASS() - self.ui.setupUi(self) - self.setWindowTitle( - "%s - %s" % (self.tr("Layer Properties"), layer.name()) - ) - - self.layer = layer - # signals - self.ui.horizontalSlider_Transparency.valueChanged.connect( - self.ui.spinBox_Transparency.setValue - ) - self.ui.spinBox_Transparency.valueChanged.connect( - self.ui.horizontalSlider_Transparency.setValue - ) - self.ui.horizontalSlider_Brightness.valueChanged.connect( - self.ui.spinBox_Brightness.setValue - ) - self.ui.spinBox_Brightness.valueChanged.connect( - self.ui.horizontalSlider_Brightness.setValue - ) - self.ui.horizontalSlider_Contrast.valueChanged.connect( - lambda x: self.ui.doubleSpinBox_Contrast.setValue(x / 100.0) - ) - self.ui.doubleSpinBox_Contrast.valueChanged.connect( - lambda x: self.ui.horizontalSlider_Contrast.setValue(x * 100) - ) - - self.ui.buttonBox.button(QDialogButtonBox.Apply).clicked.connect( - self.applyClicked.emit - ) - - # set init values - self.initBlendingCombo() - self.ui.textEdit_Properties.setText(layer.metadata()) - self.ui.spinBox_Transparency.setValue(layer.transparency) - self.ui.spinBox_Brightness.setValue(layer.brigthness) - self.ui.doubleSpinBox_Contrast.setValue(layer.contrast) - i = self.ui.comboBox_BlendingMode.findText(layer.blendModeName) - if i != -1: - self.ui.comboBox_BlendingMode.setCurrentIndex(i) - - self.ui.checkBox_SmoothRender.setChecked(layer.smoothRender) - self.ui.checkBox_CreditVisibility.setChecked(layer.creditVisibility) - self.ui.checkBox_Grayscale.setChecked(layer.grayscaleRender) - - def initBlendingCombo(self): - attrs = dir(QPainter) - for attr in attrs: - if attr.startswith("CompositionMode_"): - self.ui.comboBox_BlendingMode.addItem(attr[16:]) diff --git a/src/quick_map_services/py_tiled_layer/propertiesdialog_base.ui b/src/quick_map_services/py_tiled_layer/propertiesdialog_base.ui deleted file mode 100644 index 549c8ab..0000000 --- a/src/quick_map_services/py_tiled_layer/propertiesdialog_base.ui +++ /dev/null @@ -1,262 +0,0 @@ - - - Dialog - - - - 0 - 0 - 438 - 428 - - - - Properties - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - 0 - 0 - - - - Style - - - - QLayout::SetDefaultConstraint - - - - - QFormLayout::ExpandingFieldsGrow - - - - - Transparency - - - - - - - - - 100 - - - Qt::Horizontal - - - - - - - 100 - - - - - - - - - Brightness - - - - - - - Blending mode - - - - - - - - - - - - (Default: SourceOver) - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - - - - - -255 - - - 255 - - - Qt::Horizontal - - - - - - - -255 - - - 255 - - - - - - - - - Contrast - - - - - - - - - 1000 - - - 10 - - - 100 - - - 100 - - - Qt::Horizontal - - - - - - - 10.000000000000000 - - - 0.010000000000000 - - - 1.000000000000000 - - - - - - - - - - - Grayscale - - - - - - - Smoothing - - - - - - - Place the credit on the bottom right corner - - - - - - - - - - Properties - - - - - - true - - - 80 - - - - - - - - - - - - - - buttonBox - accepted() - Dialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - Dialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/src/quick_map_services/py_tiled_layer/tilelayer.py b/src/quick_map_services/py_tiled_layer/tilelayer.py deleted file mode 100644 index 927f0af..0000000 --- a/src/quick_map_services/py_tiled_layer/tilelayer.py +++ /dev/null @@ -1,1087 +0,0 @@ -# -*- coding: utf-8 -*- -""" -/*************************************************************************** - TileLayer Plugin - A QGIS plugin - Plugin layer for Tile Maps - ------------------- - begin : 2012-12-16 - copyright : (C) 2013 by Minoru Akagi - email : akaginch@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program 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 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ -""" - -# Import the PyQt and QGIS libraries -import os -import threading - -from qgis.core import QgsImageOperation, QgsPluginLayer, QgsPluginLayerType -from qgis.gui import QgsMessageBar -from qgis.PyQt.QtCore import ( - QEventLoop, - QFile, - QObject, - QPoint, - QPointF, - QRectF, - Qt, - QTimer, - pyqtSignal, - qDebug, -) -from qgis.PyQt.QtGui import QBrush, QColor, QFont -from qgis.utils import iface - -from ..compat2qgis import QGisMessageBarLevel, QgsCoordinateReferenceSystem -from ..plugin_settings import PluginSettings -from ..qgis_proj_helper import ProjectionHelper -from ..qgis_settings import QGISSettings -from .downloader import Downloader -from .tiles import * - -try: - from osgeo import gdal - - hasGdal = True -except: - hasGdal = False - -debug_mode = 0 - - -class LayerDefaultSettings(object): - TRANSPARENCY = 0 - BLEND_MODE = "SourceOver" - SMOOTH_RENDER = True - GRAYSCALE_RENDER = False - BRIGTNESS = 0 - CONTRAST = 1.0 - - -class TileLayer(QgsPluginLayer): - CRS_ID_3857 = 3857 - CRS_3857 = QgsCoordinateReferenceSystem.fromEpsgId(CRS_ID_3857) - - LAYER_TYPE = "PyTiledLayer" - MAX_TILE_COUNT = 256 - CHANGE_SCALE_VALUE = 0.30 - - fetchRequest = pyqtSignal(list) - showMessage = pyqtSignal(str, int) - showBarMessage = pyqtSignal(str, str, int, int) - allRepliesFinished = pyqtSignal() - - def __init__(self, layerDef, creditVisibility=1): - QgsPluginLayer.__init__(self, TileLayer.LAYER_TYPE, layerDef.title) - - self.iface = iface - self.layerDef = layerDef - self.creditVisibility = 1 if creditVisibility else 0 - - # set custom properties - self.setCustomProperty("title", layerDef.title) - self.setCustomProperty( - "credit", layerDef.credit - ) # TODO: need to remove - self.setCustomProperty("serviceUrl", layerDef.serviceUrl) - self.setCustomProperty("yOriginTop", layerDef.yOriginTop) - self.setCustomProperty("zmin", layerDef.zmin) - self.setCustomProperty("zmax", layerDef.zmax) - if layerDef.bbox: - self.setCustomProperty("bbox", layerDef.bbox.toString()) - self.setCustomProperty("creditVisibility", self.creditVisibility) - - # set standard/custom crs - ProjectionHelper.set_tile_layer_proj( - self, - layerDef.epsg_crs_id, - layerDef.postgis_crs_id, - layerDef.custom_proj, - ) - - if layerDef.bbox: - self.setExtent( - BoundingBox.degreesToMercatorMeters( - layerDef.bbox - ).toQgsRectangle() - ) - else: - if self.layerDef.tile_ranges is not None: - zmin = sorted(self.layerDef.tile_ranges.keys())[0] - tbbox = self.layerDef.tile_ranges[zmin] - - size = self.layerDef.tsize1 / 2 ** (zmin - 1) - xmin = self.layerDef.originX + tbbox[0] * size - xmax = self.layerDef.originX + (tbbox[1] + 1) * size - ymin = self.layerDef.originY - (tbbox[3] + 1) * size - ymax = self.layerDef.originY - tbbox[2] * size - else: - xmin = -layerDef.tsize1 - ymin = -layerDef.tsize1 - xmax = layerDef.tsize1 - ymax = layerDef.tsize1 - - self.setExtent( - QgsRectangle( - xmin, - ymin, - xmax, - ymax, - ) - ) - - self.setValid(True) - self.tiles = None - self.useLastZoomForPrint = False - self.canvasLastZoom = 0 - self.setTransparency(LayerDefaultSettings.TRANSPARENCY) - self.setBlendModeByName(LayerDefaultSettings.BLEND_MODE) - self.setSmoothRender(LayerDefaultSettings.SMOOTH_RENDER) - self.setGrayscaleRender(LayerDefaultSettings.GRAYSCALE_RENDER) - self.setBrigthness(LayerDefaultSettings.BRIGTNESS) - self.setContrast(LayerDefaultSettings.CONTRAST) - - # downloader - self.downloader = Downloader(self) - self.downloader.userAgent = QGISSettings.get_default_user_agent() - self.downloader.default_cache_expiration = ( - QGISSettings.get_default_tile_expiry() - ) - self.downloader.max_connection = ( - PluginSettings.default_tile_layer_conn_count() - ) # TODO: Move to INI files - self.downloader.replyFinished.connect(self.networkReplyFinished) - # QObject.connect(self.downloader, SIGNAL("replyFinished(QString, int, int)"), self.networkReplyFinished) - - # network - self.downloadTimeout = QGISSettings.get_default_network_timeout() - - # multi-thread rendering - self.eventLoop = None - - self.fetchRequest.connect(self.fetchRequestSlot) - # QObject.connect(self, SIGNAL("fetchRequest(QStringList)"), self.fetchRequest) - if self.iface: - # QObject.connect(self, SIGNAL("showMessage(QString, int)"), self.showStatusMessageSlot) - # QObject.connect(self, SIGNAL("showBarMessage(QString, QString, int, int)"), self.showBarMessageSlot) - self.showMessage.connect(self.showStatusMessageSlot) - self.showBarMessage.connect(self.showBarMessageSlot) - - def setBlendModeByName(self, modeName): - self.blendModeName = modeName - blendMode = getattr(QPainter, "CompositionMode_" + modeName, 0) - self.setBlendMode(blendMode) - self.setCustomProperty("blendMode", modeName) - - def setTransparency(self, transparency): - self.transparency = transparency - self.setCustomProperty("transparency", transparency) - - def setSmoothRender(self, isSmooth): - self.smoothRender = isSmooth - self.setCustomProperty("smoothRender", 1 if isSmooth else 0) - - def setCreditVisibility(self, visible): - self.creditVisibility = visible - self.setCustomProperty("creditVisibility", 1 if visible else 0) - - def setGrayscaleRender(self, isGrayscale): - self.grayscaleRender = isGrayscale - self.setCustomProperty("grayscaleRender", 1 if isGrayscale else 0) - - def setBrigthness(self, brigthness): - self.brigthness = brigthness - self.setCustomProperty("brigthness", brigthness) - - def setContrast(self, contrast): - self.contrast = contrast - self.setCustomProperty("contrast", contrast) - - def draw(self, renderContext): - self.renderContext = renderContext - extent = renderContext.extent() - if extent.isEmpty() or extent.width() == float("inf"): - qDebug("Drawing is skipped because map extent is empty or inf.") - return True - - mapSettings = self.iface.mapCanvas().mapSettings() - painter = renderContext.painter() - isDpiEqualToCanvas = ( - painter.device().logicalDpiX() == mapSettings.outputDpi() - ) - if isDpiEqualToCanvas or not self.useLastZoomForPrint: - # calculate zoom level - tile_mpp1 = ( - self.layerDef.tsize1 / self.layerDef.TILE_SIZE - ) # should be attribute, not method call.. - viewport_mpp = extent.width() / painter.viewport().width() - lg = math.log(float(tile_mpp1) / float(viewport_mpp), 2) - zoom = ( - int(math.modf(lg)[1]) - + 1 * (math.modf(lg)[0] > self.CHANGE_SCALE_VALUE) - + 1 - ) - zoom = max(0, min(zoom, self.layerDef.zmax)) - # zoom = max(self.layerDef.zmin, zoom) - else: - # for print composer output image, use last zoom level of map item on print composer (or map canvas) - zoom = self.canvasLastZoom - - # zoom limit - if zoom < self.layerDef.zmin: - msg = self.tr( - "Current zoom level ({0}) is smaller than zmin ({1}): {2}" - ).format(zoom, self.layerDef.zmin, self.layerDef.title) - self.emitShowBarMessage(msg, QGisMessageBarLevel.Info, 2) - return True - - while True: - # calculate tile range (yOrigin is top) - size = self.layerDef.tsize1 / 2 ** (zoom - 1) - if ( - self.layerDef.tile_ranges is None - ): # should add xOffset & yOffset in first part of conditional - matrixSize = 2**zoom - ulx = max( - 0, int((extent.xMinimum() + self.layerDef.tsize1) / size) - ) - uly = max( - 0, int((self.layerDef.tsize1 - extent.yMaximum()) / size) - ) - lrx = min( - int((extent.xMaximum() + self.layerDef.tsize1) / size), - matrixSize - 1, - ) - lry = min( - int((self.layerDef.tsize1 - extent.yMinimum()) / size), - matrixSize - 1, - ) - else: # for tile_ranges - xmin, xmax, ymin, ymax = self.layerDef.tile_ranges[zoom] - - ulx = max( - int((extent.xMinimum() - self.layerDef.originX) / size), - xmin, - ) - uly = max( - int((self.layerDef.originY - extent.yMaximum()) / size), - ymin, - ) - lrx = min( - int((extent.xMaximum() - self.layerDef.originX) / size), - xmax, - ) - lry = min( - int((self.layerDef.originY - extent.yMinimum()) / size), - ymax, - ) - - # bounding box limit - if self.layerDef.bbox: - trange = self.layerDef.bboxDegreesToTileRange( - zoom, self.layerDef.bbox - ) - ulx = max(ulx, trange.xmin) - uly = max(uly, trange.ymin) - lrx = min(lrx, trange.xmax) - lry = min(lry, trange.ymax) - if lrx < ulx or lry < uly: - # tile range is out of the bounding box - return True - - # tile count limit - tileCount = (lrx - ulx + 1) * (lry - uly + 1) - if tileCount > self.MAX_TILE_COUNT: - # as tile count is over the limit, decrease zoom level - zoom -= 1 - - # if the zoom level is less than the minimum, do not draw - if zoom < self.layerDef.zmin: - msg = self.tr( - "Tile count is over limit ({0}, max={1})" - ).format(tileCount, self.MAX_TILE_COUNT) - self.emitShowBarMessage( - msg, QGisMessageBarLevel.Warning, 4 - ) - return True - continue - - # zoom level has been determined - break - - self.logT( - "TileLayer.draw: {0} {1} {2} {3} {4}".format( - zoom, ulx, uly, lrx, lry - ) - ) - - # save painter state - painter.save() - - # set pen and font - painter.setPen(Qt.GlobalColor.black) - font = QFont(painter.font()) - font.setPointSize(10) - painter.setFont(font) - - if self.layerDef.serviceUrl[0] == ":": - painter.setBrush(QBrush(Qt.BrushStyle.NoBrush)) - self.drawDebugInfo(renderContext, zoom, ulx, uly, lrx, lry) - else: - # create Tiles class object and throw url into it - tiles = Tiles(zoom, ulx, uly, lrx, lry, self.layerDef) - urls = [] - cacheHits = 0 - for ty in range(uly, lry + 1): - for tx in range(ulx, lrx + 1): - data = None - url = self.layerDef.tileUrl(zoom, tx, ty) - if ( - self.tiles - and zoom == self.tiles.zoom - and url in self.tiles.tiles - ): - data = self.tiles.tiles[url].data - tiles.addTile(url, Tile(zoom, tx, ty, data)) - if data is None: - urls.append(url) - elif data: # memory cache exists - cacheHits += 1 - # else: # tile was not found (Downloader.NOT_FOUND=0) - - self.tiles = tiles - if len(urls) > 0: - # fetch tile data - files = self.fetchFiles(urls) - - for url in files.keys(): - self.tiles.setImageData(url, files[url]) - - if self.iface: - cacheHits += self.downloader.cacheHits - downloadedCount = ( - self.downloader.fetchSuccesses - - self.downloader.cacheHits - ) - msg = self.tr( - "{0} files downloaded. {1} cache hits." - ).format(downloadedCount, cacheHits) - barmsg = None - if self.downloader.errorStatus != Downloader.NO_ERROR: - if ( - self.downloader.errorStatus - == Downloader.TIMEOUT_ERROR - ): - barmsg = self.tr("Download Timeout - {}").format( - self.name() - ) - else: - msg += self.tr(" {} files failed.").format( - self.downloader.fetchErrors - ) - if self.downloader.fetchSuccesses == 0: - barmsg = self.tr( - "Failed to download all {0} files. - {1}" - ).format( - self.downloader.fetchErrors, self.name() - ) - self.showStatusMessage(msg, 5000) - if barmsg: - self.emitShowBarMessage( - barmsg, QGisMessageBarLevel.Warning, 4 - ) - - # apply layer style - oldOpacity = painter.opacity() - painter.setOpacity(0.01 * (100 - self.transparency)) - oldSmoothRenderHint = painter.testRenderHint( - QPainter.RenderHint.SmoothPixmapTransform - ) - if self.smoothRender: - painter.setRenderHint( - QPainter.RenderHint.SmoothPixmapTransform - ) - - # draw tiles - if not renderContext.coordinateTransform(): - # no need to reproject tiles - self.drawTiles(renderContext, self.tiles) - # self.drawTilesDirectly(renderContext, self.tiles) - else: - # reproject tiles - self.drawTilesOnTheFly(renderContext, self.tiles) - - # restore layer style - painter.setOpacity(oldOpacity) - if self.smoothRender: - painter.setRenderHint( - QPainter.RenderHint.SmoothPixmapTransform, - oldSmoothRenderHint, - ) - - # draw credit on the bottom right corner - if self.creditVisibility and self.layerDef.credit: - margin, paddingH, paddingV = (3, 4, 3) - # scale - scaleX, scaleY = self.getScaleToVisibleExtent(renderContext) - scale = max(scaleX, scaleY) - painter.scale(scale, scale) - - visibleSWidth = painter.viewport().width() * scaleX / scale - visibleSHeight = painter.viewport().height() * scaleY / scale - rect = QRect( - 0, 0, visibleSWidth - margin, visibleSHeight - margin - ) - textRect = painter.boundingRect( - rect, - Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignRight, - self.layerDef.credit, - ) - bgRect = QRect( - textRect.left() - paddingH, - textRect.top() - paddingV, - textRect.width() + 2 * paddingH, - textRect.height() + 2 * paddingV, - ) - painter.fillRect( - bgRect, QColor(240, 240, 240, 150) - ) # 197, 234, 243, 150)) - painter.drawText( - rect, - Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignRight, - self.layerDef.credit, - ) - - # restore painter state - painter.restore() - - if isDpiEqualToCanvas: - # save zoom level for printing (output with different dpi from map canvas) - self.canvasLastZoom = zoom - return True - - def drawTiles(self, renderContext, tiles, sdx=1.0, sdy=1.0): - # create an image that has the same resolution as the tiles - image = tiles.image() - if self.grayscaleRender: - QgsImageOperation.convertToGrayscale(image) - if ( - self.brigthness != LayerDefaultSettings.BRIGTNESS - or self.contrast != LayerDefaultSettings.CONTRAST - ): - QgsImageOperation.adjustBrightnessContrast( - image, self.brigthness, self.contrast - ) - - # tile extent to pixel - map2pixel = renderContext.mapToPixel() - extent = tiles.extent() - topLeft = map2pixel.transform(extent.xMinimum(), extent.yMaximum()) - bottomRight = map2pixel.transform(extent.xMaximum(), extent.yMinimum()) - rect = QRectF( - QPointF(topLeft.x() * sdx, topLeft.y() * sdy), - QPointF(bottomRight.x() * sdx, bottomRight.y() * sdy), - ) - - # draw the image on the map canvas - renderContext.painter().drawImage(rect, image) - - self.log("Tiles extent: " + str(extent)) - self.log("Draw into canvas rect: " + str(rect)) - - def drawTilesOnTheFly(self, renderContext, tiles, sdx=1.0, sdy=1.0): - if not hasGdal: - msg = self.tr("Reprojection requires python-gdal") - self.emitShowBarMessage(msg, QGisMessageBarLevel.Info, 2) - return - - transform = renderContext.coordinateTransform() - if not transform: - return - - # create an image that has the same resolution as the tiles - image = tiles.image() - if self.grayscaleRender: - QgsImageOperation.convertToGrayscale(image) - if ( - self.brigthness != LayerDefaultSettings.BRIGTNESS - or self.contrast != LayerDefaultSettings.CONTRAST - ): - QgsImageOperation.adjustBrightnessContrast( - image, self.brigthness, self.contrast - ) - - # tile extent - extent = tiles.extent() - geotransform = [ - extent.xMinimum(), - extent.width() / image.width(), - 0, - extent.yMaximum(), - 0, - -extent.height() / image.height(), - ] - - driver = gdal.GetDriverByName("MEM") - tile_ds = driver.Create( - "", image.width(), image.height(), 1, gdal.GDT_UInt32 - ) - tile_ds.SetProjection(str(transform.sourceCrs().toWkt())) - tile_ds.SetGeoTransform(geotransform) - - # QImage to raster - ba = image.bits().asstring(image.numBytes()) - tile_ds.GetRasterBand(1).WriteRaster( - 0, 0, image.width(), image.height(), ba - ) - - # canvas extent - m2p = renderContext.mapToPixel() - viewport = renderContext.painter().viewport() - width = viewport.width() - height = viewport.height() - extent = QgsRectangle( - m2p.toMapCoordinatesF(0, 0), m2p.toMapCoordinatesF(width, height) - ) - geotransform = [ - extent.xMinimum(), - extent.width() / width, - 0, - extent.yMaximum(), - 0, - -extent.height() / height, - ] - - canvas_ds = driver.Create("", width, height, 1, gdal.GDT_UInt32) - canvas_ds.SetProjection(str(transform.destCRS().toWkt())) - canvas_ds.SetGeoTransform(geotransform) - - # reproject image - gdal.ReprojectImage(tile_ds, canvas_ds) - - # raster to QImage - ba = canvas_ds.GetRasterBand(1).ReadRaster(0, 0, width, height) - reprojected_image = QImage( - ba, width, height, QImage.Format_ARGB32_Premultiplied - ) - - # draw the image on the map canvas - rect = QRectF( - QPointF(0, 0), - QPointF(viewport.width() * sdx, viewport.height() * sdy), - ) - renderContext.painter().drawImage(rect, reprojected_image) - - def drawTilesDirectly(self, renderContext, tiles, sdx=1.0, sdy=1.0): - p = renderContext.painter() - for url, tile in tiles.tiles.items(): - self.log( - "Draw tile: zoom: %d, x:%d, y:%d, data:%s" - % (tile.zoom, tile.x, tile.y, str(tile.data)) - ) - rect = self.getTileRect( - renderContext, tile.zoom, tile.x, tile.y, sdx, sdy - ) - if tile.data: - image = QImage() - image.loadFromData(tile.data) - p.drawImage(rect, image) - - def drawDebugInfo(self, renderContext, zoom, ulx, uly, lrx, lry): - painter = renderContext.painter() - scaleX, scaleY = self.getScaleToVisibleExtent(renderContext) - painter.scale(scaleX, scaleY) - - if "frame" in self.layerDef.serviceUrl: - self.drawFrames( - renderContext, - zoom, - ulx, - uly, - lrx, - lry, - 1.0 / scaleX, - 1.0 / scaleY, - ) - if "number" in self.layerDef.serviceUrl: - self.drawNumbers( - renderContext, - zoom, - ulx, - uly, - lrx, - lry, - 1.0 / scaleX, - 1.0 / scaleY, - ) - if "info" in self.layerDef.serviceUrl: - self.drawInfo(renderContext, zoom, ulx, uly, lrx, lry) - - def drawFrame(self, renderContext, zoom, x, y, sdx, sdy): - rect = self.getTileRect(renderContext, zoom, x, y, sdx, sdy) - p = renderContext.painter() - # p.drawRect(rect) # A slash appears on the top-right tile without Antialiasing render hint. - pts = [ - rect.topLeft(), - rect.topRight(), - rect.bottomRight(), - rect.bottomLeft(), - rect.topLeft(), - ] - for i in range(4): - p.drawLine(pts[i], pts[i + 1]) - - def drawFrames( - self, renderContext, zoom, xmin, ymin, xmax, ymax, sdx, sdy - ): - for y in range(ymin, ymax + 1): - for x in range(xmin, xmax + 1): - self.drawFrame(renderContext, zoom, x, y, sdx, sdy) - - def drawNumber(self, renderContext, zoom, x, y, sdx, sdy): - rect = self.getTileRect(renderContext, zoom, x, y, sdx, sdy) - p = renderContext.painter() - if not self.layerDef.yOriginTop: - y = (2**zoom - 1) - y - p.drawText( - rect, - Qt.AlignmentFlag.AlignCenter, - "(%d, %d)\nzoom: %d" % (x, y, zoom), - ) - - def drawNumbers( - self, renderContext, zoom, xmin, ymin, xmax, ymax, sdx, sdy - ): - for y in range(ymin, ymax + 1): - for x in range(xmin, xmax + 1): - self.drawNumber(renderContext, zoom, x, y, sdx, sdy) - - def drawInfo(self, renderContext, zoom, xmin, ymin, xmax, ymax): - # debug information - mapSettings = self.iface.mapCanvas().mapSettings() - lines = [] - lines.append("TileLayer") - lines.append( - " zoom: %d, tile matrix extent: (%d, %d) - (%d, %d), tile count: %d * %d" - % (zoom, xmin, ymin, xmax, ymax, xmax - xmin, ymax - ymin) - ) - extent = renderContext.extent() - lines.append(" map extent (renderContext): %s" % extent.toString()) - lines.append( - " map center: %lf, %lf" - % (extent.center().x(), extent.center().y()) - ) - lines.append(" map size: %f, %f" % (extent.width(), extent.height())) - lines.append( - " map extent (map canvas): %s" - % self.iface.mapCanvas().extent().toString() - ) - m2p = renderContext.mapToPixel() - painter = renderContext.painter() - viewport = painter.viewport() - mapExtent = QgsRectangle( - m2p.toMapCoordinatesF(0, 0), - m2p.toMapCoordinatesF(viewport.width(), viewport.height()), - ) - lines.append(" map extent (calculated): %s" % mapExtent.toString()) - lines.append( - " viewport size (pixel): %d, %d" - % (viewport.width(), viewport.height()) - ) - lines.append( - " window size (pixel): %d, %d" - % (painter.window().width(), painter.window().height()) - ) - lines.append( - " outputSize (pixel): %d, %d" - % ( - mapSettings.outputSize().width(), - mapSettings.outputSize().height(), - ) - ) - device = painter.device() - lines.append( - " deviceSize (pixel): %f, %f" % (device.width(), device.height()) - ) - lines.append( - " logicalDpi: %f, %f" - % (device.logicalDpiX(), device.logicalDpiY()) - ) - lines.append(" outputDpi: %f" % mapSettings.outputDpi()) - lines.append(" mapToPixel: %s" % m2p.showParameters()) - lines.append( - " meters per pixel: %f" % (extent.width() / viewport.width()) - ) - lines.append(" scaleFactor: %f" % renderContext.scaleFactor()) - lines.append(" rendererScale: %f" % renderContext.rendererScale()) - scaleX, scaleY = self.getScaleToVisibleExtent(renderContext) - lines.append(" scale: %f, %f" % (scaleX, scaleY)) - - # draw information - textRect = painter.boundingRect( - QRect(QPoint(0, 0), viewport.size()), - Qt.AlignmentFlag.AlignLeft, - "Q", - ) - for i, line in enumerate(lines): - painter.drawText(10, (i + 1) * textRect.height(), line) - self.log(line) - - # diagonal - painter.drawLine( - QPointF(0, 0), - QPointF(painter.viewport().width(), painter.viewport().height()), - ) - painter.drawLine( - QPointF(painter.viewport().width(), 0), - QPointF(0, painter.viewport().height()), - ) - - # credit label - margin, paddingH, paddingV = (3, 4, 3) - credit = "This is credit" - rect = QRect( - 0, - 0, - painter.viewport().width() - margin, - painter.viewport().height() - margin, - ) - textRect = painter.boundingRect( - rect, - Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignRight, - credit, - ) - bgRect = QRect( - textRect.left() - paddingH, - textRect.top() - paddingV, - textRect.width() + 2 * paddingH, - textRect.height() + 2 * paddingV, - ) - painter.drawRect(bgRect) - painter.drawText( - rect, - Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignRight, - credit, - ) - - def getScaleToVisibleExtent(self, renderContext): - mapSettings = self.iface.mapCanvas().mapSettings() - painter = renderContext.painter() - if painter.device().logicalDpiX() == mapSettings.outputDpi(): - return 1.0, 1.0 # scale should be 1.0 in rendering on map canvas - - extent = renderContext.extent() - ct = renderContext.coordinateTransform() - if ct: - # FIX ME: want to get original visible extent in project CRS or visible view size in pixels - - # extent = ct.transformBoundingBox(extent) - # xmax, ymin = extent.xMaximum(), extent.yMinimum() - - pt1 = ct.transform(extent.xMaximum(), extent.yMaximum()) - pt2 = ct.transform(extent.xMaximum(), extent.yMinimum()) - pt3 = ct.transform(extent.xMinimum(), extent.yMinimum()) - xmax, ymin = min(pt1.x(), pt2.x()), max(pt2.y(), pt3.y()) - else: - xmax, ymin = extent.xMaximum(), extent.yMinimum() - - bottomRight = renderContext.mapToPixel().transform(xmax, ymin) - viewport = painter.viewport() - scaleX = bottomRight.x() / viewport.width() - scaleY = bottomRight.y() / viewport.height() - return scaleX, scaleY - - def getTileRect( - self, renderContext, zoom, x, y, sdx=1.0, sdy=1.0, toInt=True - ): - """get tile pixel rect in the render context""" - r = self.layerDef.getTileRect(zoom, x, y) - map2pix = renderContext.mapToPixel() - topLeft = map2pix.transform(r.xMinimum(), r.yMaximum()) - bottomRight = map2pix.transform(r.xMaximum(), r.yMinimum()) - if toInt: - return QRect( - QPoint(round(topLeft.x() * sdx), round(topLeft.y() * sdy)), - QPoint( - round(bottomRight.x() * sdx), round(bottomRight.y() * sdy) - ), - ) - else: - return QRectF( - QPointF(topLeft.x() * sdx, topLeft.y() * sdy), - QPointF(bottomRight.x() * sdx, bottomRight.y() * sdy), - ) - - def isProjectCrsWebMercator(self): - mapSettings = self.iface.mapCanvas().mapSettings() - return mapSettings.destinationCrs().postgisSrid() == 3857 - - def networkReplyFinished(self, url, error, isFromCache): - if self.iface is None or isFromCache: - return - unfinishedCount = self.downloader.unfinishedCount() - if unfinishedCount == 0: - # self.emit(SIGNAL("allRepliesFinished()")) - self.allRepliesFinished.emit() - - downloadedCount = ( - self.downloader.fetchSuccesses - self.downloader.cacheHits - ) - totalCount = self.downloader.finishedCount() + unfinishedCount - msg = self.tr("{0} of {1} files downloaded.").format( - downloadedCount, totalCount - ) - if self.downloader.fetchErrors: - msg += self.tr(" {} files failed.").format( - self.downloader.fetchErrors - ) - self.showStatusMessage(msg) - - def readXml(self, node): - self.readCustomProperties(node) - self.layerDef.title = self.customProperty("title", "") - self.layerDef.credit = self.customProperty("credit", "") - if self.layerDef.credit == "": - self.layerDef.credit = self.customProperty( - "providerName", "" - ) # for compatibility with 0.11 - self.layerDef.serviceUrl = self.customProperty("serviceUrl", "") - self.layerDef.yOriginTop = int(self.customProperty("yOriginTop", 1)) - self.layerDef.zmin = int( - self.customProperty("zmin", TileDefaultSettings.ZMIN) - ) - self.layerDef.zmax = int( - self.customProperty("zmax", TileDefaultSettings.ZMAX) - ) - bbox = self.customProperty("bbox", None) - if bbox: - self.layerDef.bbox = BoundingBox.fromString(bbox) - self.setExtent( - BoundingBox.degreesToMercatorMeters( - self.layerDef.bbox - ).toQgsRectangle() - ) - # layer style - self.setTransparency( - int( - self.customProperty( - "transparency", LayerDefaultSettings.TRANSPARENCY - ) - ) - ) - self.setBlendModeByName( - self.customProperty("blendMode", LayerDefaultSettings.BLEND_MODE) - ) - self.setSmoothRender( - int( - self.customProperty( - "smoothRender", LayerDefaultSettings.SMOOTH_RENDER - ) - ) - ) - self.creditVisibility = int(self.customProperty("creditVisibility", 1)) - self.setGrayscaleRender( - int( - self.customProperty( - "grayscaleRender", LayerDefaultSettings.GRAYSCALE_RENDER - ) - ) - ) - self.setBrigthness( - int( - self.customProperty( - "brigthness", LayerDefaultSettings.BRIGTNESS - ) - ) - ) - self.setContrast( - float( - self.customProperty("contrast", LayerDefaultSettings.CONTRAST) - ) - ) - - return True - - def writeXml(self, node, doc): - element = node.toElement() - element.setAttribute("type", "plugin") - element.setAttribute("name", TileLayer.LAYER_TYPE) - return True - - def readSymbology(self, node, errorMessage): - return False - - def writeSymbology(self, node, doc, errorMessage): - return False - - def metadata(self): - lines = [] - fmt = "%s:\t%s" - lines.append(fmt % (self.tr("Title"), self.layerDef.title)) - lines.append(fmt % (self.tr("Credit"), self.layerDef.credit)) - lines.append(fmt % (self.tr("URL"), self.layerDef.serviceUrl)) - lines.append( - fmt - % ( - self.tr("yOrigin"), - "%s (yOriginTop=%d)" - % ( - ("Bottom", "Top")[self.layerDef.yOriginTop], - self.layerDef.yOriginTop, - ), - ) - ) - if self.layerDef.bbox: - extent = self.layerDef.bbox.toString() - else: - extent = self.tr("Not set") - lines.append( - fmt - % ( - self.tr("Zoom range"), - "%d - %d" % (self.layerDef.zmin, self.layerDef.zmax), - ) - ) - lines.append(fmt % (self.tr("Layer Extent"), extent)) - return "\n".join(lines) - - def log(self, msg): - if debug_mode: - qDebug(msg) - - def logT(self, msg): - if debug_mode: - qDebug("%s: %s" % (str(threading.current_thread()), msg)) - - def dump(self, detail=False, bbox=None): - pass - - # functions for multi-thread rendering - def fetchFiles(self, urls): - self.logT("TileLayer.fetchFiles() starts") - # create a QEventLoop object that belongs to the current thread (if ver. > 2.1, it is render thread) - eventLoop = QEventLoop() - self.logT("Create event loop: " + str(eventLoop)) # DEBUG - # QObject.connect(self, SIGNAL("allRepliesFinished()"), eventLoop.quit) - self.allRepliesFinished.connect(eventLoop.quit) - - # create a timer to watch whether rendering is stopped - watchTimer = QTimer() - watchTimer.timeout.connect(eventLoop.quit) - - # send a fetch request to the main thread - # self.emit(SIGNAL("fetchRequest(QStringList)"), urls) - self.fetchRequest.emit(urls) - - # wait for the fetch to finish - tick = 0 - interval = 500 - timeoutTick = self.downloadTimeout / interval - watchTimer.start(interval) - while tick < timeoutTick: - # run event loop for 0.5 seconds at maximum - eventLoop.exec() - - if debug_mode: - qDebug("watchTimerTick: %d" % tick) - qDebug( - "unfinished downloads: %d" - % self.downloader.unfinishedCount() - ) - - if ( - self.downloader.unfinishedCount() == 0 - or self.renderContext.renderingStopped() - ): - break - tick += 1 - watchTimer.stop() - - if tick == timeoutTick and self.downloader.unfinishedCount() > 0: - self.log("fetchFiles timeout") - # self.emitShowBarMessage("fetchFiles timeout", duration=5) #DEBUG - self.downloader.abort() - self.downloader.errorStatus = Downloader.TIMEOUT_ERROR - files = self.downloader.fetchedFiles - - watchTimer.timeout.disconnect(eventLoop.quit) # - # QObject.disconnect(self, SIGNAL("allRepliesFinished()"), eventLoop.quit) - self.allRepliesFinished.disconnect(eventLoop.quit) - - self.logT("TileLayer.fetchFiles() ends") - return files - - def fetchRequestSlot(self, urls): - self.logT("TileLayer.fetchRequestSlot()") - self.downloader.fetchFilesAsync(urls, self.downloadTimeout) - - def showStatusMessage(self, msg, timeout=0): - # self.emit(SIGNAL("showMessage(QString, int)"), msg, timeout) - self.showMessage.emit(msg, timeout) - - def showStatusMessageSlot(self, msg, timeout): - self.iface.mainWindow().statusBar().showMessage(msg, timeout) - - def emitShowBarMessage( - self, text, level=QGisMessageBarLevel.Info, duration=0, title=None - ): - if PluginSettings.show_messages_in_bar(): - if title is None: - title = PluginSettings.product_name() - # self.emit(SIGNAL("showBarMessage(QString, QString, int, int)"), title, text, level, duration) - self.showBarMessage.emit(title, text, level, duration) - - def showBarMessageSlot(self, title, text, level, duration): - self.iface.messageBar().pushMessage(title, text, level, duration) - - -# def createMapRenderer(self, renderContext): -# qDebug("createMapRenderer") -# self.renderer = QgsPluginLayerRenderer(self, renderContext) -# return self.renderer - - -class TileLayerType(QgsPluginLayerType): - def __init__(self, plugin): - QgsPluginLayerType.__init__(self, TileLayer.LAYER_TYPE) - - def createLayer(self): - return TileLayer(TileServiceInfo.createEmptyInfo()) - - def showLayerProperties(self, layer): - from .propertiesdialog import PropertiesDialog - - dialog = PropertiesDialog(layer) - # QObject.connect(dialog, SIGNAL("applyClicked()"), self.applyClicked) - dialog.applyClicked.connect(self.applyClicked) - dialog.show() - accepted = dialog.exec() - if accepted: - self.applyProperties(dialog) - return True - - def applyClicked(self): - self.applyProperties(QObject().sender()) - - def applyProperties(self, dialog): - layer = dialog.layer - layer.setTransparency(dialog.ui.spinBox_Transparency.value()) - layer.setBlendModeByName(dialog.ui.comboBox_BlendingMode.currentText()) - layer.setSmoothRender(dialog.ui.checkBox_SmoothRender.isChecked()) - layer.setCreditVisibility( - dialog.ui.checkBox_CreditVisibility.isChecked() - ) - layer.setGrayscaleRender(dialog.ui.checkBox_Grayscale.isChecked()) - layer.setBrigthness(dialog.ui.spinBox_Brightness.value()) - layer.setContrast(dialog.ui.doubleSpinBox_Contrast.value()) - # layer.emit(SIGNAL("repaintRequested()")) - layer.repaintRequested.emit() diff --git a/src/quick_map_services/py_tiled_layer/tiles.py b/src/quick_map_services/py_tiled_layer/tiles.py deleted file mode 100644 index e7d4d06..0000000 --- a/src/quick_map_services/py_tiled_layer/tiles.py +++ /dev/null @@ -1,271 +0,0 @@ -# -*- coding: utf-8 -*- -""" -/*************************************************************************** - TileLayer Plugin - A QGIS plugin - Plugin layer for Tile Maps - ------------------- - begin : 2012-12-16 - copyright : (C) 2013 by Minoru Akagi - email : akaginch@gmail.com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program 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 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ -""" - -# Import the PyQt and QGIS libraries -import math - -from qgis.core import QgsRectangle -from qgis.PyQt.QtCore import QRect, Qt -from qgis.PyQt.QtGui import QImage, QPainter - -R = 6378137 - -debug_mode = 0 - - -class TileDefaultSettings(object): - ZMIN = 0 - ZMAX = 18 - - -def degreesToMercatorMeters(lon, lat): - # formula: http://en.wikipedia.org/wiki/Mercator_projection#Mathematics_of_the_Mercator_projection - x = R * lon * math.pi / 180 - y = R * math.log(math.tan((90 + lat) * math.pi / 360)) - return x, y - - -class BoundingBox(object): - def __init__(self, xmin, ymin, xmax, ymax): - self.xmin = xmin - self.ymin = ymin - self.xmax = xmax - self.ymax = ymax - - def toQgsRectangle(self): - return QgsRectangle(self.xmin, self.ymin, self.xmax, self.ymax) - - def toString(self, digitsAfterPoint=None): - if digitsAfterPoint is None: - return "%f,%f,%f,%f" % (self.xmin, self.ymin, self.xmax, self.ymax) - return "%.{0}f,%.{0}f,%.{0}f,%.{0}f".format(digitsAfterPoint) % ( - self.xmin, - self.ymin, - self.xmax, - self.ymax, - ) - - @classmethod - def degreesToMercatorMeters(cls, bbox): - xmin, ymin = degreesToMercatorMeters(bbox.xmin, bbox.ymin) - xmax, ymax = degreesToMercatorMeters(bbox.xmax, bbox.ymax) - return BoundingBox(xmin, ymin, xmax, ymax) - - @classmethod - def fromString(cls, s): - a = map(float, s.split(",")) - return BoundingBox(a[0], a[1], a[2], a[3]) - - -class Tile(object): - def __init__(self, zoom, x, y, data=None): - self.zoom = zoom - self.x = x - self.y = y - self.data = data - - -class Tiles(object): - def __init__(self, zoom, xmin, ymin, xmax, ymax, serviceInfo): - self.zoom = zoom - self.xmin = xmin - self.ymin = ymin - self.xmax = xmax - self.ymax = ymax - self.TILE_SIZE = serviceInfo.TILE_SIZE - self.tsize1 = serviceInfo.tsize1 - self.yOriginTop = serviceInfo.yOriginTop - self.serviceInfo = serviceInfo - self.tiles = {} - - def addTile(self, url, tile): - self.tiles[url] = tile - - def setImageData(self, url, data): - if url in self.tiles: - self.tiles[url].data = data - - def image(self): - width = (self.xmax - self.xmin + 1) * self.TILE_SIZE - height = (self.ymax - self.ymin + 1) * self.TILE_SIZE - - image = QImage(width, height, QImage.Format_ARGB32_Premultiplied) - image.fill(Qt.GlobalColor.transparent) - p = QPainter(image) - - for tile in self.tiles.values(): - if not tile.data: - continue - - x = tile.x - self.xmin - y = tile.y - self.ymin - rect = QRect( - x * self.TILE_SIZE, - y * self.TILE_SIZE, - self.TILE_SIZE, - self.TILE_SIZE, - ) - - timg = QImage() - res = timg.loadFromData(tile.data) - if res: - p.drawImage(rect, timg) - - if debug_mode: - p.setPen(Qt.GlobalColor.black) - p.drawText( - rect, - Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignRight, - # "x: %s, y:%s\nz: %s, data: %s" % (x, y, tile.zoom, tile.data.size()) - "z: %s, data: %s" % (tile.zoom, tile.data.size()), - ) - if not res: - p.setPen(Qt.GlobalColor.darkRed) - p.drawText(rect, Qt.AlignmentFlag.AlignCenter, "Bad tile") - - p.setPen(Qt.GlobalColor.black) - p.drawRect(rect) - - return image - - def extent(self): - size = self.tsize1 / 2 ** (self.zoom - 1) - if self.serviceInfo.tile_ranges is None: - return QgsRectangle( - self.xmin * size - self.tsize1, - self.tsize1 - (self.ymax + 1) * size, - (self.xmax + 1) * size - self.tsize1, - self.tsize1 - self.ymin * size, - ) - else: - originX = self.serviceInfo.originX - originY = self.serviceInfo.originY - return QgsRectangle( - originX + self.xmin * size, - originY - (self.ymax + 1) * size, - originX + (self.xmax + 1) * size, - originY - self.ymin * size, - ) - - -class TileServiceInfo(object): - TILE_SIZE = 256 - # TSIZE1 = 20037508.342789244 # (R * math.pi) - - def __init__( - self, - title, - credit, - serviceUrl, - yOriginTop=1, - zmin=TileDefaultSettings.ZMIN, - zmax=TileDefaultSettings.ZMAX, - bbox=None, - epsg_crs_id=None, - postgis_crs_id=None, - custom_proj=None, - tile_ranges=None, - tsize1=R * math.pi, - originX=-R * math.pi, - originY=R * math.pi, - ): - self.title = title - self.credit = credit - self.serviceUrl = serviceUrl - self.yOriginTop = yOriginTop - self.zmin = max(zmin, 0) - self.zmax = zmax - self.bbox = bbox - self.epsg_crs_id = epsg_crs_id - self.postgis_crs_id = postgis_crs_id - self.custom_proj = custom_proj - self.tsize1 = tsize1 - self.tile_ranges = tile_ranges - self.originX = originX - self.originY = originY - - def tileUrl(self, zoom, x, y): - if not self.yOriginTop: - y = (2**zoom - 1) - y - # Added the form to obtain the quadkey and remplace to use. - # With the following change using the quadkey allowed, take the variable {q} to represent quadkey field. - # Source and credits of procedure https://github.com/buckheroux/QuadKey/blob/master/quadkey/tile_system.py - # Adapting code Nelson Ugalde Araya nugaldea@gmail.com - if "{q}" in self.serviceUrl: - quadkey = "" - for i in range(zoom): - bit = zoom - i - digit = ord("0") - mask = 1 << (bit - 1) # if (bit - 1) > 0 else 1 >> (bit - 1) - if (x & mask) != 0: - digit += 1 - if (y & mask) != 0: - digit += 2 - quadkey += chr(digit) - return self.serviceUrl.replace("{q}", str(quadkey)) - - return ( - self.serviceUrl.replace("{z}", str(zoom)) - .replace("{x}", str(x)) - .replace("{y}", str(y)) - ) - - def getTileRect(self, zoom, x, y): - size = self.tsize1 / 2 ** (zoom - 1) - return QgsRectangle( - x * size - self.tsize1, - self.tsize1 - y * size, - (x + 1) * size - self.tsize1, - self.tsize1 - (y + 1) * size, - ) - - def degreesToTile(self, zoom, lon, lat): - x, y = degreesToMercatorMeters(lon, lat) - size = self.tsize1 / 2 ** (zoom - 1) - tx = int((x + self.tsize1) / size) - ty = int((self.tsize1 - y) / size) - return tx, ty - - def bboxDegreesToTileRange(self, zoom, bbox): - xmin, ymin = self.degreesToTile(zoom, bbox.xmin, bbox.ymax) - xmax, ymax = self.degreesToTile(zoom, bbox.xmax, bbox.ymin) - return BoundingBox(xmin, ymin, xmax, ymax) - - def __str__(self): - return "%s (%s)" % (self.title, self.serviceUrl) - - def toArrayForTreeView(self): - extent = "" - if self.bbox: - extent = self.bbox.toString(2) - return [ - self.title, - self.credit, - self.serviceUrl, - "%d-%d" % (self.zmin, self.zmax), - extent, - self.yOriginTop, - ] - - @classmethod - def createEmptyInfo(cls): - return TileServiceInfo("", "", "") diff --git a/src/quick_map_services/qgis_map_helpers.py b/src/quick_map_services/qgis_map_helpers.py index 7122389..a19bf7d 100644 --- a/src/quick_map_services/qgis_map_helpers.py +++ b/src/quick_map_services/qgis_map_helpers.py @@ -4,6 +4,7 @@ from qgis.core import ( Qgis, + QgsCoordinateReferenceSystem, QgsMessageLog, QgsProject, QgsRasterLayer, @@ -21,8 +22,6 @@ message_log_levels, ) from .plugin_settings import PluginSettings -from .py_tiled_layer.tilelayer import TileLayer -from .py_tiled_layer.tiles import TileDefaultSettings, TileServiceInfo from .qgis_proj_helper import ProjectionHelper from .qgis_settings import QGISSettings from .supported_drivers import KNOWN_DRIVERS @@ -56,8 +55,8 @@ def add_layer_to_map(ds): service_url = service_url.replace("{y}", "{-y}") qgis_tms_uri = "type=xyz&zmin={0}&zmax={1}&url={2}".format( - ds.tms_zmin or TileDefaultSettings.ZMIN, - ds.tms_zmax or TileDefaultSettings.ZMAX, + ds.tms_zmin if ds.tms_zmin is not None else 0, + ds.tms_zmax if ds.tms_zmax is not None else 18, service_url, ) @@ -71,31 +70,6 @@ def add_layer_to_map(ds): ds.tms_custom_proj, ) layers4add.append(layer) - else: - service_info = TileServiceInfo( - tr(ds.alias), ds.copyright_text, tms_url - ) - service_info.zmin = ds.tms_zmin or service_info.zmin - service_info.zmax = ds.tms_zmax or service_info.zmax - if ds.tms_y_origin_top is not None: - service_info.yOriginTop = ds.tms_y_origin_top - service_info.epsg_crs_id = ds.tms_epsg_crs_id - service_info.postgis_crs_id = ds.tms_postgis_crs_id - service_info.custom_proj = ds.tms_custom_proj - - if ( - ds.tms_tile_ranges is not None - ): # needs try block & checks that keys are integers etc.. - service_info.tile_ranges = ast.literal_eval(ds.tms_tile_ranges) - if ds.tms_tsize1 is not None: - service_info.tsize1 = ds.tms_tsize1 - if ds.tms_origin_x is not None: - service_info.originX = ds.tms_origin_x - if ds.tms_origin_y is not None: - service_info.originY = ds.tms_origin_y - - layer = TileLayer(service_info, False) - layers4add.append(layer) if ds.type.lower() == KNOWN_DRIVERS.GDAL.lower(): layer = QgsRasterLayer(ds.gdal_source_file, tr(ds.alias)) layers4add.append(layer) @@ -216,15 +190,12 @@ def add_layer_to_map(ds): if ( PluginSettings.enable_otf_3857() and ds.type.lower() == KNOWN_DRIVERS.TMS.lower() - and ds.tms_epsg_crs_id == TileLayer.CRS_ID_3857 + and ds.tms_epsg_crs_id == 3857 ): - if hasattr(iface.mapCanvas(), "setCrsTransformEnabled"): - # Need for QGIS2. In QGIS3 CRS transformation is always enabled - iface.mapCanvas().setCrsTransformEnabled(True) - iface.mapCanvas().setDestinationCrs(TileLayer.CRS_3857) + iface.mapCanvas().setDestinationCrs(QgsCoordinateReferenceSystem.fromEpsgId(3857)) if ( QGISSettings.get_new_project_crs_behavior() == QGISSettings.NEW_PROJECT_USE_PRESET_CRS ): - QgsProject.instance().setCrs(TileLayer.CRS_3857) + QgsProject.instance().setCrs(QgsCoordinateReferenceSystem.fromEpsgId(3857)) diff --git a/src/quick_map_services/quick_map_services.py b/src/quick_map_services/quick_map_services.py index 62f270b..c91ebae 100644 --- a/src/quick_map_services/quick_map_services.py +++ b/src/quick_map_services/quick_map_services.py @@ -55,7 +55,6 @@ from .groups_list import GroupsList from .plugin_locale import Locale from .plugin_settings import PluginSettings -from .py_tiled_layer.tilelayer import TileLayer, TileLayerType from .qgis_map_helpers import add_layer_to_map from .qms_service_toolbox import QmsServiceToolbox from .settings_dialog import SettingsDialog @@ -125,10 +124,6 @@ def initGui(self): # import pydevd # pydevd.settrace('localhost', port=9921, stdoutToServer=True, stderrToServer=True, suspend=False) - # Register plugin layer type - self.tileLayerType = TileLayerType(self) - qgisRegistryInstance.addPluginLayerType(self.tileLayerType) - # Create menu icon_path = self.plugin_dir + "/icons/mActionAddLayer.svg" self.menu = QMenu(self.tr("QuickMapServices")) @@ -209,8 +204,6 @@ def unload(self): self.ds_list = None self.groups_list = None self.service_layers = None - # # Unregister plugin layer type - qgisRegistryInstance.removePluginLayerType(TileLayer.LAYER_TYPE) qms_create_service_action = None set_nearest_scale_act = None From 7d96fa8048cdc7fd1f0296189bad552d8f7c7246 Mon Sep 17 00:00:00 2001 From: ArtemL Date: Thu, 17 Jul 2025 21:13:45 +0300 Subject: [PATCH 2/2] Remove compat2qgis and legacy version checks --- src/quick_map_services/compat2qgis.py | 141 ------------------ src/quick_map_services/data_sources_list.py | 7 +- src/quick_map_services/ds_edit_dialog.py | 5 +- src/quick_map_services/extra_sources.py | 5 +- .../file_selection_widget.py | 6 +- src/quick_map_services/group_edit_dialog.py | 15 +- src/quick_map_services/groups_list.py | 7 +- .../i18n/QuickMapServices_ru.ts | 112 +++++++------- src/quick_map_services/plugin_settings.py | 21 +-- src/quick_map_services/qgis_map_helpers.py | 77 ++++++---- src/quick_map_services/qgis_proj_helper.py | 12 +- src/quick_map_services/qms_service_toolbox.py | 19 +-- src/quick_map_services/quick_map_services.py | 1 - src/quick_map_services/rb_result_renderer.py | 41 +++-- src/quick_map_services/settings_dialog.py | 33 ++-- src/quick_map_services/singleton.py | 7 +- 16 files changed, 171 insertions(+), 338 deletions(-) delete mode 100644 src/quick_map_services/compat2qgis.py diff --git a/src/quick_map_services/compat2qgis.py b/src/quick_map_services/compat2qgis.py deleted file mode 100644 index f69546e..0000000 --- a/src/quick_map_services/compat2qgis.py +++ /dev/null @@ -1,141 +0,0 @@ -# -*- coding: utf-8 -*- -""" -/*************************************************************************** - QuickMapServices - A QGIS plugin - Collection of internet map services - ------------------- - begin : 2014-11-21 - git sha : $Format:%H$ - copyright : (C) 2014 by NextGIS - email : info@nextgis.com - ***************************************************************************/ -/*************************************************************************** - * * - * This program 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 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ -""" - -import os -import sys - -from qgis import core -from qgis.PyQt.QtWidgets import QFileDialog - -if hasattr(core, "QGis"): - from qgis.core import QGis -else: - from qgis.core import Qgis as QGis - - -if QGis.QGIS_VERSION_INT >= 30000: - getQGisUserDatabaseFilePath = core.QgsApplication.qgisUserDatabaseFilePath - - addMapLayer = core.QgsProject.instance().addMapLayer - - message_log_levels = { - "Info": QGis.Info, - "Warning": QGis.Warning, - "Critical": QGis.Critical, - } - message_bar_levels = message_log_levels - - geometry_types = { - "Point": core.QgsWkbTypes.PointGeometry, - } - - qgisRegistryInstance = core.QgsApplication.pluginLayerRegistry() - - imageActionShowAllLayers = ( - ":/images/themes/default/mActionShowAllLayers.svg" - ) - imageActionHideAllLayers = ( - ":images/themes/default/mActionHideAllLayers.svg" - ) -else: - getQGisUserDatabaseFilePath = core.QgsApplication.qgisUserDbFilePath - - addMapLayer = core.QgsMapLayerRegistry.instance().addMapLayer - - from qgis.core import QgsMessageLog - from qgis.gui import QgsMessageBar - - message_log_levels = { - "Info": QgsMessageLog.INFO, - "Warning": QgsMessageLog.WARNING, - "Critical": QgsMessageLog.CRITICAL, - } - message_bar_levels = { - "Info": QgsMessageBar.INFO, - "Warning": QgsMessageBar.WARNING, - "Critical": QgsMessageBar.CRITICAL, - } - - geometry_types = { - "Point": QGis.Point, - } - - qgisRegistryInstance = core.QgsPluginLayerRegistry.instance() - - imageActionShowAllLayers = ( - ":/images/themes/default/mActionShowAllLayers.png" - ) - imageActionHideAllLayers = ( - ":images/themes/default/mActionHideAllLayers.png" - ) - -QGisMessageLogLevel = type("QGisMessageLogLevel", (), (message_log_levels)) -QGisMessageBarLevel = type("QGisMessageBarLevel", (), (message_bar_levels)) -QGisGeometryType = type("QGisGeometryType", (), (geometry_types)) - - -def getCanvasDestinationCrs(iface): - if QGis.QGIS_VERSION_INT >= 30000: - return iface.mapCanvas().mapSettings().destinationCrs() - else: - return iface.mapCanvas().mapRenderer().destinationCrs() - - -class QgsCoordinateTransform(core.QgsCoordinateTransform): - def __init__(self, src_crs, dst_crs): - super(QgsCoordinateTransform, self).__init__() - - self.setSourceCrs(src_crs) - self.setDestinationCrs(dst_crs) - - def setDestinationCrs(self, dst_crs): - if QGis.QGIS_VERSION_INT >= 30000: - super(QgsCoordinateTransform, self).setDestinationCrs(dst_crs) - else: - self.setDestCRS(dst_crs) - - -def getOpenFileName(parent, caption, filedir, search_filter): - result = QFileDialog.getOpenFileName( - parent, caption, filedir, search_filter - ) - - if isinstance(result, tuple): - return result[0] - return result - - -class QgsCoordinateReferenceSystem(core.QgsCoordinateReferenceSystem): - def __init__(self, id, type): - if QGis.QGIS_VERSION_INT >= 30000: - super(QgsCoordinateReferenceSystem, self).__init__( - core.QgsCoordinateReferenceSystem.fromEpsgId(id) - ) - else: - super(QgsCoordinateReferenceSystem, self).__init__(id, type) - - @staticmethod - def fromEpsgId(id): - if QGis.QGIS_VERSION_INT >= 30000: - return core.QgsCoordinateReferenceSystem.fromEpsgId(id) - else: - return core.QgsCoordinateReferenceSystem(id) diff --git a/src/quick_map_services/data_sources_list.py b/src/quick_map_services/data_sources_list.py index 6dbcd2b..3d247b1 100644 --- a/src/quick_map_services/data_sources_list.py +++ b/src/quick_map_services/data_sources_list.py @@ -25,13 +25,12 @@ import os import sys -from qgis.core import QgsMessageLog +from qgis.core import Qgis, QgsMessageLog from qgis.PyQt.QtCore import QCoreApplication from qgis.PyQt.QtGui import QIcon from qgis.PyQt.QtWidgets import QAction from . import extra_sources -from .compat2qgis import message_log_levels from .config_reader_helper import ConfigReaderHelper from .custom_translator import CustomTranslator from .data_source_info import DataSourceCategory, DataSourceInfo @@ -96,10 +95,10 @@ def _fill_data_sources_list(self): except Exception as e: error_message = ( - "INI file can't be parsed: " + e.message + "INI file can't be parsed: " + str(e) ) QgsMessageLog.logMessage( - error_message, level=message_log_levels["Critical"] + error_message, level=Qgis.Critical ) # noinspection PyMethodMayBeStatic diff --git a/src/quick_map_services/ds_edit_dialog.py b/src/quick_map_services/ds_edit_dialog.py index f706d54..db964d9 100644 --- a/src/quick_map_services/ds_edit_dialog.py +++ b/src/quick_map_services/ds_edit_dialog.py @@ -4,10 +4,9 @@ from qgis.PyQt import uic from qgis.PyQt.QtGui import QIcon, QPixmap -from qgis.PyQt.QtWidgets import QDialog, QMessageBox +from qgis.PyQt.QtWidgets import QDialog, QFileDialog, QMessageBox from . import extra_sources -from .compat2qgis import getOpenFileName from .data_source_info import DataSourceInfo from .data_source_serializer import DataSourceSerializer from .data_sources_list import DataSourcesList @@ -112,7 +111,7 @@ def fill_ds_info(self, ds_info): self.feel_specific_fields() def choose_icon(self): - icon_path = getOpenFileName( + icon_path = QFileDialog.getOpenFileName( self, self.tr("Select icon for data source"), PluginSettings.get_default_user_icon_path(), diff --git a/src/quick_map_services/extra_sources.py b/src/quick_map_services/extra_sources.py index 6a94a4f..067315d 100644 --- a/src/quick_map_services/extra_sources.py +++ b/src/quick_map_services/extra_sources.py @@ -27,15 +27,14 @@ import tempfile from zipfile import ZipFile -from qgis.core import QgsNetworkAccessManager +from qgis.core import QgsApplication, QgsNetworkAccessManager from qgis.PyQt.QtCore import QEventLoop, QFile, QUrl from qgis.PyQt.QtNetwork import QNetworkReply, QNetworkRequest from .compat import OpenModeFlag -from .compat2qgis import getQGisUserDatabaseFilePath from .plugin_settings import PluginSettings -LOCAL_SETTINGS_PATH = os.path.dirname(getQGisUserDatabaseFilePath()) +LOCAL_SETTINGS_PATH = os.path.dirname(QgsApplication.qgisUserDatabaseFilePath()) PLUGIN_SETTINGS_PATH = os.path.join( LOCAL_SETTINGS_PATH, PluginSettings.product_name() ) diff --git a/src/quick_map_services/file_selection_widget.py b/src/quick_map_services/file_selection_widget.py index 67d6e38..bd70780 100644 --- a/src/quick_map_services/file_selection_widget.py +++ b/src/quick_map_services/file_selection_widget.py @@ -39,8 +39,6 @@ QWidget, ) -from .compat2qgis import getOpenFileName - try: _fromUtf8 = QtCore.QString.fromUtf8 except AttributeError: @@ -79,7 +77,7 @@ def show_selection_dialog(self): self.leText.setText(folder) PluginSettings.set_last_icon_path(os.path.dirname(folder)) else: - filename = getOpenFileName(self, self.dialog_title, path, self.ext) + filename = QFileDialog.getOpenFileName(self, self.dialog_title, path, self.ext) if filename: self.leText.setText(filename) PluginSettings.set_last_icon_path(os.path.dirname(filename)) @@ -97,7 +95,7 @@ def set_dialog_ext(self, ext): self.ext = ext def set_dialog_title(self, title): - self.dialog_titledialog_title = title + self.dialog_title = title def setupUi(self, Form): Form.setObjectName(_fromUtf8("Form")) diff --git a/src/quick_map_services/group_edit_dialog.py b/src/quick_map_services/group_edit_dialog.py index d6838ea..dcc3c29 100644 --- a/src/quick_map_services/group_edit_dialog.py +++ b/src/quick_map_services/group_edit_dialog.py @@ -5,10 +5,9 @@ from qgis.PyQt import uic from qgis.PyQt.QtGui import QPixmap -from qgis.PyQt.QtWidgets import QDialog, QMessageBox +from qgis.PyQt.QtWidgets import QDialog, QFileDialog, QMessageBox from . import extra_sources -from .compat2qgis import getOpenFileName from .fixed_config_parser import FixedConfigParser from .groups_list import GroupsList from .gui.line_edit_color_validator import LineEditColorValidator @@ -73,8 +72,16 @@ def fill_group_info(self, group_info): self.set_icon(self.group_info.icon) def choose_icon(self): + """ + Opens a file dialog to select an icon for the group and sets it. + + The selected icon path is stored using PluginSettings and applied + via `set_icon()`. If no icon is selected, the method exits silently. + """ print(PluginSettings.get_default_user_icon_path()) - icon_path = getOpenFileName( + + # Open a file dialog to choose an icon + icon_path, _ = QFileDialog.getOpenFileName( self, self.tr("Select icon for group"), PluginSettings.get_default_user_icon_path(), @@ -84,6 +91,8 @@ def choose_icon(self): ) print(icon_path) + + # If a file was selected, update the default path and apply the icon if icon_path != "": PluginSettings.set_default_user_icon_path(icon_path) self.set_icon(icon_path) diff --git a/src/quick_map_services/groups_list.py b/src/quick_map_services/groups_list.py index ded36a6..1f2354c 100644 --- a/src/quick_map_services/groups_list.py +++ b/src/quick_map_services/groups_list.py @@ -26,13 +26,12 @@ import os import sys -from qgis.core import QgsMessageLog +from qgis.core import Qgis, QgsMessageLog from qgis.PyQt.QtCore import QCoreApplication from qgis.PyQt.QtGui import QIcon from qgis.PyQt.QtWidgets import QMenu from . import extra_sources -from .compat2qgis import message_log_levels from .config_reader_helper import ConfigReaderHelper from .custom_translator import CustomTranslator from .group_info import GroupCategory, GroupInfo @@ -123,10 +122,10 @@ def _read_ini_file(self, root, ini_file_path, category): ) except Exception as e: error_message = ( - self.tr("Group INI file can't be parsed: ") + e.message + self.tr("Group INI file can't be parsed: ") + str(e) ) QgsMessageLog.logMessage( - error_message, level=message_log_levels["Critical"] + error_message, level=Qgis.Critical ) def get_group_menu(self, group_id): diff --git a/src/quick_map_services/i18n/QuickMapServices_ru.ts b/src/quick_map_services/i18n/QuickMapServices_ru.ts index 462ab6b..42f3932 100644 --- a/src/quick_map_services/i18n/QuickMapServices_ru.ts +++ b/src/quick_map_services/i18n/QuickMapServices_ru.ts @@ -4,12 +4,12 @@ @default - + Layer %s can't be added to the map! Слой %s невозможно добавить на карту! - + Error Ошибка @@ -213,67 +213,67 @@ p, li { white-space: pre-wrap; } DsEditDialog - + Select icon for data source Выберите иконку для источника данных - + Any text Любой текст - + Error on save group Ошибка при сохранении группы - + Data source with such id already exists! Select new id for data source! Источник данных с таким id уже существует! Выберите новый id! - + Error on save data source Ошибка при сохранении источника данных - + Icons (*.ico *.jpg *.jpeg *.png *.svg);;All files (*.*) Икоки (*.ico *.jpg *.jpeg *.png *.svg);;Все файлы (*.*) - + Please, enter data source id Пожалуйста введите id источника данных - + Please, enter data source alias Пожалуйста введите алиас источника данных - + Please, select icon for data source Пожалуйста, выберите иконку для источника данных - + Please, select group for data source Пожалуйста, выберите группу для источника данных - + Please, select type for data source Пожалуйста, выберите тип для источника данных - + Please, enter correct value for data source id Пожалуйста введите корректное значение id для источника данных - + Please, enter correct value for data source alias Пожалуйста введите корректное значение для алиаса источника данных @@ -391,12 +391,12 @@ p, li { white-space: pre-wrap; } FileSelectionWidget - + Select folder Выберите директорию - + ... ... @@ -472,42 +472,42 @@ p, li { white-space: pre-wrap; } GroupEditDialog - + Select icon for group Выберите иконку для группы - + Any text Любой текст - + Error on save group Ошибка при сохранении группы - + Please, enter group id Пожалуйста введите id группы - + Group with such id already exists! Select new id for group! Группа с таким id уже существует! Выберите новый id! - + All icon files (*.ico *.jpg *.jpeg *.png *.svg);;All files (*.*) Икоки (*.ico *.jpg *.jpeg *.png *.svg);;Все файлы (*.*) - + Please, enter group alias Пожалуйста введите алиас группы - + Please, select icon for group Выберите иконку для группы @@ -515,7 +515,7 @@ p, li { white-space: pre-wrap; } GroupsList - + Group INI file can't be parsed: INI файл группы не может быть разобран: @@ -622,17 +622,17 @@ p, li { white-space: pre-wrap; } QmsSearchResultItemWidget - + details подробнее - + report a problem сообщить о проблеме - + Add Добавить @@ -640,22 +640,22 @@ p, li { white-space: pre-wrap; } QmsServiceToolbox - + Search string... Поиск геосервиса... - + Searching... Поиск... - + No results! Нет результатов! - + Last used: Недавно использованы: @@ -670,12 +670,12 @@ p, li { white-space: pre-wrap; } Введите часть имени сервиса - + No results. Ничего не найдено. - + You can add a service to become searchable. Start <a href='{}'>here</a>. Вы можете <a href='{}'>добавить сервис</a> самостоятельно, и он станет доступен через поиск. @@ -685,22 +685,22 @@ p, li { white-space: pre-wrap; } Фильтр по охвату - + All Все - + Error Ошибка - + Valid Рабочие - + Need at least 3 symbols to start searching... Для поиска нужно минимум 3 символа... @@ -708,52 +708,52 @@ p, li { white-space: pre-wrap; } QuickMapServices - + QuickMapServices QuickMapServices - + Set proper scale Ближайший масштаб - + Set SlippyMap scales Установить масштабы SlippyMap - + Settings Настройки - + About QMS О QMS - + Error Ошибка - + Extra dirs for %s can't be created: %s %s Невозможно создать дополнительные директории для %s: %s %s - + Search QMS Поиск в QMS - + Add to Search Добавить в Поиск - + Set SlippyMap scales for current project? The previous settings will be overwritten! Установить масштабы мозаичной карты для текущего проекта? @@ -763,21 +763,21 @@ The previous settings will be overwritten! SearchThread - + Network error! {0} Сетевая ошибка! {0} - + Error of processing! {0}: {1} Ошибка обработки! {0}: {1} - + Network error: {0} Сетевая ошибка @@ -785,22 +785,22 @@ The previous settings will be overwritten! SettingsDialog - + Last version of contrib pack was downloaded! Последняя версия пакета с источниками данных была успешно загружена! - + Show all Показать все - + Hide all Скрыть все - + Error on getting contrib pack: %s Ошибка при получении источников данных: %s diff --git a/src/quick_map_services/plugin_settings.py b/src/quick_map_services/plugin_settings.py index 97ef342..fffb4f0 100644 --- a/src/quick_map_services/plugin_settings.py +++ b/src/quick_map_services/plugin_settings.py @@ -25,8 +25,6 @@ from qgis.PyQt.QtCore import QByteArray, QDir, QSettings, Qt -from .compat2qgis import QGis - class PluginSettings(object): _company_name = "NextGIS" @@ -151,21 +149,4 @@ def set_last_used_services(cls, services): settings.remove("last_used_services") settings.beginGroup("last_used_services") for geoservice in services: - geoservice.saveSelf(settings) - - @classmethod - def use_native_tms(cls): - if QGis.QGIS_VERSION_INT >= 30000: - return True - elif QGis.QGIS_VERSION_INT >= 21808: - return cls.get_settings().value( - "tile_layer/use_native_tms", True, bool - ) - else: - return False - - @classmethod - def set_use_native_tms(cls, bool_val): - return cls.get_settings().setValue( - "tile_layer/use_native_tms", bool_val - ) + geoservice.saveSelf(settings) \ No newline at end of file diff --git a/src/quick_map_services/qgis_map_helpers.py b/src/quick_map_services/qgis_map_helpers.py index a19bf7d..06b6fcb 100644 --- a/src/quick_map_services/qgis_map_helpers.py +++ b/src/quick_map_services/qgis_map_helpers.py @@ -15,12 +15,6 @@ from qgis.utils import iface from .compat import QGIS_3_38 -from .compat2qgis import ( - QGisMessageBarLevel, - QGisMessageLogLevel, - addMapLayer, - message_log_levels, -) from .plugin_settings import PluginSettings from .qgis_proj_helper import ProjectionHelper from .qgis_settings import QGISSettings @@ -36,9 +30,21 @@ def tr(message): def add_layer_to_map(ds): + """ + Adds a layer to the current QGIS project based on the datasource config. + + Supports TMS, WMS, WFS, GDAL, and GeoJSON formats. Sets attribution, + projection, and correct insertion position in the layer tree. + Shows a message if the layer is invalid. + + :param ds: Datasource description with all needed properties + :type ds: Datasource + """ layers4add = [] + # === TMS LAYERS === if ds.type.lower() == KNOWN_DRIVERS.TMS.lower(): + # Use alternative TMS URL if available if ds.alt_tms_urls: tms_url = ds.alt_tms_urls[ random.randint(0, len(ds.alt_tms_urls) - 1) @@ -46,33 +52,38 @@ def add_layer_to_map(ds): else: tms_url = ds.tms_url - if PluginSettings.use_native_tms(): # add version check - service_url = tms_url.replace("=", "%3D").replace("&", "%26") - if ( - ds.tms_y_origin_top is not None - and ds.tms_y_origin_top == False - ): - service_url = service_url.replace("{y}", "{-y}") + service_url = tms_url.replace("=", "%3D").replace("&", "%26") + if ( + ds.tms_y_origin_top is not None + and ds.tms_y_origin_top == False + ): + service_url = service_url.replace("{y}", "{-y}") + + # Construct TMS URI for QGIS + qgis_tms_uri = "type=xyz&zmin={0}&zmax={1}&url={2}".format( + ds.tms_zmin if ds.tms_zmin is not None else 0, + ds.tms_zmax if ds.tms_zmax is not None else 18, + service_url, + ) - qgis_tms_uri = "type=xyz&zmin={0}&zmax={1}&url={2}".format( - ds.tms_zmin if ds.tms_zmin is not None else 0, - ds.tms_zmax if ds.tms_zmax is not None else 18, - service_url, - ) + # Create and configure TMS raster layer + layer = QgsRasterLayer( + qgis_tms_uri, tr(ds.alias), KNOWN_DRIVERS.WMS.lower() + ) + ProjectionHelper.set_tile_layer_proj( + layer, + ds.tms_epsg_crs_id, + ds.tms_postgis_crs_id, + ds.tms_custom_proj, + ) + layers4add.append(layer) - layer = QgsRasterLayer( - qgis_tms_uri, tr(ds.alias), KNOWN_DRIVERS.WMS.lower() - ) - ProjectionHelper.set_tile_layer_proj( - layer, - ds.tms_epsg_crs_id, - ds.tms_postgis_crs_id, - ds.tms_custom_proj, - ) - layers4add.append(layer) + # === GDAL LAYERS === if ds.type.lower() == KNOWN_DRIVERS.GDAL.lower(): layer = QgsRasterLayer(ds.gdal_source_file, tr(ds.alias)) layers4add.append(layer) + + # === WMS LAYERS === if ds.type.lower() == KNOWN_DRIVERS.WMS.lower(): qgis_wms_uri = "" if ds.wms_params: @@ -98,6 +109,8 @@ def add_layer_to_map(ds): qgis_wms_uri, tr(ds.alias), KNOWN_DRIVERS.WMS.lower() ) layers4add.append(layer) + + # === WFS LAYERS === if ds.type.lower() == KNOWN_DRIVERS.WFS.lower(): qgis_wfs_uri_base = ds.wfs_url @@ -135,20 +148,22 @@ def add_layer_to_map(ds): ) layers4add.append(layer) + # === GEOJSON LAYERS === if ds.type.lower() == KNOWN_DRIVERS.GEOJSON.lower(): layer = QgsVectorLayer(ds.geojson_url, tr(ds.alias), "ogr") layers4add.append(layer) + # === ADD LAYERS TO PROJECT === for layer in layers4add: if not layer.isValid(): error_message = ( tr("Layer %s can't be added to the map!") % ds.alias ) iface.messageBar().pushMessage( - tr("Error"), error_message, level=QGisMessageBarLevel.Critical + tr("Error"), error_message, level=Qgis.Critical ) QgsMessageLog.logMessage( - error_message, level=message_log_levels["Critical"] + error_message, level=Qgis.Critical ) else: # Set attribs @@ -180,7 +195,7 @@ def add_layer_to_map(ds): else: position = 0 # insert to top - addMapLayer(layer, False) + QgsProject.instance().addMapLayer(layer, False) toc_root.insertLayer(position, layer) diff --git a/src/quick_map_services/qgis_proj_helper.py b/src/quick_map_services/qgis_proj_helper.py index 77e6d4a..edcf8c1 100644 --- a/src/quick_map_services/qgis_proj_helper.py +++ b/src/quick_map_services/qgis_proj_helper.py @@ -1,7 +1,7 @@ +from qgis.core import Qgis, QgsCoordinateReferenceSystem from qgis.gui import QgsMessageBar from qgis.utils import iface -from .compat2qgis import QGisMessageBarLevel, QgsCoordinateReferenceSystem from .plugin_settings import PluginSettings @@ -18,8 +18,8 @@ def set_tile_layer_proj( try: crs = None if epsg_crs_id is not None: - crs = QgsCoordinateReferenceSystem( - epsg_crs_id, QgsCoordinateReferenceSystem.EpsgCrsId + crs = QgsCoordinateReferenceSystem.fromEpsgId( + epsg_crs_id, ) if postgis_crs_id is not None: crs = QgsCoordinateReferenceSystem( @@ -32,7 +32,7 @@ def set_tile_layer_proj( # try to search in db searched = custom_crs.findMatchingProj() if searched: - crs = QgsCoordinateReferenceSystem( + crs = QgsCoordinateReferenceSystem.fromCrsId( searched, QgsCoordinateReferenceSystem.InternalCrsId ) else: @@ -53,11 +53,11 @@ def set_tile_layer_proj( layer.setCrs(crs) except: msg = "Custom crs can't be set for layer {0}!".format(layer.name()) - cls.show_bar_message(msg, QGisMessageBarLevel.Warning, 4) + cls.show_bar_message(msg, Qgis.Warning, 4) @classmethod def show_bar_message( - cls, text, level=QGisMessageBarLevel.Info, duration=0, title=None + cls, text, level=Qgis.Info, duration=0, title=None ): if PluginSettings.show_messages_in_bar(): if title is None: diff --git a/src/quick_map_services/qms_service_toolbox.py b/src/quick_map_services/qms_service_toolbox.py index 5d476e2..f16aae8 100644 --- a/src/quick_map_services/qms_service_toolbox.py +++ b/src/quick_map_services/qms_service_toolbox.py @@ -5,7 +5,14 @@ from pathlib import Path from urllib.error import URLError -from qgis.core import QgsGeometry, QgsMessageLog, QgsSettings +from qgis.core import ( + Qgis, + QgsCoordinateReferenceSystem, + QgsCoordinateTransform, + QgsGeometry, + QgsMessageLog, + QgsSettings, +) from qgis.PyQt import uic from qgis.PyQt.QtCore import ( QByteArray, @@ -33,12 +40,6 @@ QWidget, ) -from .compat2qgis import ( - QGisMessageLogLevel, - QgsCoordinateReferenceSystem, - QgsCoordinateTransform, - getCanvasDestinationCrs, -) from .data_source_serializer import DataSourceSerializer from .plugin_settings import PluginSettings from .qgis_map_helpers import add_layer_to_map @@ -50,7 +51,7 @@ from .singleton import singleton -def plPrint(msg, level=QGisMessageLogLevel.Info): +def plPrint(msg, level=Qgis.Info): QgsMessageLog.logMessage(msg, "QMS", level) @@ -266,7 +267,7 @@ def start_search(self): else: # extent filter extent = self.iface.mapCanvas().extent() - map_crs = getCanvasDestinationCrs(self.iface) + map_crs = self.iface.mapCanvas().mapSettings().destinationCrs() if map_crs.postgisSrid() != 4326: crsDest = QgsCoordinateReferenceSystem.fromEpsgId( 4326 diff --git a/src/quick_map_services/quick_map_services.py b/src/quick_map_services/quick_map_services.py index c91ebae..c6b511d 100644 --- a/src/quick_map_services/quick_map_services.py +++ b/src/quick_map_services/quick_map_services.py @@ -48,7 +48,6 @@ ) from .about_dialog import AboutDialog -from .compat2qgis import qgisRegistryInstance from .custom_translator import CustomTranslator, QTranslator from .data_sources_list import DataSourcesList from .extra_sources import ExtraSources diff --git a/src/quick_map_services/rb_result_renderer.py b/src/quick_map_services/rb_result_renderer.py index 0cd001c..b7035bd 100644 --- a/src/quick_map_services/rb_result_renderer.py +++ b/src/quick_map_services/rb_result_renderer.py @@ -19,17 +19,16 @@ ***************************************************************************/ """ -from qgis.core import QgsRectangle -from qgis.gui import QgsRubberBand -from qgis.PyQt.QtGui import QColor -from qgis.utils import iface - -from .compat2qgis import ( - QGisGeometryType, +from qgis.core import ( QgsCoordinateReferenceSystem, QgsCoordinateTransform, - getCanvasDestinationCrs, + QgsProject, + QgsRectangle, + QgsWkbTypes, ) +from qgis.gui import QgsRubberBand +from qgis.PyQt.QtGui import QColor +from qgis.utils import iface class RubberBandResultRenderer: @@ -38,15 +37,17 @@ def __init__(self): self.srs_wgs84 = QgsCoordinateReferenceSystem.fromEpsgId(4326) self.transform_decorator = QgsCoordinateTransform( - self.srs_wgs84, self.srs_wgs84 + self.srs_wgs84, + self.srs_wgs84, + QgsProject.instance() ) - self.rb = QgsRubberBand(self.iface.mapCanvas(), QGisGeometryType.Point) + self.rb = QgsRubberBand(self.iface.mapCanvas(), QgsWkbTypes.PointGeometry) self.rb.setColor(QColor("magenta")) self.rb.setIconSize(12) self.features_rb = QgsRubberBand( - self.iface.mapCanvas(), QGisGeometryType.Point + self.iface.mapCanvas(), QgsWkbTypes.PointGeometry ) magenta_transp = QColor("#3388ff") magenta_transp.setAlpha(120) @@ -64,16 +65,14 @@ def show_point(self, point, center=False): self.center_to_point(point) def clear(self): - self.rb.reset(QGisGeometryType.Point) + self.rb.reset(QgsWkbTypes.PointGeometry) def need_transform(self): - return getCanvasDestinationCrs(self.iface).postgisSrid() != 4326 + return self.iface.mapCanvas().mapSettings().destinationCrs().postgisSrid() != 4326 def transform_point(self, point): - # dest_srs_id = getCanvasDestinationCrs(self.iface).srsid() - # self.transformation.setDestCRSID(dest_srs_id) self.transform_decorator.setDestinationCrs( - getCanvasDestinationCrs(self.iface) + self.iface.mapCanvas().mapSettings().destinationCrs() ) try: return self.transform_decorator.transform(point) @@ -82,10 +81,8 @@ def transform_point(self, point): return def transform_bbox(self, bbox): - # dest_srs_id = getCanvasDestinationCrs(self.iface).srsid() - # self.transformation.setDestCRSID(dest_srs_id) self.transform_decorator.setDestinationCrs( - getCanvasDestinationCrs(self.iface) + self.iface.mapCanvas().mapSettings().destinationCrs() ) try: return self.transform_decorator.transformBoundingBox(bbox) @@ -94,10 +91,8 @@ def transform_bbox(self, bbox): return def transform_geom(self, geom): - # dest_srs_id = getCanvasDestinationCrs(self.iface).srsid() - # self.transformation.setDestCRSID(dest_srs_id) self.transform_decorator.setDestinationCrs( - getCanvasDestinationCrs(self.iface) + self.iface.mapCanvas().mapSettings().destinationCrs() ) try: geom.transform(self.transform_decorator) @@ -125,4 +120,4 @@ def show_feature(self, geom): self.features_rb.setToGeometry(geom, None) def clear_feature(self): - self.features_rb.reset(QGisGeometryType.Point) + self.features_rb.reset(QgsWkbTypes.PointGeometry) diff --git a/src/quick_map_services/settings_dialog.py b/src/quick_map_services/settings_dialog.py index 848d05c..d053b7b 100644 --- a/src/quick_map_services/settings_dialog.py +++ b/src/quick_map_services/settings_dialog.py @@ -24,17 +24,12 @@ import os import sys -from qgis.core import QgsApplication +from qgis.core import Qgis, QgsApplication from qgis.PyQt import uic from qgis.PyQt.QtCore import * from qgis.PyQt.QtGui import * from qgis.PyQt.QtWidgets import * -from .compat2qgis import ( - QGis, - imageActionHideAllLayers, - imageActionShowAllLayers, -) from .data_sources_model import DSManagerModel from .extra_sources import ExtraSources from .plugin_settings import PluginSettings @@ -69,12 +64,12 @@ def __init__(self, parent=None): ) # !!! need to check showAllAction = self.toolBarForDSTreeView.addAction( - QIcon(imageActionShowAllLayers), self.tr("Show all") + QIcon(":/images/themes/default/mActionShowAllLayers.svg"), self.tr("Show all") ) showAllAction.triggered.connect(self.dsManagerViewModel.checkAll) hideAllAction = self.toolBarForDSTreeView.addAction( - QIcon(imageActionHideAllLayers), self.tr("Hide all") + QIcon(":images/themes/default/mActionHideAllLayers.svg"), self.tr("Hide all") ) hideAllAction.triggered.connect(self.dsManagerViewModel.uncheckAll) self.dsManagerViewModel.sort(DSManagerModel.COLUMN_GROUP_DS) @@ -96,18 +91,12 @@ def fill_pages(self): self.spnNetworkTimeout.setValue( QGISSettings.get_default_network_timeout() ) - if QGis.QGIS_VERSION_INT >= 30000: - self.useNativeRenderer2188AndHigherLabel.setVisible(False) - self.chkUseNativeRenderer.setChecked(True) - self.chkUseNativeRenderer.setEnabled(False) - self.chkUseNativeRenderer.setVisible(False) - elif QGis.QGIS_VERSION_INT >= 21808: - self.chkUseNativeRenderer.setChecked( - PluginSettings.use_native_tms() - ) - else: - self.chkUseNativeRenderer.setChecked(False) - self.chkUseNativeRenderer.setEnabled(False) + + # Native renderer options (hidden for now) + self.useNativeRenderer2188AndHigherLabel.setVisible(False) + self.chkUseNativeRenderer.setChecked(True) + self.chkUseNativeRenderer.setEnabled(False) + self.chkUseNativeRenderer.setVisible(False) # contrib pack @@ -125,10 +114,6 @@ def save_settings(self): QGISSettings.set_default_network_timeout( self.spnNetworkTimeout.value() ) - if QGis.QGIS_VERSION_INT >= 21808: - PluginSettings.set_use_native_tms( - self.chkUseNativeRenderer.isChecked() - ) # contrib pack # ds visibility diff --git a/src/quick_map_services/singleton.py b/src/quick_map_services/singleton.py index 6ece72e..a1d7c86 100644 --- a/src/quick_map_services/singleton.py +++ b/src/quick_map_services/singleton.py @@ -21,12 +21,7 @@ ***************************************************************************/ """ -from .compat2qgis import QGis - -if QGis.QGIS_VERSION_INT >= 30000: - from qgis.PyQt.QtCore import QObject as QParentClass -else: - from qgis.PyQt.QtCore import pyqtWrapperType as QParentClass +from qgis.PyQt.QtCore import QObject as QParentClass def singleton(class_):