QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 "qgscoordinatetransform.h"
21 #include "qgssettings.h"
22 #include "qgsprojutils.h"
23 
25 {
27 }
28 
29 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
30 template<>
31 bool 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 
47  : d( rhs.d )
48 {}
49 
51 {
52  d = rhs.d;
53  return *this;
54 }
55 
57 {
58  if ( d == rhs.d )
59  return true;
60 
61  d->mLock.lockForRead();
62  rhs.d->mLock.lockForRead();
63  const bool equal = d->mSourceDestDatumTransforms == rhs.d->mSourceDestDatumTransforms;
64  d->mLock.unlock();
65  rhs.d->mLock.unlock();
66  return equal;
67 }
68 
70 {
71  d.detach();
72  // play it safe
73  d->mLock.lockForWrite();
74  d->mSourceDestDatumTransforms.clear();
75  d->mLock.unlock();
76 }
77 
79 {
80  return QMap<QPair<QString, QString>, QgsDatumTransform::TransformPair>();
81 }
82 
83 QMap<QPair<QString, QString>, QString> QgsCoordinateTransformContext::coordinateOperations() const
84 {
85  d->mLock.lockForRead();
86  auto res = d->mSourceDestDatumTransforms;
87  res.detach();
88  d->mLock.unlock();
89  QMap<QPair<QString, QString>, QString> results;
90  for ( auto it = res.constBegin(); it != res.constEnd(); ++it )
91  results.insert( qMakePair( it.key().first.authid(), it.key().second.authid() ), it.value().operation );
92 
93  return results;
94 }
95 
96 bool QgsCoordinateTransformContext::addSourceDestinationDatumTransform( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, int sourceTransform, int destinationTransform )
97 {
98  if ( !sourceCrs.isValid() || !destinationCrs.isValid() )
99  return false;
100  Q_UNUSED( sourceTransform )
101  Q_UNUSED( destinationTransform )
102  return false;
103 }
104 
105 bool QgsCoordinateTransformContext::addCoordinateOperation( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QString &coordinateOperationProjString, bool allowFallback )
106 {
107  if ( !sourceCrs.isValid() || !destinationCrs.isValid() )
108  return false;
109  d.detach();
110  d->mLock.lockForWrite();
111  QgsCoordinateTransformContextPrivate::OperationDetails details;
112  details.operation = coordinateOperationProjString;
113  details.allowFallback = allowFallback;
114  d->mSourceDestDatumTransforms.insert( qMakePair( sourceCrs, destinationCrs ), details );
115  d->mLock.unlock();
116  return true;
117 }
118 
120 {
121  removeCoordinateOperation( sourceCrs, destinationCrs );
122 }
123 
125 {
126  d->mSourceDestDatumTransforms.remove( qMakePair( sourceCrs, destinationCrs ) );
127 }
128 
130 {
131  const QString t = calculateCoordinateOperation( source, destination );
132  return !t.isEmpty();
133 }
134 
136 {
137  Q_UNUSED( source )
138  Q_UNUSED( destination )
139  return QgsDatumTransform::TransformPair( -1, -1 );
140 }
141 
143 {
144  if ( !source.isValid() || !destination.isValid() )
145  return QString();
146 
147  d->mLock.lockForRead();
148  QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( source, destination ), QgsCoordinateTransformContextPrivate::OperationDetails() );
149  if ( res.operation.isEmpty() )
150  {
151  // try to reverse
152  res = d->mSourceDestDatumTransforms.value( qMakePair( destination, source ), QgsCoordinateTransformContextPrivate::OperationDetails() );
153  }
154  d->mLock.unlock();
155  return res.operation;
156 }
157 
159 {
160  if ( !source.isValid() || !destination.isValid() )
161  return false;
162 
163  d->mLock.lockForRead();
164  QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( source, destination ), QgsCoordinateTransformContextPrivate::OperationDetails() );
165  if ( res.operation.isEmpty() )
166  {
167  // try to reverse
168  res = d->mSourceDestDatumTransforms.value( qMakePair( destination, source ), QgsCoordinateTransformContextPrivate::OperationDetails() );
169  }
170  d->mLock.unlock();
171  return res.allowFallback;
172 }
173 
175 {
176  if ( !source.isValid() || !destination.isValid() )
177  return false;
178 
179  d->mLock.lockForRead();
180  QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( source, destination ), QgsCoordinateTransformContextPrivate::OperationDetails() );
181  if ( !res.operation.isEmpty() )
182  {
183  d->mLock.unlock();
184  return false;
185  }
186  // see if the reverse operation is present
187  res = d->mSourceDestDatumTransforms.value( qMakePair( destination, source ), QgsCoordinateTransformContextPrivate::OperationDetails() );
188  if ( !res.operation.isEmpty() )
189  {
190  d->mLock.unlock();
191  return true;
192  }
193 
194  d->mLock.unlock();
195  return false;
196 }
197 
198 bool QgsCoordinateTransformContext::readXml( const QDomElement &element, const QgsReadWriteContext &, QStringList &missingTransforms )
199 {
200  d.detach();
201  d->mLock.lockForWrite();
202 
203  d->mSourceDestDatumTransforms.clear();
204 
205  const QDomNodeList contextNodes = element.elementsByTagName( QStringLiteral( "transformContext" ) );
206  if ( contextNodes.count() < 1 )
207  {
208  d->mLock.unlock();
209  return true;
210  }
211 
212  missingTransforms.clear();
213  bool result = true;
214 
215  const QDomElement contextElem = contextNodes.at( 0 ).toElement();
216 
217  // src/dest transforms
218  const QDomNodeList srcDestNodes = contextElem.elementsByTagName( QStringLiteral( "srcDest" ) );
219  for ( int i = 0; i < srcDestNodes.size(); ++i )
220  {
221  const QDomElement transformElem = srcDestNodes.at( i ).toElement();
222 
223  const QDomElement srcElem = transformElem.firstChildElement( QStringLiteral( "src" ) );
224  const QDomElement destElem = transformElem.firstChildElement( QStringLiteral( "dest" ) );
225 
228  if ( !srcElem.isNull() && !destElem.isNull() )
229  {
230  srcCrs.readXml( srcElem );
231  destCrs.readXml( destElem );
232  }
233  else
234  {
235  // for older project compatibility
236  const QString key1 = transformElem.attribute( QStringLiteral( "source" ) );
237  const QString key2 = transformElem.attribute( QStringLiteral( "dest" ) );
238  srcCrs = QgsCoordinateReferenceSystem( key1 );
239  destCrs = QgsCoordinateReferenceSystem( key2 );
240  }
241 
242  if ( !srcCrs.isValid() || !destCrs.isValid() )
243  continue;
244 
245  const QString coordinateOp = transformElem.attribute( QStringLiteral( "coordinateOp" ) );
246  const bool allowFallback = transformElem.attribute( QStringLiteral( "allowFallback" ), QStringLiteral( "1" ) ).toInt();
247 
248  // try to instantiate operation, and check for missing grids
249  if ( !QgsProjUtils::coordinateOperationIsAvailable( coordinateOp ) )
250  {
251  // not possible in current Proj 6 api!
252  // QgsCoordinateTransform will alert users to this, we don't need to use missingTransforms here
253  result = false;
254  }
255 
256  QgsCoordinateTransformContextPrivate::OperationDetails deets;
257  deets.operation = coordinateOp;
258  deets.allowFallback = allowFallback;
259  d->mSourceDestDatumTransforms.insert( qMakePair( srcCrs, destCrs ), deets );
260  }
261 
262  d->mLock.unlock();
263  return result;
264 }
265 
266 void QgsCoordinateTransformContext::writeXml( QDomElement &element, const QgsReadWriteContext & ) const
267 {
268  d->mLock.lockForRead();
269 
270  QDomDocument doc = element.ownerDocument();
271 
272  QDomElement contextElem = doc.createElement( QStringLiteral( "transformContext" ) );
273 
274  //src/dest transforms
275  for ( auto it = d->mSourceDestDatumTransforms.constBegin(); it != d->mSourceDestDatumTransforms.constEnd(); ++ it )
276  {
277  QDomElement transformElem = doc.createElement( QStringLiteral( "srcDest" ) );
278  QDomElement srcElem = doc.createElement( QStringLiteral( "src" ) );
279  QDomElement destElem = doc.createElement( QStringLiteral( "dest" ) );
280 
281  it.key().first.writeXml( srcElem, doc );
282  it.key().second.writeXml( destElem, doc );
283 
284  transformElem.appendChild( srcElem );
285  transformElem.appendChild( destElem );
286 
287  transformElem.setAttribute( QStringLiteral( "coordinateOp" ), it.value().operation );
288  transformElem.setAttribute( QStringLiteral( "allowFallback" ), it.value().allowFallback ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
289  contextElem.appendChild( transformElem );
290  }
291 
292  element.appendChild( contextElem );
293  d->mLock.unlock();
294 }
295 
297 {
298  d.detach();
299  d->mLock.lockForWrite();
300 
301  d->mSourceDestDatumTransforms.clear();
302 
303  QgsSettings settings;
304  settings.beginGroup( QStringLiteral( "/Projections" ) );
305  const QStringList projectionKeys = settings.allKeys();
306 
307  //collect src and dest entries that belong together
308  QMap< QPair< QgsCoordinateReferenceSystem, QgsCoordinateReferenceSystem >, QgsCoordinateTransformContextPrivate::OperationDetails > transforms;
309  QStringList::const_iterator pkeyIt = projectionKeys.constBegin();
310  for ( ; pkeyIt != projectionKeys.constEnd(); ++pkeyIt )
311  {
312  if ( pkeyIt->contains( QLatin1String( "coordinateOp" ) ) )
313  {
314  const QStringList split = pkeyIt->split( '/' );
315  QString srcAuthId, destAuthId;
316  if ( ! split.isEmpty() )
317  {
318  srcAuthId = split.at( 0 );
319  }
320  if ( split.size() > 1 )
321  {
322  destAuthId = split.at( 1 ).split( '_' ).at( 0 );
323  }
324 
325  if ( srcAuthId.isEmpty() || destAuthId.isEmpty() )
326  continue;
327 
328  const QString proj = settings.value( *pkeyIt ).toString();
329  const bool allowFallback = settings.value( QStringLiteral( "%1//%2_allowFallback" ).arg( srcAuthId, destAuthId ) ).toBool();
330  QgsCoordinateTransformContextPrivate::OperationDetails deets;
331  deets.operation = proj;
332  deets.allowFallback = allowFallback;
333  transforms[ qMakePair( QgsCoordinateReferenceSystem( srcAuthId ), QgsCoordinateReferenceSystem( destAuthId ) )] = deets;
334  }
335  }
336 
337  // add transforms to context
338  auto transformIt = transforms.constBegin();
339  for ( ; transformIt != transforms.constEnd(); ++transformIt )
340  {
341  d->mSourceDestDatumTransforms.insert( transformIt.key(), transformIt.value() );
342  }
343 
344  d->mLock.unlock();
345  settings.endGroup();
346 }
347 
349 {
350  QgsSettings settings;
351  settings.beginGroup( QStringLiteral( "/Projections" ) );
352  const QStringList groupKeys = settings.allKeys();
353  QStringList::const_iterator groupKeyIt = groupKeys.constBegin();
354  for ( ; groupKeyIt != groupKeys.constEnd(); ++groupKeyIt )
355  {
356  if ( groupKeyIt->contains( QLatin1String( "srcTransform" ) ) || groupKeyIt->contains( QLatin1String( "destTransform" ) ) || groupKeyIt->contains( QLatin1String( "coordinateOp" ) ) )
357  {
358  settings.remove( *groupKeyIt );
359  }
360  }
361 
362  for ( auto transformIt = d->mSourceDestDatumTransforms.constBegin(); transformIt != d->mSourceDestDatumTransforms.constEnd(); ++transformIt )
363  {
364  const QString srcAuthId = transformIt.key().first.authid();
365  const QString destAuthId = transformIt.key().second.authid();
366 
367  if ( srcAuthId.isEmpty() || destAuthId.isEmpty() )
368  continue; // not so nice, but alternative would be to shove whole CRS wkt into the settings values...
369 
370  const QString proj = transformIt.value().operation;
371  const bool allowFallback = transformIt.value().allowFallback;
372  settings.setValue( srcAuthId + "//" + destAuthId + "_coordinateOp", proj );
373  settings.setValue( srcAuthId + "//" + destAuthId + "_allowFallback", allowFallback );
374  }
375 
376  settings.endGroup();
377 }
QgsSettings::remove
void remove(const QString &key, QgsSettings::Section section=QgsSettings::NoSection)
Removes the setting key and any sub-settings of key in a section.
Definition: qgssettings.cpp:192
QgsCoordinateTransformContext
Contains information about the context in which a coordinate transform is executed.
Definition: qgscoordinatetransformcontext.h:57
QgsCoordinateTransformContext::removeSourceDestinationDatumTransform
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...
Definition: qgscoordinatetransformcontext.cpp:119
QgsSettings::endGroup
void endGroup()
Resets the group to what it was before the corresponding beginGroup() call.
Definition: qgssettings.cpp:99
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:161
QgsReadWriteContext
The class is used as a container of context for various read/write operations on other objects.
Definition: qgsreadwritecontext.h:34
QgsCoordinateTransformContext::operator=
QgsCoordinateTransformContext & operator=(const QgsCoordinateTransformContext &rhs)
Assignment operator.
Definition: qgscoordinatetransformcontext.cpp:50
QgsCoordinateTransformContext::removeCoordinateOperation
void removeCoordinateOperation(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs)
Removes the coordinate operation for the specified sourceCrs and destinationCrs.
Definition: qgscoordinatetransformcontext.cpp:124
QgsCoordinateReferenceSystem::WKT_PREFERRED
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
Definition: qgscoordinatereferencesystem.h:680
qgscoordinatetransformcontext.h
crs
const QgsCoordinateReferenceSystem & crs
Definition: qgswfsgetfeature.cpp:105
QgsSettings::allKeys
QStringList allKeys() const
Returns a list of all keys, including subkeys, that can be read using the QSettings object.
Definition: qgssettings.cpp:113
QgsDatumTransform::TransformPair
Contains datum transform information.
Definition: qgsdatumtransform.h:52
QgsCoordinateTransformContext::~QgsCoordinateTransformContext
~QgsCoordinateTransformContext()
qgscoordinatetransformcontext_p.h
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
QgsCoordinateTransformContext::operator==
bool operator==(const QgsCoordinateTransformContext &rhs) const
Definition: qgscoordinatetransformcontext.cpp:56
QgsCoordinateReferenceSystem::readXml
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
Definition: qgscoordinatereferencesystem.cpp:1860
QgsCoordinateTransformContext::readSettings
void readSettings()
Reads the context's state from application settings.
Definition: qgscoordinatetransformcontext.cpp:296
QgsCoordinateTransformContext::addCoordinateOperation
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...
Definition: qgscoordinatetransformcontext.cpp:105
QgsCoordinateTransformContext::allowFallbackTransform
bool allowFallbackTransform(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination) const
Returns true if approximate "ballpark" transforms may be used when transforming between a source and ...
Definition: qgscoordinatetransformcontext.cpp:158
QgsCoordinateTransformContext::coordinateOperations
QMap< QPair< QString, QString >, QString > coordinateOperations() const
Returns the stored mapping for source to destination CRS pairs to associated coordinate operation to ...
Definition: qgscoordinatetransformcontext.cpp:83
QgsCoordinateTransformContext::readXml
bool readXml(const QDomElement &element, const QgsReadWriteContext &context, QStringList &missingTransforms)
Reads the context's state from a DOM element.
Definition: qgscoordinatetransformcontext.cpp:198
QgsCoordinateTransformContext::clear
void clear()
Clears all stored transform information from the context.
Definition: qgscoordinatetransformcontext.cpp:69
QgsCoordinateTransformContext::sourceDestinationDatumTransforms
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.
Definition: qgscoordinatetransformcontext.cpp:78
QgsCoordinateReferenceSystem::toWkt
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
Definition: qgscoordinatereferencesystem.cpp:1810
QgsCoordinateReferenceSystem::isValid
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Definition: qgscoordinatereferencesystem.cpp:977
qgscoordinatetransform.h
QgsCoordinateTransformContext::addSourceDestinationDatumTransform
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...
Definition: qgscoordinatetransformcontext.cpp:96
QgsProjUtils::coordinateOperationIsAvailable
static bool coordinateOperationIsAvailable(const QString &projDef)
Returns true if a coordinate operation (specified via proj string) is available.
Definition: qgsprojutils.cpp:290
QgsSettings::setValue
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Definition: qgssettings.cpp:279
QgsCoordinateReferenceSystem
This class represents a coordinate reference system (CRS).
Definition: qgscoordinatereferencesystem.h:211
QgsSettings::beginGroup
void beginGroup(const QString &prefix, QgsSettings::Section section=QgsSettings::NoSection)
Appends prefix to the current group.
Definition: qgssettings.cpp:89
QgsCoordinateTransformContext::writeXml
void writeXml(QDomElement &element, const QgsReadWriteContext &context) const
Writes the context's state to a DOM element.
Definition: qgscoordinatetransformcontext.cpp:266
QgsCoordinateTransformContext::calculateCoordinateOperation
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...
Definition: qgscoordinatetransformcontext.cpp:142
qgsprojutils.h
qgssettings.h
QgsCoordinateTransformContext::calculateDatumTransforms
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...
Definition: qgscoordinatetransformcontext.cpp:135
QgsCoordinateReferenceSystem::authid
QString authid
Definition: qgscoordinatereferencesystem.h:217
crsToKey
QString crsToKey(const QgsCoordinateReferenceSystem &crs)
Definition: qgscoordinatetransformcontext.cpp:24
QgsCoordinateTransformContext::mustReverseCoordinateOperation
bool mustReverseCoordinateOperation(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination) const
Returns true if the coordinate operation returned by calculateCoordinateOperation() for the source to...
Definition: qgscoordinatetransformcontext.cpp:174
QgsCoordinateTransformContext::hasTransform
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...
Definition: qgscoordinatetransformcontext.cpp:129
QgsCoordinateTransformContext::writeSettings
void writeSettings()
Write the context's state to application settings.
Definition: qgscoordinatetransformcontext.cpp:348
QgsCoordinateTransformContext::QgsCoordinateTransformContext
QgsCoordinateTransformContext()
Constructor for QgsCoordinateTransformContext.
Definition: qgscoordinatetransformcontext.cpp:40