QGIS API Documentation  3.2.0-Bonn (bc43194)
qgscoordinatetransform.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  QgsCoordinateTransform.cpp - Coordinate Transforms
3  -------------------
4  begin : Dec 2004
5  copyright : (C) 2004 Tim Sutton
6  email : tim at linfiniti.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 #include "qgscoordinatetransform.h"
19 #include "qgsapplication.h"
20 #include "qgsmessagelog.h"
21 #include "qgslogger.h"
22 #include "qgspointxy.h"
23 #include "qgsrectangle.h"
24 #include "qgsexception.h"
25 #include "qgsproject.h"
26 
27 //qt includes
28 #include <QDomNode>
29 #include <QDomElement>
30 #include <QApplication>
31 #include <QPolygonF>
32 #include <QStringList>
33 #include <QVector>
34 
35 extern "C"
36 {
37 #include <proj_api.h>
38 }
39 #include <sqlite3.h>
40 
41 // if defined shows all information about transform to stdout
42 // #define COORDINATE_TRANSFORM_VERBOSE
43 
44 QReadWriteLock QgsCoordinateTransform::sCacheLock;
45 QMultiHash< QPair< QString, QString >, QgsCoordinateTransform > QgsCoordinateTransform::sTransforms; //same auth_id pairs might have different datum transformations
46 
48 {
49  d = new QgsCoordinateTransformPrivate();
50 }
51 
53 {
54  d = new QgsCoordinateTransformPrivate( source, destination, QgsCoordinateTransformContext() );
55 
56  if ( !d->checkValidity() )
57  return;
58 
59  if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
60  {
61  d->initialize();
62  addToCache();
63  }
64 }
65 
67 {
68  mContext = context;
69  d = new QgsCoordinateTransformPrivate( source, destination, mContext );
70 #ifdef QGISDEBUG
71  mHasContext = true;
72 #endif
73 
74  if ( !d->checkValidity() )
75  return;
76 
77  if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
78  {
79  d->initialize();
80  addToCache();
81  }
82 }
83 
85 {
86  mContext = project ? project->transformContext() : QgsCoordinateTransformContext();
87  d = new QgsCoordinateTransformPrivate( source, destination, mContext );
88 #ifdef QGISDEBUG
89  if ( project )
90  mHasContext = true;
91 #endif
92 
93  if ( !d->checkValidity() )
94  return;
95 
96  if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
97  {
98  d->initialize();
99  addToCache();
100  }
101 }
102 
103 QgsCoordinateTransform::QgsCoordinateTransform( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination, int sourceDatumTransform, int destinationDatumTransform )
104 {
105  d = new QgsCoordinateTransformPrivate( source, destination, sourceDatumTransform, destinationDatumTransform );
106 #ifdef QGISDEBUG
107  mHasContext = true; // not strictly true, but we don't need to worry if datums have been explicitly set
108 #endif
109 
110  if ( !d->checkValidity() )
111  return;
112 
113  if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
114  {
115  d->initialize();
116  addToCache();
117  }
118 }
119 
121  : mContext( o.mContext )
122 #ifdef QGISDEBUG
123  , mHasContext( o.mHasContext )
124 #endif
125 {
126  d = o.d;
127 }
128 
130 {
131  d = o.d;
132 #ifdef QGISDEBUG
133  mHasContext = o.mHasContext;
134 #endif
135  mContext = o.mContext;
136  return *this;
137 }
138 
140 
142 {
143  d.detach();
144  d->mSourceCRS = crs;
145  if ( !d->checkValidity() )
146  return;
147 
148  d->calculateTransforms( mContext );
149  if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
150  {
151  d->initialize();
152  addToCache();
153  }
154 }
156 {
157  d.detach();
158  d->mDestCRS = crs;
159  if ( !d->checkValidity() )
160  return;
161 
162  d->calculateTransforms( mContext );
163  if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
164  {
165  d->initialize();
166  addToCache();
167  }
168 }
169 
171 {
172  d.detach();
173  mContext = context;
174 #ifdef QGISDEBUG
175  mHasContext = true;
176 #endif
177  if ( !d->checkValidity() )
178  return;
179 
180  d->calculateTransforms( mContext );
181  if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
182  {
183  d->initialize();
184  addToCache();
185  }
186 }
187 
189 {
190  return d->mSourceCRS;
191 }
192 
194 {
195  return d->mDestCRS;
196 }
197 
199 {
200  if ( !d->mIsValid || d->mShortCircuit )
201  return point;
202 
203  // transform x
204  double x = point.x();
205  double y = point.y();
206  double z = 0.0;
207  try
208  {
209  transformCoords( 1, &x, &y, &z, direction );
210  }
211  catch ( const QgsCsException & )
212  {
213  // rethrow the exception
214  QgsDebugMsg( "rethrowing exception" );
215  throw;
216  }
217 
218  return QgsPointXY( x, y );
219 }
220 
221 
222 QgsPointXY QgsCoordinateTransform::transform( const double theX, const double theY = 0.0, TransformDirection direction ) const
223 {
224  try
225  {
226  return transform( QgsPointXY( theX, theY ), direction );
227  }
228  catch ( const QgsCsException & )
229  {
230  // rethrow the exception
231  QgsDebugMsg( "rethrowing exception" );
232  throw;
233  }
234 }
235 
237 {
238  if ( !d->mIsValid || d->mShortCircuit )
239  return rect;
240  // transform x
241  double x1 = rect.xMinimum();
242  double y1 = rect.yMinimum();
243  double x2 = rect.xMaximum();
244  double y2 = rect.yMaximum();
245 
246  // Number of points to reproject------+
247  // |
248  // V
249  try
250  {
251  double z = 0.0;
252  transformCoords( 1, &x1, &y1, &z, direction );
253  transformCoords( 1, &x2, &y2, &z, direction );
254  }
255  catch ( const QgsCsException & )
256  {
257  // rethrow the exception
258  QgsDebugMsg( "rethrowing exception" );
259  throw;
260  }
261 
262 #ifdef COORDINATE_TRANSFORM_VERBOSE
263  QgsDebugMsg( "Rect projection..." );
264  QgsDebugMsg( QString( "Xmin : %1 --> %2" ).arg( rect.xMinimum() ).arg( x1 ) );
265  QgsDebugMsg( QString( "Ymin : %1 --> %2" ).arg( rect.yMinimum() ).arg( y1 ) );
266  QgsDebugMsg( QString( "Xmax : %1 --> %2" ).arg( rect.xMaximum() ).arg( x2 ) );
267  QgsDebugMsg( QString( "Ymax : %1 --> %2" ).arg( rect.yMaximum() ).arg( y2 ) );
268 #endif
269  return QgsRectangle( x1, y1, x2, y2 );
270 }
271 
272 void QgsCoordinateTransform::transformInPlace( double &x, double &y, double &z,
273  TransformDirection direction ) const
274 {
275  if ( !d->mIsValid || d->mShortCircuit )
276  return;
277 #ifdef QGISDEBUG
278 // QgsDebugMsg(QString("Using transform in place %1 %2").arg(__FILE__).arg(__LINE__));
279 #endif
280  // transform x
281  try
282  {
283  transformCoords( 1, &x, &y, &z, direction );
284  }
285  catch ( const QgsCsException & )
286  {
287  // rethrow the exception
288  QgsDebugMsg( "rethrowing exception" );
289  throw;
290  }
291 }
292 
293 void QgsCoordinateTransform::transformInPlace( float &x, float &y, double &z,
294  TransformDirection direction ) const
295 {
296  double xd = static_cast< double >( x ), yd = static_cast< double >( y );
297  transformInPlace( xd, yd, z, direction );
298  x = xd;
299  y = yd;
300 }
301 
302 void QgsCoordinateTransform::transformInPlace( float &x, float &y, float &z,
303  TransformDirection direction ) const
304 {
305  if ( !d->mIsValid || d->mShortCircuit )
306  return;
307 #ifdef QGISDEBUG
308  // QgsDebugMsg(QString("Using transform in place %1 %2").arg(__FILE__).arg(__LINE__));
309 #endif
310  // transform x
311  try
312  {
313  double xd = x;
314  double yd = y;
315  double zd = z;
316  transformCoords( 1, &xd, &yd, &zd, direction );
317  x = xd;
318  y = yd;
319  z = zd;
320  }
321  catch ( QgsCsException & )
322  {
323  // rethrow the exception
324  QgsDebugMsg( "rethrowing exception" );
325  throw;
326  }
327 }
328 
329 void QgsCoordinateTransform::transformPolygon( QPolygonF &poly, TransformDirection direction ) const
330 {
331  if ( !d->mIsValid || d->mShortCircuit )
332  {
333  return;
334  }
335 
336  //create x, y arrays
337  int nVertices = poly.size();
338 
339  QVector<double> x( nVertices );
340  QVector<double> y( nVertices );
341  QVector<double> z( nVertices );
342  double *destX = x.data();
343  double *destY = y.data();
344  double *destZ = z.data();
345 
346  const QPointF *polyData = poly.constData();
347  for ( int i = 0; i < nVertices; ++i )
348  {
349  *destX++ = polyData->x();
350  *destY++ = polyData->y();
351  *destZ++ = 0;
352  polyData++;
353  }
354 
355  try
356  {
357  transformCoords( nVertices, x.data(), y.data(), z.data(), direction );
358  }
359  catch ( const QgsCsException & )
360  {
361  // rethrow the exception
362  QgsDebugMsg( "rethrowing exception" );
363  throw;
364  }
365 
366  QPointF *destPoint = poly.data();
367  const double *srcX = x.constData();
368  const double *srcY = y.constData();
369  for ( int i = 0; i < nVertices; ++i )
370  {
371  destPoint->rx() = *srcX++;
372  destPoint->ry() = *srcY++;
373  destPoint++;
374  }
375 }
376 
378  QVector<double> &x, QVector<double> &y, QVector<double> &z,
379  TransformDirection direction ) const
380 {
381 
382  if ( !d->mIsValid || d->mShortCircuit )
383  return;
384 
385  Q_ASSERT( x.size() == y.size() );
386 
387  // Apparently, if one has a std::vector, it is valid to use the
388  // address of the first element in the vector as a pointer to an
389  // array of the vectors data, and hence easily interface with code
390  // that wants C-style arrays.
391 
392  try
393  {
394  transformCoords( x.size(), &x[0], &y[0], &z[0], direction );
395  }
396  catch ( const QgsCsException & )
397  {
398  // rethrow the exception
399  QgsDebugMsg( "rethrowing exception" );
400  throw;
401  }
402 }
403 
404 
406  QVector<float> &x, QVector<float> &y, QVector<float> &z,
407  TransformDirection direction ) const
408 {
409  if ( !d->mIsValid || d->mShortCircuit )
410  return;
411 
412  Q_ASSERT( x.size() == y.size() );
413 
414  // Apparently, if one has a std::vector, it is valid to use the
415  // address of the first element in the vector as a pointer to an
416  // array of the vectors data, and hence easily interface with code
417  // that wants C-style arrays.
418 
419  try
420  {
421  //copy everything to double vectors since proj needs double
422  int vectorSize = x.size();
423  QVector<double> xd( x.size() );
424  QVector<double> yd( y.size() );
425  QVector<double> zd( z.size() );
426 
427  double *destX = xd.data();
428  double *destY = yd.data();
429  double *destZ = zd.data();
430 
431  const float *srcX = x.constData();
432  const float *srcY = y.constData();
433  const float *srcZ = z.constData();
434 
435  for ( int i = 0; i < vectorSize; ++i )
436  {
437  *destX++ = static_cast< double >( *srcX++ );
438  *destY++ = static_cast< double >( *srcY++ );
439  *destZ++ = static_cast< double >( *srcZ++ );
440  }
441 
442  transformCoords( x.size(), &xd[0], &yd[0], &zd[0], direction );
443 
444  //copy back
445  float *destFX = x.data();
446  float *destFY = y.data();
447  float *destFZ = z.data();
448  const double *srcXD = xd.constData();
449  const double *srcYD = yd.constData();
450  const double *srcZD = zd.constData();
451  for ( int i = 0; i < vectorSize; ++i )
452  {
453  *destFX++ = static_cast< float >( *srcXD++ );
454  *destFY++ = static_cast< float >( *srcYD++ );
455  *destFZ++ = static_cast< float >( *srcZD++ );
456  }
457  }
458  catch ( QgsCsException & )
459  {
460  // rethrow the exception
461  QgsDebugMsg( "rethrowing exception" );
462  throw;
463  }
464 }
465 
466 QgsRectangle QgsCoordinateTransform::transformBoundingBox( const QgsRectangle &rect, TransformDirection direction, const bool handle180Crossover ) const
467 {
468  // Calculate the bounding box of a QgsRectangle in the source CRS
469  // when projected to the destination CRS (or the inverse).
470  // This is done by looking at a number of points spread evenly
471  // across the rectangle
472 
473  if ( !d->mIsValid || d->mShortCircuit )
474  return rect;
475 
476  if ( rect.isEmpty() )
477  {
478  QgsPointXY p = transform( rect.xMinimum(), rect.yMinimum(), direction );
479  return QgsRectangle( p, p );
480  }
481 
482  // 64 points (<=2.12) is not enough, see #13665, for EPSG:4326 -> EPSG:3574 (say that it is a hard one),
483  // are decent result from about 500 points and more. This method is called quite often, but
484  // even with 1000 points it takes < 1ms
485  // TODO: how to effectively and precisely reproject bounding box?
486  const int nPoints = 1000;
487  double d = std::sqrt( ( rect.width() * rect.height() ) / std::pow( std::sqrt( static_cast< double >( nPoints ) ) - 1, 2.0 ) );
488  int nXPoints = static_cast< int >( std::ceil( rect.width() / d ) ) + 1;
489  int nYPoints = static_cast< int >( std::ceil( rect.height() / d ) ) + 1;
490 
491  QgsRectangle bb_rect;
492  bb_rect.setMinimal();
493 
494  // We're interfacing with C-style vectors in the
495  // end, so let's do C-style vectors here too.
496 
497  QVector<double> x( nXPoints * nYPoints );
498  QVector<double> y( nXPoints * nYPoints );
499  QVector<double> z( nXPoints * nYPoints );
500 
501  QgsDebugMsgLevel( "Entering transformBoundingBox...", 4 );
502 
503  // Populate the vectors
504 
505  double dx = rect.width() / static_cast< double >( nXPoints - 1 );
506  double dy = rect.height() / static_cast< double >( nYPoints - 1 );
507 
508  double pointY = rect.yMinimum();
509 
510  for ( int i = 0; i < nYPoints ; i++ )
511  {
512 
513  // Start at right edge
514  double pointX = rect.xMinimum();
515 
516  for ( int j = 0; j < nXPoints; j++ )
517  {
518  x[( i * nXPoints ) + j] = pointX;
519  y[( i * nXPoints ) + j] = pointY;
520  // and the height...
521  z[( i * nXPoints ) + j] = 0.0;
522  // QgsDebugMsg(QString("BBox coord: (%1, %2)").arg(x[(i*numP) + j]).arg(y[(i*numP) + j]));
523  pointX += dx;
524  }
525  pointY += dy;
526  }
527 
528  // Do transformation. Any exception generated must
529  // be handled in above layers.
530  try
531  {
532  transformCoords( nXPoints * nYPoints, x.data(), y.data(), z.data(), direction );
533  }
534  catch ( const QgsCsException & )
535  {
536  // rethrow the exception
537  QgsDebugMsg( "rethrowing exception" );
538  throw;
539  }
540 
541  // Calculate the bounding box and use that for the extent
542 
543  for ( int i = 0; i < nXPoints * nYPoints; i++ )
544  {
545  if ( !std::isfinite( x[i] ) || !std::isfinite( y[i] ) )
546  {
547  continue;
548  }
549 
550  if ( handle180Crossover )
551  {
552  //if crossing the date line, temporarily add 360 degrees to -ve longitudes
553  bb_rect.combineExtentWith( x[i] >= 0.0 ? x[i] : x[i] + 360.0, y[i] );
554  }
555  else
556  {
557  bb_rect.combineExtentWith( x[i], y[i] );
558  }
559  }
560 
561  if ( bb_rect.isNull() )
562  {
563  // something bad happened when reprojecting the filter rect... no finite points were left!
564  throw QgsCsException( QObject::tr( "Could not transform bounding box to target CRS" ) );
565  }
566 
567  if ( handle180Crossover )
568  {
569  //subtract temporary addition of 360 degrees from longitudes
570  if ( bb_rect.xMinimum() > 180.0 )
571  bb_rect.setXMinimum( bb_rect.xMinimum() - 360.0 );
572  if ( bb_rect.xMaximum() > 180.0 )
573  bb_rect.setXMaximum( bb_rect.xMaximum() - 360.0 );
574  }
575 
576  QgsDebugMsgLevel( "Projected extent: " + bb_rect.toString(), 4 );
577 
578  if ( bb_rect.isEmpty() )
579  {
580  QgsDebugMsgLevel( "Original extent: " + rect.toString(), 4 );
581  }
582 
583  return bb_rect;
584 }
585 
586 void QgsCoordinateTransform::transformCoords( int numPoints, double *x, double *y, double *z, TransformDirection direction ) const
587 {
588  if ( !d->mIsValid || d->mShortCircuit )
589  return;
590  // Refuse to transform the points if the srs's are invalid
591  if ( !d->mSourceCRS.isValid() )
592  {
593  QgsMessageLog::logMessage( QObject::tr( "The source spatial reference system (CRS) is not valid. "
594  "The coordinates can not be reprojected. The CRS is: %1" )
595  .arg( d->mSourceCRS.toProj4() ), QObject::tr( "CRS" ) );
596  return;
597  }
598  if ( !d->mDestCRS.isValid() )
599  {
600  QgsMessageLog::logMessage( QObject::tr( "The destination spatial reference system (CRS) is not valid. "
601  "The coordinates can not be reprojected. The CRS is: %1" ).arg( d->mDestCRS.toProj4() ), QObject::tr( "CRS" ) );
602  return;
603  }
604 
605 #ifdef COORDINATE_TRANSFORM_VERBOSE
606  double xorg = *x;
607  double yorg = *y;
608  QgsDebugMsg( QString( "[[[[[[ Number of points to transform: %1 ]]]]]]" ).arg( numPoints ) );
609 #endif
610 
611 #ifdef QGISDEBUG
612  if ( !mHasContext )
613  qWarning( "No QgsCoordinateTransformContext context set for transform" );
614 #endif
615 
616  // use proj4 to do the transform
617 
618  // if the source/destination projection is lat/long, convert the points to radians
619  // prior to transforming
620  QPair<projPJ, projPJ> projData = d->threadLocalProjData();
621  projPJ sourceProj = projData.first;
622  projPJ destProj = projData.second;
623 
624  if ( ( pj_is_latlong( destProj ) && ( direction == ReverseTransform ) )
625  || ( pj_is_latlong( sourceProj ) && ( direction == ForwardTransform ) ) )
626  {
627  for ( int i = 0; i < numPoints; ++i )
628  {
629  x[i] *= DEG_TO_RAD;
630  y[i] *= DEG_TO_RAD;
631  }
632 
633  }
634  int projResult;
635  if ( direction == ReverseTransform )
636  {
637  projResult = pj_transform( destProj, sourceProj, numPoints, 0, x, y, z );
638  }
639  else
640  {
641  Q_ASSERT( sourceProj );
642  Q_ASSERT( destProj );
643  projResult = pj_transform( sourceProj, destProj, numPoints, 0, x, y, z );
644  }
645 
646  if ( projResult != 0 )
647  {
648  //something bad happened....
649  QString points;
650 
651  for ( int i = 0; i < numPoints; ++i )
652  {
653  if ( direction == ForwardTransform )
654  {
655  points += QStringLiteral( "(%1, %2)\n" ).arg( x[i], 0, 'f' ).arg( y[i], 0, 'f' );
656  }
657  else
658  {
659  points += QStringLiteral( "(%1, %2)\n" ).arg( x[i] * RAD_TO_DEG, 0, 'f' ).arg( y[i] * RAD_TO_DEG, 0, 'f' );
660  }
661  }
662 
663  QString dir = ( direction == ForwardTransform ) ? QObject::tr( "forward transform" ) : QObject::tr( "inverse transform" );
664 
665  char *srcdef = pj_get_def( sourceProj, 0 );
666  char *dstdef = pj_get_def( destProj, 0 );
667 
668  QString msg = QObject::tr( "%1 of\n"
669  "%2"
670  "PROJ: %3 +to %4\n"
671  "Error: %5" )
672  .arg( dir,
673  points,
674  srcdef, dstdef,
675  QString::fromUtf8( pj_strerrno( projResult ) ) );
676 
677  pj_dalloc( srcdef );
678  pj_dalloc( dstdef );
679 
680  QgsDebugMsg( "Projection failed emitting invalid transform signal: " + msg );
681  QgsDebugMsg( "throwing exception" );
682 
683  throw QgsCsException( msg );
684  }
685 
686  // if the result is lat/long, convert the results from radians back
687  // to degrees
688  if ( ( pj_is_latlong( destProj ) && ( direction == ForwardTransform ) )
689  || ( pj_is_latlong( sourceProj ) && ( direction == ReverseTransform ) ) )
690  {
691  for ( int i = 0; i < numPoints; ++i )
692  {
693  x[i] *= RAD_TO_DEG;
694  y[i] *= RAD_TO_DEG;
695  }
696  }
697 #ifdef COORDINATE_TRANSFORM_VERBOSE
698  QgsDebugMsg( QString( "[[[[[[ Projected %1, %2 to %3, %4 ]]]]]]" )
699  .arg( xorg, 0, 'g', 15 ).arg( yorg, 0, 'g', 15 )
700  .arg( *x, 0, 'g', 15 ).arg( *y, 0, 'g', 15 ) );
701 #endif
702 }
703 
705 {
706  return d->mIsValid;
707 }
708 
710 {
711  return !d->mIsValid || d->mShortCircuit;
712 }
713 
714 const char *finder( const char *name )
715 {
716  QString proj;
717 #ifdef Q_OS_WIN
718  proj = QApplication::applicationDirPath()
719  + "/share/proj/" + QString( name );
720 #else
721  Q_UNUSED( name );
722 #endif
723  return proj.toUtf8();
724 }
725 
726 bool QgsCoordinateTransform::setFromCache( const QgsCoordinateReferenceSystem &src, const QgsCoordinateReferenceSystem &dest, int srcDatumTransform, int destDatumTransform )
727 {
728  if ( !src.isValid() || !dest.isValid() )
729  return false;
730 
731  sCacheLock.lockForRead();
732  const QList< QgsCoordinateTransform > values = sTransforms.values( qMakePair( src.authid(), dest.authid() ) );
733  for ( auto valIt = values.constBegin(); valIt != values.constEnd(); ++valIt )
734  {
735  if ( ( *valIt ).sourceDatumTransformId() == srcDatumTransform &&
736  ( *valIt ).destinationDatumTransformId() == destDatumTransform )
737  {
738  // need to save, and then restore the context... we don't want this to be cached or to use the values from the cache
739  QgsCoordinateTransformContext context = mContext;
740 #ifdef QGISDEBUG
741  bool hasContext = mHasContext;
742 #endif
743  *this = *valIt;
744  sCacheLock.unlock();
745 
746  mContext = context;
747 #ifdef QGISDEBUG
748  mHasContext = hasContext;
749 #endif
750 
751  return true;
752  }
753  }
754  sCacheLock.unlock();
755  return false;
756 }
757 
758 void QgsCoordinateTransform::addToCache()
759 {
760  if ( !d->mSourceCRS.isValid() || !d->mDestCRS.isValid() )
761  return;
762 
763  sCacheLock.lockForWrite();
764  sTransforms.insertMulti( qMakePair( d->mSourceCRS.authid(), d->mDestCRS.authid() ), *this );
765  sCacheLock.unlock();
766 }
767 
769 {
770  return d->mSourceDatumTransform;
771 }
772 
774 {
775  d.detach();
776  d->mSourceDatumTransform = dt;
777 }
778 
780 {
781  return d->mDestinationDatumTransform;
782 }
783 
785 {
786  d.detach();
787  d->mDestinationDatumTransform = dt;
788 }
789 
791 {
792  sCacheLock.lockForWrite();
793  sTransforms.clear();
794  sCacheLock.unlock();
795 }
A rectangle specified with double values.
Definition: qgsrectangle.h:40
void setDestinationDatumTransformId(int datumId)
Sets the datumId ID of the datum transform to use when projecting to the destination CRS...
void setMinimal()
Set a rectangle so that min corner is at max and max corner is at min.
Definition: qgsrectangle.h:150
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:134
void setContext(const QgsCoordinateTransformContext &context)
Sets the context in which the coordinate transform should be calculated.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsPointXY transform(const QgsPointXY &point, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transform the point from the source CRS to the destination CRS.
double y
Definition: qgspointxy.h:48
A class to represent a 2D point.
Definition: qgspointxy.h:43
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
QgsCoordinateTransform & operator=(const QgsCoordinateTransform &o)
Assignment operator.
QgsCoordinateTransformContext transformContext() const
Returns a copy of the project&#39;s coordinate transform context, which stores various information regard...
Definition: qgsproject.cpp:543
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
void transformPolygon(QPolygonF &polygon, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transforms a polygon to the destination coordinate system.
QgsCoordinateTransform()
Default constructor, creates an invalid QgsCoordinateTransform.
QgsCoordinateReferenceSystem sourceCrs() const
Returns the source coordinate reference system, which the transform will transform coordinates from...
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination coordinate reference system.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:419
bool isShortCircuited() const
Returns true if the transform short circuits because the source and destination are equivalent...
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:201
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
Reads and writes project states.
Definition: qgsproject.h:85
Contains information about the context in which a coordinate transform is executed.
int destinationDatumTransformId() const
Returns the ID of the datum transform to use when projecting to the destination CRS.
void transformCoords(int numPoint, double *x, double *y, double *z, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transform an array of coordinates to the destination CRS.
double x
Definition: qgspointxy.h:47
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:176
void transformInPlace(double &x, double &y, double &z, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transforms an array of x, y and z double coordinates in place, from the source CRS to the destination...
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:161
void setSourceDatumTransformId(int datumId)
Sets the datumId ID of the datum transform to use when projecting from the source CRS...
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle...
Definition: qgsrectangle.h:352
Transform from destination to source CRS.
static void invalidateCache()
Clears the internal cache used to initialize QgsCoordinateTransform objects.
This class represents a coordinate reference system (CRS).
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:429
Class for doing transforms between two map coordinate systems.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:166
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:171
Transform from source to destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
int sourceDatumTransformId() const
Returns the ID of the datum transform to use when projecting from the source CRS. ...
void setSourceCrs(const QgsCoordinateReferenceSystem &crs)
Sets the source coordinate reference system.
QString authid() const
Returns the authority identifier for the CRS.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, TransformDirection direction=ForwardTransform, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:129
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:208
const char * finder(const char *name)
bool isValid() const
Returns whether this CRS is correctly initialized and usable.