QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 #include "qgsreadwritelocker.h"
27 #include "qgsvector3d.h"
28 #include "qgis.h"
29 
30 //qt includes
31 #include <QDomNode>
32 #include <QDomElement>
33 #include <QApplication>
34 #include <QPolygonF>
35 #include <QStringList>
36 #include <QVector>
37 
38 #include <proj.h>
39 #include "qgsprojutils.h"
40 
41 #include <sqlite3.h>
42 #include <qlogging.h>
43 #include <vector>
44 #include <algorithm>
45 
46 // if defined shows all information about transform to stdout
47 // #define COORDINATE_TRANSFORM_VERBOSE
48 
49 QReadWriteLock QgsCoordinateTransform::sCacheLock;
50 QMultiHash< QPair< QString, QString >, QgsCoordinateTransform > QgsCoordinateTransform::sTransforms; //same auth_id pairs might have different datum transformations
51 bool QgsCoordinateTransform::sDisableCache = false;
52 
53 std::function< void( const QgsCoordinateReferenceSystem &sourceCrs,
54  const QgsCoordinateReferenceSystem &destinationCrs,
55  const QString &desiredOperation )> QgsCoordinateTransform::sFallbackOperationOccurredHandler = nullptr;
56 
58 {
59  d = new QgsCoordinateTransformPrivate();
60 }
61 
62 QgsCoordinateTransform::QgsCoordinateTransform( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination, const QgsCoordinateTransformContext &context, Qgis::CoordinateTransformationFlags flags )
63 {
64  mContext = context;
65  d = new QgsCoordinateTransformPrivate( source, destination, mContext );
66 
68  mIgnoreImpossible = true;
69 
70 #ifdef QGISDEBUG
71  mHasContext = true;
72 #endif
73 
74  if ( mIgnoreImpossible && !isTransformationPossible( source, destination ) )
75  {
76  d->invalidate();
77  return;
78  }
79 
80  if ( !d->checkValidity() )
81  return;
82 
84  if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
85  {
86  d->initialize();
87  addToCache();
88  }
90 
92  mBallparkTransformsAreAppropriate = true;
93 }
94 
95 QgsCoordinateTransform::QgsCoordinateTransform( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination, const QgsProject *project, Qgis::CoordinateTransformationFlags flags )
96 {
97  mContext = project ? project->transformContext() : QgsCoordinateTransformContext();
98  d = new QgsCoordinateTransformPrivate( source, destination, mContext );
99 #ifdef QGISDEBUG
100  if ( project )
101  mHasContext = true;
102 #endif
103 
105  mIgnoreImpossible = true;
106 
107  if ( mIgnoreImpossible && !isTransformationPossible( source, destination ) )
108  {
109  d->invalidate();
110  return;
111  }
112 
113  if ( !d->checkValidity() )
114  return;
115 
117  if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
118  {
119  d->initialize();
120  addToCache();
121  }
123 
125  mBallparkTransformsAreAppropriate = true;
126 }
127 
128 QgsCoordinateTransform::QgsCoordinateTransform( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination, int sourceDatumTransform, int destinationDatumTransform )
129 {
130  d = new QgsCoordinateTransformPrivate( source, destination, sourceDatumTransform, destinationDatumTransform );
131 #ifdef QGISDEBUG
132  mHasContext = true; // not strictly true, but we don't need to worry if datums have been explicitly set
133 #endif
134 
135  if ( !d->checkValidity() )
136  return;
137 
139  if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
140  {
141  d->initialize();
142  addToCache();
143  }
145 }
146 
148  : mContext( o.mContext )
149 #ifdef QGISDEBUG
150  , mHasContext( o.mHasContext )
151 #endif
152  , mLastError()
153 {
154  d = o.d;
155 }
156 
158 {
159  d = o.d;
160 #ifdef QGISDEBUG
161  mHasContext = o.mHasContext;
162 #endif
163  mContext = o.mContext;
164  mLastError = QString();
165  return *this;
166 }
167 
169 
171 {
172  if ( !source.isValid() || !destination.isValid() )
173  return false;
174 
175 #if PROJ_VERSION_MAJOR>8 || (PROJ_VERSION_MAJOR==8 && PROJ_VERSION_MINOR>=1)
176  if ( source.celestialBodyName() != destination.celestialBodyName() )
177  return false;
178 #endif
179 
180  return true;
181 }
182 
184 {
185  d.detach();
186  d->mSourceCRS = crs;
187 
188  if ( mIgnoreImpossible && !isTransformationPossible( d->mSourceCRS, d->mDestCRS ) )
189  {
190  d->invalidate();
191  return;
192  }
193 
194  if ( !d->checkValidity() )
195  return;
196 
197  d->calculateTransforms( mContext );
199  if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
200  {
201  d->initialize();
202  addToCache();
203  }
205 }
207 {
208  d.detach();
209  d->mDestCRS = crs;
210 
211  if ( mIgnoreImpossible && !isTransformationPossible( d->mSourceCRS, d->mDestCRS ) )
212  {
213  d->invalidate();
214  return;
215  }
216 
217  if ( !d->checkValidity() )
218  return;
219 
220  d->calculateTransforms( mContext );
222  if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
223  {
224  d->initialize();
225  addToCache();
226  }
228 }
229 
231 {
232  d.detach();
233  mContext = context;
234 #ifdef QGISDEBUG
235  mHasContext = true;
236 #endif
237 
238  if ( mIgnoreImpossible && !isTransformationPossible( d->mSourceCRS, d->mDestCRS ) )
239  {
240  d->invalidate();
241  return;
242  }
243 
244  if ( !d->checkValidity() )
245  return;
246 
247  d->calculateTransforms( mContext );
249  if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
250  {
251  d->initialize();
252  addToCache();
253  }
255 }
256 
258 {
259  return mContext;
260 }
261 
263 {
264  return d->mSourceCRS;
265 }
266 
268 {
269  return d->mDestCRS;
270 }
271 
273 {
274  if ( !d->mIsValid || d->mShortCircuit )
275  return point;
276 
277  // transform x
278  double x = point.x();
279  double y = point.y();
280  double z = 0.0;
281  try
282  {
283  transformCoords( 1, &x, &y, &z, direction );
284  }
285  catch ( const QgsCsException & )
286  {
287  // rethrow the exception
288  QgsDebugMsgLevel( QStringLiteral( "rethrowing exception" ), 2 );
289  throw;
290  }
291 
292  return QgsPointXY( x, y );
293 }
294 
295 
296 QgsPointXY QgsCoordinateTransform::transform( const double theX, const double theY = 0.0, Qgis::TransformDirection direction ) const
297 {
298  try
299  {
300  return transform( QgsPointXY( theX, theY ), direction );
301  }
302  catch ( const QgsCsException & )
303  {
304  // rethrow the exception
305  QgsDebugMsgLevel( QStringLiteral( "rethrowing exception" ), 2 );
306  throw;
307  }
308 }
309 
311 {
312  if ( !d->mIsValid || d->mShortCircuit )
313  return rect;
314  // transform x
315  double x1 = rect.xMinimum();
316  double y1 = rect.yMinimum();
317  double x2 = rect.xMaximum();
318  double y2 = rect.yMaximum();
319 
320  // Number of points to reproject------+
321  // |
322  // V
323  try
324  {
325  double z = 0.0;
326  transformCoords( 1, &x1, &y1, &z, direction );
327  transformCoords( 1, &x2, &y2, &z, direction );
328  }
329  catch ( const QgsCsException & )
330  {
331  // rethrow the exception
332  QgsDebugMsgLevel( QStringLiteral( "rethrowing exception" ), 2 );
333  throw;
334  }
335 
336 #ifdef COORDINATE_TRANSFORM_VERBOSE
337  QgsDebugMsg( QStringLiteral( "Rect projection..." ) );
338  QgsDebugMsg( QStringLiteral( "Xmin : %1 --> %2" ).arg( rect.xMinimum() ).arg( x1 ) );
339  QgsDebugMsg( QStringLiteral( "Ymin : %1 --> %2" ).arg( rect.yMinimum() ).arg( y1 ) );
340  QgsDebugMsg( QStringLiteral( "Xmax : %1 --> %2" ).arg( rect.xMaximum() ).arg( x2 ) );
341  QgsDebugMsg( QStringLiteral( "Ymax : %1 --> %2" ).arg( rect.yMaximum() ).arg( y2 ) );
342 #endif
343  return QgsRectangle( x1, y1, x2, y2 );
344 }
345 
347 {
348  double x = point.x();
349  double y = point.y();
350  double z = point.z();
351  try
352  {
353  transformCoords( 1, &x, &y, &z, direction );
354  }
355  catch ( const QgsCsException & )
356  {
357  // rethrow the exception
358  QgsDebugMsgLevel( QStringLiteral( "rethrowing exception" ), 2 );
359  throw;
360  }
361  return QgsVector3D( x, y, z );
362 }
363 
364 void QgsCoordinateTransform::transformInPlace( double &x, double &y, double &z,
365  Qgis::TransformDirection direction ) const
366 {
367  if ( !d->mIsValid || d->mShortCircuit )
368  return;
369 #ifdef QGISDEBUG
370 // QgsDebugMsg(QString("Using transform in place %1 %2").arg(__FILE__).arg(__LINE__));
371 #endif
372  // transform x
373  try
374  {
375  transformCoords( 1, &x, &y, &z, direction );
376  }
377  catch ( const QgsCsException & )
378  {
379  // rethrow the exception
380  QgsDebugMsgLevel( QStringLiteral( "rethrowing exception" ), 2 );
381  throw;
382  }
383 }
384 
385 void QgsCoordinateTransform::transformInPlace( float &x, float &y, double &z,
386  Qgis::TransformDirection direction ) const
387 {
388  double xd = static_cast< double >( x ), yd = static_cast< double >( y );
389  transformInPlace( xd, yd, z, direction );
390  x = xd;
391  y = yd;
392 }
393 
394 void QgsCoordinateTransform::transformInPlace( float &x, float &y, float &z,
395  Qgis::TransformDirection direction ) const
396 {
397  if ( !d->mIsValid || d->mShortCircuit )
398  return;
399 #ifdef QGISDEBUG
400  // QgsDebugMsg(QString("Using transform in place %1 %2").arg(__FILE__).arg(__LINE__));
401 #endif
402  // transform x
403  try
404  {
405  double xd = x;
406  double yd = y;
407  double zd = z;
408  transformCoords( 1, &xd, &yd, &zd, direction );
409  x = xd;
410  y = yd;
411  z = zd;
412  }
413  catch ( QgsCsException & )
414  {
415  // rethrow the exception
416  QgsDebugMsgLevel( QStringLiteral( "rethrowing exception" ), 2 );
417  throw;
418  }
419 }
420 
422 {
423  if ( !d->mIsValid || d->mShortCircuit )
424  {
425  return;
426  }
427 
428  //create x, y arrays
429  const int nVertices = poly.size();
430 
431  QVector<double> x( nVertices );
432  QVector<double> y( nVertices );
433  QVector<double> z( nVertices );
434  double *destX = x.data();
435  double *destY = y.data();
436  double *destZ = z.data();
437 
438  const QPointF *polyData = poly.constData();
439  for ( int i = 0; i < nVertices; ++i )
440  {
441  *destX++ = polyData->x();
442  *destY++ = polyData->y();
443  *destZ++ = 0;
444  polyData++;
445  }
446 
447  QString err;
448  try
449  {
450  transformCoords( nVertices, x.data(), y.data(), z.data(), direction );
451  }
452  catch ( const QgsCsException &e )
453  {
454  // record the exception, but don't rethrow it until we've recorded the coordinates we *could* transform
455  err = e.what();
456  }
457 
458  QPointF *destPoint = poly.data();
459  const double *srcX = x.constData();
460  const double *srcY = y.constData();
461  for ( int i = 0; i < nVertices; ++i )
462  {
463  destPoint->rx() = *srcX++;
464  destPoint->ry() = *srcY++;
465  destPoint++;
466  }
467 
468  // rethrow the exception
469  if ( !err.isEmpty() )
470  throw QgsCsException( err );
471 }
472 
473 void QgsCoordinateTransform::transformInPlace( QVector<double> &x, QVector<double> &y, QVector<double> &z,
474  Qgis::TransformDirection direction ) const
475 {
476 
477  if ( !d->mIsValid || d->mShortCircuit )
478  return;
479 
480  Q_ASSERT( x.size() == y.size() );
481 
482  // Apparently, if one has a std::vector, it is valid to use the
483  // address of the first element in the vector as a pointer to an
484  // array of the vectors data, and hence easily interface with code
485  // that wants C-style arrays.
486 
487  try
488  {
489  transformCoords( x.size(), &x[0], &y[0], &z[0], direction );
490  }
491  catch ( const QgsCsException & )
492  {
493  // rethrow the exception
494  QgsDebugMsgLevel( QStringLiteral( "rethrowing exception" ), 2 );
495  throw;
496  }
497 }
498 
499 
500 void QgsCoordinateTransform::transformInPlace( QVector<float> &x, QVector<float> &y, QVector<float> &z,
501  Qgis::TransformDirection direction ) const
502 {
503  if ( !d->mIsValid || d->mShortCircuit )
504  return;
505 
506  Q_ASSERT( x.size() == y.size() );
507 
508  // Apparently, if one has a std::vector, it is valid to use the
509  // address of the first element in the vector as a pointer to an
510  // array of the vectors data, and hence easily interface with code
511  // that wants C-style arrays.
512 
513  try
514  {
515  //copy everything to double vectors since proj needs double
516  const int vectorSize = x.size();
517  QVector<double> xd( x.size() );
518  QVector<double> yd( y.size() );
519  QVector<double> zd( z.size() );
520 
521  double *destX = xd.data();
522  double *destY = yd.data();
523  double *destZ = zd.data();
524 
525  const float *srcX = x.constData();
526  const float *srcY = y.constData();
527  const float *srcZ = z.constData();
528 
529  for ( int i = 0; i < vectorSize; ++i )
530  {
531  *destX++ = static_cast< double >( *srcX++ );
532  *destY++ = static_cast< double >( *srcY++ );
533  *destZ++ = static_cast< double >( *srcZ++ );
534  }
535 
536  transformCoords( x.size(), &xd[0], &yd[0], &zd[0], direction );
537 
538  //copy back
539  float *destFX = x.data();
540  float *destFY = y.data();
541  float *destFZ = z.data();
542  const double *srcXD = xd.constData();
543  const double *srcYD = yd.constData();
544  const double *srcZD = zd.constData();
545  for ( int i = 0; i < vectorSize; ++i )
546  {
547  *destFX++ = static_cast< float >( *srcXD++ );
548  *destFY++ = static_cast< float >( *srcYD++ );
549  *destFZ++ = static_cast< float >( *srcZD++ );
550  }
551  }
552  catch ( QgsCsException & )
553  {
554  // rethrow the exception
555  QgsDebugMsgLevel( QStringLiteral( "rethrowing exception" ), 2 );
556  throw;
557  }
558 }
559 
560 QgsRectangle QgsCoordinateTransform::transformBoundingBox( const QgsRectangle &rect, Qgis::TransformDirection direction, const bool handle180Crossover ) const
561 {
562  // Calculate the bounding box of a QgsRectangle in the source CRS
563  // when projected to the destination CRS (or the inverse).
564  // This is done by looking at a number of points spread evenly
565  // across the rectangle
566 
567  if ( !d->mIsValid || d->mShortCircuit )
568  return rect;
569 
570  if ( rect.isEmpty() )
571  {
572  const QgsPointXY p = transform( rect.xMinimum(), rect.yMinimum(), direction );
573  return QgsRectangle( p, p );
574  }
575 
576  double yMin = rect.yMinimum();
577  double yMax = rect.yMaximum();
578  if ( d->mGeographicToWebMercator &&
579  ( ( direction == Qgis::TransformDirection::Forward && !d->mIsReversed ) ||
580  ( direction == Qgis::TransformDirection::Reverse && d->mIsReversed ) ) )
581  {
582  // Latitudes close to 90 degree project to infinite northing in theory.
583  // We limit to 90 - 1e-1 which reproject to northing of ~ 44e6 m (about twice
584  // the maximum easting of ~20e6 m).
585  // For reference, GoogleMercator tiles are limited to a northing ~85 deg / ~20e6 m
586  // so limiting to 90 - 1e-1 is reasonable.
587  constexpr double EPS = 1e-1;
588  if ( yMin < -90 + EPS )
589  {
590  if ( yMax < -90 + EPS )
591  throw QgsCsException( QObject::tr( "Could not transform bounding box to target CRS" ) );
592  yMin = -90 + EPS;
593  }
594  if ( yMax > 90 - EPS )
595  {
596  if ( yMin > 90 - EPS )
597  throw QgsCsException( QObject::tr( "Could not transform bounding box to target CRS" ) );
598  yMax = 90 - EPS;
599  }
600  }
601 
602  // 64 points (<=2.12) is not enough, see #13665, for EPSG:4326 -> EPSG:3574 (say that it is a hard one),
603  // are decent result from about 500 points and more. This method is called quite often, but
604  // even with 1000 points it takes < 1ms.
605  // TODO: how to effectively and precisely reproject bounding box?
606  const int nPoints = 1000;
607  const double d = std::sqrt( ( rect.width() * ( yMax - yMin ) ) / std::pow( std::sqrt( static_cast< double >( nPoints ) ) - 1, 2.0 ) );
608  const int nXPoints = std::min( static_cast< int >( std::ceil( rect.width() / d ) ) + 1, 1000 );
609  const int nYPoints = std::min( static_cast< int >( std::ceil( ( yMax - yMin ) / d ) ) + 1, 1000 );
610 
611  QgsRectangle bb_rect;
612  bb_rect.setMinimal();
613 
614  // We're interfacing with C-style vectors in the
615  // end, so let's do C-style vectors here too.
616  QVector<double> x( nXPoints * nYPoints );
617  QVector<double> y( nXPoints * nYPoints );
618  QVector<double> z( nXPoints * nYPoints );
619 
620  QgsDebugMsgLevel( QStringLiteral( "Entering transformBoundingBox..." ), 4 );
621 
622  // Populate the vectors
623 
624  const double dx = rect.width() / static_cast< double >( nXPoints - 1 );
625  const double dy = ( yMax - yMin ) / static_cast< double >( nYPoints - 1 );
626 
627  double pointY = yMin;
628 
629  for ( int i = 0; i < nYPoints ; i++ )
630  {
631 
632  // Start at right edge
633  double pointX = rect.xMinimum();
634 
635  for ( int j = 0; j < nXPoints; j++ )
636  {
637  x[( i * nXPoints ) + j] = pointX;
638  y[( i * nXPoints ) + j] = pointY;
639  // and the height...
640  z[( i * nXPoints ) + j] = 0.0;
641  // QgsDebugMsg(QString("BBox coord: (%1, %2)").arg(x[(i*numP) + j]).arg(y[(i*numP) + j]));
642  pointX += dx;
643  }
644  pointY += dy;
645  }
646 
647  // Do transformation. Any exception generated must
648  // be handled in above layers.
649  try
650  {
651  transformCoords( nXPoints * nYPoints, x.data(), y.data(), z.data(), direction );
652  }
653  catch ( const QgsCsException & )
654  {
655  // rethrow the exception
656  QgsDebugMsgLevel( QStringLiteral( "rethrowing exception" ), 2 );
657  throw;
658  }
659 
660  // Calculate the bounding box and use that for the extent
661 
662  for ( int i = 0; i < nXPoints * nYPoints; i++ )
663  {
664  if ( !std::isfinite( x[i] ) || !std::isfinite( y[i] ) )
665  {
666  continue;
667  }
668 
669  if ( handle180Crossover )
670  {
671  //if crossing the date line, temporarily add 360 degrees to -ve longitudes
672  bb_rect.combineExtentWith( x[i] >= 0.0 ? x[i] : x[i] + 360.0, y[i] );
673  }
674  else
675  {
676  bb_rect.combineExtentWith( x[i], y[i] );
677  }
678  }
679 
680  if ( bb_rect.isNull() )
681  {
682  // something bad happened when reprojecting the filter rect... no finite points were left!
683  throw QgsCsException( QObject::tr( "Could not transform bounding box to target CRS" ) );
684  }
685 
686  if ( handle180Crossover )
687  {
688  //subtract temporary addition of 360 degrees from longitudes
689  if ( bb_rect.xMinimum() > 180.0 )
690  bb_rect.setXMinimum( bb_rect.xMinimum() - 360.0 );
691  if ( bb_rect.xMaximum() > 180.0 )
692  bb_rect.setXMaximum( bb_rect.xMaximum() - 360.0 );
693  }
694 
695  QgsDebugMsgLevel( "Projected extent: " + bb_rect.toString(), 4 );
696 
697  if ( bb_rect.isEmpty() )
698  {
699  QgsDebugMsgLevel( "Original extent: " + rect.toString(), 4 );
700  }
701 
702  return bb_rect;
703 }
704 
705 void QgsCoordinateTransform::transformCoords( int numPoints, double *x, double *y, double *z, Qgis::TransformDirection direction ) const
706 {
707  if ( !d->mIsValid || d->mShortCircuit )
708  return;
709  // Refuse to transform the points if the srs's are invalid
710  if ( !d->mSourceCRS.isValid() )
711  {
712  QgsMessageLog::logMessage( QObject::tr( "The source spatial reference system (CRS) is not valid. "
713  "The coordinates can not be reprojected. The CRS is: %1" )
714  .arg( d->mSourceCRS.toProj() ), QObject::tr( "CRS" ) );
715  return;
716  }
717  if ( !d->mDestCRS.isValid() )
718  {
719  QgsMessageLog::logMessage( QObject::tr( "The destination spatial reference system (CRS) is not valid. "
720  "The coordinates can not be reprojected. The CRS is: %1" ).arg( d->mDestCRS.toProj() ), QObject::tr( "CRS" ) );
721  return;
722  }
723 
724  std::vector< int > zNanPositions;
725  for ( int i = 0; i < numPoints; i++ )
726  {
727  if ( std::isnan( z[i] ) )
728  {
729  zNanPositions.push_back( i );
730  z[i] = 0.0;
731  }
732  }
733 
734  std::vector< double > xprev( numPoints );
735  memcpy( xprev.data(), x, sizeof( double ) * numPoints );
736  std::vector< double > yprev( numPoints );
737  memcpy( yprev.data(), y, sizeof( double ) * numPoints );
738  std::vector< double > zprev( numPoints );
739  memcpy( zprev.data(), z, sizeof( double ) * numPoints );
740 
741  const bool useTime = !std::isnan( d->mDefaultTime );
742  std::vector< double > t( useTime ? numPoints : 0, d->mDefaultTime );
743 
744 #ifdef COORDINATE_TRANSFORM_VERBOSE
745  double xorg = *x;
746  double yorg = *y;
747  QgsDebugMsg( QStringLiteral( "[[[[[[ Number of points to transform: %1 ]]]]]]" ).arg( numPoints ) );
748 #endif
749 
750 #ifdef QGISDEBUG
751  if ( !mHasContext )
752  QgsDebugMsgLevel( QStringLiteral( "No QgsCoordinateTransformContext context set for transform" ), 4 );
753 #endif
754 
755  // use proj4 to do the transform
756 
757  // if the source/destination projection is lat/long, convert the points to radians
758  // prior to transforming
759  ProjData projData = d->threadLocalProjData();
760 
761  int projResult = 0;
762 
763  proj_errno_reset( projData );
764  proj_trans_generic( projData, ( direction == Qgis::TransformDirection::Forward && !d->mIsReversed ) || ( direction == Qgis::TransformDirection::Reverse && d->mIsReversed ) ? PJ_FWD : PJ_INV,
765  x, sizeof( double ), numPoints,
766  y, sizeof( double ), numPoints,
767  z, sizeof( double ), numPoints,
768  useTime ? t.data() : nullptr, sizeof( double ), useTime ? numPoints : 0 );
769  // Try to - approximatively - emulate the behavior of pj_transform()...
770  // In the case of a single point transform, and a transformation error occurs,
771  // pj_transform() would return the errno. In cases of multiple point transform,
772  // it would continue (for non-transient errors, that is pipeline definition
773  // errors) and just set the resulting x,y to infinity. This is in fact a
774  // bit more subtle than that, and I'm not completely sure the logic in
775  // pj_transform() was really sane & fully bullet proof
776  // So here just check proj_errno() for single point transform
777  int actualRes = 0;
778  if ( numPoints == 1 )
779  {
780  projResult = proj_errno( projData );
781  actualRes = projResult;
782  }
783  else
784  {
785  actualRes = proj_errno( projData );
786  }
787  if ( actualRes == 0 )
788  {
789  // proj_errno is sometimes not an accurate method to test for transform failures - so we need to
790  // manually scan for nan values
791  if ( std::any_of( x, x + numPoints, []( double v ) { return std::isinf( v ); } )
792  || std::any_of( y, y + numPoints, []( double v ) { return std::isinf( v ); } )
793  || std::any_of( z, z + numPoints, []( double v ) { return std::isinf( v ); } ) )
794  {
795  actualRes = 1;
796  }
797  }
798 
799  mFallbackOperationOccurred = false;
800  if ( actualRes != 0
801  && ( d->mAvailableOpCount > 1 || d->mAvailableOpCount == -1 ) // only use fallbacks if more than one operation is possible -- otherwise we've already tried it and it failed
802  && ( d->mAllowFallbackTransforms || mBallparkTransformsAreAppropriate ) )
803  {
804  // fail #1 -- try with getting proj to auto-pick an appropriate coordinate operation for the points
805  if ( PJ *transform = d->threadLocalFallbackProjData() )
806  {
807  projResult = 0;
808  proj_errno_reset( transform );
809  proj_trans_generic( transform, direction == Qgis::TransformDirection::Forward ? PJ_FWD : PJ_INV,
810  xprev.data(), sizeof( double ), numPoints,
811  yprev.data(), sizeof( double ), numPoints,
812  zprev.data(), sizeof( double ), numPoints,
813  useTime ? t.data() : nullptr, sizeof( double ), useTime ? numPoints : 0 );
814  // Try to - approximatively - emulate the behavior of pj_transform()...
815  // In the case of a single point transform, and a transformation error occurs,
816  // pj_transform() would return the errno. In cases of multiple point transform,
817  // it would continue (for non-transient errors, that is pipeline definition
818  // errors) and just set the resulting x,y to infinity. This is in fact a
819  // bit more subtle than that, and I'm not completely sure the logic in
820  // pj_transform() was really sane & fully bullet proof
821  // So here just check proj_errno() for single point transform
822  if ( numPoints == 1 )
823  {
824  // hmm - something very odd here. We can't trust proj_errno( transform ), as that's giving us incorrect error numbers
825  // (such as "failed to load datum shift file", which is definitely incorrect for a default proj created operation!)
826  // so we resort to testing values ourselves...
827  projResult = std::isinf( xprev[0] ) || std::isinf( yprev[0] ) || std::isinf( zprev[0] ) ? 1 : 0;
828  }
829 
830  if ( projResult == 0 )
831  {
832  memcpy( x, xprev.data(), sizeof( double ) * numPoints );
833  memcpy( y, yprev.data(), sizeof( double ) * numPoints );
834  memcpy( z, zprev.data(), sizeof( double ) * numPoints );
835  mFallbackOperationOccurred = true;
836  }
837 
838  if ( !mBallparkTransformsAreAppropriate && !mDisableFallbackHandler && sFallbackOperationOccurredHandler )
839  {
840  sFallbackOperationOccurredHandler( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation );
841 #if 0
842  const QString warning = QStringLiteral( "A fallback coordinate operation was used between %1 and %2" ).arg( d->mSourceCRS.authid(),
843  d->mDestCRS.authid() );
844  qWarning( "%s", warning.toLatin1().constData() );
845 #endif
846  }
847  }
848  }
849 
850  for ( const int &pos : zNanPositions )
851  {
852  z[pos] = std::numeric_limits<double>::quiet_NaN();
853  }
854 
855  if ( projResult != 0 )
856  {
857  //something bad happened....
858  QString points;
859 
860  for ( int i = 0; i < numPoints; ++i )
861  {
862  if ( direction == Qgis::TransformDirection::Forward )
863  {
864  points += QStringLiteral( "(%1, %2)\n" ).arg( x[i], 0, 'f' ).arg( y[i], 0, 'f' );
865  }
866  else
867  {
868  points += QStringLiteral( "(%1, %2)\n" ).arg( x[i], 0, 'f' ).arg( y[i], 0, 'f' );
869  }
870  }
871 
872  const QString dir = ( direction == Qgis::TransformDirection::Forward ) ? QObject::tr( "forward transform" ) : QObject::tr( "inverse transform" );
873 
874  const QString msg = QObject::tr( "%1 of\n"
875  "%2"
876  "Error: %3" )
877  .arg( dir,
878  points,
879  projResult < 0 ? QString::fromUtf8( proj_errno_string( projResult ) ) : QObject::tr( "Fallback transform failed" ) );
880 
881 
882  // don't flood console with thousands of duplicate transform error messages
883  if ( msg != mLastError )
884  {
885  QgsDebugMsg( "Projection failed emitting invalid transform signal: " + msg );
886  mLastError = msg;
887  }
888  QgsDebugMsgLevel( QStringLiteral( "rethrowing exception" ), 2 );
889 
890  throw QgsCsException( msg );
891  }
892 
893 #ifdef COORDINATE_TRANSFORM_VERBOSE
894  QgsDebugMsg( QStringLiteral( "[[[[[[ Projected %1, %2 to %3, %4 ]]]]]]" )
895  .arg( xorg, 0, 'g', 15 ).arg( yorg, 0, 'g', 15 )
896  .arg( *x, 0, 'g', 15 ).arg( *y, 0, 'g', 15 ) );
897 #endif
898 }
899 
901 {
902  return d->mIsValid;
903 }
904 
906 {
907  return !d->mIsValid || d->mShortCircuit;
908 }
909 
911 {
912  return d->mProjCoordinateOperation;
913 }
914 
916 {
917  ProjData projData = d->threadLocalProjData();
919 }
920 
921 void QgsCoordinateTransform::setCoordinateOperation( const QString &operation ) const
922 {
923  d.detach();
924  d->mProjCoordinateOperation = operation;
925  d->mShouldReverseCoordinateOperation = false;
926 }
927 
929 {
930  d.detach();
931  d->mAllowFallbackTransforms = allowed;
932 }
933 
935 {
936  return d->mAllowFallbackTransforms;
937 }
938 
940 {
941  mBallparkTransformsAreAppropriate = appropriate;
942 }
943 
945 {
946  mDisableFallbackHandler = disabled;
947 }
948 
950 {
951  return mFallbackOperationOccurred;
952 }
953 
954 const char *finder( const char *name )
955 {
956  QString proj;
957 #ifdef Q_OS_WIN
958  proj = QApplication::applicationDirPath()
959  + "/share/proj/" + QString( name );
960 #else
961  Q_UNUSED( name )
962 #endif
963  return proj.toUtf8();
964 }
965 
966 bool QgsCoordinateTransform::setFromCache( const QgsCoordinateReferenceSystem &src, const QgsCoordinateReferenceSystem &dest, const QString &coordinateOperationProj, bool allowFallback )
967 {
968  if ( !src.isValid() || !dest.isValid() )
969  return false;
970 
971  const QString sourceKey = src.authid().isEmpty() ?
973  const QString destKey = dest.authid().isEmpty() ?
975 
976  if ( sourceKey.isEmpty() || destKey.isEmpty() )
977  return false;
978 
979  QgsReadWriteLocker locker( sCacheLock, QgsReadWriteLocker::Read );
980  if ( sDisableCache )
981  return false;
982 
983  const QList< QgsCoordinateTransform > values = sTransforms.values( qMakePair( sourceKey, destKey ) );
984  for ( auto valIt = values.constBegin(); valIt != values.constEnd(); ++valIt )
985  {
986  if ( ( *valIt ).coordinateOperation() == coordinateOperationProj
987  && ( *valIt ).allowFallbackTransforms() == allowFallback
988  && qgsNanCompatibleEquals( src.coordinateEpoch(), ( *valIt ).sourceCrs().coordinateEpoch() )
989  && qgsNanCompatibleEquals( dest.coordinateEpoch(), ( *valIt ).destinationCrs().coordinateEpoch() )
990  )
991  {
992  // need to save, and then restore the context... we don't want this to be cached or to use the values from the cache
993  const QgsCoordinateTransformContext context = mContext;
994 #ifdef QGISDEBUG
995  const bool hasContext = mHasContext;
996 #endif
997  *this = *valIt;
998  locker.unlock();
999 
1000  mContext = context;
1001 #ifdef QGISDEBUG
1002  mHasContext = hasContext;
1003 #endif
1004 
1005  return true;
1006  }
1007  }
1008  return false;
1009 }
1010 
1011 void QgsCoordinateTransform::addToCache()
1012 {
1013  if ( !d->mSourceCRS.isValid() || !d->mDestCRS.isValid() )
1014  return;
1015 
1016  const QString sourceKey = d->mSourceCRS.authid().isEmpty() ?
1017  d->mSourceCRS.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) : d->mSourceCRS.authid();
1018  const QString destKey = d->mDestCRS.authid().isEmpty() ?
1019  d->mDestCRS.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) : d->mDestCRS.authid();
1020 
1021  if ( sourceKey.isEmpty() || destKey.isEmpty() )
1022  return;
1023 
1024  const QgsReadWriteLocker locker( sCacheLock, QgsReadWriteLocker::Write );
1025  if ( sDisableCache )
1026  return;
1027 
1028  sTransforms.insert( qMakePair( sourceKey, destKey ), *this );
1029 }
1030 
1032 {
1034  return d->mSourceDatumTransform;
1036 }
1037 
1039 {
1040  d.detach();
1042  d->mSourceDatumTransform = dt;
1044 }
1045 
1047 {
1049  return d->mDestinationDatumTransform;
1051 }
1052 
1054 {
1055  d.detach();
1057  d->mDestinationDatumTransform = dt;
1059 }
1060 
1062 {
1063  const QgsReadWriteLocker locker( sCacheLock, QgsReadWriteLocker::Write );
1064  if ( sDisableCache )
1065  return;
1066 
1067  if ( disableCache )
1068  {
1069  sDisableCache = true;
1070  }
1071 
1072  sTransforms.clear();
1073 }
1074 
1075 void QgsCoordinateTransform::removeFromCacheObjectsBelongingToCurrentThread( void *pj_context )
1076 {
1077  // Not completely sure about object order destruction after main() has
1078  // exited. So it is safer to check sDisableCache before using sCacheLock
1079  // in case sCacheLock would have been destroyed before the current TLS
1080  // QgsProjContext object that has called us...
1081  if ( sDisableCache )
1082  return;
1083 
1084  const QgsReadWriteLocker locker( sCacheLock, QgsReadWriteLocker::Write );
1085  // cppcheck-suppress identicalConditionAfterEarlyExit
1086  if ( sDisableCache )
1087  return;
1088 
1089  for ( auto it = sTransforms.begin(); it != sTransforms.end(); )
1090  {
1091  auto &v = it.value();
1092  if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
1093  it = sTransforms.erase( it );
1094  else
1095  ++it;
1096  }
1097 }
1098 
1099 double QgsCoordinateTransform::scaleFactor( const QgsRectangle &ReferenceExtent ) const
1100 {
1101  const QgsPointXY source1( ReferenceExtent.xMinimum(), ReferenceExtent.yMinimum() );
1102  const QgsPointXY source2( ReferenceExtent.xMaximum(), ReferenceExtent.yMaximum() );
1103  const double distSourceUnits = std::sqrt( source1.sqrDist( source2 ) );
1104  const QgsPointXY dest1 = transform( source1 );
1105  const QgsPointXY dest2 = transform( source2 );
1106  const double distDestUnits = std::sqrt( dest1.sqrDist( dest2 ) );
1107  return distDestUnits / distSourceUnits;
1108 }
1109 
1111 {
1112  QgsCoordinateTransformPrivate::setCustomMissingRequiredGridHandler( handler );
1113 }
1114 
1116 {
1117  QgsCoordinateTransformPrivate::setCustomMissingPreferredGridHandler( handler );
1118 }
1119 
1121 {
1122  QgsCoordinateTransformPrivate::setCustomCoordinateOperationCreationErrorHandler( handler );
1123 }
1124 
1126 {
1127  QgsCoordinateTransformPrivate::setCustomMissingGridUsedByContextHandler( handler );
1128 }
1129 
1130 void QgsCoordinateTransform::setFallbackOperationOccurredHandler( const std::function<void ( const QgsCoordinateReferenceSystem &, const QgsCoordinateReferenceSystem &, const QString & )> &handler )
1131 {
1132  sFallbackOperationOccurredHandler = handler;
1133 }
1134 
1136 {
1137  QgsCoordinateTransformPrivate::setDynamicCrsToDynamicCrsWarningHandler( handler );
1138 }
qgsreadwritelocker.h
QgsCoordinateTransform::transformCoords
void transformCoords(int numPoint, double *x, double *y, double *z, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const SIP_THROW(QgsCsException)
Transform an array of coordinates to the destination CRS.
Definition: qgscoordinatetransform.cpp:705
QgsReadWriteLocker::Read
@ Read
Lock for read.
Definition: qgsreadwritelocker.h:75
QgsCoordinateTransform::setCustomMissingPreferredGridHandler
static void setCustomMissingPreferredGridHandler(const std::function< void(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QgsDatumTransform::TransformDetails &preferredOperation, const QgsDatumTransform::TransformDetails &availableOperation)> &handler)
Sets a custom handler to use when a coordinate transform is created between sourceCrs and destination...
Definition: qgscoordinatetransform.cpp:1115
QgsCoordinateTransform::fallbackOperationOccurred
bool fallbackOperationOccurred() const
Returns true if a fallback operation occurred for the most recent transform.
Definition: qgscoordinatetransform.cpp:949
QgsCoordinateTransform::transformInPlace
void transformInPlace(double &x, double &y, double &z, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const SIP_THROW(QgsCsException)
Transforms an array of x, y and z double coordinates in place, from the source CRS to the destination...
Definition: qgscoordinatetransform.cpp:364
QgsPointXY::y
double y
Definition: qgspointxy.h:63
QgsCoordinateTransform::setCustomCoordinateOperationCreationErrorHandler
static void setCustomCoordinateOperationCreationErrorHandler(const std::function< void(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QString &error)> &handler)
Sets a custom handler to use when a coordinate transform was required between sourceCrs and destinati...
Definition: qgscoordinatetransform.cpp:1120
QgsCoordinateTransformContext
Contains information about the context in which a coordinate transform is executed.
Definition: qgscoordinatetransformcontext.h:57
QgsCoordinateTransform::setCoordinateOperation
void setCoordinateOperation(const QString &operation) const
Sets a Proj string representing the coordinate operation which will be used to transform coordinates.
Definition: qgscoordinatetransform.cpp:921
QgsRectangle::combineExtentWith
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
Definition: qgsrectangle.h:391
qgsrectangle.h
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsDatumTransform::transformDetailsFromPj
static QgsDatumTransform::TransformDetails transformDetailsFromPj(PJ *op)
Returns the transform details for a Proj coordinate operation op.
Definition: qgsdatumtransform.cpp:307
qgsNanCompatibleEquals
bool qgsNanCompatibleEquals(double a, double b)
Compare two doubles, treating nan values as equal.
Definition: qgis.h:2249
QgsVector3D::y
double y() const
Returns Y coordinate.
Definition: qgsvector3d.h:64
QgsCoordinateReferenceSystem::WKT_PREFERRED
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
Definition: qgscoordinatereferencesystem.h:680
QgsVector3D
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
Definition: qgsvector3d.h:31
crs
const QgsCoordinateReferenceSystem & crs
Definition: qgswfsgetfeature.cpp:105
QgsCoordinateTransform::setContext
void setContext(const QgsCoordinateTransformContext &context)
Sets the context in which the coordinate transform should be calculated.
Definition: qgscoordinatetransform.cpp:230
QgsRectangle::yMinimum
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
QgsCoordinateTransform::transformBoundingBox
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
Definition: qgscoordinatetransform.cpp:560
QgsCoordinateTransform::setSourceCrs
void setSourceCrs(const QgsCoordinateReferenceSystem &crs)
Sets the source coordinate reference system.
Definition: qgscoordinatetransform.cpp:183
QgsCoordinateTransform::context
QgsCoordinateTransformContext context() const
Returns the context in which the coordinate transform will be calculated.
Definition: qgscoordinatetransform.cpp:257
QgsCoordinateTransform::allowFallbackTransforms
bool allowFallbackTransforms() const
Returns whether "ballpark" fallback transformations will be used in the case that the specified coord...
Definition: qgscoordinatetransform.cpp:934
QgsProject::transformContext
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:110
qgis.h
QgsRectangle::setMinimal
void setMinimal() SIP_HOLDGIL
Set a rectangle so that min corner is at max and max corner is at min.
Definition: qgsrectangle.h:172
QgsCoordinateTransform::operator=
QgsCoordinateTransform & operator=(const QgsCoordinateTransform &o)
Assignment operator.
Definition: qgscoordinatetransform.cpp:157
QgsCoordinateTransform::~QgsCoordinateTransform
~QgsCoordinateTransform()
Definition: qgscoordinatetransform.cpp:168
QgsCoordinateTransform::setCustomMissingGridUsedByContextHandler
static void setCustomMissingGridUsedByContextHandler(const std::function< void(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QgsDatumTransform::TransformDetails &desiredOperation)> &handler)
Sets a custom handler to use when a coordinate operation was specified for use between sourceCrs and ...
Definition: qgscoordinatetransform.cpp:1125
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Qgis::CoordinateTransformationFlag::IgnoreImpossibleTransformations
@ IgnoreImpossibleTransformations
Indicates that impossible transformations (such as those which attempt to transform between two diffe...
QgsCoordinateTransform::isValid
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
Definition: qgscoordinatetransform.cpp:900
QgsCoordinateTransform::setDynamicCrsToDynamicCrsWarningHandler
static void setDynamicCrsToDynamicCrsWarningHandler(const std::function< void(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs)> &handler)
Sets a custom handler to use when the desired coordinate operation for use between sourceCrs and dest...
Definition: qgscoordinatetransform.cpp:1135
Qgis::CoordinateTransformationFlag::BallparkTransformsAreAppropriate
@ BallparkTransformsAreAppropriate
Indicates that approximate "ballpark" results are appropriate for this coordinate transform....
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:41
QgsDatumTransform::GridDetails
Contains information about a projection transformation grid file.
Definition: qgsdatumtransform.h:132
QgsCoordinateTransform::invalidateCache
static void invalidateCache(bool disableCache=false)
Clears the internal cache used to initialize QgsCoordinateTransform objects.
Definition: qgscoordinatetransform.cpp:1061
QgsProject
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:103
QgsReadWriteLocker
The QgsReadWriteLocker class is a convenience class that simplifies locking and unlocking QReadWriteL...
Definition: qgsreadwritelocker.h:40
qgsapplication.h
QgsPointXY::sqrDist
double sqrDist(double x, double y) const SIP_HOLDGIL
Returns the squared distance between this point a specified x, y coordinate.
Definition: qgspointxy.h:190
QgsCoordinateTransform::destinationCrs
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
Definition: qgscoordinatetransform.cpp:267
QgsCoordinateTransform::isTransformationPossible
static bool isTransformationPossible(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination)
Returns true if it is theoretically possible to transform between source and destination CRSes.
Definition: qgscoordinatetransform.cpp:170
QgsCoordinateTransform::QgsCoordinateTransform
QgsCoordinateTransform()
Default constructor, creates an invalid QgsCoordinateTransform.
Definition: qgscoordinatetransform.cpp:57
Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:2820
QgsRectangle::xMaximum
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
QgsCoordinateTransform::sourceCrs
QgsCoordinateReferenceSystem sourceCrs() const
Returns the source coordinate reference system, which the transform will transform coordinates from.
Definition: qgscoordinatetransform.cpp:262
QgsCsException
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
QgsCoordinateTransform::isShortCircuited
bool isShortCircuited() const
Returns true if the transform short circuits because the source and destination are equivalent.
Definition: qgscoordinatetransform.cpp:905
finder
const char * finder(const char *name)
Definition: qgscoordinatetransform.cpp:954
QgsCoordinateTransform::setDestinationCrs
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination coordinate reference system.
Definition: qgscoordinatetransform.cpp:206
QgsCoordinateTransform::setBallparkTransformsAreAppropriate
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
Definition: qgscoordinatetransform.cpp:939
QgsVector3D::z
double z() const
Returns Z coordinate.
Definition: qgsvector3d.h:66
qgscoordinatetransform_p.h
QgsCoordinateReferenceSystem::celestialBodyName
QString celestialBodyName() const SIP_THROW(QgsNotSupportedException)
Attempts to retrieve the name of the celestial body associated with the CRS (e.g.
Definition: qgscoordinatereferencesystem.cpp:1323
QgsException::what
QString what() const
Definition: qgsexception.h:48
QgsCoordinateTransform::coordinateOperation
QString coordinateOperation() const
Returns a Proj string representing the coordinate operation which will be used to transform coordinat...
Definition: qgscoordinatetransform.cpp:910
QgsReadWriteLocker::Write
@ Write
Lock for write.
Definition: qgsreadwritelocker.h:76
QgsCoordinateTransform::setCustomMissingRequiredGridHandler
static void setCustomMissingRequiredGridHandler(const std::function< void(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QgsDatumTransform::GridDetails &grid)> &handler)
Sets a custom handler to use when a coordinate transform is created between sourceCrs and destination...
Definition: qgscoordinatetransform.cpp:1110
QgsCoordinateReferenceSystem::toWkt
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
Definition: qgscoordinatereferencesystem.cpp:1810
QgsRectangle::setXMinimum
void setXMinimum(double x) SIP_HOLDGIL
Set the minimum x value.
Definition: qgsrectangle.h:151
QgsMessageLog::logMessage
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Definition: qgsmessagelog.cpp:27
QgsCoordinateReferenceSystem::isValid
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Definition: qgscoordinatereferencesystem.cpp:977
qgscoordinatetransform.h
QgsCoordinateTransform::disableFallbackOperationHandler
void disableFallbackOperationHandler(bool disabled)
Sets whether the default fallback operation handler is disabled for this transform instance.
Definition: qgscoordinatetransform.cpp:944
QgsRectangle::xMinimum
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
QgsDatumTransform::TransformDetails
Contains information about a coordinate transformation operation.
Definition: qgsdatumtransform.h:180
QgsCoordinateReferenceSystem
This class represents a coordinate reference system (CRS).
Definition: qgscoordinatereferencesystem.h:211
QgsRectangle::setXMaximum
void setXMaximum(double x) SIP_HOLDGIL
Set the maximum x value.
Definition: qgsrectangle.h:156
QgsRectangle::toString
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
Definition: qgsrectangle.cpp:127
QgsPointXY
A class to represent a 2D point.
Definition: qgspointxy.h:58
QgsCoordinateTransform::scaleFactor
double scaleFactor(const QgsRectangle &referenceExtent) const
Computes an estimated conversion factor between source and destination units:
Definition: qgscoordinatetransform.cpp:1099
QgsCoordinateTransform::setSourceDatumTransformId
Q_DECL_DEPRECATED void setSourceDatumTransformId(int datumId)
Sets the datumId ID of the datum transform to use when projecting from the source CRS.
Definition: qgscoordinatetransform.cpp:1038
QgsCoordinateTransform::setFallbackOperationOccurredHandler
static void setFallbackOperationOccurredHandler(const std::function< void(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QString &desiredOperation)> &handler)
Sets a custom handler to use when the desired coordinate operation for use between sourceCrs and dest...
Definition: qgscoordinatetransform.cpp:1130
QgsRectangle::yMaximum
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
qgsvector3d.h
QgsRectangle::width
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
qgsprojutils.h
QgsPointXY::x
double x
Definition: qgspointxy.h:62
QgsCoordinateTransform::sourceDatumTransformId
Q_DECL_DEPRECATED int sourceDatumTransformId() const
Returns the ID of the datum transform to use when projecting from the source CRS.
Definition: qgscoordinatetransform.cpp:1031
QgsCoordinateReferenceSystem::authid
QString authid
Definition: qgscoordinatereferencesystem.h:217
QgsCoordinateTransform::transformPolygon
void transformPolygon(QPolygonF &polygon, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const SIP_THROW(QgsCsException)
Transforms a polygon to the destination coordinate system.
Definition: qgscoordinatetransform.cpp:421
qgsexception.h
QgsCoordinateTransform::setAllowFallbackTransforms
void setAllowFallbackTransforms(bool allowed)
Sets whether "ballpark" fallback transformations can be used in the case that the specified coordinat...
Definition: qgscoordinatetransform.cpp:928
Qgis::TransformDirection
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition: qgis.h:1235
QgsCoordinateReferenceSystem::coordinateEpoch
double coordinateEpoch() const
Returns the coordinate epoch, as a decimal year.
Definition: qgscoordinatereferencesystem.cpp:1350
qgslogger.h
QgsCoordinateTransform::transform
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const SIP_THROW(QgsCsException)
Transform the point from the source CRS to the destination CRS.
Definition: qgscoordinatetransform.cpp:272
QgsCoordinateTransform
Class for doing transforms between two map coordinate systems.
Definition: qgscoordinatetransform.h:57
Q_NOWARN_DEPRECATED_PUSH
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:2819
QgsRectangle::isEmpty
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:469
QgsVector3D::x
double x() const
Returns X coordinate.
Definition: qgsvector3d.h:62
QgsCoordinateTransform::destinationDatumTransformId
Q_DECL_DEPRECATED int destinationDatumTransformId() const
Returns the ID of the datum transform to use when projecting to the destination CRS.
Definition: qgscoordinatetransform.cpp:1046
QgsCoordinateTransform::setDestinationDatumTransformId
Q_DECL_DEPRECATED void setDestinationDatumTransformId(int datumId)
Sets the datumId ID of the datum transform to use when projecting to the destination CRS.
Definition: qgscoordinatetransform.cpp:1053
QgsRectangle::isNull
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:479
qgsproject.h
QgsCoordinateTransform::instantiatedCoordinateOperationDetails
QgsDatumTransform::TransformDetails instantiatedCoordinateOperationDetails() const
Returns the transform details representing the coordinate operation which is being used to transform ...
Definition: qgscoordinatetransform.cpp:915
qgspointxy.h
PJ
struct PJconsts PJ
Definition: qgscoordinatereferencesystem.h:49
qgsmessagelog.h