QGIS API Documentation  3.6.0-Noosa (5873452)
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:96
QgsCoordinateTransformContext & operator=(const QgsCoordinateTransformContext &rhs)
Assignment operator.
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...
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void remove(const QString &key, QgsSettings::Section section=QgsSettings::NoSection)
Removes the setting key and any sub-settings of key in a section.
QStringList allKeys() const
Returns a list of all keys, including subkeys, that can be read using the QSettings object...
const QgsCoordinateReferenceSystem & crs
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.
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.
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 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 writeXml(QDomElement &element, const QgsReadWriteContext &context) const
Writes the context&#39;s state to a DOM element.
void beginGroup(const QString &prefix, QgsSettings::Section section=QgsSettings::NoSection)
Appends prefix to the current group.
Definition: qgssettings.cpp:86
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...
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).
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.
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...
QString authid() const
Returns the authority identifier for the CRS.
QgsCoordinateTransformContext()
Constructor for QgsCoordinateTransformContext.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.