From 0eb55e0a5224b81b64da1c20fe452039cba643d5 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 6 Feb 2024 19:32:55 +0100 Subject: [PATCH 1/5] qgscoordinatetransform.h: add a couple missing SIP_THROW( QgsCsException ), and add Doxygen \throw hint to make it obvious exceptions might be thrown --- .../proj/qgscoordinatetransform.sip.in | 20 +++++++++++++++++-- src/core/proj/qgscoordinatetransform.h | 16 +++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/python/core/auto_generated/proj/qgscoordinatetransform.sip.in b/python/core/auto_generated/proj/qgscoordinatetransform.sip.in index a2fe3e653ad5..3395e8f9ca65 100644 --- a/python/core/auto_generated/proj/qgscoordinatetransform.sip.in +++ b/python/core/auto_generated/proj/qgscoordinatetransform.sip.in @@ -241,9 +241,11 @@ otherwise points are transformed from destination to source CRS. :param direction: transform direction (defaults to ForwardTransform) :return: transformed point + +:raises QgsCsException: if the transformation fails %End - QgsPointXY transform( double x, double y, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const; + QgsPointXY transform( double x, double y, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const throw( QgsCsException ); %Docstring Transform the point specified by x,y from the source CRS to the destination CRS. If the direction is ForwardTransform then coordinates are transformed from source to destination, @@ -254,9 +256,11 @@ otherwise points are transformed from destination to source CRS. :param direction: transform direction (defaults to ForwardTransform) :return: transformed point + +:raises QgsCsException: if the transformation fails %End - QgsVector3D transform( const QgsVector3D &point, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const; + QgsVector3D transform( const QgsVector3D &point, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const throw( QgsCsException ); %Docstring Transform the point specified in 3D coordinates from the source CRS to the destination CRS. If the direction is ForwardTransform then coordinates are transformed from source to destination, @@ -267,6 +271,8 @@ otherwise points are transformed from destination to source CRS. :return: transformed point +:raises QgsCsException: if the transformation fails + .. versionadded:: 3.18 %End @@ -285,6 +291,8 @@ the returned rectangle. crossing the 180 degree longitude line is required :return: rectangle in destination CRS + +:raises QgsCsException: if the transformation fails %End void transformInPlace( double &x, double &y, double &z, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const throw( QgsCsException ); @@ -299,6 +307,8 @@ otherwise points are transformed from destination to source CRS. must represent height relative to the vertical datum of the source CRS (generally ellipsoidal heights) and must be expressed in its vertical units (generally meters) :param direction: transform direction (defaults to ForwardTransform) + +:raises QgsCsException: if the transformation fails %End @@ -311,6 +321,8 @@ Transforms a polygon to the destination coordinate system. :param polygon: polygon to transform (occurs in place) :param direction: transform direction (defaults to forward transformation) + +:raises QgsCsException: if the transformation fails %End QgsRectangle transform( const QgsRectangle &rectangle, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const throw( QgsCsException ); @@ -323,6 +335,8 @@ otherwise points are transformed from destination to source CRS. :param direction: transform direction (defaults to ForwardTransform) :return: transformed rectangle + +:raises QgsCsException: if the transformation fails %End void transformCoords( int numPoint, double *x, double *y, double *z, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const throw( QgsCsException ); @@ -336,6 +350,8 @@ otherwise points are transformed from destination to source CRS. :param y: array of y coordinates to transform :param z: array of z coordinates to transform :param direction: transform direction (defaults to ForwardTransform) + +:raises QgsCsException: if the transformation fails %End bool isShortCircuited() const; diff --git a/src/core/proj/qgscoordinatetransform.h b/src/core/proj/qgscoordinatetransform.h index acea19a9a592..987c7b8dfa82 100644 --- a/src/core/proj/qgscoordinatetransform.h +++ b/src/core/proj/qgscoordinatetransform.h @@ -226,6 +226,7 @@ class CORE_EXPORT QgsCoordinateTransform * \param point point to transform * \param direction transform direction (defaults to ForwardTransform) * \returns transformed point + * \throws QgsCsException if the transformation fails */ QgsPointXY transform( const QgsPointXY &point, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const SIP_THROW( QgsCsException ); @@ -237,8 +238,9 @@ class CORE_EXPORT QgsCoordinateTransform * \param y y coordinate of point to transform * \param direction transform direction (defaults to ForwardTransform) * \returns transformed point + * \throws QgsCsException if the transformation fails */ - QgsPointXY transform( double x, double y, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const; + QgsPointXY transform( double x, double y, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const SIP_THROW( QgsCsException ); /** * Transform the point specified in 3D coordinates from the source CRS to the destination CRS. @@ -247,9 +249,10 @@ class CORE_EXPORT QgsCoordinateTransform * \param point coordinates of point to transform * \param direction transform direction (defaults to ForwardTransform) * \returns transformed point + * \throws QgsCsException if the transformation fails * \since QGIS 3.18 */ - QgsVector3D transform( const QgsVector3D &point, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const; + QgsVector3D transform( const QgsVector3D &point, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const SIP_THROW( QgsCsException ); /** * Transforms a rectangle from the source CRS to the destination CRS. @@ -263,6 +266,7 @@ class CORE_EXPORT QgsCoordinateTransform * \param handle180Crossover set to TRUE if destination CRS is geographic and handling of extents * crossing the 180 degree longitude line is required * \returns rectangle in destination CRS + * \throws QgsCsException if the transformation fails */ QgsRectangle transformBoundingBox( const QgsRectangle &rectangle, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward, bool handle180Crossover = false ) const SIP_THROW( QgsCsException ); @@ -276,6 +280,7 @@ class CORE_EXPORT QgsCoordinateTransform * must represent height relative to the vertical datum of the source CRS (generally ellipsoidal * heights) and must be expressed in its vertical units (generally meters) * \param direction transform direction (defaults to ForwardTransform) + * \throws QgsCsException if the transformation fails */ void transformInPlace( double &x, double &y, double &z, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const SIP_THROW( QgsCsException ); @@ -289,6 +294,7 @@ class CORE_EXPORT QgsCoordinateTransform * must represent height relative to the vertical datum of the source CRS (generally ellipsoidal * heights) and must be expressed in its vertical units (generally meters) * \param direction transform direction (defaults to ForwardTransform) + * \throws QgsCsException if the transformation fails * \note not available in Python bindings */ void transformInPlace( float &x, float &y, double &z, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const SIP_SKIP; @@ -303,6 +309,7 @@ class CORE_EXPORT QgsCoordinateTransform * must represent height relative to the vertical datum of the source CRS (generally ellipsoidal * heights) and must be expressed in its vertical units (generally meters) * \param direction transform direction (defaults to ForwardTransform) + * \throws QgsCsException if the transformation fails * \note not available in Python bindings */ void transformInPlace( float &x, float &y, float &z, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const SIP_SKIP; @@ -317,6 +324,7 @@ class CORE_EXPORT QgsCoordinateTransform * must represent height relative to the vertical datum of the source CRS (generally ellipsoidal * heights) and must be expressed in its vertical units (generally meters) * \param direction transform direction (defaults to ForwardTransform) + * \throws QgsCsException if the transformation fails * \note not available in Python bindings */ void transformInPlace( QVector &x, QVector &y, QVector &z, @@ -332,6 +340,7 @@ class CORE_EXPORT QgsCoordinateTransform * must represent height relative to the vertical datum of the source CRS (generally ellipsoidal * heights) and must be expressed in its vertical units (generally meters) * \param direction transform direction (defaults to ForwardTransform) + * \throws QgsCsException if the transformation fails * \note not available in Python bindings */ void transformInPlace( QVector &x, QVector &y, QVector &z, @@ -341,6 +350,7 @@ class CORE_EXPORT QgsCoordinateTransform * Transforms a polygon to the destination coordinate system. * \param polygon polygon to transform (occurs in place) * \param direction transform direction (defaults to forward transformation) + * \throws QgsCsException if the transformation fails */ void transformPolygon( QPolygonF &polygon, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const SIP_THROW( QgsCsException ); @@ -351,6 +361,7 @@ class CORE_EXPORT QgsCoordinateTransform * \param rectangle rectangle to transform * \param direction transform direction (defaults to ForwardTransform) * \returns transformed rectangle + * \throws QgsCsException if the transformation fails */ QgsRectangle transform( const QgsRectangle &rectangle, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const SIP_THROW( QgsCsException ); @@ -363,6 +374,7 @@ class CORE_EXPORT QgsCoordinateTransform * \param y array of y coordinates to transform * \param z array of z coordinates to transform * \param direction transform direction (defaults to ForwardTransform) + * \throws QgsCsException if the transformation fails */ void transformCoords( int numPoint, double *x, double *y, double *z, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const SIP_THROW( QgsCsException ); From 48093a2791b74074645740b1fe436db545e5deb9 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 6 Feb 2024 19:33:18 +0100 Subject: [PATCH 2/5] QgsDxfExport::writeEntitiesSymbolLevels(): catch potential QgsCsException --- src/core/dxf/qgsdxfexport.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/core/dxf/qgsdxfexport.cpp b/src/core/dxf/qgsdxfexport.cpp index c555a5087f58..e09e6abd78a1 100644 --- a/src/core/dxf/qgsdxfexport.cpp +++ b/src/core/dxf/qgsdxfexport.cpp @@ -833,7 +833,15 @@ void QgsDxfExport::writeEntitiesSymbolLevels( DxfLayerJob *job ) QgsFeatureRequest req; req.setSubsetOfAttributes( job->renderer->usedAttributes( ctx ), job->featureSource.fields() ); QgsCoordinateTransform ct( mMapSettings.destinationCrs(), job->crs, mMapSettings.transformContext() ); - req.setFilterRect( ct.transform( mExtent ) ); + try + { + req.setFilterRect( ct.transform( mMapSettings.extent() ) ); + } + catch ( const QgsCsException & ) + { + QgsDebugError( QStringLiteral( "QgsDxfExport::writeEntitiesSymbolLevels(): extent reprojection failed" ) ); + return; + } QgsFeatureIterator fit = job->featureSource.getFeatures( req ); From 4cc33f419c4dd5d069a75065edc06ad2c3bdc567 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 6 Feb 2024 19:33:39 +0100 Subject: [PATCH 3/5] QgsRenderContext::convertMetersToMapUnits(): catch potential QgsCsException --- src/core/qgsrendercontext.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/core/qgsrendercontext.cpp b/src/core/qgsrendercontext.cpp index 545a94910778..588019ef9ddc 100644 --- a/src/core/qgsrendercontext.cpp +++ b/src/core/qgsrendercontext.cpp @@ -598,7 +598,16 @@ double QgsRenderContext::convertMetersToMapUnits( double meters ) const // Note: the default QgsCoordinateTransform() : authid() will return an empty String if ( !mCoordTransform.isShortCircuited() ) { - pointCenter = mCoordTransform.transform( pointCenter ); + try + { + pointCenter = mCoordTransform.transform( pointCenter ); + } + catch ( const QgsCsException & ) + { + QgsDebugError( QStringLiteral( "QgsRenderContext::convertMetersToMapUnits(): failed to reproject pointCenter" ) ); + // what should we return;.. ? + return meters; + } } const int multiplier = meters < 0 ? -1 : 1; From 5c2ab8384d7d9e4cae5bdaddd9ccfb8e6e5376c7 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 6 Feb 2024 19:34:13 +0100 Subject: [PATCH 4/5] WFS provider: catch potential QgsCsException --- src/providers/wfs/qgswfscapabilities.cpp | 34 +++++++++++++++--------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/providers/wfs/qgswfscapabilities.cpp b/src/providers/wfs/qgswfscapabilities.cpp index fa2adde12872..c5343a14f433 100644 --- a/src/providers/wfs/qgswfscapabilities.cpp +++ b/src/providers/wfs/qgswfscapabilities.cpp @@ -580,23 +580,31 @@ void QgsWfsCapabilities::capabilitiesReplyFinished() QgsCoordinateReferenceSystem crsWGS84 = QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "CRS:84" ) ); QgsCoordinateTransform ct( crsWGS84, crs, mOptions.transformContext ); + try + { + + QgsPointXY ptMin( featureType.bbox.xMinimum(), featureType.bbox.yMinimum() ); + QgsPointXY ptMinBack( ct.transform( ct.transform( ptMin, Qgis::TransformDirection::Forward ), Qgis::TransformDirection::Reverse ) ); + QgsPointXY ptMax( featureType.bbox.xMaximum(), featureType.bbox.yMaximum() ); + QgsPointXY ptMaxBack( ct.transform( ct.transform( ptMax, Qgis::TransformDirection::Forward ), Qgis::TransformDirection::Reverse ) ); - QgsPointXY ptMin( featureType.bbox.xMinimum(), featureType.bbox.yMinimum() ); - QgsPointXY ptMinBack( ct.transform( ct.transform( ptMin, Qgis::TransformDirection::Forward ), Qgis::TransformDirection::Reverse ) ); - QgsPointXY ptMax( featureType.bbox.xMaximum(), featureType.bbox.yMaximum() ); - QgsPointXY ptMaxBack( ct.transform( ct.transform( ptMax, Qgis::TransformDirection::Forward ), Qgis::TransformDirection::Reverse ) ); + QgsDebugMsgLevel( featureType.bbox.toString(), 2 ); + QgsDebugMsgLevel( ptMinBack.toString(), 2 ); + QgsDebugMsgLevel( ptMaxBack.toString(), 2 ); - QgsDebugMsgLevel( featureType.bbox.toString(), 2 ); - QgsDebugMsgLevel( ptMinBack.toString(), 2 ); - QgsDebugMsgLevel( ptMaxBack.toString(), 2 ); + if ( std::fabs( featureType.bbox.xMinimum() - ptMinBack.x() ) < 1e-5 && + std::fabs( featureType.bbox.yMinimum() - ptMinBack.y() ) < 1e-5 && + std::fabs( featureType.bbox.xMaximum() - ptMaxBack.x() ) < 1e-5 && + std::fabs( featureType.bbox.yMaximum() - ptMaxBack.y() ) < 1e-5 ) + { + QgsDebugMsgLevel( QStringLiteral( "Values of LatLongBoundingBox are consistent with WGS84 long/lat bounds, so as the CRS is projected, assume they are indeed in WGS84 and not in the CRS units" ), 2 ); + featureType.bboxSRSIsWGS84 = true; + } - if ( std::fabs( featureType.bbox.xMinimum() - ptMinBack.x() ) < 1e-5 && - std::fabs( featureType.bbox.yMinimum() - ptMinBack.y() ) < 1e-5 && - std::fabs( featureType.bbox.xMaximum() - ptMaxBack.x() ) < 1e-5 && - std::fabs( featureType.bbox.yMaximum() - ptMaxBack.y() ) < 1e-5 ) + } + catch ( const QgsCsException & ) { - QgsDebugMsgLevel( QStringLiteral( "Values of LatLongBoundingBox are consistent with WGS84 long/lat bounds, so as the CRS is projected, assume they are indeed in WGS84 and not in the CRS units" ), 2 ); - featureType.bboxSRSIsWGS84 = true; + // can be silently ignored } } } From a3599fe5b5ff25630f51abdf5135283ed25ca2a3 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 6 Feb 2024 19:34:24 +0100 Subject: [PATCH 5/5] WMS provider: catch potential QgsCsException --- src/providers/wms/qgswmsprovider.cpp | 25 +++++++++++++++++++------ src/providers/wms/qgswmsprovider.h | 2 +- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/providers/wms/qgswmsprovider.cpp b/src/providers/wms/qgswmsprovider.cpp index b643e3cc906c..b0d6dbebed24 100644 --- a/src/providers/wms/qgswmsprovider.cpp +++ b/src/providers/wms/qgswmsprovider.cpp @@ -152,7 +152,10 @@ QgsWmsProvider::QgsWmsProvider( QString const &uri, const ProviderOptions &optio // we are working with XYZ tiles // no need to get capabilities, the whole definition is in URI // so we just generate a dummy WMTS definition - setupXyzCapabilities( uri ); + if ( !setupXyzCapabilities( uri ) ) + { + return; + } } else { @@ -1760,7 +1763,7 @@ bool QgsWmsProvider::retrieveServerCapabilities( bool forceRefresh ) } -void QgsWmsProvider::setupXyzCapabilities( const QString &uri, const QgsRectangle &sourceExtent, int sourceMinZoom, int sourceMaxZoom, double sourceTilePixelRatio ) +bool QgsWmsProvider::setupXyzCapabilities( const QString &uri, const QgsRectangle &sourceExtent, int sourceMinZoom, int sourceMaxZoom, double sourceTilePixelRatio ) { QgsDataSourceUri parsedUri; parsedUri.setEncodedUri( uri ); @@ -1773,8 +1776,18 @@ void QgsWmsProvider::setupXyzCapabilities( const QString &uri, const QgsRectangl // Y going from ~85 N to ~85 S (=atan(sinh(pi)) ... to get a square) QgsPointXY topLeftLonLat( -180, 180.0 / M_PI * std::atan( std::sinh( M_PI ) ) ); QgsPointXY bottomRightLonLat( 180, 180.0 / M_PI * std::atan( std::sinh( -M_PI ) ) ); - QgsPointXY topLeft = ct.transform( topLeftLonLat ); - QgsPointXY bottomRight = ct.transform( bottomRightLonLat ); + QgsPointXY topLeft; + QgsPointXY bottomRight; + try + { + topLeft = ct.transform( topLeftLonLat ); + bottomRight = ct.transform( bottomRightLonLat ); + } + catch ( const QgsCsException & ) + { + QgsDebugError( QStringLiteral( "setupXyzCapabilities: failed to reproject corner coordinates" ) ); + return false; + } double xspan = ( bottomRight.x() - topLeft.x() ); QgsWmsBoundingBoxProperty bbox; @@ -1877,6 +1890,7 @@ void QgsWmsProvider::setupXyzCapabilities( const QString &uri, const QgsRectangl tmsLinkRef.limits[tm.identifier] = limits; } } + return true; } bool QgsWmsProvider::setupMBTilesCapabilities( const QString &uri ) @@ -1925,8 +1939,7 @@ bool QgsWmsProvider::setupMBTilesCapabilities( const QString &uri ) // MBTiles spec does not say anything about resolutions... double sourceTilePixelRatio = 1; - setupXyzCapabilities( uri, sourceExtent, sourceMinZoom, sourceMaxZoom, sourceTilePixelRatio ); - return true; + return setupXyzCapabilities( uri, sourceExtent, sourceMinZoom, sourceMaxZoom, sourceTilePixelRatio ); } diff --git a/src/providers/wms/qgswmsprovider.h b/src/providers/wms/qgswmsprovider.h index 175a8d99376c..8cc6fdfbe8cd 100644 --- a/src/providers/wms/qgswmsprovider.h +++ b/src/providers/wms/qgswmsprovider.h @@ -384,7 +384,7 @@ class QgsWmsProvider final: public QgsRasterDataProvider private: //! In case of XYZ tile layer, setup capabilities from its URI - void setupXyzCapabilities( const QString &uri, const QgsRectangle &sourceExtent = QgsRectangle(), int sourceMinZoom = -1, int sourceMaxZoom = -1, double sourceTilePixelRatio = 0. ); + bool setupXyzCapabilities( const QString &uri, const QgsRectangle &sourceExtent = QgsRectangle(), int sourceMinZoom = -1, int sourceMaxZoom = -1, double sourceTilePixelRatio = 0. ); //! In case of MBTiles layer, setup capabilities from its metadata bool setupMBTilesCapabilities( const QString &uri );