QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgscoordinatetransformcontext.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgscoordinatetransformcontext.cpp
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
21#include "qgssettings.h"
22#include "qgsprojutils.h"
23
25{
27}
28
29#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
30template<>
31bool qMapLessThanKey<QPair<QgsCoordinateReferenceSystem, QgsCoordinateReferenceSystem>>( const QPair<QgsCoordinateReferenceSystem, QgsCoordinateReferenceSystem> &key1,
32 const QPair<QgsCoordinateReferenceSystem, QgsCoordinateReferenceSystem> &key2 )
33{
34 const QPair< QString, QString > key1String = qMakePair( crsToKey( key1.first ), crsToKey( key1.second ) );
35 const QPair< QString, QString > key2String = qMakePair( crsToKey( key2.first ), crsToKey( key2.second ) );
36 return key1String < key2String;
37}
38#endif
39
41 : d( new QgsCoordinateTransformContextPrivate() )
42{}
43
45
47 : d( rhs.d )
48{}
49
51{
52 d = rhs.d;
53 return *this;
54}
55
57{
58 if ( d == rhs.d )
59 return true;
60
61 d->mLock.lockForRead();
62 rhs.d->mLock.lockForRead();
63 const bool equal = d->mSourceDestDatumTransforms == rhs.d->mSourceDestDatumTransforms;
64 d->mLock.unlock();
65 rhs.d->mLock.unlock();
66 return equal;
67}
68
70{
71 d.detach();
72 // play it safe
73 d->mLock.lockForWrite();
74 d->mSourceDestDatumTransforms.clear();
75 d->mLock.unlock();
76}
77
79{
80 return QMap<QPair<QString, QString>, QgsDatumTransform::TransformPair>();
81}
82
83QMap<QPair<QString, QString>, QString> QgsCoordinateTransformContext::coordinateOperations() const
84{
85 d->mLock.lockForRead();
86 auto res = d->mSourceDestDatumTransforms;
87 res.detach();
88 d->mLock.unlock();
89 QMap<QPair<QString, QString>, QString> results;
90 for ( auto it = res.constBegin(); it != res.constEnd(); ++it )
91 results.insert( qMakePair( it.key().first.authid(), it.key().second.authid() ), it.value().operation );
92
93 return results;
94}
95
96bool QgsCoordinateTransformContext::addSourceDestinationDatumTransform( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, int sourceTransform, int destinationTransform )
97{
98 if ( !sourceCrs.isValid() || !destinationCrs.isValid() )
99 return false;
100 Q_UNUSED( sourceTransform )
101 Q_UNUSED( destinationTransform )
102 return false;
103}
104
105bool QgsCoordinateTransformContext::addCoordinateOperation( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QString &coordinateOperationProjString, bool allowFallback )
106{
107 if ( !sourceCrs.isValid() || !destinationCrs.isValid() )
108 return false;
109 d.detach();
110 d->mLock.lockForWrite();
111 QgsCoordinateTransformContextPrivate::OperationDetails details;
112 details.operation = coordinateOperationProjString;
113 details.allowFallback = allowFallback;
114 d->mSourceDestDatumTransforms.insert( qMakePair( sourceCrs, destinationCrs ), details );
115 d->mLock.unlock();
116 return true;
117}
118
120{
121 removeCoordinateOperation( sourceCrs, destinationCrs );
122}
123
125{
126 d->mSourceDestDatumTransforms.remove( qMakePair( sourceCrs, destinationCrs ) );
127}
128
130{
131 const QString t = calculateCoordinateOperation( source, destination );
132 return !t.isEmpty();
133}
134
136{
137 Q_UNUSED( source )
138 Q_UNUSED( destination )
139 return QgsDatumTransform::TransformPair( -1, -1 );
140}
141
143{
144 if ( !source.isValid() || !destination.isValid() )
145 return QString();
146
147 d->mLock.lockForRead();
148 QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( source, destination ), QgsCoordinateTransformContextPrivate::OperationDetails() );
149 if ( res.operation.isEmpty() )
150 {
151 // try to reverse
152 res = d->mSourceDestDatumTransforms.value( qMakePair( destination, source ), QgsCoordinateTransformContextPrivate::OperationDetails() );
153 }
154 d->mLock.unlock();
155 return res.operation;
156}
157
159{
160 if ( !source.isValid() || !destination.isValid() )
161 return false;
162
163 d->mLock.lockForRead();
164 QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( source, destination ), QgsCoordinateTransformContextPrivate::OperationDetails() );
165 if ( res.operation.isEmpty() )
166 {
167 // try to reverse
168 res = d->mSourceDestDatumTransforms.value( qMakePair( destination, source ), QgsCoordinateTransformContextPrivate::OperationDetails() );
169 }
170 d->mLock.unlock();
171 return res.allowFallback;
172}
173
175{
176 if ( !source.isValid() || !destination.isValid() )
177 return false;
178
179 d->mLock.lockForRead();
180 QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( source, destination ), QgsCoordinateTransformContextPrivate::OperationDetails() );
181 if ( !res.operation.isEmpty() )
182 {
183 d->mLock.unlock();
184 return false;
185 }
186 // see if the reverse operation is present
187 res = d->mSourceDestDatumTransforms.value( qMakePair( destination, source ), QgsCoordinateTransformContextPrivate::OperationDetails() );
188 if ( !res.operation.isEmpty() )
189 {
190 d->mLock.unlock();
191 return true;
192 }
193
194 d->mLock.unlock();
195 return false;
196}
197
198bool QgsCoordinateTransformContext::readXml( const QDomElement &element, const QgsReadWriteContext &, QStringList &missingTransforms )
199{
200 d.detach();
201 d->mLock.lockForWrite();
202
203 d->mSourceDestDatumTransforms.clear();
204
205 const QDomNodeList contextNodes = element.elementsByTagName( QStringLiteral( "transformContext" ) );
206 if ( contextNodes.count() < 1 )
207 {
208 d->mLock.unlock();
209 return true;
210 }
211
212 missingTransforms.clear();
213 bool result = true;
214
215 const QDomElement contextElem = contextNodes.at( 0 ).toElement();
216
217 // src/dest transforms
218 const QDomNodeList srcDestNodes = contextElem.elementsByTagName( QStringLiteral( "srcDest" ) );
219 for ( int i = 0; i < srcDestNodes.size(); ++i )
220 {
221 const QDomElement transformElem = srcDestNodes.at( i ).toElement();
222
223 const QDomElement srcElem = transformElem.firstChildElement( QStringLiteral( "src" ) );
224 const QDomElement destElem = transformElem.firstChildElement( QStringLiteral( "dest" ) );
225
228 if ( !srcElem.isNull() && !destElem.isNull() )
229 {
230 srcCrs.readXml( srcElem );
231 destCrs.readXml( destElem );
232 }
233 else
234 {
235 // for older project compatibility
236 const QString key1 = transformElem.attribute( QStringLiteral( "source" ) );
237 const QString key2 = transformElem.attribute( QStringLiteral( "dest" ) );
238 srcCrs = QgsCoordinateReferenceSystem( key1 );
239 destCrs = QgsCoordinateReferenceSystem( key2 );
240 }
241
242 if ( !srcCrs.isValid() || !destCrs.isValid() )
243 continue;
244
245 const QString coordinateOp = transformElem.attribute( QStringLiteral( "coordinateOp" ) );
246 const bool allowFallback = transformElem.attribute( QStringLiteral( "allowFallback" ), QStringLiteral( "1" ) ).toInt();
247
248 // try to instantiate operation, and check for missing grids
250 {
251 // not possible in current Proj 6 api!
252 // QgsCoordinateTransform will alert users to this, we don't need to use missingTransforms here
253 result = false;
254 }
255
256 QgsCoordinateTransformContextPrivate::OperationDetails deets;
257 deets.operation = coordinateOp;
258 deets.allowFallback = allowFallback;
259 d->mSourceDestDatumTransforms.insert( qMakePair( srcCrs, destCrs ), deets );
260 }
261
262 d->mLock.unlock();
263 return result;
264}
265
266void QgsCoordinateTransformContext::writeXml( QDomElement &element, const QgsReadWriteContext & ) const
267{
268 d->mLock.lockForRead();
269
270 QDomDocument doc = element.ownerDocument();
271
272 QDomElement contextElem = doc.createElement( QStringLiteral( "transformContext" ) );
273
274 //src/dest transforms
275 for ( auto it = d->mSourceDestDatumTransforms.constBegin(); it != d->mSourceDestDatumTransforms.constEnd(); ++ it )
276 {
277 QDomElement transformElem = doc.createElement( QStringLiteral( "srcDest" ) );
278 QDomElement srcElem = doc.createElement( QStringLiteral( "src" ) );
279 QDomElement destElem = doc.createElement( QStringLiteral( "dest" ) );
280
281 it.key().first.writeXml( srcElem, doc );
282 it.key().second.writeXml( destElem, doc );
283
284 transformElem.appendChild( srcElem );
285 transformElem.appendChild( destElem );
286
287 transformElem.setAttribute( QStringLiteral( "coordinateOp" ), it.value().operation );
288 transformElem.setAttribute( QStringLiteral( "allowFallback" ), it.value().allowFallback ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
289 contextElem.appendChild( transformElem );
290 }
291
292 element.appendChild( contextElem );
293 d->mLock.unlock();
294}
295
297{
298 d.detach();
299 d->mLock.lockForWrite();
300
301 d->mSourceDestDatumTransforms.clear();
302
303 QgsSettings settings;
304 settings.beginGroup( QStringLiteral( "/Projections" ) );
305 const QStringList projectionKeys = settings.allKeys();
306
307 //collect src and dest entries that belong together
308 QMap< QPair< QgsCoordinateReferenceSystem, QgsCoordinateReferenceSystem >, QgsCoordinateTransformContextPrivate::OperationDetails > transforms;
309 QStringList::const_iterator pkeyIt = projectionKeys.constBegin();
310 for ( ; pkeyIt != projectionKeys.constEnd(); ++pkeyIt )
311 {
312 if ( pkeyIt->contains( QLatin1String( "coordinateOp" ) ) )
313 {
314 const QStringList split = pkeyIt->split( '/' );
315 QString srcAuthId, destAuthId;
316 if ( ! split.isEmpty() )
317 {
318 srcAuthId = split.at( 0 );
319 }
320 if ( split.size() > 1 )
321 {
322 destAuthId = split.at( 1 ).split( '_' ).at( 0 );
323 }
324
325 if ( srcAuthId.isEmpty() || destAuthId.isEmpty() )
326 continue;
327
328 const QString proj = settings.value( *pkeyIt ).toString();
329 const bool allowFallback = settings.value( QStringLiteral( "%1//%2_allowFallback" ).arg( srcAuthId, destAuthId ) ).toBool();
330 QgsCoordinateTransformContextPrivate::OperationDetails deets;
331 deets.operation = proj;
332 deets.allowFallback = allowFallback;
333 transforms[ qMakePair( QgsCoordinateReferenceSystem( srcAuthId ), QgsCoordinateReferenceSystem( destAuthId ) )] = deets;
334 }
335 }
336
337 // add transforms to context
338 auto transformIt = transforms.constBegin();
339 for ( ; transformIt != transforms.constEnd(); ++transformIt )
340 {
341 d->mSourceDestDatumTransforms.insert( transformIt.key(), transformIt.value() );
342 }
343
344 d->mLock.unlock();
345 settings.endGroup();
346}
347
349{
350 QgsSettings settings;
351 settings.beginGroup( QStringLiteral( "/Projections" ) );
352 const QStringList groupKeys = settings.allKeys();
353 QStringList::const_iterator groupKeyIt = groupKeys.constBegin();
354 for ( ; groupKeyIt != groupKeys.constEnd(); ++groupKeyIt )
355 {
356 if ( groupKeyIt->contains( QLatin1String( "srcTransform" ) ) || groupKeyIt->contains( QLatin1String( "destTransform" ) ) || groupKeyIt->contains( QLatin1String( "coordinateOp" ) ) )
357 {
358 settings.remove( *groupKeyIt );
359 }
360 }
361
362 for ( auto transformIt = d->mSourceDestDatumTransforms.constBegin(); transformIt != d->mSourceDestDatumTransforms.constEnd(); ++transformIt )
363 {
364 const QString srcAuthId = transformIt.key().first.authid();
365 const QString destAuthId = transformIt.key().second.authid();
366
367 if ( srcAuthId.isEmpty() || destAuthId.isEmpty() )
368 continue; // not so nice, but alternative would be to shove whole CRS wkt into the settings values...
369
370 const QString proj = transformIt.value().operation;
371 const bool allowFallback = transformIt.value().allowFallback;
372 settings.setValue( srcAuthId + "//" + destAuthId + "_coordinateOp", proj );
373 settings.setValue( srcAuthId + "//" + destAuthId + "_allowFallback", allowFallback );
374 }
375
376 settings.endGroup();
377}
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
Contains information about the context in which a coordinate transform is executed.
void clear()
Clears all stored transform information from the context.
bool allowFallbackTransform(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination) const
Returns true if approximate "ballpark" transforms may be used when transforming between a source and ...
QString calculateCoordinateOperation(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination) const
Returns the Proj coordinate operation string to use when transforming from the specified source CRS t...
void readSettings()
Reads the context's state from application settings.
void writeSettings()
Write the context's state to application settings.
Q_DECL_DEPRECATED bool addSourceDestinationDatumTransform(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, int sourceTransformId, int destinationTransformId)
Adds a new sourceTransform and destinationTransform to use when projecting coordinates from the speci...
QMap< QPair< QString, QString >, QString > coordinateOperations() const
Returns the stored mapping for source to destination CRS pairs to associated coordinate operation to ...
void writeXml(QDomElement &element, const QgsReadWriteContext &context) const
Writes the context's state to a DOM element.
Q_DECL_DEPRECATED QgsDatumTransform::TransformPair calculateDatumTransforms(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination) const
Returns the pair of source and destination datum transforms to use for a transform from the specified...
Q_DECL_DEPRECATED QMap< QPair< QString, QString >, QgsDatumTransform::TransformPair > sourceDestinationDatumTransforms() const
Returns the stored mapping for source to destination CRS pairs to associated datum transforms to use.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context, QStringList &missingTransforms)
Reads the context's state from a DOM element.
bool addCoordinateOperation(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QString &coordinateOperationProjString, bool allowFallback=true)
Adds a new coordinateOperationProjString to use when projecting coordinates from the specified source...
void removeCoordinateOperation(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs)
Removes the coordinate operation for the specified sourceCrs and destinationCrs.
QgsCoordinateTransformContext()
Constructor for QgsCoordinateTransformContext.
bool hasTransform(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination) const
Returns true if the context has a valid coordinate operation to use when transforming from the specif...
bool mustReverseCoordinateOperation(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination) const
Returns true if the coordinate operation returned by calculateCoordinateOperation() for the source to...
bool operator==(const QgsCoordinateTransformContext &rhs) const
Q_DECL_DEPRECATED void removeSourceDestinationDatumTransform(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs)
Removes the source to destination datum transform pair for the specified sourceCrs and destinationCrs...
QgsCoordinateTransformContext & operator=(const QgsCoordinateTransformContext &rhs)
Assignment operator.
static bool coordinateOperationIsAvailable(const QString &projDef)
Returns true if a coordinate operation (specified via proj string) is available.
The class is used as a container of context for various read/write operations on other objects.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
void endGroup()
Resets the group to what it was before the corresponding beginGroup() call.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void beginGroup(const QString &prefix, QgsSettings::Section section=QgsSettings::NoSection)
Appends prefix to the current group.
Definition: qgssettings.cpp:90
void remove(const QString &key, QgsSettings::Section section=QgsSettings::NoSection)
Removes the setting key and any sub-settings of key in a section.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
QStringList allKeys() const
Returns a list of all keys, including subkeys, that can be read using the QSettings object.
QString crsToKey(const QgsCoordinateReferenceSystem &crs)
const QgsCoordinateReferenceSystem & crs
Contains datum transform information.