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