QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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 
24  : d( new QgsCoordinateTransformContextPrivate() )
25 {}
26 
28 
30  : d( rhs.d )
31 {}
32 
34 {
35  d = rhs.d;
36  return *this;
37 }
38 
40 {
41  if ( d == rhs.d )
42  return true;
43 
44  d->mLock.lockForRead();
45  rhs.d->mLock.lockForRead();
46  bool equal = d->mSourceDestDatumTransforms == rhs.d->mSourceDestDatumTransforms;
47  d->mLock.unlock();
48  rhs.d->mLock.unlock();
49  return equal;
50 }
51 
53 {
54  d.detach();
55  // play it safe
56  d->mLock.lockForWrite();
57  d->mSourceDestDatumTransforms.clear();
58 #if 0
59  d->mSourceDatumTransforms.clear();
60  d->mDestDatumTransforms.clear();
61 #endif
62  d->mLock.unlock();
63 }
64 
65 #ifdef singlesourcedest
66 QMap<QString, int> QgsCoordinateTransformContext::sourceDatumTransforms() const
67 {
68  d->mLock.lockForRead();
69  auto res = d->mSourceDatumTransforms;
70  res.detach();
71  d->mLock.unlock();
72  return res;
73 }
74 
75 bool QgsCoordinateTransformContext::addSourceDatumTransform( const QgsCoordinateReferenceSystem &crs, int transform )
76 {
77  if ( !crs.isValid() )
78  return false;
79 
80  d.detach();
81  d->mLock.lockForWrite();
82  d->mSourceDatumTransforms.insert( crs.authid(), transform );
83  d->mLock.unlock();
84  return true;
85 }
86 
87 void QgsCoordinateTransformContext::removeSourceDatumTransform( const QgsCoordinateReferenceSystem &crs )
88 {
89  d->mSourceDatumTransforms.remove( crs.authid() );
90 }
91 
92 QMap<QString, int> QgsCoordinateTransformContext::destinationDatumTransforms() const
93 {
94  d->mLock.lockForRead();
95  auto res = d->mDestDatumTransforms;
96  res.detach();
97  d->mLock.unlock();
98  return res;
99 }
100 
101 bool QgsCoordinateTransformContext::addDestinationDatumTransform( const QgsCoordinateReferenceSystem &crs, int transform )
102 {
103  if ( !crs.isValid() )
104  return false;
105 
106  d.detach();
107 
108  d->mLock.lockForWrite();
109  d->mDestDatumTransforms.insert( crs.authid(), transform );
110  d->mLock.unlock();
111  return true;
112 }
113 
114 void QgsCoordinateTransformContext::removeDestinationDatumTransform( const QgsCoordinateReferenceSystem &crs )
115 {
116  d->mDestDatumTransforms.remove( crs.authid() );
117 }
118 
119 #endif
120 
122 {
123  d->mLock.lockForRead();
124  auto res = d->mSourceDestDatumTransforms;
125  res.detach();
126  d->mLock.unlock();
127  return res;
128 }
129 
130 bool QgsCoordinateTransformContext::addSourceDestinationDatumTransform( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, int sourceTransform, int destinationTransform )
131 {
132  if ( !sourceCrs.isValid() || !destinationCrs.isValid() )
133  return false;
134 
135  d.detach();
136  d->mLock.lockForWrite();
137  d->mSourceDestDatumTransforms.insert( qMakePair( sourceCrs.authid(), destinationCrs.authid() ), QgsDatumTransform::TransformPair( sourceTransform, destinationTransform ) );
138  d->mLock.unlock();
139  return true;
140 }
141 
143 {
144  d->mSourceDestDatumTransforms.remove( qMakePair( sourceCrs.authid(), destinationCrs.authid() ) );
145 }
146 
148 {
150  // calculateDatumTransforms already takes care of switching source and destination
151  return t.sourceTransformId != -1 || t.destinationTransformId != -1;
152 }
153 
155 {
156  QString srcKey = source.authid();
157  QString destKey = destination.authid();
158 
159  d->mLock.lockForRead();
160  // highest priority is exact match for source/dest pair
161  QgsDatumTransform::TransformPair res = d->mSourceDestDatumTransforms.value( qMakePair( srcKey, destKey ), QgsDatumTransform::TransformPair( -1, -1 ) );
162  if ( res.sourceTransformId == -1 && res.destinationTransformId == -1 )
163  {
164  // try to reverse
165  QgsDatumTransform::TransformPair res2 = d->mSourceDestDatumTransforms.value( qMakePair( destKey, srcKey ), QgsDatumTransform::TransformPair( -1, -1 ) );
167  }
168  d->mLock.unlock();
169  return res;
170 
171 #ifdef singlesourcedest
172  // fallback to checking src and dest separately
173  int srcTransform = d->mSourceDatumTransforms.value( srcKey, -1 );
174  int destTransform = d->mDestDatumTransforms.value( destKey, -1 );
175  d->mLock.unlock();
176  return qMakePair( srcTransform, destTransform );
177 #endif
178 }
179 
180 bool QgsCoordinateTransformContext::readXml( const QDomElement &element, const QgsReadWriteContext &, QStringList &missingTransforms )
181 {
182  d.detach();
183  d->mLock.lockForWrite();
184 
185  d->mSourceDestDatumTransforms.clear();
186 #if 0
187  d->mSourceDatumTransforms.clear();
188  d->mDestDatumTransforms.clear();
189 #endif
190 
191  const QDomNodeList contextNodes = element.elementsByTagName( QStringLiteral( "transformContext" ) );
192  if ( contextNodes.count() < 1 )
193  {
194  d->mLock.unlock();
195  return true;
196  }
197 
198  missingTransforms.clear();
199  bool result = true;
200 
201  const QDomElement contextElem = contextNodes.at( 0 ).toElement();
202 
203  // src/dest transforms
204  const QDomNodeList srcDestNodes = contextElem.elementsByTagName( QStringLiteral( "srcDest" ) );
205  for ( int i = 0; i < srcDestNodes.size(); ++i )
206  {
207  const QDomElement transformElem = srcDestNodes.at( i ).toElement();
208  QString key1 = transformElem.attribute( QStringLiteral( "source" ) );
209  QString key2 = transformElem.attribute( QStringLiteral( "dest" ) );
210 
211  QString value1 = transformElem.attribute( QStringLiteral( "sourceTransform" ) );
212  QString value2 = transformElem.attribute( QStringLiteral( "destTransform" ) );
213 
214  int datumId1 = -1;
215  int datumId2 = -1;
216  //warn if value1 or value2 is non-empty, yet no matching transform was found
217  if ( !value1.isEmpty() )
218  {
220  if ( datumId1 < 0 )
221  {
222  result = false;
223  missingTransforms << value1;
224  }
225  }
226  if ( !value2.isEmpty() )
227  {
229  if ( datumId2 < 0 )
230  {
231  result = false;
232  missingTransforms << value2;
233  }
234  }
235 
236  d->mSourceDestDatumTransforms.insert( qMakePair( key1, key2 ), QgsDatumTransform::TransformPair( datumId1, datumId2 ) );
237  }
238 
239 #if 0
240  // src transforms
241  const QDomNodeList srcNodes = contextElem.elementsByTagName( QStringLiteral( "source" ) );
242  for ( int i = 0; i < srcNodes .size(); ++i )
243  {
244  const QDomElement transformElem = srcNodes.at( i ).toElement();
245  QString key = transformElem.attribute( QStringLiteral( "crs" ) );
246  QString value = transformElem.attribute( QStringLiteral( "transform" ) );
247  if ( value.isEmpty() )
248  continue;
249 
250  int datumId = QgsCoordinateTransform::projStringToDatumTransformId( value );
251  //TODO - throw warning if datumId is -1
252  d->mSourceDatumTransforms.insert( key, datumId );
253  }
254 
255  // dest transforms
256  const QDomNodeList destNodes = contextElem.elementsByTagName( QStringLiteral( "dest" ) );
257  for ( int i = 0; i < destNodes.size(); ++i )
258  {
259  const QDomElement transformElem = destNodes.at( i ).toElement();
260  QString key = transformElem.attribute( QStringLiteral( "crs" ) );
261  QString value = transformElem.attribute( QStringLiteral( "transform" ) );
262  if ( value.isEmpty() )
263  continue;
264 
265  int datumId = QgsCoordinateTransform::projStringToDatumTransformId( value );
266  //TODO - throw warning if datumId is -1
267  d->mDestDatumTransforms.insert( key, datumId );
268  }
269 #endif
270 
271  d->mLock.unlock();
272  return result;
273 }
274 
275 void QgsCoordinateTransformContext::writeXml( QDomElement &element, const QgsReadWriteContext & ) const
276 {
277  d->mLock.lockForRead();
278 
279  QDomElement contextElem = element.ownerDocument().createElement( QStringLiteral( "transformContext" ) );
280 
281  //src/dest transforms
282  for ( auto it = d->mSourceDestDatumTransforms.constBegin(); it != d->mSourceDestDatumTransforms.constEnd(); ++ it )
283  {
284  QDomElement transformElem = element.ownerDocument().createElement( QStringLiteral( "srcDest" ) );
285  transformElem.setAttribute( QStringLiteral( "source" ), it.key().first );
286  transformElem.setAttribute( QStringLiteral( "dest" ), it.key().second );
287  transformElem.setAttribute( QStringLiteral( "sourceTransform" ), it.value().sourceTransformId < 0 ? QString() : QgsDatumTransform::datumTransformToProj( it.value().sourceTransformId ) );
288  transformElem.setAttribute( QStringLiteral( "destTransform" ), it.value().destinationTransformId < 0 ? QString() : QgsDatumTransform::datumTransformToProj( it.value().destinationTransformId ) );
289  contextElem.appendChild( transformElem );
290  }
291 
292 #if 0
293  // src transforms
294  for ( auto it = d->mSourceDatumTransforms.constBegin(); it != d->mSourceDatumTransforms.constEnd(); ++ it )
295  {
296  QDomElement transformElem = element.ownerDocument().createElement( QStringLiteral( "source" ) );
297  transformElem.setAttribute( QStringLiteral( "crs" ), it.key() );
298  transformElem.setAttribute( QStringLiteral( "transform" ), it.value() < 0 ? QString() : it.value() );
299  contextElem.appendChild( transformElem );
300  }
301 
302  // dest transforms
303  for ( auto it = d->mDestDatumTransforms.constBegin(); it != d->mDestDatumTransforms.constEnd(); ++ it )
304  {
305  QDomElement transformElem = element.ownerDocument().createElement( QStringLiteral( "dest" ) );
306  transformElem.setAttribute( QStringLiteral( "crs" ), it.key() );
307  transformElem.setAttribute( QStringLiteral( "transform" ), it.value() < 0 ? QString() : it.value() );
308  contextElem.appendChild( transformElem );
309  }
310 #endif
311 
312  element.appendChild( contextElem );
313  d->mLock.unlock();
314 }
315 
317 {
318  d.detach();
319  d->mLock.lockForWrite();
320 
321  d->mSourceDestDatumTransforms.clear();
322 #if 0
323  d->mSourceDatumTransforms.clear();
324  d->mDestDatumTransforms.clear();
325 #endif
326 
327  QgsSettings settings;
328  settings.beginGroup( QStringLiteral( "/Projections" ) );
329  QStringList projectionKeys = settings.allKeys();
330 
331  //collect src and dest entries that belong together
332  QMap< QPair< QString, QString >, QPair< int, int > > transforms;
333  QStringList::const_iterator pkeyIt = projectionKeys.constBegin();
334  for ( ; pkeyIt != projectionKeys.constEnd(); ++pkeyIt )
335  {
336  if ( pkeyIt->contains( QLatin1String( "srcTransform" ) ) || pkeyIt->contains( QLatin1String( "destTransform" ) ) )
337  {
338  QStringList split = pkeyIt->split( '/' );
339  QString srcAuthId, destAuthId;
340  if ( ! split.isEmpty() )
341  {
342  srcAuthId = split.at( 0 );
343  }
344  if ( split.size() > 1 )
345  {
346  destAuthId = split.at( 1 ).split( '_' ).at( 0 );
347  }
348 
349  QString proj = settings.value( *pkeyIt ).toString();
351  if ( pkeyIt->contains( QLatin1String( "srcTransform" ) ) )
352  {
353  transforms[ qMakePair( srcAuthId, destAuthId )].first = datumId;
354  }
355  else if ( pkeyIt->contains( QLatin1String( "destTransform" ) ) )
356  {
357  transforms[ qMakePair( srcAuthId, destAuthId )].second = datumId;
358  }
359  }
360  }
361 
362  // add transforms to context
363  QMap< QPair< QString, QString >, QPair< int, int > >::const_iterator transformIt = transforms.constBegin();
364  for ( ; transformIt != transforms.constEnd(); ++transformIt )
365  {
366  d->mSourceDestDatumTransforms.insert( transformIt.key(), QgsDatumTransform::TransformPair( transformIt.value().first, transformIt.value().second ) );
367  }
368 
369  d->mLock.unlock();
370  settings.endGroup();
371 }
372 
374 {
375  QgsSettings settings;
376  settings.beginGroup( QStringLiteral( "/Projections" ) );
377  QStringList groupKeys = settings.allKeys();
378  QStringList::const_iterator groupKeyIt = groupKeys.constBegin();
379  for ( ; groupKeyIt != groupKeys.constEnd(); ++groupKeyIt )
380  {
381  if ( groupKeyIt->contains( QLatin1String( "srcTransform" ) ) || groupKeyIt->contains( QLatin1String( "destTransform" ) ) )
382  {
383  settings.remove( *groupKeyIt );
384  }
385  }
386 
387  for ( auto transformIt = d->mSourceDestDatumTransforms.constBegin(); transformIt != d->mSourceDestDatumTransforms.constEnd(); ++transformIt )
388  {
389  QString srcAuthId = transformIt.key().first;
390  QString destAuthId = transformIt.key().second;
391  int sourceDatumTransform = transformIt.value().sourceTransformId;
392  QString sourceDatumProj;
393  if ( sourceDatumTransform >= 0 )
394  sourceDatumProj = QgsDatumTransform::datumTransformToProj( sourceDatumTransform );
395  int destinationDatumTransform = transformIt.value().destinationTransformId;
396  QString destinationDatumProj;
397  if ( destinationDatumTransform >= 0 )
398  destinationDatumProj = QgsDatumTransform::datumTransformToProj( destinationDatumTransform );
399 
400  settings.setValue( srcAuthId + "//" + destAuthId + "_srcTransform", sourceDatumProj );
401  settings.setValue( srcAuthId + "//" + destAuthId + "_destTransform", destinationDatumProj );
402  }
403 
404  settings.endGroup();
405 }
The class is used as a container of context for various read/write operations on other objects...
void endGroup()
Resets the group to what it was before the corresponding beginGroup() call.
Definition: qgssettings.cpp:97
QgsCoordinateTransformContext & operator=(const QgsCoordinateTransformContext &rhs)
Assignment operator.
QStringList allKeys() const
Returns a list of all keys, including subkeys, that can be read using the QSettings object...
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
void removeSourceDestinationDatumTransform(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs)
Removes the source to destination datum transform pair for the specified sourceCrs and destinationCrs...
QMap< QPair< QString, QString >, QgsDatumTransform::TransformPair > sourceDestinationDatumTransforms() const
Returns the stored mapping for source to destination CRS pairs to associated datum transforms to use...
void remove(const QString &key, QgsSettings::Section section=QgsSettings::NoSection)
Removes the setting key and any sub-settings of key in a section.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
const QgsCoordinateReferenceSystem & crs
bool hasTransform(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination) const
Returns true if the context has a valid datum transform to use when transforming from the specified s...
bool operator==(const QgsCoordinateTransformContext &rhs) const
bool readXml(const QDomElement &element, const QgsReadWriteContext &context, QStringList &missingTransforms)
Reads the context&#39;s state from a DOM element.
void readSettings()
Reads the context&#39;s state from application settings.
void clear()
Clears all stored transform information from the context.
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...
void writeXml(QDomElement &element, const QgsReadWriteContext &context) const
Writes the context&#39;s state to a DOM element.
Contains information about the context in which a coordinate transform is executed.
int destinationTransformId
ID for the datum transform to use when projecting to the destination CRS.
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...
void beginGroup(const QString &prefix, QgsSettings::Section section=QgsSettings::NoSection)
Appends prefix to the current group.
Definition: qgssettings.cpp:87
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Contains datum transform information.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
This class represents a coordinate reference system (CRS).
QString authid() const
Returns the authority identifier for the CRS.
static QString datumTransformToProj(int datumTransformId)
Returns a proj string representing the specified datumTransformId datum transform ID...
static int projStringToDatumTransformId(const QString &string)
Returns the datum transform ID corresponding to a specified proj string.
void writeSettings()
Write the context&#39;s state to application settings.
int sourceTransformId
ID for the datum transform to use when projecting from the source CRS.
QgsCoordinateTransformContext()
Constructor for QgsCoordinateTransformContext.