QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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
112 const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, int sourceTransform, int destinationTransform
113)
114{
115 if ( !sourceCrs.isValid() || !destinationCrs.isValid() )
116 return false;
117 Q_UNUSED( sourceTransform )
118 Q_UNUSED( destinationTransform )
119 return false;
120}
121
123 const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QString &coordinateOperationProjString, bool allowFallback
124)
125{
126 if ( !sourceCrs.isValid() || !destinationCrs.isValid() )
127 return false;
128 d.detach();
129 d->mLock.lockForWrite();
130 QgsCoordinateTransformContextPrivate::OperationDetails details;
131 details.operation = coordinateOperationProjString;
132 details.allowFallback = allowFallback;
133 d->mSourceDestDatumTransforms.insert( qMakePair( sourceCrs, destinationCrs ), details );
134 d->mLock.unlock();
135 return true;
136}
137
142
144{
145 d->mSourceDestDatumTransforms.remove( qMakePair( sourceCrs, destinationCrs ) );
146}
147
149{
150 const QString t = calculateCoordinateOperation( source, destination );
151 return !t.isEmpty();
152}
153
155{
156 Q_UNUSED( source )
157 Q_UNUSED( destination )
158 return QgsDatumTransform::TransformPair( -1, -1 );
159}
160
162{
163 if ( !source.isValid() || !destination.isValid() )
164 return QString();
165
166 d->mLock.lockForRead();
167
168 auto it = d->mSourceDestDatumTransforms.constFind( qMakePair( source, destination ) );
169 if ( it == d->mSourceDestDatumTransforms.constEnd() )
170 {
171 // try to reverse
172 it = d->mSourceDestDatumTransforms.constFind( qMakePair( destination, source ) );
173 }
174
175 const QString result = it == d->mSourceDestDatumTransforms.constEnd() ? QString() : it.value().operation;
176 d->mLock.unlock();
177 return result;
178}
179
181{
182 if ( !source.isValid() || !destination.isValid() )
183 return false;
184
185 d->mLock.lockForRead();
186 QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( source, destination ), QgsCoordinateTransformContextPrivate::OperationDetails() );
187 if ( res.operation.isEmpty() )
188 {
189 // try to reverse
190 res = d->mSourceDestDatumTransforms.value( qMakePair( destination, source ), QgsCoordinateTransformContextPrivate::OperationDetails() );
191 }
192 d->mLock.unlock();
193 return res.allowFallback;
194}
195
197{
198 if ( !source.isValid() || !destination.isValid() )
199 return false;
200
201 d->mLock.lockForRead();
202 QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( source, destination ), QgsCoordinateTransformContextPrivate::OperationDetails() );
203 if ( !res.operation.isEmpty() )
204 {
205 d->mLock.unlock();
206 return false;
207 }
208 // see if the reverse operation is present
209 res = d->mSourceDestDatumTransforms.value( qMakePair( destination, source ), QgsCoordinateTransformContextPrivate::OperationDetails() );
210 if ( !res.operation.isEmpty() )
211 {
212 d->mLock.unlock();
213 return true;
214 }
215
216 d->mLock.unlock();
217 return false;
218}
219
220bool QgsCoordinateTransformContext::readXml( const QDomElement &element, const QgsReadWriteContext &, QStringList &missingTransforms )
221{
222 d.detach();
223 d->mLock.lockForWrite();
224
225 d->mSourceDestDatumTransforms.clear();
226
227 const QDomNodeList contextNodes = element.elementsByTagName( u"transformContext"_s );
228 if ( contextNodes.count() < 1 )
229 {
230 d->mLock.unlock();
231 return true;
232 }
233
234 missingTransforms.clear();
235 bool result = true;
236
237 const QDomElement contextElem = contextNodes.at( 0 ).toElement();
238
239 // src/dest transforms
240 const QDomNodeList srcDestNodes = contextElem.elementsByTagName( u"srcDest"_s );
241 for ( int i = 0; i < srcDestNodes.size(); ++i )
242 {
243 const QDomElement transformElem = srcDestNodes.at( i ).toElement();
244
245 const QDomElement srcElem = transformElem.firstChildElement( u"src"_s );
246 const QDomElement destElem = transformElem.firstChildElement( u"dest"_s );
247
250 if ( !srcElem.isNull() && !destElem.isNull() )
251 {
252 srcCrs.readXml( srcElem );
253 destCrs.readXml( destElem );
254 }
255 else
256 {
257 // for older project compatibility
258 const QString key1 = transformElem.attribute( u"source"_s );
259 const QString key2 = transformElem.attribute( u"dest"_s );
260 srcCrs = QgsCoordinateReferenceSystem( key1 );
261 destCrs = QgsCoordinateReferenceSystem( key2 );
262 }
263
264 if ( !srcCrs.isValid() || !destCrs.isValid() )
265 continue;
266
267 const QString coordinateOp = transformElem.attribute( u"coordinateOp"_s );
268 const bool allowFallback = transformElem.attribute( u"allowFallback"_s, u"1"_s ).toInt();
269
270 // try to instantiate operation, and check for missing grids
272 {
273 // not possible in current Proj 6 api!
274 // QgsCoordinateTransform will alert users to this, we don't need to use missingTransforms here
275 result = false;
276 }
277
278 QgsCoordinateTransformContextPrivate::OperationDetails deets;
279 deets.operation = coordinateOp;
280 deets.allowFallback = allowFallback;
281 d->mSourceDestDatumTransforms.insert( qMakePair( srcCrs, destCrs ), deets );
282 }
283
284 d->mLock.unlock();
285 return result;
286}
287
288void QgsCoordinateTransformContext::writeXml( QDomElement &element, const QgsReadWriteContext & ) const
289{
290 d->mLock.lockForRead();
291
292 QDomDocument doc = element.ownerDocument();
293
294 QDomElement contextElem = doc.createElement( u"transformContext"_s );
295
296 //src/dest transforms
297 for ( auto it = d->mSourceDestDatumTransforms.constBegin(); it != d->mSourceDestDatumTransforms.constEnd(); ++it )
298 {
299 QDomElement transformElem = doc.createElement( u"srcDest"_s );
300 QDomElement srcElem = doc.createElement( u"src"_s );
301 QDomElement destElem = doc.createElement( u"dest"_s );
302
303 it.key().first.writeXml( srcElem, doc );
304 it.key().second.writeXml( destElem, doc );
305
306 transformElem.appendChild( srcElem );
307 transformElem.appendChild( destElem );
308
309 transformElem.setAttribute( u"coordinateOp"_s, it.value().operation );
310 transformElem.setAttribute( u"allowFallback"_s, it.value().allowFallback ? u"1"_s : u"0"_s );
311 contextElem.appendChild( transformElem );
312 }
313
314 element.appendChild( contextElem );
315 d->mLock.unlock();
316}
317
319{
320 d.detach();
321 d->mLock.lockForWrite();
322
323 d->mSourceDestDatumTransforms.clear();
324
325 QgsSettings settings;
326 settings.beginGroup( u"/Projections"_s );
327 const QStringList projectionKeys = settings.allKeys();
328
329 //collect src and dest entries that belong together
330 QMap< QPair< QgsCoordinateReferenceSystem, QgsCoordinateReferenceSystem >, QgsCoordinateTransformContextPrivate::OperationDetails > transforms;
331 QStringList::const_iterator pkeyIt = projectionKeys.constBegin();
332 for ( ; pkeyIt != projectionKeys.constEnd(); ++pkeyIt )
333 {
334 if ( pkeyIt->contains( "coordinateOp"_L1 ) )
335 {
336 const QStringList split = pkeyIt->split( '/' );
337 QString srcAuthId, destAuthId;
338 if ( !split.isEmpty() )
339 {
340 srcAuthId = split.at( 0 );
341 }
342 if ( split.size() > 1 )
343 {
344 destAuthId = split.at( 1 ).split( '_' ).at( 0 );
345 }
346
347 if ( srcAuthId.isEmpty() || destAuthId.isEmpty() )
348 continue;
349
350 const QString proj = settings.value( *pkeyIt ).toString();
351 const bool allowFallback = settings.value( u"%1//%2_allowFallback"_s.arg( srcAuthId, destAuthId ) ).toBool();
352 QgsCoordinateTransformContextPrivate::OperationDetails deets;
353 deets.operation = proj;
354 deets.allowFallback = allowFallback;
355 transforms[qMakePair( QgsCoordinateReferenceSystem( srcAuthId ), QgsCoordinateReferenceSystem( destAuthId ) )] = deets;
356 }
357 }
358
359 // add transforms to context
360 auto transformIt = transforms.constBegin();
361 for ( ; transformIt != transforms.constEnd(); ++transformIt )
362 {
363 d->mSourceDestDatumTransforms.insert( transformIt.key(), transformIt.value() );
364 }
365
366 d->mLock.unlock();
367 settings.endGroup();
368}
369
371{
372 QgsSettings settings;
373 settings.beginGroup( u"/Projections"_s );
374 const QStringList groupKeys = settings.allKeys();
375 QStringList::const_iterator groupKeyIt = groupKeys.constBegin();
376 for ( ; groupKeyIt != groupKeys.constEnd(); ++groupKeyIt )
377 {
378 if ( groupKeyIt->contains( "srcTransform"_L1 ) || groupKeyIt->contains( "destTransform"_L1 ) || groupKeyIt->contains( "coordinateOp"_L1 ) )
379 {
380 settings.remove( *groupKeyIt );
381 }
382 }
383
384 for ( auto transformIt = d->mSourceDestDatumTransforms.constBegin(); transformIt != d->mSourceDestDatumTransforms.constEnd(); ++transformIt )
385 {
386 const QString srcAuthId = transformIt.key().first.authid();
387 const QString destAuthId = transformIt.key().second.authid();
388
389 if ( srcAuthId.isEmpty() || destAuthId.isEmpty() )
390 continue; // not so nice, but alternative would be to shove whole CRS wkt into the settings values...
391
392 const QString proj = transformIt.value().operation;
393 const bool allowFallback = transformIt.value().allowFallback;
394 settings.setValue( srcAuthId + "//" + destAuthId + "_coordinateOp", proj );
395 settings.setValue( srcAuthId + "//" + destAuthId + "_allowFallback", allowFallback );
396 }
397
398 settings.endGroup();
399}
@ Preferred
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
Definition qgis.h:2527
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.