Address review: move some WMS-related logic to GdalUtils

This commit is contained in:
Germán Carrillo 2025-09-04 12:55:42 +02:00 committed by Nyall Dawson
parent 8ce111115c
commit 40180873ff
3 changed files with 80 additions and 86 deletions

View File

@ -24,7 +24,6 @@ import os
from qgis.PyQt.QtGui import QIcon
from qgis.core import (
QgsDistanceArea,
QgsRasterFileWriter,
QgsProcessingException,
QgsProcessingParameterDefinition,
@ -36,11 +35,9 @@ from qgis.core import (
QgsProcessingParameterBoolean,
QgsProcessingParameterRasterDestination,
QgsProcessingRasterLayerDefinition,
QgsProcessingUtils,
QgsWmsUtils,
)
from processing.algs.gdal.GdalAlgorithm import GdalAlgorithm
from processing.algs.gdal.GdalUtils import GdalConnectionDetails, GdalUtils
from processing.algs.gdal.GdalUtils import GdalUtils
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
@ -193,48 +190,20 @@ class ClipRasterByExtent(GdalAlgorithm):
)
)
input_details = GdalUtils.gdal_connection_details_from_layer(
inLayer,
self.INPUT,
parameters,
context,
bbox,
)
arguments = []
# If it's a WMS layer and scale/DPI were given, craft a XML description file
if inLayer.providerType() == "wms" and isinstance(
# If not a WMS or scale/DPI were not given, add -projwin
if inLayer.providerType() != "wms" or not isinstance(
parameters[self.INPUT], QgsProcessingRasterLayerDefinition
):
# If the scale is greater than 0, we'll have a QgsProcessingRasterLayerDefinition
param_def = parameters[self.INPUT]
scale = param_def.referenceScale
dpi = param_def.dpi
distanceArea = None
if inLayer.crs().isGeographic():
distanceArea = QgsDistanceArea()
distanceArea.setSourceCrs(inLayer.crs(), context.transformContext())
distanceArea.setEllipsoid(inLayer.crs().ellipsoidAcronym())
width, height = GdalUtils._wms_dimensions_for_scale(
bbox, inLayer.crs(), scale, dpi, distanceArea
)
wms_description_file_path = QgsProcessingUtils.generateTempFilename(
"wms_description_file.xml", context
)
res_xml_wms, xml_wms_error = GdalUtils.gdal_wms_xml_description_file(
inLayer,
QgsWmsUtils.wmsVersion(inLayer),
bbox,
width,
height,
wms_description_file_path,
)
if not res_xml_wms:
raise QgsProcessingException(
"Cannot create XML description file for WMS layer. Details: {}".format(
xml_wms_error
)
)
input_details = GdalConnectionDetails(
connection_string=wms_description_file_path
)
else:
input_details = GdalUtils.gdal_connection_details_from_layer(inLayer)
arguments.extend(
[
"-projwin",

View File

@ -24,7 +24,6 @@ import os
from qgis.PyQt.QtGui import QIcon
from qgis.core import (
QgsDistanceArea,
QgsRasterFileWriter,
QgsProcessing,
QgsProcessingException,
@ -39,11 +38,9 @@ from qgis.core import (
QgsProcessingParameterBoolean,
QgsProcessingParameterRasterDestination,
QgsProcessingRasterLayerDefinition,
QgsProcessingUtils,
QgsWmsUtils,
)
from processing.algs.gdal.GdalAlgorithm import GdalAlgorithm
from processing.algs.gdal.GdalUtils import GdalConnectionDetails, GdalUtils
from processing.algs.gdal.GdalUtils import GdalUtils
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
@ -284,18 +281,14 @@ class ClipRasterByMask(GdalAlgorithm):
out = self.parameterAsOutputLayer(parameters, self.OUTPUT, context)
self.setOutputValue(self.OUTPUT, out)
# If it's a WMS layer and scale/DPI were given, craft a XML description file
_bbox = None
# If it's a WMS layer and scale/DPI were given,
# choose a BBOX depending on alg parameters
if inLayer.providerType() == "wms" and isinstance(
parameters[self.INPUT], QgsProcessingRasterLayerDefinition
):
# If the scale is greater than 0, we'll have a QgsProcessingRasterLayerDefinition
param_def = parameters[self.INPUT]
scale = param_def.referenceScale
dpi = param_def.dpi
# For the input WMS's BBOX, if -te is given, then use -te, projected to input's
# crs. Otherwise, use mask layer's extent, projected to input's crs.
_bbox = None
if not bbox.isNull() and not bbox.isEmpty():
if bboxCrs != inLayer.crs():
_bbox = self.parameterAsExtent(
@ -320,37 +313,13 @@ class ClipRasterByMask(GdalAlgorithm):
)
)
distanceArea = None
if inLayer.crs().isGeographic():
distanceArea = QgsDistanceArea()
distanceArea.setSourceCrs(inLayer.crs(), context.transformContext())
distanceArea.setEllipsoid(inLayer.crs().ellipsoidAcronym())
width, height = GdalUtils._wms_dimensions_for_scale(
_bbox, inLayer.crs(), scale, dpi, distanceArea
)
wms_description_file_path = QgsProcessingUtils.generateTempFilename(
"wms_description_file.xml", context
)
res_xml_wms, xml_wms_error = GdalUtils.gdal_wms_xml_description_file(
inLayer,
QgsWmsUtils.wmsVersion(inLayer),
_bbox,
width,
height,
wms_description_file_path,
)
if not res_xml_wms:
raise QgsProcessingException(
"Cannot create XML description file for WMS layer. Details: {}".format(
xml_wms_error
)
)
input_details = GdalConnectionDetails(
connection_string=wms_description_file_path
)
else:
input_details = GdalUtils.gdal_connection_details_from_layer(inLayer)
input_details = GdalUtils.gdal_connection_details_from_layer(
inLayer,
self.INPUT,
parameters,
context,
_bbox, # Chosen BBOX for WMS
)
arguments = ["-overwrite"]

View File

@ -49,6 +49,8 @@ from qgis.core import (
QgsPointXY,
QgsDistanceArea,
QgsRasterLayer,
QgsWmsUtils,
QgsProcessingRasterLayerDefinition,
)
from qgis.PyQt.QtCore import (
@ -369,9 +371,16 @@ class GdalUtils:
return GdalUtils.gdal_connection_details_from_layer(layer)
@staticmethod
def gdal_connection_details_from_layer(layer: QgsMapLayer) -> GdalConnectionDetails:
def gdal_connection_details_from_layer(
layer: QgsMapLayer,
parameter_name: Optional[str] = "",
parameters: Optional[dict] = {},
context: Optional[QgsProcessingContext] = None,
extent: Optional[QgsRectangle] = None,
) -> GdalConnectionDetails:
"""
Builds GDAL connection details from a QGIS map layer
and related settings
"""
provider = layer.providerType()
if provider == "spatialite":
@ -520,6 +529,53 @@ class GdalUtils:
return GdalConnectionDetails(
connection_string=gdal_source, format='"PostGISRaster"'
)
elif provider.lower() == "wms":
if parameter_name and parameters and context and extent:
# Create an XML description file for the WMS
parameter_value = parameters[parameter_name]
scale, dpi = 0, 0
if layer.providerType() == "wms" and isinstance(
parameter_value, QgsProcessingRasterLayerDefinition
):
scale = parameter_value.referenceScale
dpi = parameter_value.dpi
if scale > 0:
distanceArea = None
if layer.crs().isGeographic():
distanceArea = QgsDistanceArea()
distanceArea.setSourceCrs(
layer.crs(), context.transformContext()
)
distanceArea.setEllipsoid(layer.crs().ellipsoidAcronym())
width, height = GdalUtils._wms_dimensions_for_scale(
extent, layer.crs(), scale, dpi, distanceArea
)
wms_description_file_path = QgsProcessingUtils.generateTempFilename(
"wms_description_file.xml", context
)
res_xml_wms, xml_wms_error = (
GdalUtils.gdal_wms_xml_description_file(
layer,
QgsWmsUtils.wmsVersion(layer),
extent,
width,
height,
wms_description_file_path,
)
)
if res_xml_wms:
return GdalConnectionDetails(
connection_string=wms_description_file_path
)
else:
message = "Cannot create XML description file for WMS layer. Details: {}".format(
xml_wms_error
)
QgsMessageLog.logMessage(
message, "Processing", Qgis.MessageLevel.Warning
)
ogrstr = str(layer.source()).split("|")[0]
path, ext = os.path.splitext(ogrstr)
@ -715,7 +771,7 @@ class GdalUtils:
crs: QgsCoordinateReferenceSystem,
scale: int,
dpi: float = 96.0,
distanceArea=Optional[QgsDistanceArea],
distanceArea: Optional[QgsDistanceArea] = None,
) -> tuple[int, int]:
"""
Returns a tuple with WIDTH and HEIGHT in pixels that would match