QGIS API Documentation 4.1.0-Master (376402f9aeb)
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"
23#include "qgssettingstree.h"
24
25#include <QString>
26
27using namespace Qt::StringLiterals;
28
29QgsSettingsTreeNamedListNode *QgsCoordinateTransformContext::sTreeCoordinateOperationsSource = QgsSettingsTree::sTreeCrs->createChildNode( u"coordinate-operations"_s )->createNamedListNode( u"source"_s );
30QgsSettingsTreeNamedListNode *QgsCoordinateTransformContext::sTreeCoordinateOperationsDestination = sTreeCoordinateOperationsSource->createNamedListNode( u"destination"_s );
32 = new QgsSettingsEntryString( u"operation"_s, sTreeCoordinateOperationsDestination, QString(), u"PROJ coordinate operation string used when transforming between the source and destination CRS pair."_s );
34 = new QgsSettingsEntryBool( u"allow-fallback"_s, sTreeCoordinateOperationsDestination, true, u"If true, transformations between the source and destination CRS pair are allowed to fall back to a less accurate operation when the preferred coordinate operation fails."_s );
35
37{
38 return crs.authid().isEmpty() ? crs.toWkt( Qgis::CrsWktVariant::Preferred ) : crs.authid();
39}
40
42 : d( new QgsCoordinateTransformContextPrivate() )
43{}
44
46
50
54
55
57{
58 if ( &rhs == this )
59 return *this;
60
61 d = rhs.d;
62 return *this;
63}
64
66{
67 if ( &rhs == this )
68 return *this;
69
70 d = std::move( rhs.d );
71 return *this;
72}
73
75{
76 if ( d == rhs.d )
77 return true;
78
79 d->mLock.lockForRead();
80 rhs.d->mLock.lockForRead();
81 const bool equal = d->mSourceDestDatumTransforms == rhs.d->mSourceDestDatumTransforms;
82 d->mLock.unlock();
83 rhs.d->mLock.unlock();
84 return equal;
85}
86
88{
89 return !( *this == rhs );
90}
91
93{
94 d.detach();
95 // play it safe
96 d->mLock.lockForWrite();
97 d->mSourceDestDatumTransforms.clear();
98 d->mLock.unlock();
99}
100
102{
103 return QMap<QPair<QString, QString>, QgsDatumTransform::TransformPair>();
104}
105
106QMap<QPair<QString, QString>, QString> QgsCoordinateTransformContext::coordinateOperations() const
107{
108 d->mLock.lockForRead();
109 auto res = d->mSourceDestDatumTransforms;
110 res.detach();
111 d->mLock.unlock();
112 QMap<QPair<QString, QString>, QString> results;
113 for ( auto it = res.constBegin(); it != res.constEnd(); ++it )
114 results.insert( qMakePair( it.key().first.authid(), it.key().second.authid() ), it.value().operation );
115
116 return results;
117}
118
120 const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, int sourceTransform, int destinationTransform
121)
122{
123 if ( !sourceCrs.isValid() || !destinationCrs.isValid() )
124 return false;
125 Q_UNUSED( sourceTransform )
126 Q_UNUSED( destinationTransform )
127 return false;
128}
129
131 const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QString &coordinateOperationProjString, bool allowFallback
132)
133{
134 if ( !sourceCrs.isValid() || !destinationCrs.isValid() )
135 return false;
136 d.detach();
137 d->mLock.lockForWrite();
138 QgsCoordinateTransformContextPrivate::OperationDetails details;
139 details.operation = coordinateOperationProjString;
140 details.allowFallback = allowFallback;
141 d->mSourceDestDatumTransforms.insert( qMakePair( sourceCrs, destinationCrs ), details );
142 d->mLock.unlock();
143 return true;
144}
145
150
152{
153 d->mSourceDestDatumTransforms.remove( qMakePair( sourceCrs, destinationCrs ) );
154}
155
157{
158 const QString t = calculateCoordinateOperation( source, destination );
159 return !t.isEmpty();
160}
161
163{
164 Q_UNUSED( source )
165 Q_UNUSED( destination )
166 return QgsDatumTransform::TransformPair( -1, -1 );
167}
168
170{
171 if ( !source.isValid() || !destination.isValid() )
172 return QString();
173
174 d->mLock.lockForRead();
175
176 auto it = d->mSourceDestDatumTransforms.constFind( qMakePair( source, destination ) );
177 if ( it == d->mSourceDestDatumTransforms.constEnd() )
178 {
179 // try to reverse
180 it = d->mSourceDestDatumTransforms.constFind( qMakePair( destination, source ) );
181 }
182
183 const QString result = it == d->mSourceDestDatumTransforms.constEnd() ? QString() : it.value().operation;
184 d->mLock.unlock();
185 return result;
186}
187
189{
190 if ( !source.isValid() || !destination.isValid() )
191 return false;
192
193 d->mLock.lockForRead();
194 QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( source, destination ), QgsCoordinateTransformContextPrivate::OperationDetails() );
195 if ( res.operation.isEmpty() )
196 {
197 // try to reverse
198 res = d->mSourceDestDatumTransforms.value( qMakePair( destination, source ), QgsCoordinateTransformContextPrivate::OperationDetails() );
199 }
200 d->mLock.unlock();
201 return res.allowFallback;
202}
203
205{
206 if ( !source.isValid() || !destination.isValid() )
207 return false;
208
209 d->mLock.lockForRead();
210 QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( source, destination ), QgsCoordinateTransformContextPrivate::OperationDetails() );
211 if ( !res.operation.isEmpty() )
212 {
213 d->mLock.unlock();
214 return false;
215 }
216 // see if the reverse operation is present
217 res = d->mSourceDestDatumTransforms.value( qMakePair( destination, source ), QgsCoordinateTransformContextPrivate::OperationDetails() );
218 if ( !res.operation.isEmpty() )
219 {
220 d->mLock.unlock();
221 return true;
222 }
223
224 d->mLock.unlock();
225 return false;
226}
227
228bool QgsCoordinateTransformContext::readXml( const QDomElement &element, const QgsReadWriteContext &, QStringList &missingTransforms )
229{
230 d.detach();
231 d->mLock.lockForWrite();
232
233 d->mSourceDestDatumTransforms.clear();
234
235 const QDomNodeList contextNodes = element.elementsByTagName( u"transformContext"_s );
236 if ( contextNodes.count() < 1 )
237 {
238 d->mLock.unlock();
239 return true;
240 }
241
242 missingTransforms.clear();
243 bool result = true;
244
245 const QDomElement contextElem = contextNodes.at( 0 ).toElement();
246
247 // src/dest transforms
248 const QDomNodeList srcDestNodes = contextElem.elementsByTagName( u"srcDest"_s );
249 for ( int i = 0; i < srcDestNodes.size(); ++i )
250 {
251 const QDomElement transformElem = srcDestNodes.at( i ).toElement();
252
253 const QDomElement srcElem = transformElem.firstChildElement( u"src"_s );
254 const QDomElement destElem = transformElem.firstChildElement( u"dest"_s );
255
258 if ( !srcElem.isNull() && !destElem.isNull() )
259 {
260 srcCrs.readXml( srcElem );
261 destCrs.readXml( destElem );
262 }
263 else
264 {
265 // for older project compatibility
266 const QString key1 = transformElem.attribute( u"source"_s );
267 const QString key2 = transformElem.attribute( u"dest"_s );
268 srcCrs = QgsCoordinateReferenceSystem( key1 );
269 destCrs = QgsCoordinateReferenceSystem( key2 );
270 }
271
272 if ( !srcCrs.isValid() || !destCrs.isValid() )
273 continue;
274
275 const QString coordinateOp = transformElem.attribute( u"coordinateOp"_s );
276 const bool allowFallback = transformElem.attribute( u"allowFallback"_s, u"1"_s ).toInt();
277
278 // try to instantiate operation, and check for missing grids
280 {
281 // not possible in current Proj 6 api!
282 // QgsCoordinateTransform will alert users to this, we don't need to use missingTransforms here
283 result = false;
284 }
285
286 QgsCoordinateTransformContextPrivate::OperationDetails deets;
287 deets.operation = coordinateOp;
288 deets.allowFallback = allowFallback;
289 d->mSourceDestDatumTransforms.insert( qMakePair( srcCrs, destCrs ), deets );
290 }
291
292 d->mLock.unlock();
293 return result;
294}
295
296void QgsCoordinateTransformContext::writeXml( QDomElement &element, const QgsReadWriteContext & ) const
297{
298 d->mLock.lockForRead();
299
300 QDomDocument doc = element.ownerDocument();
301
302 QDomElement contextElem = doc.createElement( u"transformContext"_s );
303
304 //src/dest transforms
305 for ( auto it = d->mSourceDestDatumTransforms.constBegin(); it != d->mSourceDestDatumTransforms.constEnd(); ++it )
306 {
307 QDomElement transformElem = doc.createElement( u"srcDest"_s );
308 QDomElement srcElem = doc.createElement( u"src"_s );
309 QDomElement destElem = doc.createElement( u"dest"_s );
310
311 it.key().first.writeXml( srcElem, doc );
312 it.key().second.writeXml( destElem, doc );
313
314 transformElem.appendChild( srcElem );
315 transformElem.appendChild( destElem );
316
317 transformElem.setAttribute( u"coordinateOp"_s, it.value().operation );
318 transformElem.setAttribute( u"allowFallback"_s, it.value().allowFallback ? u"1"_s : u"0"_s );
319 contextElem.appendChild( transformElem );
320 }
321
322 element.appendChild( contextElem );
323 d->mLock.unlock();
324}
325
327{
328 d.detach();
329 d->mLock.lockForWrite();
330
331 d->mSourceDestDatumTransforms.clear();
332
333 QMap<QPair<QgsCoordinateReferenceSystem, QgsCoordinateReferenceSystem>, QgsCoordinateTransformContextPrivate::OperationDetails> transforms;
334 const QStringList srcAuthIds = sTreeCoordinateOperationsSource->items();
335 for ( const QString &srcAuthId : srcAuthIds )
336 {
337 const QStringList destAuthIds = sTreeCoordinateOperationsDestination->items( { srcAuthId } );
338 for ( const QString &destAuthId : destAuthIds )
339 {
340 QgsCoordinateTransformContextPrivate::OperationDetails deets;
341 deets.operation = settingsCoordinateOperation->value( { srcAuthId, destAuthId } );
342 deets.allowFallback = settingsAllowFallback->value( { srcAuthId, destAuthId } );
343 transforms[qMakePair( QgsCoordinateReferenceSystem( srcAuthId ), QgsCoordinateReferenceSystem( destAuthId ) )] = deets;
344 }
345 }
346
347 // add transforms to context
348 auto transformIt = transforms.constBegin();
349 for ( ; transformIt != transforms.constEnd(); ++transformIt )
350 {
351 d->mSourceDestDatumTransforms.insert( transformIt.key(), transformIt.value() );
352 }
353
354 d->mLock.unlock();
355}
356
358{
359 sTreeCoordinateOperationsSource->deleteAllItems();
360
361 for ( auto transformIt = d->mSourceDestDatumTransforms.constBegin(); transformIt != d->mSourceDestDatumTransforms.constEnd(); ++transformIt )
362 {
363 const QString srcAuthId = transformIt.key().first.authid();
364 const QString destAuthId = transformIt.key().second.authid();
365
366 if ( srcAuthId.isEmpty() || destAuthId.isEmpty() )
367 continue; // not so nice, but alternative would be to shove whole CRS wkt into the settings values...
368
369 settingsCoordinateOperation->setValue( transformIt.value().operation, { srcAuthId, destAuthId } );
370 settingsAllowFallback->setValue( transformIt.value().allowFallback, { srcAuthId, destAuthId } );
371 }
372}
@ Preferred
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
Definition qgis.h:2580
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...
static QgsSettingsTreeNamedListNode * sTreeCoordinateOperationsSource
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...
static const QgsSettingsEntryString * settingsCoordinateOperation
static const QgsSettingsEntryBool * settingsAllowFallback
static QgsSettingsTreeNamedListNode * sTreeCoordinateOperationsDestination
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.
A boolean settings entry.
A string settings entry.
A named list tree node for the settings tree to help organizing and introspecting the tree.
static QgsSettingsTreeNode * sTreeCrs
QString crsToKey(const QgsCoordinateReferenceSystem &crs)
Contains datum transform information.