QGIS API Documentation  3.2.0-Bonn (bc43194)
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  d.detach();
42  // play it safe
43  d->mLock.lockForWrite();
44  d->mSourceDestDatumTransforms.clear();
45 #if 0
46  d->mSourceDatumTransforms.clear();
47  d->mDestDatumTransforms.clear();
48 #endif
49  d->mLock.unlock();
50 }
51 
52 #ifdef singlesourcedest
53 QMap<QString, int> QgsCoordinateTransformContext::sourceDatumTransforms() const
54 {
55  d->mLock.lockForRead();
56  auto res = d->mSourceDatumTransforms;
57  res.detach();
58  d->mLock.unlock();
59  return res;
60 }
61 
62 bool QgsCoordinateTransformContext::addSourceDatumTransform( const QgsCoordinateReferenceSystem &crs, int transform )
63 {
64  if ( !crs.isValid() )
65  return false;
66 
67  d.detach();
68  d->mLock.lockForWrite();
69  d->mSourceDatumTransforms.insert( crs.authid(), transform );
70  d->mLock.unlock();
71  return true;
72 }
73 
74 void QgsCoordinateTransformContext::removeSourceDatumTransform( const QgsCoordinateReferenceSystem &crs )
75 {
76  d->mSourceDatumTransforms.remove( crs.authid() );
77 }
78 
79 QMap<QString, int> QgsCoordinateTransformContext::destinationDatumTransforms() const
80 {
81  d->mLock.lockForRead();
82  auto res = d->mDestDatumTransforms;
83  res.detach();
84  d->mLock.unlock();
85  return res;
86 }
87 
88 bool QgsCoordinateTransformContext::addDestinationDatumTransform( const QgsCoordinateReferenceSystem &crs, int transform )
89 {
90  if ( !crs.isValid() )
91  return false;
92 
93  d.detach();
94 
95  d->mLock.lockForWrite();
96  d->mDestDatumTransforms.insert( crs.authid(), transform );
97  d->mLock.unlock();
98  return true;
99 }
100 
101 void QgsCoordinateTransformContext::removeDestinationDatumTransform( const QgsCoordinateReferenceSystem &crs )
102 {
103  d->mDestDatumTransforms.remove( crs.authid() );
104 }
105 
106 #endif
107 
109 {
110  d->mLock.lockForRead();
111  auto res = d->mSourceDestDatumTransforms;
112  res.detach();
113  d->mLock.unlock();
114  return res;
115 }
116 
117 bool QgsCoordinateTransformContext::addSourceDestinationDatumTransform( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, int sourceTransform, int destinationTransform )
118 {
119  if ( !sourceCrs.isValid() || !destinationCrs.isValid() )
120  return false;
121 
122  d.detach();
123  d->mLock.lockForWrite();
124  d->mSourceDestDatumTransforms.insert( qMakePair( sourceCrs.authid(), destinationCrs.authid() ), QgsDatumTransform::TransformPair( sourceTransform, destinationTransform ) );
125  d->mLock.unlock();
126  return true;
127 }
128 
130 {
131  d->mSourceDestDatumTransforms.remove( qMakePair( sourceCrs.authid(), destinationCrs.authid() ) );
132 }
133 
135 {
137  // calculateDatumTransforms already takes care of switching source and destination
138  return t.sourceTransformId != -1 || t.destinationTransformId != -1;
139 }
140 
142 {
143  QString srcKey = source.authid();
144  QString destKey = destination.authid();
145 
146  d->mLock.lockForRead();
147  // highest priority is exact match for source/dest pair
148  QgsDatumTransform::TransformPair res = d->mSourceDestDatumTransforms.value( qMakePair( srcKey, destKey ), QgsDatumTransform::TransformPair( -1, -1 ) );
149  if ( res.sourceTransformId == -1 && res.destinationTransformId == -1 )
150  {
151  // try to reverse
152  QgsDatumTransform::TransformPair res2 = d->mSourceDestDatumTransforms.value( qMakePair( destKey, srcKey ), QgsDatumTransform::TransformPair( -1, -1 ) );
154  }
155  d->mLock.unlock();
156  return res;
157 
158 #ifdef singlesourcedest
159  // fallback to checking src and dest separately
160  int srcTransform = d->mSourceDatumTransforms.value( srcKey, -1 );
161  int destTransform = d->mDestDatumTransforms.value( destKey, -1 );
162  d->mLock.unlock();
163  return qMakePair( srcTransform, destTransform );
164 #endif
165 }
166 
167 bool QgsCoordinateTransformContext::readXml( const QDomElement &element, const QgsReadWriteContext &, QStringList &missingTransforms )
168 {
169  d.detach();
170  d->mLock.lockForWrite();
171 
172  d->mSourceDestDatumTransforms.clear();
173 #if 0
174  d->mSourceDatumTransforms.clear();
175  d->mDestDatumTransforms.clear();
176 #endif
177 
178  const QDomNodeList contextNodes = element.elementsByTagName( QStringLiteral( "transformContext" ) );
179  if ( contextNodes.count() < 1 )
180  {
181  d->mLock.unlock();
182  return true;
183  }
184 
185  missingTransforms.clear();
186  bool result = true;
187 
188  const QDomElement contextElem = contextNodes.at( 0 ).toElement();
189 
190  // src/dest transforms
191  const QDomNodeList srcDestNodes = contextElem.elementsByTagName( QStringLiteral( "srcDest" ) );
192  for ( int i = 0; i < srcDestNodes.size(); ++i )
193  {
194  const QDomElement transformElem = srcDestNodes.at( i ).toElement();
195  QString key1 = transformElem.attribute( QStringLiteral( "source" ) );
196  QString key2 = transformElem.attribute( QStringLiteral( "dest" ) );
197 
198  QString value1 = transformElem.attribute( QStringLiteral( "sourceTransform" ) );
199  QString value2 = transformElem.attribute( QStringLiteral( "destTransform" ) );
200 
201  int datumId1 = -1;
202  int datumId2 = -1;
203  //warn if value1 or value2 is non-empty, yet no matching transform was found
204  if ( !value1.isEmpty() )
205  {
207  if ( datumId1 < 0 )
208  {
209  result = false;
210  missingTransforms << value1;
211  }
212  }
213  if ( !value2.isEmpty() )
214  {
216  if ( datumId2 < 0 )
217  {
218  result = false;
219  missingTransforms << value2;
220  }
221  }
222 
223  d->mSourceDestDatumTransforms.insert( qMakePair( key1, key2 ), QgsDatumTransform::TransformPair( datumId1, datumId2 ) );
224  }
225 
226 #if 0
227  // src transforms
228  const QDomNodeList srcNodes = contextElem.elementsByTagName( QStringLiteral( "source" ) );
229  for ( int i = 0; i < srcNodes .size(); ++i )
230  {
231  const QDomElement transformElem = srcNodes.at( i ).toElement();
232  QString key = transformElem.attribute( QStringLiteral( "crs" ) );
233  QString value = transformElem.attribute( QStringLiteral( "transform" ) );
234  if ( value.isEmpty() )
235  continue;
236 
237  int datumId = QgsCoordinateTransform::projStringToDatumTransformId( value );
238  //TODO - throw warning if datumId is -1
239  d->mSourceDatumTransforms.insert( key, datumId );
240  }
241 
242  // dest transforms
243  const QDomNodeList destNodes = contextElem.elementsByTagName( QStringLiteral( "dest" ) );
244  for ( int i = 0; i < destNodes.size(); ++i )
245  {
246  const QDomElement transformElem = destNodes.at( i ).toElement();
247  QString key = transformElem.attribute( QStringLiteral( "crs" ) );
248  QString value = transformElem.attribute( QStringLiteral( "transform" ) );
249  if ( value.isEmpty() )
250  continue;
251 
252  int datumId = QgsCoordinateTransform::projStringToDatumTransformId( value );
253  //TODO - throw warning if datumId is -1
254  d->mDestDatumTransforms.insert( key, datumId );
255  }
256 #endif
257 
258  d->mLock.unlock();
259  return result;
260 }
261 
262 void QgsCoordinateTransformContext::writeXml( QDomElement &element, const QgsReadWriteContext & ) const
263 {
264  d->mLock.lockForRead();
265 
266  QDomElement contextElem = element.ownerDocument().createElement( QStringLiteral( "transformContext" ) );
267 
268  //src/dest transforms
269  for ( auto it = d->mSourceDestDatumTransforms.constBegin(); it != d->mSourceDestDatumTransforms.constEnd(); ++ it )
270  {
271  QDomElement transformElem = element.ownerDocument().createElement( QStringLiteral( "srcDest" ) );
272  transformElem.setAttribute( QStringLiteral( "source" ), it.key().first );
273  transformElem.setAttribute( QStringLiteral( "dest" ), it.key().second );
274  transformElem.setAttribute( QStringLiteral( "sourceTransform" ), it.value().sourceTransformId < 0 ? QString() : QgsDatumTransform::datumTransformToProj( it.value().sourceTransformId ) );
275  transformElem.setAttribute( QStringLiteral( "destTransform" ), it.value().destinationTransformId < 0 ? QString() : QgsDatumTransform::datumTransformToProj( it.value().destinationTransformId ) );
276  contextElem.appendChild( transformElem );
277  }
278 
279 #if 0
280  // src transforms
281  for ( auto it = d->mSourceDatumTransforms.constBegin(); it != d->mSourceDatumTransforms.constEnd(); ++ it )
282  {
283  QDomElement transformElem = element.ownerDocument().createElement( QStringLiteral( "source" ) );
284  transformElem.setAttribute( QStringLiteral( "crs" ), it.key() );
285  transformElem.setAttribute( QStringLiteral( "transform" ), it.value() < 0 ? QString() : it.value() );
286  contextElem.appendChild( transformElem );
287  }
288 
289  // dest transforms
290  for ( auto it = d->mDestDatumTransforms.constBegin(); it != d->mDestDatumTransforms.constEnd(); ++ it )
291  {
292  QDomElement transformElem = element.ownerDocument().createElement( QStringLiteral( "dest" ) );
293  transformElem.setAttribute( QStringLiteral( "crs" ), it.key() );
294  transformElem.setAttribute( QStringLiteral( "transform" ), it.value() < 0 ? QString() : it.value() );
295  contextElem.appendChild( transformElem );
296  }
297 #endif
298 
299  element.appendChild( contextElem );
300  d->mLock.unlock();
301 }
302 
304 {
305  d.detach();
306  d->mLock.lockForWrite();
307 
308  d->mSourceDestDatumTransforms.clear();
309 #if 0
310  d->mSourceDatumTransforms.clear();
311  d->mDestDatumTransforms.clear();
312 #endif
313 
314  QgsSettings settings;
315  settings.beginGroup( QStringLiteral( "/Projections" ) );
316  QStringList projectionKeys = settings.allKeys();
317 
318  //collect src and dest entries that belong together
319  QMap< QPair< QString, QString >, QPair< int, int > > transforms;
320  QStringList::const_iterator pkeyIt = projectionKeys.constBegin();
321  for ( ; pkeyIt != projectionKeys.constEnd(); ++pkeyIt )
322  {
323  if ( pkeyIt->contains( QLatin1String( "srcTransform" ) ) || pkeyIt->contains( QLatin1String( "destTransform" ) ) )
324  {
325  QStringList split = pkeyIt->split( '/' );
326  QString srcAuthId, destAuthId;
327  if ( ! split.isEmpty() )
328  {
329  srcAuthId = split.at( 0 );
330  }
331  if ( split.size() > 1 )
332  {
333  destAuthId = split.at( 1 ).split( '_' ).at( 0 );
334  }
335 
336  QString proj = settings.value( *pkeyIt ).toString();
338  if ( pkeyIt->contains( QLatin1String( "srcTransform" ) ) )
339  {
340  transforms[ qMakePair( srcAuthId, destAuthId )].first = datumId;
341  }
342  else if ( pkeyIt->contains( QLatin1String( "destTransform" ) ) )
343  {
344  transforms[ qMakePair( srcAuthId, destAuthId )].second = datumId;
345  }
346  }
347  }
348 
349  // add transforms to context
350  QMap< QPair< QString, QString >, QPair< int, int > >::const_iterator transformIt = transforms.constBegin();
351  for ( ; transformIt != transforms.constEnd(); ++transformIt )
352  {
353  d->mSourceDestDatumTransforms.insert( transformIt.key(), QgsDatumTransform::TransformPair( transformIt.value().first, transformIt.value().second ) );
354  }
355 
356  d->mLock.unlock();
357  settings.endGroup();
358 }
359 
361 {
362  QgsSettings settings;
363  settings.beginGroup( QStringLiteral( "/Projections" ) );
364  QStringList groupKeys = settings.allKeys();
365  QStringList::const_iterator groupKeyIt = groupKeys.constBegin();
366  for ( ; groupKeyIt != groupKeys.constEnd(); ++groupKeyIt )
367  {
368  if ( groupKeyIt->contains( QLatin1String( "srcTransform" ) ) || groupKeyIt->contains( QLatin1String( "destTransform" ) ) )
369  {
370  settings.remove( *groupKeyIt );
371  }
372  }
373 
374  for ( auto transformIt = d->mSourceDestDatumTransforms.constBegin(); transformIt != d->mSourceDestDatumTransforms.constEnd(); ++transformIt )
375  {
376  QString srcAuthId = transformIt.key().first;
377  QString destAuthId = transformIt.key().second;
378  int sourceDatumTransform = transformIt.value().sourceTransformId;
379  QString sourceDatumProj;
380  if ( sourceDatumTransform >= 0 )
381  sourceDatumProj = QgsDatumTransform::datumTransformToProj( sourceDatumTransform );
382  int destinationDatumTransform = transformIt.value().destinationTransformId;
383  QString destinationDatumProj;
384  if ( destinationDatumTransform >= 0 )
385  destinationDatumProj = QgsDatumTransform::datumTransformToProj( destinationDatumTransform );
386 
387  settings.setValue( srcAuthId + "//" + destAuthId + "_srcTransform", sourceDatumProj );
388  settings.setValue( srcAuthId + "//" + destAuthId + "_destTransform", destinationDatumProj );
389  }
390 
391  settings.endGroup();
392 }
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...
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.