1 /***************************************************************************
2                          qgscoordinatetransformcontext.h
3                          -------------------------------
4     begin                : November 2017
5     copyright            : (C) 2017 by Nyall Dawson
6     email                : nyall dot dawson at gmail dot 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 
18 #ifndef QGSCOORDINATETRANSFORMCONTEXT_H
19 #define QGSCOORDINATETRANSFORMCONTEXT_H
20 
21 #include "qgis_core.h"
22 #include "qgis_sip.h"
23 #include "qgsdatumtransform.h"
24 
25 #include <QMetaType>
26 #include <QExplicitlySharedDataPointer>
27 class QgsCoordinateReferenceSystem;
28 class QgsReadWriteContext;
29 class QgsCoordinateTransformContextPrivate;
30 class QDomElement;
31 
32 /***************************************************************************
33  * This class is considered CRITICAL and any change MUST be accompanied with
34  * full unit tests in testqgsfeature.cpp.
35  * See details in QEP #17
36  ****************************************************************************/
37 
38 /**
39  * \class QgsCoordinateTransformContext
40  * \ingroup core
41  * \brief Contains information about the context in which a coordinate transform is executed.
42  *
43  * The context stores various information regarding which coordinate operations should
44  * be used when transforming points from a source to destination coordinate reference
45  * system.
46  *
47  * \note QgsCoordinateTransformContext objects are thread safe for read and write.
48  *
49  * \note QgsCoordinateTransformContext objects are implicitly shared.
50  *
51  * \see QgsDatumTransform
52  * \see QgsCoordinateTransform
53  *
54  * \since QGIS 3.0
55 */
56 
57 class CORE_EXPORT QgsCoordinateTransformContext
58 {
59   public:
60 
61     /**
62      * Constructor for QgsCoordinateTransformContext.
63      */
64     QgsCoordinateTransformContext();
65 
66     ~QgsCoordinateTransformContext() ;
67 
68     /**
69      * Copy constructor
70      */
71     QgsCoordinateTransformContext( const QgsCoordinateTransformContext &rhs );
72 
73     /**
74      * Assignment operator
75      */
76     QgsCoordinateTransformContext &operator=( const QgsCoordinateTransformContext &rhs ) SIP_SKIP;
77 
78     bool operator==( const QgsCoordinateTransformContext &rhs ) const ;
79 
80     /**
81      * Clears all stored transform information from the context.
82      */
83     void clear();
84 
85     /**
86      * Returns the stored mapping for source to destination CRS pairs to associated datum transforms to use.
87      * The map keys will be QgsCoordinateReferenceSystems::authid()s.
88      *
89      * If either the source transform ID or destination transform ID is -1, then no datum transform is
90      * required for transformations for that source or destination.
91      *
92      * \warning This method should not be used to calculate the corresponding datum transforms
93      * to use for a coordinate transform. Instead, always use calculateDatumTransforms()
94      * to determine this.
95      *
96      * \see addSourceDestinationDatumTransform()
97      *
98      * \deprecated Has no effect on builds based on Proj 6.0 or later, use coordinateOperations() instead.
99      */
100     Q_DECL_DEPRECATED QMap< QPair< QString, QString>, QgsDatumTransform::TransformPair > sourceDestinationDatumTransforms() const SIP_DEPRECATED;
101 
102     /**
103      * Returns the stored mapping for source to destination CRS pairs to associated coordinate operation to use
104      * (as a proj string). The map keys will be QgsCoordinateReferenceSystems::authid()s.
105      *
106      * \warning This method should not be used to calculate the corresponding coordinate operation
107      * to use for a coordinate transform. Instead, always use calculateCoordinateOperation()
108      * to determine this.
109      *
110      * \see addCoordinateOperation()
111      *
112      * \note Requires Proj 6.0 or later. Builds based on earlier Proj versions will always return an empty list,
113      * and the deprecated sourceDestinationDatumTransforms() method must be used instead.
114      *
115      * \since QGIS 3.8
116      */
117     QMap< QPair< QString, QString>, QString > coordinateOperations() const;
118 
119     /**
120      * Adds a new \a sourceTransform and \a destinationTransform to use when projecting coordinates
121      * from the specified \a sourceCrs to the specified \a destinationCrs.
122      *
123      * If either \a sourceTransformId or \a destinationTransformId is -1, then no datum transform is
124      * required for transformations for that source or destination.
125      *
126      * Returns TRUE if the new transform pair was added successfully.
127      *
128      * \see sourceDestinationDatumTransforms()
129      * \see removeSourceDestinationDatumTransform()
130      *
131      * \deprecated Has no effect on builds based on Proj 6.0 or later, use addCoordinateOperation() instead.
132      */
133     Q_DECL_DEPRECATED bool addSourceDestinationDatumTransform( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, int sourceTransformId, int destinationTransformId ) SIP_DEPRECATED;
134 
135     /**
136      * Adds a new \a coordinateOperationProjString to use when projecting coordinates
137      * from the specified \a sourceCrs to the specified \a destinationCrs.
138      *
139      * \a coordinateOperationProjString should be set to a valid Proj coordinate operation
140      * string. If \a coordinateOperationProjString is empty, then the default Proj operation
141      * will be used when transforming between the coordinate reference systems.
142      *
143      * If \a allowFallback is TRUE (since QGIS 3.12), then "ballpark" fallback transformations
144      * will be used in the case that the specified coordinate operation fails (such as when
145      * coordinates from outside a required grid shift file are transformed). See
146      * QgsCoordinateTransform::fallbackOperationOccurred() for further details. Note that if an
147      * existing \a sourceCrs and \a destinationCrs pair are added with a different \a allowFallback
148      * value, that value will replace the existing one (i.e. each combination of \a sourceCrs and
149      * \a destinationCrs must be unique).
150      *
151      * \warning coordinateOperationProjString MUST be a proj string which has been normalized for
152      * visualization, and must be constructed so that coordinates are always input and output
153      * with x/y coordinate ordering. (Proj strings output by utilities such as projinfo will NOT
154      * automatically normalize the axis order!).
155      *
156      * Returns TRUE if the new coordinate operation was added successfully.
157      *
158      * \see coordinateOperations()
159      * \see removeCoordinateOperation()
160      *
161      * \note Requires Proj 6.0 or later. Builds based on earlier Proj versions will ignore this setting,
162      * and the deprecated addSourceDestinationDatumTransform() method must be used instead.
163      *
164      * \since QGIS 3.8
165      */
166     bool addCoordinateOperation( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QString &coordinateOperationProjString, bool allowFallback = true );
167 
168     /**
169      * Removes the source to destination datum transform pair for the specified \a sourceCrs and
170      * \a destinationCrs.
171      * \see addSourceDestinationDatumTransform()
172      *
173      * \deprecated Use removeCoordinateOperation() instead
174      */
175     Q_DECL_DEPRECATED void removeSourceDestinationDatumTransform( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs ) SIP_DEPRECATED ;
176 
177     /**
178      * Removes the coordinate operation for the specified \a sourceCrs and \a destinationCrs.
179      *
180      * \since QGIS 3.8
181      */
182     void removeCoordinateOperation( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs );
183 
184     /**
185      * Returns TRUE if the context has a valid coordinate operation to use
186      * when transforming from the specified \a source CRS to \a destination CRS.
187      * \note source and destination are reversible.
188      */
189     bool hasTransform( const QgsCoordinateReferenceSystem &source,
190                        const QgsCoordinateReferenceSystem &destination ) const;
191 
192     /**
193      * Returns the pair of source and destination datum transforms to use
194      * for a transform from the specified \a source CRS to \a destination CRS.
195      *
196      * Returns an ID of -1 if a datum transform should not be used for the source or
197      * destination.
198      *
199      * \note source and destination are reversible.
200      * \deprecated Has no effect on builds based on Proj 6.0 or later. Use calculateCoordinateOperation() instead.
201      */
202     Q_DECL_DEPRECATED QgsDatumTransform::TransformPair calculateDatumTransforms( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination ) const SIP_DEPRECATED;
203 
204     /**
205      * Returns the Proj coordinate operation string to use when transforming
206      * from the specified \a source CRS to \a destination CRS.
207      *
208      * Returns an empty string if no specific coordinate operation is set for the source to
209      * destination pair, in which case the default Proj coordinate operation should
210      * be used.
211      *
212      * \note source and destination are reversible.
213      *
214      * \note Requires Proj 6.0 or later. Builds based on earlier Proj versions will always return
215      * an empty string, and the deprecated calculateDatumTransforms() method should be used instead.
216      *
217      * \warning Always check the result of mustReverseCoordinateOperation() in order to determine if the
218      * proj coordinate operation string returned by this method corresponds to the reverse operation, and
219      * must be manually flipped when calculating coordinate transforms.
220      *
221      * \since QGIS 3.8
222      */
223     QString calculateCoordinateOperation( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination ) const;
224 
225     /**
226      * Returns TRUE if approximate "ballpark" transforms may be used when transforming
227      * between a \a source and \a destination CRS pair, in the case that the preferred
228      * coordinate operation fails (such as when
229      * coordinates from outside a required grid shift file are transformed). See
230      * QgsCoordinateTransform::fallbackOperationOccurred() for further details.
231      *
232      * \since QGIS 3.12
233      */
234     bool allowFallbackTransform( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination ) const;
235 
236     /**
237      * Returns TRUE if the coordinate operation returned by calculateCoordinateOperation() for the \a source to \a destination pair
238      * must be inverted.
239      *
240      * \since QGIS 3.10.2
241      */
242     bool mustReverseCoordinateOperation( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination ) const;
243 
244     // TODO QGIS 4.0 - remove missingTransforms, not used for Proj >= 6.0 builds
245 
246     /**
247      * Reads the context's state from a DOM \a element.
248      *
249      * Returns FALSE if transforms stored in the XML are not available. In this case \a missingTransforms will be
250      * filled with missing datum transform strings.
251      *
252      * \see writeXml()
253      */
254     bool readXml( const QDomElement &element, const QgsReadWriteContext &context, QStringList &missingTransforms SIP_OUT );
255 
256     /**
257      * Writes the context's state to a DOM \a element.
258      * \see readXml()
259      */
260     void writeXml( QDomElement &element, const QgsReadWriteContext &context ) const;
261 
262 
263     /**
264      * Reads the context's state from application settings.
265      * \see readSettings()
266      */
267     void readSettings();
268 
269     /**
270      * Write the context's state to application settings.
271      * \see writeSettings()
272      */
273     void writeSettings();
274 
275 
276   private:
277 
278     QExplicitlySharedDataPointer<QgsCoordinateTransformContextPrivate> d;
279 
280 };
281 
282 Q_DECLARE_METATYPE( QgsCoordinateTransformContext )
283 
284 #endif // QGSCOORDINATETRANSFORMCONTEXT_H
285 
286 
287 
288 
289