QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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
20#include "qgssettings.h"
21#include "qgsprojutils.h"
22
24{
25 return crs.authid().isEmpty() ? crs.toWkt( Qgis::CrsWktVariant::Preferred ) : crs.authid();
26}
27
28#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
29template<>
30bool qMapLessThanKey<QPair<QgsCoordinateReferenceSystem, QgsCoordinateReferenceSystem>>( const QPair<QgsCoordinateReferenceSystem, QgsCoordinateReferenceSystem> &key1,
31 const QPair<QgsCoordinateReferenceSystem, QgsCoordinateReferenceSystem> &key2 )
32{
33 const QPair< QString, QString > key1String = qMakePair( crsToKey( key1.first ), crsToKey( key1.second ) );
34 const QPair< QString, QString > key2String = qMakePair( crsToKey( key2.first ), crsToKey( key2.second ) );
35 return key1String < key2String;
36}
37#endif
38
40 : d( new QgsCoordinateTransformContextPrivate() )
41{}
42
44
46 : d( rhs.d )
47{}
48
50{
51 d = rhs.d;
52 return *this;
53}
54
56{
57 if ( d == rhs.d )
58 return true;
59
60 d->mLock.lockForRead();
61 rhs.d->mLock.lockForRead();
62 const bool equal = d->mSourceDestDatumTransforms == rhs.d->mSourceDestDatumTransforms;
63 d->mLock.unlock();
64 rhs.d->mLock.unlock();
65 return equal;
66}
67
69{
70 d.detach();
71 // play it safe
72 d->mLock.lockForWrite();
73 d->mSourceDestDatumTransforms.clear();
74 d->mLock.unlock();
75}
76
78{
79 return QMap<QPair<QString, QString>, QgsDatumTransform::TransformPair>();
80}
81
82QMap<QPair<QString, QString>, QString> QgsCoordinateTransformContext::coordinateOperations() const
83{
84 d->mLock.lockForRead();
85 auto res = d->mSourceDestDatumTransforms;
86 res.detach();
87 d->mLock.unlock();
88 QMap<QPair<QString, QString>, QString> results;
89 for ( auto it = res.constBegin(); it != res.constEnd(); ++it )
90 results.insert( qMakePair( it.key().first.authid(), it.key().second.authid() ), it.value().operation );
91
92 return results;
93}
94
95bool QgsCoordinateTransformContext::addSourceDestinationDatumTransform( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, int sourceTransform, int destinationTransform )
96{
97 if ( !sourceCrs.isValid() || !destinationCrs.isValid() )
98 return false;
99 Q_UNUSED( sourceTransform )
100 Q_UNUSED( destinationTransform )
101 return false;
102}
103
104bool QgsCoordinateTransformContext::addCoordinateOperation( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QString &coordinateOperationProjString, bool allowFallback )
105{
106 if ( !sourceCrs.isValid() || !destinationCrs.isValid() )
107 return false;
108 d.detach();
109 d->mLock.lockForWrite();
110 QgsCoordinateTransformContextPrivate::OperationDetails details;
111 details.operation = coordinateOperationProjString;
112 details.allowFallback = allowFallback;
113 d->mSourceDestDatumTransforms.insert( qMakePair( sourceCrs, destinationCrs ), details );
114 d->mLock.unlock();
115 return true;
116}
117
119{
120 removeCoordinateOperation( sourceCrs, destinationCrs );
121}
122
124{
125 d->mSourceDestDatumTransforms.remove( qMakePair( sourceCrs, destinationCrs ) );
126}
127
129{
130 const QString t = calculateCoordinateOperation( source, destination );
131 return !t.isEmpty();
132}
133
135{
136 Q_UNUSED( source )
137 Q_UNUSED( destination )
138 return QgsDatumTransform::TransformPair( -1, -1 );
139}
140
142{
143 if ( !source.isValid() || !destination.isValid() )
144 return QString();
145
146 d->mLock.lockForRead();
147
148 auto it = d->mSourceDestDatumTransforms.constFind( qMakePair( source, destination ) );
149 if ( it == d->mSourceDestDatumTransforms.constEnd() )
150 {
151 // try to reverse
152 it = d->mSourceDestDatumTransforms.constFind( qMakePair( destination, source ) );
153 }
154
155 const QString result = it == d->mSourceDestDatumTransforms.constEnd() ? QString() : it.value().operation;
156 d->mLock.unlock();
157 return result;
158}
159
161{
162 if ( !source.isValid() || !destination.isValid() )
163 return false;
164
165 d->mLock.lockForRead();
166 QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( source, destination ), QgsCoordinateTransformContextPrivate::OperationDetails() );
167 if ( res.operation.isEmpty() )
168 {
169 // try to reverse
170 res = d->mSourceDestDatumTransforms.value( qMakePair( destination, source ), QgsCoordinateTransformContextPrivate::OperationDetails() );
171 }
172 d->mLock.unlock();
173 return res.allowFallback;
174}
175
177{
178 if ( !source.isValid() || !destination.isValid() )
179 return false;
180
181 d->mLock.lockForRead();
182 QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( source, destination ), QgsCoordinateTransformContextPrivate::OperationDetails() );
183 if ( !res.operation.isEmpty() )
184 {
185 d->mLock.unlock();
186 return false;
187 }
188 // see if the reverse operation is present
189 res = d->mSourceDestDatumTransforms.value( qMakePair( destination, source ), QgsCoordinateTransformContextPrivate::OperationDetails() );
190 if ( !res.operation.isEmpty() )
191 {
192 d->mLock.unlock();
193 return true;
194 }
195
196 d->mLock.unlock();
197 return false;
198}
199
200bool QgsCoordinateTransformContext::readXml( const QDomElement &element, const QgsReadWriteContext &, QStringList &missingTransforms )
201{
202 d.detach();
203 d->mLock.lockForWrite();
204
205 d->mSourceDestDatumTransforms.clear();
206
207 const QDomNodeList contextNodes = element.elementsByTagName( QStringLiteral( "transformContext" ) );
208 if ( contextNodes.count() < 1 )
209 {
210 d->mLock.unlock();
211 return true;
212 }
213
214 missingTransforms.clear();
215 bool result = true;
216
217 const QDomElement contextElem = contextNodes.at( 0 ).toElement();
218
219 // src/dest transforms
220 const QDomNodeList srcDestNodes = contextElem.elementsByTagName( QStringLiteral( "srcDest" ) );
221 for ( int i = 0; i < srcDestNodes.size(); ++i )
222 {
223 const QDomElement transformElem = srcDestNodes.at( i ).toElement();
224
225 const QDomElement srcElem = transformElem.firstChildElement( QStringLiteral( "src" ) );
226 const QDomElement destElem = transformElem.firstChildElement( QStringLiteral( "dest" ) );
227
230 if ( !srcElem.isNull() && !destElem.isNull() )
231 {
232 srcCrs.readXml( srcElem );
233 destCrs.readXml( destElem );
234 }
235 else
236 {
237 // for older project compatibility
238 const QString key1 = transformElem.attribute( QStringLiteral( "source" ) );
239 const QString key2 = transformElem.attribute( QStringLiteral( "dest" ) );
240 srcCrs = QgsCoordinateReferenceSystem( key1 );
241 destCrs = QgsCoordinateReferenceSystem( key2 );
242 }
243
244 if ( !srcCrs.isValid() || !destCrs.isValid() )
245 continue;
246
247 const QString coordinateOp = transformElem.attribute( QStringLiteral( "coordinateOp" ) );
248 const bool allowFallback = transformElem.attribute( QStringLiteral( "allowFallback" ), QStringLiteral( "1" ) ).toInt();
249
250 // try to instantiate operation, and check for missing grids
252 {
253 // not possible in current Proj 6 api!
254 // QgsCoordinateTransform will alert users to this, we don't need to use missingTransforms here
255 result = false;
256 }
257
258 QgsCoordinateTransformContextPrivate::OperationDetails deets;
259 deets.operation = coordinateOp;
260 deets.allowFallback = allowFallback;
261 d->mSourceDestDatumTransforms.insert( qMakePair( srcCrs, destCrs ), deets );
262 }
263
264 d->mLock.unlock();
265 return result;
266}
267
268void QgsCoordinateTransformContext::writeXml( QDomElement &element, const QgsReadWriteContext & ) const
269{
270 d->mLock.lockForRead();
271
272 QDomDocument doc = element.ownerDocument();
273
274 QDomElement contextElem = doc.createElement( QStringLiteral( "transformContext" ) );
275
276 //src/dest transforms
277 for ( auto it = d->mSourceDestDatumTransforms.constBegin(); it != d->mSourceDestDatumTransforms.constEnd(); ++ it )
278 {
279 QDomElement transformElem = doc.createElement( QStringLiteral( "srcDest" ) );
280 QDomElement srcElem = doc.createElement( QStringLiteral( "src" ) );
281 QDomElement destElem = doc.createElement( QStringLiteral( "dest" ) );
282
283 it.key().first.writeXml( srcElem, doc );
284 it.key().second.writeXml( destElem, doc );
285
286 transformElem.appendChild( srcElem );
287 transformElem.appendChild( destElem );
288
289 transformElem.setAttribute( QStringLiteral( "coordinateOp" ), it.value().operation );
290 transformElem.setAttribute( QStringLiteral( "allowFallback" ), it.value().allowFallback ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
291 contextElem.appendChild( transformElem );
292 }
293
294 element.appendChild( contextElem );
295 d->mLock.unlock();
296}
297
299{
300 d.detach();
301 d->mLock.lockForWrite();
302
303 d->mSourceDestDatumTransforms.clear();
304
305 QgsSettings settings;
306 settings.beginGroup( QStringLiteral( "/Projections" ) );
307 const QStringList projectionKeys = settings.allKeys();
308
309 //collect src and dest entries that belong together
310 QMap< QPair< QgsCoordinateReferenceSystem, QgsCoordinateReferenceSystem >, QgsCoordinateTransformContextPrivate::OperationDetails > transforms;
311 QStringList::const_iterator pkeyIt = projectionKeys.constBegin();
312 for ( ; pkeyIt != projectionKeys.constEnd(); ++pkeyIt )
313 {
314 if ( pkeyIt->contains( QLatin1String( "coordinateOp" ) ) )
315 {
316 const QStringList split = pkeyIt->split( '/' );
317 QString srcAuthId, destAuthId;
318 if ( ! split.isEmpty() )
319 {
320 srcAuthId = split.at( 0 );
321 }
322 if ( split.size() > 1 )
323 {
324 destAuthId = split.at( 1 ).split( '_' ).at( 0 );
325 }
326
327 if ( srcAuthId.isEmpty() || destAuthId.isEmpty() )
328 continue;
329
330 const QString proj = settings.value( *pkeyIt ).toString();
331 const bool allowFallback = settings.value( QStringLiteral( "%1//%2_allowFallback" ).arg( srcAuthId, destAuthId ) ).toBool();
332 QgsCoordinateTransformContextPrivate::OperationDetails deets;
333 deets.operation = proj;
334 deets.allowFallback = allowFallback;
335 transforms[ qMakePair( QgsCoordinateReferenceSystem( srcAuthId ), QgsCoordinateReferenceSystem( destAuthId ) )] = deets;
336 }
337 }
338
339 // add transforms to context
340 auto transformIt = transforms.constBegin();
341 for ( ; transformIt != transforms.constEnd(); ++transformIt )
342 {
343 d->mSourceDestDatumTransforms.insert( transformIt.key(), transformIt.value() );
344 }
345
346 d->mLock.unlock();
347 settings.endGroup();
348}
349
351{
352 QgsSettings settings;
353 settings.beginGroup( QStringLiteral( "/Projections" ) );
354 const QStringList groupKeys = settings.allKeys();
355 QStringList::const_iterator groupKeyIt = groupKeys.constBegin();
356 for ( ; groupKeyIt != groupKeys.constEnd(); ++groupKeyIt )
357 {
358 if ( groupKeyIt->contains( QLatin1String( "srcTransform" ) ) || groupKeyIt->contains( QLatin1String( "destTransform" ) ) || groupKeyIt->contains( QLatin1String( "coordinateOp" ) ) )
359 {
360 settings.remove( *groupKeyIt );
361 }
362 }
363
364 for ( auto transformIt = d->mSourceDestDatumTransforms.constBegin(); transformIt != d->mSourceDestDatumTransforms.constEnd(); ++transformIt )
365 {
366 const QString srcAuthId = transformIt.key().first.authid();
367 const QString destAuthId = transformIt.key().second.authid();
368
369 if ( srcAuthId.isEmpty() || destAuthId.isEmpty() )
370 continue; // not so nice, but alternative would be to shove whole CRS wkt into the settings values...
371
372 const QString proj = transformIt.value().operation;
373 const bool allowFallback = transformIt.value().allowFallback;
374 settings.setValue( srcAuthId + "//" + destAuthId + "_coordinateOp", proj );
375 settings.setValue( srcAuthId + "//" + destAuthId + "_allowFallback", allowFallback );
376 }
377
378 settings.endGroup();
379}
@ Preferred
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
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.
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, 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:64
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:92
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.