QGIS API Documentation 3.99.0-Master (21b3aa880ba)
Loading...
Searching...
No Matches
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
19
21#include "qgsprojutils.h"
22#include "qgssettings.h"
23
25{
26 return crs.authid().isEmpty() ? crs.toWkt( Qgis::CrsWktVariant::Preferred ) : crs.authid();
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
49
53
54
56{
57 if ( &rhs == this )
58 return *this;
59
60 d = rhs.d;
61 return *this;
62}
63
65{
66 if ( &rhs == this )
67 return *this;
68
69 d = std::move( rhs.d );
70 return *this;
71}
72
74{
75 if ( d == rhs.d )
76 return true;
77
78 d->mLock.lockForRead();
79 rhs.d->mLock.lockForRead();
80 const bool equal = d->mSourceDestDatumTransforms == rhs.d->mSourceDestDatumTransforms;
81 d->mLock.unlock();
82 rhs.d->mLock.unlock();
83 return equal;
84}
85
87{
88 return !( *this == rhs );
89}
90
92{
93 d.detach();
94 // play it safe
95 d->mLock.lockForWrite();
96 d->mSourceDestDatumTransforms.clear();
97 d->mLock.unlock();
98}
99
101{
102 return QMap<QPair<QString, QString>, QgsDatumTransform::TransformPair>();
103}
104
105QMap<QPair<QString, QString>, QString> QgsCoordinateTransformContext::coordinateOperations() const
106{
107 d->mLock.lockForRead();
108 auto res = d->mSourceDestDatumTransforms;
109 res.detach();
110 d->mLock.unlock();
111 QMap<QPair<QString, QString>, QString> results;
112 for ( auto it = res.constBegin(); it != res.constEnd(); ++it )
113 results.insert( qMakePair( it.key().first.authid(), it.key().second.authid() ), it.value().operation );
114
115 return results;
116}
117
118bool QgsCoordinateTransformContext::addSourceDestinationDatumTransform( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, int sourceTransform, int destinationTransform )
119{
120 if ( !sourceCrs.isValid() || !destinationCrs.isValid() )
121 return false;
122 Q_UNUSED( sourceTransform )
123 Q_UNUSED( destinationTransform )
124 return false;
125}
126
127bool QgsCoordinateTransformContext::addCoordinateOperation( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QString &coordinateOperationProjString, bool allowFallback )
128{
129 if ( !sourceCrs.isValid() || !destinationCrs.isValid() )
130 return false;
131 d.detach();
132 d->mLock.lockForWrite();
133 QgsCoordinateTransformContextPrivate::OperationDetails details;
134 details.operation = coordinateOperationProjString;
135 details.allowFallback = allowFallback;
136 d->mSourceDestDatumTransforms.insert( qMakePair( sourceCrs, destinationCrs ), details );
137 d->mLock.unlock();
138 return true;
139}
140
145
147{
148 d->mSourceDestDatumTransforms.remove( qMakePair( sourceCrs, destinationCrs ) );
149}
150
152{
153 const QString t = calculateCoordinateOperation( source, destination );
154 return !t.isEmpty();
155}
156
158{
159 Q_UNUSED( source )
160 Q_UNUSED( destination )
161 return QgsDatumTransform::TransformPair( -1, -1 );
162}
163
165{
166 if ( !source.isValid() || !destination.isValid() )
167 return QString();
168
169 d->mLock.lockForRead();
170
171 auto it = d->mSourceDestDatumTransforms.constFind( qMakePair( source, destination ) );
172 if ( it == d->mSourceDestDatumTransforms.constEnd() )
173 {
174 // try to reverse
175 it = d->mSourceDestDatumTransforms.constFind( qMakePair( destination, source ) );
176 }
177
178 const QString result = it == d->mSourceDestDatumTransforms.constEnd() ? QString() : it.value().operation;
179 d->mLock.unlock();
180 return result;
181}
182
184{
185 if ( !source.isValid() || !destination.isValid() )
186 return false;
187
188 d->mLock.lockForRead();
189 QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( source, destination ), QgsCoordinateTransformContextPrivate::OperationDetails() );
190 if ( res.operation.isEmpty() )
191 {
192 // try to reverse
193 res = d->mSourceDestDatumTransforms.value( qMakePair( destination, source ), QgsCoordinateTransformContextPrivate::OperationDetails() );
194 }
195 d->mLock.unlock();
196 return res.allowFallback;
197}
198
200{
201 if ( !source.isValid() || !destination.isValid() )
202 return false;
203
204 d->mLock.lockForRead();
205 QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( source, destination ), QgsCoordinateTransformContextPrivate::OperationDetails() );
206 if ( !res.operation.isEmpty() )
207 {
208 d->mLock.unlock();
209 return false;
210 }
211 // see if the reverse operation is present
212 res = d->mSourceDestDatumTransforms.value( qMakePair( destination, source ), QgsCoordinateTransformContextPrivate::OperationDetails() );
213 if ( !res.operation.isEmpty() )
214 {
215 d->mLock.unlock();
216 return true;
217 }
218
219 d->mLock.unlock();
220 return false;
221}
222
223bool QgsCoordinateTransformContext::readXml( const QDomElement &element, const QgsReadWriteContext &, QStringList &missingTransforms )
224{
225 d.detach();
226 d->mLock.lockForWrite();
227
228 d->mSourceDestDatumTransforms.clear();
229
230 const QDomNodeList contextNodes = element.elementsByTagName( QStringLiteral( "transformContext" ) );
231 if ( contextNodes.count() < 1 )
232 {
233 d->mLock.unlock();
234 return true;
235 }
236
237 missingTransforms.clear();
238 bool result = true;
239
240 const QDomElement contextElem = contextNodes.at( 0 ).toElement();
241
242 // src/dest transforms
243 const QDomNodeList srcDestNodes = contextElem.elementsByTagName( QStringLiteral( "srcDest" ) );
244 for ( int i = 0; i < srcDestNodes.size(); ++i )
245 {
246 const QDomElement transformElem = srcDestNodes.at( i ).toElement();
247
248 const QDomElement srcElem = transformElem.firstChildElement( QStringLiteral( "src" ) );
249 const QDomElement destElem = transformElem.firstChildElement( QStringLiteral( "dest" ) );
250
253 if ( !srcElem.isNull() && !destElem.isNull() )
254 {
255 srcCrs.readXml( srcElem );
256 destCrs.readXml( destElem );
257 }
258 else
259 {
260 // for older project compatibility
261 const QString key1 = transformElem.attribute( QStringLiteral( "source" ) );
262 const QString key2 = transformElem.attribute( QStringLiteral( "dest" ) );
263 srcCrs = QgsCoordinateReferenceSystem( key1 );
264 destCrs = QgsCoordinateReferenceSystem( key2 );
265 }
266
267 if ( !srcCrs.isValid() || !destCrs.isValid() )
268 continue;
269
270 const QString coordinateOp = transformElem.attribute( QStringLiteral( "coordinateOp" ) );
271 const bool allowFallback = transformElem.attribute( QStringLiteral( "allowFallback" ), QStringLiteral( "1" ) ).toInt();
272
273 // try to instantiate operation, and check for missing grids
275 {
276 // not possible in current Proj 6 api!
277 // QgsCoordinateTransform will alert users to this, we don't need to use missingTransforms here
278 result = false;
279 }
280
281 QgsCoordinateTransformContextPrivate::OperationDetails deets;
282 deets.operation = coordinateOp;
283 deets.allowFallback = allowFallback;
284 d->mSourceDestDatumTransforms.insert( qMakePair( srcCrs, destCrs ), deets );
285 }
286
287 d->mLock.unlock();
288 return result;
289}
290
291void QgsCoordinateTransformContext::writeXml( QDomElement &element, const QgsReadWriteContext & ) const
292{
293 d->mLock.lockForRead();
294
295 QDomDocument doc = element.ownerDocument();
296
297 QDomElement contextElem = doc.createElement( QStringLiteral( "transformContext" ) );
298
299 //src/dest transforms
300 for ( auto it = d->mSourceDestDatumTransforms.constBegin(); it != d->mSourceDestDatumTransforms.constEnd(); ++ it )
301 {
302 QDomElement transformElem = doc.createElement( QStringLiteral( "srcDest" ) );
303 QDomElement srcElem = doc.createElement( QStringLiteral( "src" ) );
304 QDomElement destElem = doc.createElement( QStringLiteral( "dest" ) );
305
306 it.key().first.writeXml( srcElem, doc );
307 it.key().second.writeXml( destElem, doc );
308
309 transformElem.appendChild( srcElem );
310 transformElem.appendChild( destElem );
311
312 transformElem.setAttribute( QStringLiteral( "coordinateOp" ), it.value().operation );
313 transformElem.setAttribute( QStringLiteral( "allowFallback" ), it.value().allowFallback ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
314 contextElem.appendChild( transformElem );
315 }
316
317 element.appendChild( contextElem );
318 d->mLock.unlock();
319}
320
322{
323 d.detach();
324 d->mLock.lockForWrite();
325
326 d->mSourceDestDatumTransforms.clear();
327
328 QgsSettings settings;
329 settings.beginGroup( QStringLiteral( "/Projections" ) );
330 const QStringList projectionKeys = settings.allKeys();
331
332 //collect src and dest entries that belong together
333 QMap< QPair< QgsCoordinateReferenceSystem, QgsCoordinateReferenceSystem >, QgsCoordinateTransformContextPrivate::OperationDetails > transforms;
334 QStringList::const_iterator pkeyIt = projectionKeys.constBegin();
335 for ( ; pkeyIt != projectionKeys.constEnd(); ++pkeyIt )
336 {
337 if ( pkeyIt->contains( QLatin1String( "coordinateOp" ) ) )
338 {
339 const QStringList split = pkeyIt->split( '/' );
340 QString srcAuthId, destAuthId;
341 if ( ! split.isEmpty() )
342 {
343 srcAuthId = split.at( 0 );
344 }
345 if ( split.size() > 1 )
346 {
347 destAuthId = split.at( 1 ).split( '_' ).at( 0 );
348 }
349
350 if ( srcAuthId.isEmpty() || destAuthId.isEmpty() )
351 continue;
352
353 const QString proj = settings.value( *pkeyIt ).toString();
354 const bool allowFallback = settings.value( QStringLiteral( "%1//%2_allowFallback" ).arg( srcAuthId, destAuthId ) ).toBool();
355 QgsCoordinateTransformContextPrivate::OperationDetails deets;
356 deets.operation = proj;
357 deets.allowFallback = allowFallback;
358 transforms[ qMakePair( QgsCoordinateReferenceSystem( srcAuthId ), QgsCoordinateReferenceSystem( destAuthId ) )] = deets;
359 }
360 }
361
362 // add transforms to context
363 auto transformIt = transforms.constBegin();
364 for ( ; transformIt != transforms.constEnd(); ++transformIt )
365 {
366 d->mSourceDestDatumTransforms.insert( transformIt.key(), transformIt.value() );
367 }
368
369 d->mLock.unlock();
370 settings.endGroup();
371}
372
374{
375 QgsSettings settings;
376 settings.beginGroup( QStringLiteral( "/Projections" ) );
377 const QStringList groupKeys = settings.allKeys();
378 QStringList::const_iterator groupKeyIt = groupKeys.constBegin();
379 for ( ; groupKeyIt != groupKeys.constEnd(); ++groupKeyIt )
380 {
381 if ( groupKeyIt->contains( QLatin1String( "srcTransform" ) ) || groupKeyIt->contains( QLatin1String( "destTransform" ) ) || groupKeyIt->contains( QLatin1String( "coordinateOp" ) ) )
382 {
383 settings.remove( *groupKeyIt );
384 }
385 }
386
387 for ( auto transformIt = d->mSourceDestDatumTransforms.constBegin(); transformIt != d->mSourceDestDatumTransforms.constEnd(); ++transformIt )
388 {
389 const QString srcAuthId = transformIt.key().first.authid();
390 const QString destAuthId = transformIt.key().second.authid();
391
392 if ( srcAuthId.isEmpty() || destAuthId.isEmpty() )
393 continue; // not so nice, but alternative would be to shove whole CRS wkt into the settings values...
394
395 const QString proj = transformIt.value().operation;
396 const bool allowFallback = transformIt.value().allowFallback;
397 settings.setValue( srcAuthId + "//" + destAuthId + "_coordinateOp", proj );
398 settings.setValue( srcAuthId + "//" + destAuthId + "_allowFallback", allowFallback );
399 }
400
401 settings.endGroup();
402}
@ Preferred
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
Definition qgis.h:2439
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.
QgsProjOperation operation() const
Returns information about the PROJ operation associated with the coordinate reference system,...
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)
bool operator!=(const QgsCoordinateTransformContext &rhs) const
static bool coordinateOperationIsAvailable(const QString &projDef)
Returns true if a coordinate operation (specified via proj string) is available.
A container for the context for various read/write operations on objects.
Stores settings for use within QGIS.
Definition qgssettings.h:65
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.
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)
Contains datum transform information.