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