1 /*************************************************************************** 2 qgsgcptransformer.h 3 -------------------------------------- 4 Date : 18-Feb-2009 5 Copyright : (c) 2009 by Manuel Massing 6 Email : m.massing at warped-space.de 7 *************************************************************************** 8 * * 9 * This program is free software; you can redistribute it and/or modify * 10 * it under the terms of the GNU General Public License as published by * 11 * the Free Software Foundation; either version 2 of the License, or * 12 * (at your option) any later version. * 13 * * 14 ***************************************************************************/ 15 16 #ifndef QGSGCPTRANSFORMER_H 17 #define QGSGCPTRANSFORMER_H 18 19 #include <gdal_alg.h> 20 #include "qgspoint.h" 21 #include "qgis_analysis.h" 22 #include "qgis_sip.h" 23 24 /** 25 * \ingroup analysis 26 * \brief An interface for Ground Control Points (GCP) based transformations. 27 * 28 * QgsGcpTransformerInterface implementations are able to transform point locations 29 * based on a transformation method and a list of GCPs. 30 * 31 * \since QGIS 3.20 32 */ 33 class ANALYSIS_EXPORT QgsGcpTransformerInterface SIP_ABSTRACT 34 { 35 public: 36 37 /** 38 * Available transformation methods. 39 */ 40 enum class TransformMethod : int 41 { 42 Linear, //!< Linear transform 43 Helmert, //!< Helmert transform 44 PolynomialOrder1, //!< Polynomial order 1 45 PolynomialOrder2, //!< Polyonmial order 2 46 PolynomialOrder3, //!< Polynomial order 47 ThinPlateSpline, //!< Thin plate splines 48 Projective, //!< Projective 49 InvalidTransform = 65535 //!< Invalid transform 50 }; 51 52 //! Constructor for QgsGcpTransformerInterface 53 QgsGcpTransformerInterface() = default; 54 55 virtual ~QgsGcpTransformerInterface() = default; 56 57 //! QgsGcpTransformerInterface cannot be copied - use clone() instead. 58 QgsGcpTransformerInterface( const QgsGcpTransformerInterface &other ) = delete; 59 60 //! QgsGcpTransformerInterface cannot be copied - use clone() instead. 61 QgsGcpTransformerInterface &operator=( const QgsGcpTransformerInterface &other ) = delete; 62 63 /** 64 * Clones the transformer, returning a new copy of the transformer with the same 65 * parameters as this one. 66 * 67 * Caller takes ownership of the returned object. 68 */ 69 virtual QgsGcpTransformerInterface *clone() const = 0 SIP_FACTORY; 70 71 /** 72 * Fits transformation parameters using the specified Ground Control Points (GCPs) lists of source and destination coordinates. 73 * 74 * If \a invertYAxis is set to TRUE then the y-axis of source coordinates will be inverted, e.g. to allow for transformation of raster layers 75 * with ascending top-to-bottom vertical axis coordinates. 76 * 77 * \returns TRUE on success, FALSE on failure 78 */ 79 virtual bool updateParametersFromGcps( const QVector<QgsPointXY> &sourceCoordinates, const QVector<QgsPointXY> &destinationCoordinates, bool invertYAxis = false ) SIP_THROW( QgsNotSupportedException ) = 0; 80 81 /** 82 * Returns the minimum number of Ground Control Points (GCPs) required for parameter fitting. 83 */ 84 virtual int minimumGcpCount() const = 0; 85 86 /** 87 * Returns the transformation method. 88 */ 89 virtual TransformMethod method() const = 0; 90 91 /** 92 * Transforms the point (\a x, \a y) from source to destination coordinates. 93 * 94 * If \a inverseTransform is set to TRUE, the point will be transformed from the destination to the source. 95 * 96 * \returns TRUE if transformation was successful. 97 */ 98 bool transform( double &x SIP_INOUT, double &y SIP_INOUT, bool inverseTransform = false ) const; 99 100 /** 101 * Returns a translated string representing the specified transform \a method. 102 */ 103 static QString methodToString( TransformMethod method ); 104 105 /** 106 * Creates a new QgsGcpTransformerInterface subclass representing the specified transform \a method. 107 * 108 * Caller takes ownership of the returned object. 109 */ 110 static QgsGcpTransformerInterface *create( TransformMethod method ) SIP_FACTORY; 111 112 /** 113 * Creates a new QgsGcpTransformerInterface subclass representing the specified transform \a method, initialized 114 * using the given lists of source and destination coordinates. 115 * 116 * If the parameters cannot be fit to a transform NULLPTR will be returned. 117 * 118 * Caller takes ownership of the returned object. 119 */ 120 static QgsGcpTransformerInterface *createFromParameters( TransformMethod method, const QVector<QgsPointXY> &sourceCoordinates, const QVector<QgsPointXY> &destinationCoordinates ) SIP_THROW( QgsNotSupportedException ) SIP_FACTORY; 121 122 #ifndef SIP_RUN 123 124 /** 125 * Returns function pointer to the GDALTransformer function. 126 */ 127 virtual GDALTransformerFunc GDALTransformer() const = 0; 128 129 /** 130 * Returns pointer to the GDALTransformer arguments. 131 */ 132 virtual void *GDALTransformerArgs() const = 0; 133 #endif 134 135 private: 136 137 #ifdef SIP_RUN 138 QgsGcpTransformerInterface( const QgsGcpTransformerInterface &other ) 139 #endif 140 }; 141 142 /** 143 * \brief A simple transform which is parametrized by a translation and anistotropic scale. 144 * \ingroup analysis 145 * \note Not available in Python bindings 146 * \since QGIS 3.20 147 */ 148 class ANALYSIS_EXPORT QgsLinearGeorefTransform : public QgsGcpTransformerInterface SIP_SKIP 149 { 150 public: 151 152 //! Constructor for QgsLinearGeorefTransform 153 QgsLinearGeorefTransform() = default; 154 155 /** 156 * Returns the origin and scale for the transform. 157 */ 158 bool getOriginScale( QgsPointXY &origin, double &scaleX, double &scaleY ) const; 159 160 QgsGcpTransformerInterface *clone() const override; 161 bool updateParametersFromGcps( const QVector<QgsPointXY> &sourceCoordinates, const QVector<QgsPointXY> &destinationCoordinates, bool invertYAxis = false ) override; 162 int minimumGcpCount() const override; 163 GDALTransformerFunc GDALTransformer() const override; 164 void *GDALTransformerArgs() const override; 165 TransformMethod method() const override; 166 167 private: 168 struct LinearParameters 169 { 170 QgsPointXY origin; 171 double scaleX = 1; 172 double scaleY = 1; 173 bool invertYAxis = false; 174 } mParameters; 175 176 static int linearTransform( void *pTransformerArg, int bDstToSrc, int nPointCount, 177 double *x, double *y, double *z, int *panSuccess ); 178 179 }; 180 181 /** 182 * \brief 2-dimensional helmert transform, parametrised by isotropic scale, rotation angle and translation. 183 * \ingroup analysis 184 * \note Not available in Python bindings 185 * \since QGIS 3.20 186 */ 187 class ANALYSIS_EXPORT QgsHelmertGeorefTransform : public QgsGcpTransformerInterface SIP_SKIP 188 { 189 public: 190 191 //! Constructor for QgsHelmertGeorefTransform 192 QgsHelmertGeorefTransform() = default; 193 194 /** 195 * Returns the origin, scale and rotation for the transform. 196 */ 197 bool getOriginScaleRotation( QgsPointXY &origin, double &scale, double &rotation ) const; 198 199 QgsGcpTransformerInterface *clone() const override; 200 bool updateParametersFromGcps( const QVector<QgsPointXY> &sourceCoordinates, const QVector<QgsPointXY> &destinationCoordinates, bool invertYAxis = false ) override; 201 int minimumGcpCount() const override; 202 GDALTransformerFunc GDALTransformer() const override; 203 void *GDALTransformerArgs() const override; 204 TransformMethod method() const override; 205 206 private: 207 208 struct HelmertParameters 209 { 210 QgsPointXY origin; 211 double scale; 212 double angle; 213 bool invertYAxis = false; 214 }; 215 HelmertParameters mHelmertParameters; 216 217 static int helmertTransform( void *pTransformerArg, int bDstToSrc, int nPointCount, 218 double *x, double *y, double *z, int *panSuccess ); 219 220 }; 221 222 /** 223 * \brief Interface to gdal thin plate splines and 1st/2nd/3rd order polynomials. 224 * \ingroup analysis 225 * \note Not available in Python bindings 226 * \since QGIS 3.20 227 */ 228 class ANALYSIS_EXPORT QgsGDALGeorefTransform : public QgsGcpTransformerInterface SIP_SKIP 229 { 230 public: 231 232 //! Constructor for QgsGDALGeorefTransform 233 QgsGDALGeorefTransform( bool useTPS, unsigned int polynomialOrder ); 234 ~QgsGDALGeorefTransform() override; 235 236 QgsGcpTransformerInterface *clone() const override; 237 bool updateParametersFromGcps( const QVector<QgsPointXY> &sourceCoordinates, const QVector<QgsPointXY> &destinationCoordinates, bool invertYAxis = false ) override; 238 int minimumGcpCount() const override; 239 GDALTransformerFunc GDALTransformer() const override; 240 void *GDALTransformerArgs() const override; 241 TransformMethod method() const override; 242 243 private: 244 void destroyGdalArgs(); 245 246 QVector<QgsPointXY> mSourceCoords; 247 QVector<QgsPointXY> mDestCoordinates; 248 bool mInvertYAxis = false; 249 250 const int mPolynomialOrder; 251 const bool mIsTPSTransform; 252 253 void *mGDALTransformerArgs = nullptr; 254 255 }; 256 257 /** 258 * \brief A planar projective transform, expressed by a homography. 259 * 260 * Implements model fitting which minimizes algebraic error using total least squares. 261 * 262 * \ingroup analysis 263 * \note Not available in Python bindings 264 * \since QGIS 3.20 265 */ 266 class ANALYSIS_EXPORT QgsProjectiveGeorefTransform : public QgsGcpTransformerInterface SIP_SKIP 267 { 268 public: 269 270 //! Constructor for QgsProjectiveGeorefTransform 271 QgsProjectiveGeorefTransform(); 272 273 QgsGcpTransformerInterface *clone() const override; 274 bool updateParametersFromGcps( const QVector<QgsPointXY> &sourceCoordinates, const QVector<QgsPointXY> &destinationCoordinates, bool invertYAxis = false ) override; 275 int minimumGcpCount() const override; 276 GDALTransformerFunc GDALTransformer() const override; 277 void *GDALTransformerArgs() const override; 278 TransformMethod method() const override; 279 280 private: 281 struct ProjectiveParameters 282 { 283 double H[9]; // Homography 284 double Hinv[9]; // Inverted homography 285 bool hasInverse; // Could the inverted homography be calculated? 286 } mParameters; 287 288 static int projectiveTransform( void *pTransformerArg, int bDstToSrc, int nPointCount, 289 double *x, double *y, double *z, int *panSuccess ); 290 291 }; 292 293 #endif //QGSGCPTRANSFORMER_H 294