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