1 /*************************************************************************** 2 qgscoordinatetransform.h - Coordinate Transforms 3 ------------------------ 4 begin : Dec 2004 5 copyright : (C) 2004 Tim Sutton 6 email : tim at linfiniti.com 7 ***************************************************************************/ 8 9 /*************************************************************************** 10 * * 11 * This program is free software; you can redistribute it and/or modify * 12 * it under the terms of the GNU General Public License as published by * 13 * the Free Software Foundation; either version 2 of the License, or * 14 * (at your option) any later version. * 15 * * 16 ***************************************************************************/ 17 #ifndef QGSCOORDINATETRANSFORM_H 18 #define QGSCOORDINATETRANSFORM_H 19 20 #include <QExplicitlySharedDataPointer> 21 22 #include "qgsconfig.h" 23 #include "qgis_core.h" 24 #include "qgis_sip.h" 25 #include "qgscoordinatereferencesystem.h" 26 #include "qgscoordinatetransformcontext.h" 27 28 class QgsCoordinateTransformPrivate; 29 class QgsPointXY; 30 class QgsRectangle; 31 class QPolygonF; 32 class QgsProject; 33 class QgsVector3D; 34 35 /** 36 * \ingroup core 37 * \brief Class for doing transforms between two map coordinate systems. 38 * 39 * This class can convert map coordinates to a different coordinate reference system. 40 * It is normally associated with a map layer and is used to transform between the 41 * layer's coordinate system and the coordinate system of the map canvas, although 42 * it can be used in a more general sense to transform coordinates. 43 * 44 * When used to transform between a layer and the map canvas, all references to source 45 * and destination coordinate systems refer to layer and map canvas respectively. All 46 * operations are from the perspective of the layer. For example, a forward transformation 47 * transforms coordinates from the layer's coordinate system to the map canvas. 48 * \note Since QGIS 3.0 QgsCoordinateReferenceSystem objects are implicitly shared. 49 * 50 * \warning Since QGIS 3.20 The QgsCoordinateTransform class can perform time-dependent transformations 51 * between a static and dynamic CRS based on either the source OR destination CRS coordinate epoch, 52 * however dynamic CRS to dynamic CRS transformations are not currently supported. 53 * 54 * \see QgsDatumTransform 55 * \see QgsCoordinateTransformContext 56 */ 57 class CORE_EXPORT QgsCoordinateTransform 58 { 59 60 public: 61 62 //! Default constructor, creates an invalid QgsCoordinateTransform. 63 QgsCoordinateTransform(); 64 65 /** 66 * Constructs a QgsCoordinateTransform to transform from the \a source 67 * to \a destination coordinate reference system. 68 * 69 * The \a context argument specifies the context under which the transform 70 * will be applied, and is used for calculating necessary datum transforms 71 * to utilize. 72 * 73 * Python scripts should generally use the constructor variant which accepts 74 * a QgsProject instance instead of this constructor. 75 * 76 * \warning Since QGIS 3.20 The QgsCoordinateTransform class can perform time-dependent transformations 77 * between a static and dynamic CRS based on either the source OR destination CRS coordinate epoch, 78 * however dynamic CRS to dynamic CRS transformations are not currently supported. 79 * 80 * \warning Do NOT use an empty/default constructed QgsCoordinateTransformContext() 81 * object when creating QgsCoordinateTransform objects. This prevents correct 82 * datum transform handling and may result in inaccurate transformations. Always 83 * ensure that the QgsCoordinateTransformContext object is correctly retrieved 84 * based on the current code context, or use the constructor variant which 85 * accepts a QgsProject argument instead. 86 * 87 * \since QGIS 3.0 88 */ 89 explicit QgsCoordinateTransform( const QgsCoordinateReferenceSystem &source, 90 const QgsCoordinateReferenceSystem &destination, 91 const QgsCoordinateTransformContext &context ); 92 93 /** 94 * Constructs a QgsCoordinateTransform to transform from the \a source 95 * to \a destination coordinate reference system, when used with the 96 * given \a project. 97 * 98 * No reference to \a project is stored or utilized outside of the constructor, 99 * and it is used to retrieve the project's transform context only. 100 * 101 * Python scripts should utilize the QgsProject.instance() project 102 * instance when creating QgsCoordinateTransform. This will ensure 103 * that any datum transforms defined in the project will be 104 * correctly respected during coordinate transforms. E.g. 105 * 106 * \code{.py} 107 * transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem("EPSG:3111"), 108 * QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance()) 109 * \endcode 110 * 111 * \warning Since QGIS 3.20 The QgsCoordinateTransform class can perform time-dependent transformations 112 * between a static and dynamic CRS based on either the source OR destination CRS coordinate epoch, 113 * however dynamic CRS to dynamic CRS transformations are not currently supported. 114 * \since QGIS 3.0 115 */ 116 explicit QgsCoordinateTransform( const QgsCoordinateReferenceSystem &source, 117 const QgsCoordinateReferenceSystem &destination, 118 const QgsProject *project ); 119 120 /** 121 * Constructs a QgsCoordinateTransform to transform from the \a source 122 * to \a destination coordinate reference system, with the specified 123 * datum transforms (see QgsDatumTransform). 124 * 125 * \deprecated will be removed in QGIS 4.0. Use the constructor with a QgsCoordinateTransformContext argument instead. 126 * \since QGIS 3.0 127 */ 128 Q_DECL_DEPRECATED explicit QgsCoordinateTransform( const QgsCoordinateReferenceSystem &source, 129 const QgsCoordinateReferenceSystem &destination, 130 int sourceDatumTransformId, 131 int destinationDatumTransformId ) SIP_DEPRECATED; 132 133 /** 134 * Copy constructor 135 */ 136 QgsCoordinateTransform( const QgsCoordinateTransform &o ); 137 138 /** 139 * Assignment operator 140 */ 141 QgsCoordinateTransform &operator=( const QgsCoordinateTransform &o ); 142 143 ~QgsCoordinateTransform(); 144 145 /** 146 * Returns TRUE if the coordinate transform is valid, ie both the source and destination 147 * CRS have been set and are valid. 148 * \since QGIS 3.0 149 */ 150 bool isValid() const; 151 152 /** 153 * Sets the source coordinate reference system. 154 * \param crs CRS to transform coordinates from 155 * \see sourceCrs() 156 * \see setDestinationCrs() 157 */ 158 void setSourceCrs( const QgsCoordinateReferenceSystem &crs ); 159 160 /** 161 * Sets the destination coordinate reference system. 162 * \param crs CRS to transform coordinates to 163 * \see destinationCrs() 164 * \see setSourceCrs() 165 */ 166 void setDestinationCrs( const QgsCoordinateReferenceSystem &crs ); 167 168 /** 169 * Sets the \a context in which the coordinate transform should be 170 * calculated. 171 * \see context() 172 * \since QGIS 3.0 173 */ 174 void setContext( const QgsCoordinateTransformContext &context ); 175 176 /** 177 * Returns the context in which the coordinate transform will be 178 * calculated. 179 * \see setContext() 180 * \since QGIS 3.4 181 */ 182 QgsCoordinateTransformContext context() const; 183 184 /** 185 * Returns the source coordinate reference system, which the transform will 186 * transform coordinates from. 187 * \see setSourceCrs() 188 * \see destinationCrs() 189 */ 190 QgsCoordinateReferenceSystem sourceCrs() const; 191 192 /** 193 * Returns the destination coordinate reference system, which the transform will 194 * transform coordinates to. 195 * \see setDestinationCrs() 196 * \see sourceCrs() 197 */ 198 QgsCoordinateReferenceSystem destinationCrs() const; 199 200 /** 201 * Transform the point from the source CRS to the destination CRS. 202 * If the direction is ForwardTransform then coordinates are transformed from source to destination, 203 * otherwise points are transformed from destination to source CRS. 204 * \param point point to transform 205 * \param direction transform direction (defaults to ForwardTransform) 206 * \returns transformed point 207 */ 208 QgsPointXY transform( const QgsPointXY &point, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const SIP_THROW( QgsCsException ); 209 210 /** 211 * Transform the point specified by x,y from the source CRS to the destination CRS. 212 * If the direction is ForwardTransform then coordinates are transformed from source to destination, 213 * otherwise points are transformed from destination to source CRS. 214 * \param x x coordinate of point to transform 215 * \param y y coordinate of point to transform 216 * \param direction transform direction (defaults to ForwardTransform) 217 * \returns transformed point 218 */ 219 QgsPointXY transform( double x, double y, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const; 220 221 /** 222 * Transform the point specified in 3D coordinates from the source CRS to the destination CRS. 223 * If the direction is ForwardTransform then coordinates are transformed from source to destination, 224 * otherwise points are transformed from destination to source CRS. 225 * \param point coordinates of point to transform 226 * \param direction transform direction (defaults to ForwardTransform) 227 * \returns transformed point 228 * \since QGIS 3.18 229 */ 230 QgsVector3D transform( const QgsVector3D &point, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const; 231 232 /** 233 * Transforms a rectangle from the source CRS to the destination CRS. 234 * If the direction is ForwardTransform then coordinates are transformed from source to destination, 235 * otherwise points are transformed from destination to source CRS. 236 * This method assumes that the rectangle is a bounding box, and creates a bounding box 237 * in the projected CRS, such that all points from the source rectangle are within 238 * the returned rectangle. 239 * \param rectangle rectangle to transform 240 * \param direction transform direction (defaults to ForwardTransform) 241 * \param handle180Crossover set to TRUE if destination CRS is geographic and handling of extents 242 * crossing the 180 degree longitude line is required 243 * \returns rectangle in destination CRS 244 */ 245 QgsRectangle transformBoundingBox( const QgsRectangle &rectangle, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward, bool handle180Crossover = false ) const SIP_THROW( QgsCsException ); 246 247 /** 248 * Transforms an array of x, y and z double coordinates in place, from the source CRS to the destination CRS. 249 * If the direction is ForwardTransform then coordinates are transformed from source to destination, 250 * otherwise points are transformed from destination to source CRS. 251 * \param x array of x coordinates of points to transform 252 * \param y array of y coordinates of points to transform 253 * \param z array of z coordinates of points to transform. The z coordinates of the points 254 * must represent height relative to the vertical datum of the source CRS (generally ellipsoidal 255 * heights) and must be expressed in its vertical units (generally meters) 256 * \param direction transform direction (defaults to ForwardTransform) 257 */ 258 void transformInPlace( double &x, double &y, double &z, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const SIP_THROW( QgsCsException ); 259 260 /** 261 * Transforms an array of x, y and z float coordinates in place, from the source CRS to the destination CRS. 262 * If the direction is ForwardTransform then coordinates are transformed from source to destination, 263 * otherwise points are transformed from destination to source CRS. 264 * \param x array of x coordinates of points to transform 265 * \param y array of y coordinates of points to transform 266 * \param z array of z coordinates of points to transform. The z coordinates of the points 267 * must represent height relative to the vertical datum of the source CRS (generally ellipsoidal 268 * heights) and must be expressed in its vertical units (generally meters) 269 * \param direction transform direction (defaults to ForwardTransform) 270 * \note not available in Python bindings 271 */ 272 void transformInPlace( float &x, float &y, double &z, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const SIP_SKIP; 273 274 /** 275 * Transforms an array of x, y and z float coordinates in place, from the source CRS to the destination CRS. 276 * If the direction is ForwardTransform then coordinates are transformed from source to destination, 277 * otherwise points are transformed from destination to source CRS. 278 * \param x array of x coordinates of points to transform 279 * \param y array of y coordinates of points to transform 280 * \param z array of z coordinates of points to transform. The z coordinates of the points 281 * must represent height relative to the vertical datum of the source CRS (generally ellipsoidal 282 * heights) and must be expressed in its vertical units (generally meters) 283 * \param direction transform direction (defaults to ForwardTransform) 284 * \note not available in Python bindings 285 */ 286 void transformInPlace( float &x, float &y, float &z, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const SIP_SKIP; 287 288 /** 289 * Transforms a vector of x, y and z float coordinates in place, from the source CRS to the destination CRS. 290 * If the direction is ForwardTransform then coordinates are transformed from source to destination, 291 * otherwise points are transformed from destination to source CRS. 292 * \param x vector of x coordinates of points to transform 293 * \param y vector of y coordinates of points to transform 294 * \param z vector of z coordinates of points to transform. The z coordinates of the points 295 * must represent height relative to the vertical datum of the source CRS (generally ellipsoidal 296 * heights) and must be expressed in its vertical units (generally meters) 297 * \param direction transform direction (defaults to ForwardTransform) 298 * \note not available in Python bindings 299 */ 300 void transformInPlace( QVector<float> &x, QVector<float> &y, QVector<float> &z, 301 Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const SIP_SKIP; 302 303 /** 304 * Transforms a vector of x, y and z double coordinates in place, from the source CRS to the destination CRS. 305 * If the direction is ForwardTransform then coordinates are transformed from source to destination, 306 * otherwise points are transformed from destination to source CRS. 307 * \param x vector of x coordinates of points to transform 308 * \param y vector of y coordinates of points to transform 309 * \param z vector of z coordinates of points to transform. The z coordinates of the points 310 * must represent height relative to the vertical datum of the source CRS (generally ellipsoidal 311 * heights) and must be expressed in its vertical units (generally meters) 312 * \param direction transform direction (defaults to ForwardTransform) 313 * \note not available in Python bindings 314 */ 315 void transformInPlace( QVector<double> &x, QVector<double> &y, QVector<double> &z, 316 Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const SIP_SKIP; 317 318 /** 319 * Transforms a polygon to the destination coordinate system. 320 * \param polygon polygon to transform (occurs in place) 321 * \param direction transform direction (defaults to forward transformation) 322 */ 323 void transformPolygon( QPolygonF &polygon, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const SIP_THROW( QgsCsException ); 324 325 /** 326 * Transforms a rectangle to the destination CRS. 327 * If the direction is ForwardTransform then coordinates are transformed from source to destination, 328 * otherwise points are transformed from destination to source CRS. 329 * \param rectangle rectangle to transform 330 * \param direction transform direction (defaults to ForwardTransform) 331 * \returns transformed rectangle 332 */ 333 QgsRectangle transform( const QgsRectangle &rectangle, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const SIP_THROW( QgsCsException ); 334 335 /** 336 * Transform an array of coordinates to the destination CRS. 337 * If the direction is ForwardTransform then coordinates are transformed from source to destination, 338 * otherwise points are transformed from destination to source CRS. 339 * \param numPoint number of coordinates in arrays 340 * \param x array of x coordinates to transform 341 * \param y array of y coordinates to transform 342 * \param z array of z coordinates to transform 343 * \param direction transform direction (defaults to ForwardTransform) 344 */ 345 void transformCoords( int numPoint, double *x, double *y, double *z, Qgis::TransformDirection direction = Qgis::TransformDirection::Forward ) const SIP_THROW( QgsCsException ); 346 347 /** 348 * Returns TRUE if the transform short circuits because the source and destination are equivalent. 349 */ 350 bool isShortCircuited() const; 351 352 /** 353 * Returns a Proj string representing the coordinate operation which will be used to transform 354 * coordinates. 355 * 356 * \note The string returned by this method gives the desired coordinate operation string, based on 357 * the state of the QgsCoordinateTransformContext object given in the QgsCoordinateTransform's constructor. 358 * It may be an empty string if no explicit coordinate operation is required. In order to determine the 359 * ACTUAL coordinate operation which is being used by the transform, use the instantiatedCoordinateOperationDetails() call instead. 360 * 361 * \note Requires Proj 6.0 or later. Builds based on earlier Proj versions will always return 362 * an empty string, and the deprecated sourceDatumTransformId() or destinationDatumTransformId() methods should be used instead. 363 * 364 * \see instantiatedCoordinateOperationDetails() 365 * \see setCoordinateOperation() 366 * \since QGIS 3.8 367 */ 368 QString coordinateOperation() const; 369 370 /** 371 * Returns the transform details representing the coordinate operation which is being used to transform 372 * coordinates. 373 * 374 * This may differ from the result returned by coordinateOperation() if the desired coordinate 375 * operation was not successfully instantiated. 376 * 377 * \note Requires Proj 6.0 or later. Builds based on earlier Proj versions will always return 378 * an empty result, and the deprecated sourceDatumTransformId() or destinationDatumTransformId() methods should be used instead. 379 * 380 * \see coordinateOperation() 381 * \since QGIS 3.10.2 382 */ 383 QgsDatumTransform::TransformDetails instantiatedCoordinateOperationDetails() const; 384 385 /** 386 * Sets a Proj string representing the coordinate \a operation which will be used to transform 387 * coordinates. 388 * 389 * \warning It is the caller's responsibility to ensure that \a operation is a valid Proj 390 * coordinate operation string. 391 * 392 * \note Requires Proj 6.0 or later. Builds based on earlier Proj versions will ignore this setting, 393 * and the deprecated setSourceDatumTransformId() or setDestinationDatumTransformId() methods should be used instead. 394 * 395 * \see coordinateOperation() 396 * \since QGIS 3.8 397 */ 398 void setCoordinateOperation( const QString &operation ) const; 399 400 /** 401 * Sets whether "ballpark" fallback transformations can be used in the case that the specified 402 * coordinate operation fails (such as when coordinates from outside a required grid shift file 403 * are transformed). See fallbackOperationOccurred() for further details. 404 * 405 * \note Requires Proj 6.0 or later. Builds based on earlier Proj versions will ignore this setting. 406 * 407 * \warning If setBallparkTransformsAreAppropriate() is set to TRUE, this setting will be ignored 408 * and fallback transformations will always be permitted. 409 * 410 * \see allowFallbackTransforms() 411 * \see setBallparkTransformsAreAppropriate() 412 * \since QGIS 3.12 413 */ 414 void setAllowFallbackTransforms( bool allowed ); 415 416 /** 417 * Returns whether "ballpark" fallback transformations will be used in the case that the specified 418 * coordinate operation fails (such as when coordinates from outside a required grid shift file 419 * are transformed). See fallbackOperationOccurred() for further details. 420 * 421 * \note Requires Proj 6.0 or later. Builds based on earlier Proj versions will ignore this setting. 422 * 423 * \see setAllowFallbackTransforms() 424 * \see setBallparkTransformsAreAppropriate() 425 * \since QGIS 3.12 426 */ 427 bool allowFallbackTransforms() const; 428 429 /** 430 * Sets whether approximate "ballpark" results are appropriate for this coordinate transform. 431 * 432 * When a coordinate transform is only being used to generate ballpark results then the 433 * \a appropriate argument should be set to TRUE. This indicates that its perfectable 434 * acceptable (and even expected!) for the transform to use fallback coordinate operations 435 * in the case that the preferred or user-specified operation fails (such as when coordinates 436 * from outside of a grid shift file's extent are transformed). 437 * 438 * When \a appropriate is TRUE, then no warnings will be generated when the transform 439 * falls back to a default operation, which may introduce inaccuracies when compared to 440 * the default/specified coordinate operation. 441 * 442 * This should be set when a transform expects that coordinates outside of the direct 443 * area of use while be transformed, e.g. when transforming from a global extent to a 444 * CRS with a localized area of use. 445 * 446 * If \a appropriate is FALSE (the default behavior), then transforms MAY STILL fallback to default operations 447 * when the preferred or user-specified operation fails, however whenever this occurs 448 * a user-visible warning will be generated. 449 * 450 * If \a appropriate is TRUE, then this setting overrides allowFallbackTransforms() 451 * and fallback transforms will always be allowed when required. 452 * 453 * \warning This setting applies to a single instance of a coordinate transform only, 454 * and is not copied when a coordinate transform object is copied or assigned. 455 * 456 * \note Requires Proj 6.0 or later. Builds based on earlier Proj versions will ignore this setting. 457 * 458 * \since QGIS 3.12 459 */ 460 void setBallparkTransformsAreAppropriate( bool appropriate ); 461 462 /** 463 * Sets whether the default fallback operation handler is disabled for this transform instance. 464 * 465 * If the default handler is \a disabled then it is possible to determine whether a fallback 466 * operation occurred by testing fallbackOperationOccurred() immediately after a transformation. 467 * 468 * \warning This setting applies to a single instance of a coordinate transform only, 469 * and is not copied when a coordinate transform object is copied or assigned. 470 * 471 * \note Requires Proj 6.0 or later. Builds based on earlier Proj versions will never perform fallback operations. 472 * 473 * \see fallbackOperationOccurred() 474 * \since QGIS 3.12 475 */ 476 void disableFallbackOperationHandler( bool disabled ); 477 478 /** 479 * Returns TRUE if a fallback operation occurred for the most recent transform. 480 * 481 * \note Requires Proj 6.0 or later. Builds based on earlier Proj versions will never perform fallback operations. 482 * 483 * \see disableFallbackOperationHandler() 484 * \since QGIS 3.12 485 */ 486 bool fallbackOperationOccurred() const; 487 488 /** 489 * Returns the ID of the datum transform to use when projecting from the source 490 * CRS. 491 * 492 * This is usually calculated automatically from the transform's QgsCoordinateTransformContext, 493 * but can be manually overwritten by a call to setSourceDatumTransformId(). 494 * 495 * \see QgsDatumTransform 496 * \see setSourceDatumTransformId() 497 * \see destinationDatumTransformId() 498 * 499 * \deprecated Unused on builds based on Proj 6.0 or later 500 */ 501 Q_DECL_DEPRECATED int sourceDatumTransformId() const SIP_DEPRECATED; 502 503 /** 504 * Sets the \a datumId ID of the datum transform to use when projecting from the source 505 * CRS. 506 * 507 * This is usually calculated automatically from the transform's QgsCoordinateTransformContext. 508 * Calling this method will overwrite any automatically calculated datum transform. 509 * 510 * \see QgsDatumTransform 511 * \see sourceDatumTransformId() 512 * \see setDestinationDatumTransformId() 513 * 514 * \deprecated Unused on builds based on Proj 6.0 or later 515 */ 516 Q_DECL_DEPRECATED void setSourceDatumTransformId( int datumId ) SIP_DEPRECATED; 517 518 /** 519 * Returns the ID of the datum transform to use when projecting to the destination 520 * CRS. 521 * 522 * This is usually calculated automatically from the transform's QgsCoordinateTransformContext, 523 * but can be manually overwritten by a call to setDestinationDatumTransformId(). 524 * 525 * \see QgsDatumTransform 526 * \see setDestinationDatumTransformId() 527 * \see sourceDatumTransformId() 528 * 529 * \deprecated Unused on builds based on Proj 6.0 or later 530 */ 531 Q_DECL_DEPRECATED int destinationDatumTransformId() const SIP_DEPRECATED; 532 533 /** 534 * Sets the \a datumId ID of the datum transform to use when projecting to the destination 535 * CRS. 536 * 537 * This is usually calculated automatically from the transform's QgsCoordinateTransformContext. 538 * Calling this method will overwrite any automatically calculated datum transform. 539 * 540 * \see QgsDatumTransform 541 * \see destinationDatumTransformId() 542 * \see setSourceDatumTransformId() 543 * 544 * \deprecated Unused on builds based on Proj 6.0 or later 545 */ 546 Q_DECL_DEPRECATED void setDestinationDatumTransformId( int datumId ) SIP_DEPRECATED; 547 548 #ifndef SIP_RUN 549 550 /** 551 * Clears the internal cache used to initialize QgsCoordinateTransform objects. 552 * This should be called whenever the srs database has 553 * been modified in order to ensure that outdated CRS transforms are not created. 554 * 555 * If \a disableCache is TRUE then the inbuilt cache will be completely disabled. This 556 * argument is for internal use only. 557 * 558 * \since QGIS 3.0 559 */ 560 static void invalidateCache( bool disableCache = false ); 561 #else 562 563 /** 564 * Clears the internal cache used to initialize QgsCoordinateTransform objects. 565 * This should be called whenever the srs database has 566 * been modified in order to ensure that outdated CRS transforms are not created. 567 * 568 * \since QGIS 3.0 569 */ 570 static void invalidateCache( bool disableCache SIP_PYARGREMOVE = false ); 571 #endif 572 573 /** 574 * Computes an *estimated* conversion factor between source and destination units: 575 * 576 * sourceUnits * scaleFactor = destinationUnits 577 * 578 * \param referenceExtent A reference extent based on which to perform the computation 579 * 580 * \since QGIS 3.4 581 */ 582 double scaleFactor( const QgsRectangle &referenceExtent ) const; 583 584 #ifdef SIP_RUN 585 SIP_PYOBJECT __repr__(); 586 % MethodCode 587 QString str = QStringLiteral( "<QgsCoordinateTransform: %1 to %2>" ).arg( sipCpp->sourceCrs().isValid() ? sipCpp->sourceCrs().authid() : QStringLiteral( "NULL" ), 588 sipCpp->destinationCrs().isValid() ? sipCpp->destinationCrs().authid() : QStringLiteral( "NULL" ) ); 589 sipRes = PyUnicode_FromString( str.toUtf8().constData() ); 590 % End 591 #endif 592 593 #ifndef SIP_RUN 594 595 /** 596 * Sets a custom handler to use when a coordinate transform is created between \a sourceCrs and 597 * \a destinationCrs, yet the coordinate operation requires a transform \a grid which is not present 598 * on the system. 599 * 600 * \see setCustomMissingPreferredGridHandler() 601 * \see setCustomCoordinateOperationCreationErrorHandler() 602 * \see setCustomMissingGridUsedByContextHandler() 603 * 604 * \note Not available in Python bindings 605 * \since QGIS 3.8 606 */ 607 static void setCustomMissingRequiredGridHandler( const std::function< void( const QgsCoordinateReferenceSystem &sourceCrs, 608 const QgsCoordinateReferenceSystem &destinationCrs, 609 const QgsDatumTransform::GridDetails &grid )> &handler ); 610 611 /** 612 * Sets a custom handler to use when a coordinate transform is created between \a sourceCrs and 613 * \a destinationCrs, yet a preferred (more accurate?) operation is available which could not 614 * be created on the system (e.g. due to missing transform grids). 615 * 616 * \a preferredOperation gives the details of the preferred coordinate operation, and 617 * \a availableOperation gives the details of the actual operation to be used during the 618 * transform. 619 * 620 * \see setCustomMissingRequiredGridHandler() 621 * \see setCustomCoordinateOperationCreationErrorHandler() 622 * \see setCustomMissingGridUsedByContextHandler() 623 * 624 * \note Not available in Python bindings 625 * \since QGIS 3.8 626 */ 627 static void setCustomMissingPreferredGridHandler( const std::function< void( const QgsCoordinateReferenceSystem &sourceCrs, 628 const QgsCoordinateReferenceSystem &destinationCrs, 629 const QgsDatumTransform::TransformDetails &preferredOperation, 630 const QgsDatumTransform::TransformDetails &availableOperation )> &handler ); 631 632 /** 633 * Sets a custom handler to use when a coordinate transform was required between \a sourceCrs and 634 * \a destinationCrs, yet the coordinate operation could not be created. The \a error argument 635 * specifies the error message obtained. 636 * 637 * \see setCustomMissingRequiredGridHandler() 638 * \see setCustomMissingPreferredGridHandler() 639 * \see setCustomMissingGridUsedByContextHandler() 640 * 641 * \note Not available in Python bindings 642 * \since QGIS 3.8 643 */ 644 static void setCustomCoordinateOperationCreationErrorHandler( const std::function< void( const QgsCoordinateReferenceSystem &sourceCrs, 645 const QgsCoordinateReferenceSystem &destinationCrs, 646 const QString &error )> &handler ); 647 648 /** 649 * Sets a custom handler to use when a coordinate operation was specified for use between \a sourceCrs and 650 * \a destinationCrs by the transform context, yet the coordinate operation could not be created. The \a desiredOperation argument 651 * specifies the desired transform details as specified by the context. 652 * 653 * \see setCustomMissingRequiredGridHandler() 654 * \see setCustomMissingPreferredGridHandler() 655 * \see setCustomCoordinateOperationCreationErrorHandler() 656 * 657 * \note Not available in Python bindings 658 * \since QGIS 3.8 659 */ 660 static void setCustomMissingGridUsedByContextHandler( const std::function< void( const QgsCoordinateReferenceSystem &sourceCrs, 661 const QgsCoordinateReferenceSystem &destinationCrs, 662 const QgsDatumTransform::TransformDetails &desiredOperation )> &handler ); 663 664 665 /** 666 * Sets a custom \a handler to use when the desired coordinate operation for use between \a sourceCrs and 667 * \a destinationCrs failed and an alternative fallback operation was utilized instead. 668 * 669 * \since QGIS 3.10.3 670 */ 671 static void setFallbackOperationOccurredHandler( const std::function< void( const QgsCoordinateReferenceSystem &sourceCrs, 672 const QgsCoordinateReferenceSystem &destinationCrs, 673 const QString &desiredOperation )> &handler ); 674 675 /** 676 * Sets a custom \a handler to use when the desired coordinate operation for use between \a sourceCrs and 677 * \a destinationCrs is a dynamic CRS to dynamic CRS transform, not currently supported by PROJ. 678 * 679 * \since QGIS 3.20 680 */ 681 static void setDynamicCrsToDynamicCrsWarningHandler( const std::function< void( const QgsCoordinateReferenceSystem &sourceCrs, 682 const QgsCoordinateReferenceSystem &destinationCrs )> &handler ); 683 684 #endif 685 686 private: 687 688 #ifndef SIP_RUN 689 friend class QgsProjContext; 690 691 // Only meant to be called by QgsProjContext::~QgsProjContext() 692 static void removeFromCacheObjectsBelongingToCurrentThread( void *pj_context ); 693 #endif 694 695 mutable QExplicitlySharedDataPointer<QgsCoordinateTransformPrivate> d; 696 697 //! Transform context 698 QgsCoordinateTransformContext mContext; 699 700 #ifdef QGISDEBUG 701 bool mHasContext = false; 702 #endif 703 704 mutable QString mLastError; 705 bool mBallparkTransformsAreAppropriate = false; 706 bool mDisableFallbackHandler = false; 707 mutable bool mFallbackOperationOccurred = false; 708 709 bool setFromCache( const QgsCoordinateReferenceSystem &src, 710 const QgsCoordinateReferenceSystem &dest, 711 const QString &coordinateOperationProj, bool allowFallback ); 712 713 void addToCache(); 714 715 // cache 716 static QReadWriteLock sCacheLock; 717 718 /** 719 * Map of cached transforms. The keys are formed by pairs of strings uniquely identifying the source and 720 * destination CRS, using the auth:id were available or a full WKT2 definition where an auth:id is not available. 721 * 722 * The same auth_id pairs might have multiple transformations, as they can be based on different coordinate 723 * operations, allowance of ballpark transforms, and the source or destination coordinate epoch. 724 */ 725 static QMultiHash< QPair< QString, QString >, QgsCoordinateTransform > sTransforms; 726 static bool sDisableCache; 727 728 729 static std::function< void( const QgsCoordinateReferenceSystem &sourceCrs, 730 const QgsCoordinateReferenceSystem &destinationCrs, 731 const QString &desiredOperation )> sFallbackOperationOccurredHandler; 732 733 }; 734 735 //! Output stream operator 736 #ifndef SIP_RUN 737 inline std::ostream &operator << ( std::ostream &os, const QgsCoordinateTransform &r ) 738 { 739 QString mySummary( QStringLiteral( "\n%%%%%%%%%%%%%%%%%%%%%%%%\nCoordinate Transform def begins:" ) ); 740 mySummary += QLatin1String( "\n\tInitialized? : " ); 741 //prevent warnings 742 if ( r.isValid() ) 743 { 744 //do nothing this is a dummy 745 } 746 747 #if 0 748 if ( r.isValid() ) 749 { 750 mySummary += "Yes"; 751 } 752 else 753 { 754 mySummary += "No"; 755 } 756 mySummary += "\n\tShort Circuit? : "; 757 if ( r.isShortCircuited() ) 758 { 759 mySummary += "Yes"; 760 } 761 else 762 { 763 mySummary += "No"; 764 } 765 766 mySummary += "\n\tSource Spatial Ref Sys : "; 767 if ( r.sourceCrs() ) 768 { 769 mySummary << r.sourceCrs(); 770 } 771 else 772 { 773 mySummary += "Undefined"; 774 } 775 776 mySummary += "\n\tDest Spatial Ref Sys : "; 777 if ( r.destCRS() ) 778 { 779 mySummary << r.destCRS(); 780 } 781 else 782 { 783 mySummary += "Undefined"; 784 } 785 #endif 786 787 mySummary += QLatin1String( "\nCoordinate Transform def ends \n%%%%%%%%%%%%%%%%%%%%%%%%\n" ); 788 return os << mySummary.toLocal8Bit().data() << std::endl; 789 } 790 #endif 791 792 793 #endif // QGSCOORDINATETRANSFORM_H 794