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