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