QGIS API Documentation  2.99.0-Master (19b062c)
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 
26 //qt includes
27 #include <QDomNode>
28 #include <QDomElement>
29 #include <QApplication>
30 #include <QPolygonF>
31 #include <QStringList>
32 #include <QVector>
33 
34 extern "C"
35 {
36 #include <proj_api.h>
37 }
38 #include <sqlite3.h>
39 
40 // if defined shows all information about transform to stdout
41 // #define COORDINATE_TRANSFORM_VERBOSE
42 
44 {
45  d = new QgsCoordinateTransformPrivate();
46 }
47 
49 {
50  d = new QgsCoordinateTransformPrivate( source, destination );
51 }
52 
54 {
55  d = o.d;
56 }
57 
59 {
60  d = o.d;
61  return *this;
62 }
63 
65 
67 {
68  d.detach();
69  d->mSourceCRS = crs;
70  d->initialize();
71 }
73 {
74  d.detach();
75  d->mDestCRS = crs;
76  d->initialize();
77 }
78 
80 {
81  return d->mSourceCRS;
82 }
83 
85 {
86  return d->mDestCRS;
87 }
88 
90 {
91  if ( !d->mIsValid || d->mShortCircuit )
92  return point;
93 
94  // transform x
95  double x = point.x();
96  double y = point.y();
97  double z = 0.0;
98  try
99  {
100  transformCoords( 1, &x, &y, &z, direction );
101  }
102  catch ( const QgsCsException & )
103  {
104  // rethrow the exception
105  QgsDebugMsg( "rethrowing exception" );
106  throw;
107  }
108 
109  return QgsPointXY( x, y );
110 }
111 
112 
113 QgsPointXY QgsCoordinateTransform::transform( const double theX, const double theY = 0.0, TransformDirection direction ) const
114 {
115  try
116  {
117  return transform( QgsPointXY( theX, theY ), direction );
118  }
119  catch ( const QgsCsException & )
120  {
121  // rethrow the exception
122  QgsDebugMsg( "rethrowing exception" );
123  throw;
124  }
125 }
126 
128 {
129  if ( !d->mIsValid || d->mShortCircuit )
130  return rect;
131  // transform x
132  double x1 = rect.xMinimum();
133  double y1 = rect.yMinimum();
134  double x2 = rect.xMaximum();
135  double y2 = rect.yMaximum();
136 
137  // Number of points to reproject------+
138  // |
139  // V
140  try
141  {
142  double z = 0.0;
143  transformCoords( 1, &x1, &y1, &z, direction );
144  transformCoords( 1, &x2, &y2, &z, direction );
145  }
146  catch ( const QgsCsException & )
147  {
148  // rethrow the exception
149  QgsDebugMsg( "rethrowing exception" );
150  throw;
151  }
152 
153 #ifdef COORDINATE_TRANSFORM_VERBOSE
154  QgsDebugMsg( "Rect projection..." );
155  QgsDebugMsg( QString( "Xmin : %1 --> %2" ).arg( rect.xMinimum() ).arg( x1 ) );
156  QgsDebugMsg( QString( "Ymin : %1 --> %2" ).arg( rect.yMinimum() ).arg( y1 ) );
157  QgsDebugMsg( QString( "Xmax : %1 --> %2" ).arg( rect.xMaximum() ).arg( x2 ) );
158  QgsDebugMsg( QString( "Ymax : %1 --> %2" ).arg( rect.yMaximum() ).arg( y2 ) );
159 #endif
160  return QgsRectangle( x1, y1, x2, y2 );
161 }
162 
163 void QgsCoordinateTransform::transformInPlace( double &x, double &y, double &z,
164  TransformDirection direction ) const
165 {
166  if ( !d->mIsValid || d->mShortCircuit )
167  return;
168 #ifdef QGISDEBUG
169 // QgsDebugMsg(QString("Using transform in place %1 %2").arg(__FILE__).arg(__LINE__));
170 #endif
171  // transform x
172  try
173  {
174  transformCoords( 1, &x, &y, &z, direction );
175  }
176  catch ( const QgsCsException & )
177  {
178  // rethrow the exception
179  QgsDebugMsg( "rethrowing exception" );
180  throw;
181  }
182 }
183 
184 void QgsCoordinateTransform::transformInPlace( float &x, float &y, double &z,
185  TransformDirection direction ) const
186 {
187  double xd = static_cast< double >( x ), yd = static_cast< double >( y );
188  transformInPlace( xd, yd, z, direction );
189  x = xd;
190  y = yd;
191 }
192 
193 void QgsCoordinateTransform::transformInPlace( float &x, float &y, float &z,
194  TransformDirection direction ) const
195 {
196  if ( !d->mIsValid || d->mShortCircuit )
197  return;
198 #ifdef QGISDEBUG
199  // QgsDebugMsg(QString("Using transform in place %1 %2").arg(__FILE__).arg(__LINE__));
200 #endif
201  // transform x
202  try
203  {
204  double xd = x;
205  double yd = y;
206  double zd = z;
207  transformCoords( 1, &xd, &yd, &zd, direction );
208  x = xd;
209  y = yd;
210  z = zd;
211  }
212  catch ( QgsCsException & )
213  {
214  // rethrow the exception
215  QgsDebugMsg( "rethrowing exception" );
216  throw;
217  }
218 }
219 
220 void QgsCoordinateTransform::transformPolygon( QPolygonF &poly, TransformDirection direction ) const
221 {
222  if ( !d->mIsValid || d->mShortCircuit )
223  {
224  return;
225  }
226 
227  //create x, y arrays
228  int nVertices = poly.size();
229 
230  QVector<double> x( nVertices );
231  QVector<double> y( nVertices );
232  QVector<double> z( nVertices );
233 
234  for ( int i = 0; i < nVertices; ++i )
235  {
236  const QPointF &pt = poly.at( i );
237  x[i] = pt.x();
238  y[i] = pt.y();
239  z[i] = 0;
240  }
241 
242  try
243  {
244  transformCoords( nVertices, x.data(), y.data(), z.data(), direction );
245  }
246  catch ( const QgsCsException & )
247  {
248  // rethrow the exception
249  QgsDebugMsg( "rethrowing exception" );
250  throw;
251  }
252 
253  for ( int i = 0; i < nVertices; ++i )
254  {
255  QPointF &pt = poly[i];
256  pt.rx() = x[i];
257  pt.ry() = y[i];
258  }
259 }
260 
262  QVector<double> &x, QVector<double> &y, QVector<double> &z,
263  TransformDirection direction ) const
264 {
265 
266  if ( !d->mIsValid || d->mShortCircuit )
267  return;
268 
269  Q_ASSERT( x.size() == y.size() );
270 
271  // Apparently, if one has a std::vector, it is valid to use the
272  // address of the first element in the vector as a pointer to an
273  // array of the vectors data, and hence easily interface with code
274  // that wants C-style arrays.
275 
276  try
277  {
278  transformCoords( x.size(), &x[0], &y[0], &z[0], direction );
279  }
280  catch ( const QgsCsException & )
281  {
282  // rethrow the exception
283  QgsDebugMsg( "rethrowing exception" );
284  throw;
285  }
286 }
287 
288 
290  QVector<float> &x, QVector<float> &y, QVector<float> &z,
291  TransformDirection direction ) const
292 {
293  if ( !d->mIsValid || d->mShortCircuit )
294  return;
295 
296  Q_ASSERT( x.size() == y.size() );
297 
298  // Apparently, if one has a std::vector, it is valid to use the
299  // address of the first element in the vector as a pointer to an
300  // array of the vectors data, and hence easily interface with code
301  // that wants C-style arrays.
302 
303  try
304  {
305  //copy everything to double vectors since proj needs double
306  int vectorSize = x.size();
307  QVector<double> xd( x.size() );
308  QVector<double> yd( y.size() );
309  QVector<double> zd( z.size() );
310  for ( int i = 0; i < vectorSize; ++i )
311  {
312  xd[i] = x[i];
313  yd[i] = y[i];
314  zd[i] = z[i];
315  }
316  transformCoords( x.size(), &xd[0], &yd[0], &zd[0], direction );
317 
318  //copy back
319  for ( int i = 0; i < vectorSize; ++i )
320  {
321  x[i] = xd[i];
322  y[i] = yd[i];
323  z[i] = zd[i];
324  }
325  }
326  catch ( QgsCsException & )
327  {
328  // rethrow the exception
329  QgsDebugMsg( "rethrowing exception" );
330  throw;
331  }
332 }
333 
334 QgsRectangle QgsCoordinateTransform::transformBoundingBox( const QgsRectangle &rect, TransformDirection direction, const bool handle180Crossover ) const
335 {
336  // Calculate the bounding box of a QgsRectangle in the source CRS
337  // when projected to the destination CRS (or the inverse).
338  // This is done by looking at a number of points spread evenly
339  // across the rectangle
340 
341  if ( !d->mIsValid || d->mShortCircuit )
342  return rect;
343 
344  if ( rect.isEmpty() )
345  {
346  QgsPointXY p = transform( rect.xMinimum(), rect.yMinimum(), direction );
347  return QgsRectangle( p, p );
348  }
349 
350  // 64 points (<=2.12) is not enough, see #13665, for EPSG:4326 -> EPSG:3574 (say that it is a hard one),
351  // are decent result from about 500 points and more. This method is called quite often, but
352  // even with 1000 points it takes < 1ms
353  // TODO: how to effectively and precisely reproject bounding box?
354  const int nPoints = 1000;
355  double d = std::sqrt( ( rect.width() * rect.height() ) / std::pow( std::sqrt( static_cast< double >( nPoints ) ) - 1, 2.0 ) );
356  int nXPoints = static_cast< int >( std::ceil( rect.width() / d ) ) + 1;
357  int nYPoints = static_cast< int >( std::ceil( rect.height() / d ) ) + 1;
358 
359  QgsRectangle bb_rect;
360  bb_rect.setMinimal();
361 
362  // We're interfacing with C-style vectors in the
363  // end, so let's do C-style vectors here too.
364 
365  QVector<double> x( nXPoints * nYPoints );
366  QVector<double> y( nXPoints * nYPoints );
367  QVector<double> z( nXPoints * nYPoints );
368 
369  QgsDebugMsgLevel( "Entering transformBoundingBox...", 4 );
370 
371  // Populate the vectors
372 
373  double dx = rect.width() / static_cast< double >( nXPoints - 1 );
374  double dy = rect.height() / static_cast< double >( nYPoints - 1 );
375 
376  double pointY = rect.yMinimum();
377 
378  for ( int i = 0; i < nYPoints ; i++ )
379  {
380 
381  // Start at right edge
382  double pointX = rect.xMinimum();
383 
384  for ( int j = 0; j < nXPoints; j++ )
385  {
386  x[( i * nXPoints ) + j] = pointX;
387  y[( i * nXPoints ) + j] = pointY;
388  // and the height...
389  z[( i * nXPoints ) + j] = 0.0;
390  // QgsDebugMsg(QString("BBox coord: (%1, %2)").arg(x[(i*numP) + j]).arg(y[(i*numP) + j]));
391  pointX += dx;
392  }
393  pointY += dy;
394  }
395 
396  // Do transformation. Any exception generated must
397  // be handled in above layers.
398  try
399  {
400  transformCoords( nXPoints * nYPoints, x.data(), y.data(), z.data(), direction );
401  }
402  catch ( const QgsCsException & )
403  {
404  // rethrow the exception
405  QgsDebugMsg( "rethrowing exception" );
406  throw;
407  }
408 
409  // Calculate the bounding box and use that for the extent
410 
411  for ( int i = 0; i < nXPoints * nYPoints; i++ )
412  {
413  if ( !std::isfinite( x[i] ) || !std::isfinite( y[i] ) )
414  {
415  continue;
416  }
417 
418  if ( handle180Crossover )
419  {
420  //if crossing the date line, temporarily add 360 degrees to -ve longitudes
421  bb_rect.combineExtentWith( x[i] >= 0.0 ? x[i] : x[i] + 360.0, y[i] );
422  }
423  else
424  {
425  bb_rect.combineExtentWith( x[i], y[i] );
426  }
427  }
428 
429  if ( bb_rect.isNull() )
430  {
431  // something bad happened when reprojecting the filter rect... no finite points were left!
432  throw QgsCsException( QObject::tr( "Could not transform bounding box to target CRS" ) );
433  }
434 
435  if ( handle180Crossover )
436  {
437  //subtract temporary addition of 360 degrees from longitudes
438  if ( bb_rect.xMinimum() > 180.0 )
439  bb_rect.setXMinimum( bb_rect.xMinimum() - 360.0 );
440  if ( bb_rect.xMaximum() > 180.0 )
441  bb_rect.setXMaximum( bb_rect.xMaximum() - 360.0 );
442  }
443 
444  QgsDebugMsgLevel( "Projected extent: " + bb_rect.toString(), 4 );
445 
446  if ( bb_rect.isEmpty() )
447  {
448  QgsDebugMsgLevel( "Original extent: " + rect.toString(), 4 );
449  }
450 
451  return bb_rect;
452 }
453 
454 void QgsCoordinateTransform::transformCoords( int numPoints, double *x, double *y, double *z, TransformDirection direction ) const
455 {
456  if ( !d->mIsValid || d->mShortCircuit )
457  return;
458  // Refuse to transform the points if the srs's are invalid
459  if ( !d->mSourceCRS.isValid() )
460  {
461  QgsMessageLog::logMessage( QObject::tr( "The source spatial reference system (CRS) is not valid. "
462  "The coordinates can not be reprojected. The CRS is: %1" )
463  .arg( d->mSourceCRS.toProj4() ), QObject::tr( "CRS" ) );
464  return;
465  }
466  if ( !d->mDestCRS.isValid() )
467  {
468  QgsMessageLog::logMessage( QObject::tr( "The destination spatial reference system (CRS) is not valid. "
469  "The coordinates can not be reprojected. The CRS is: %1" ).arg( d->mDestCRS.toProj4() ), QObject::tr( "CRS" ) );
470  return;
471  }
472 
473 #ifdef COORDINATE_TRANSFORM_VERBOSE
474  double xorg = *x;
475  double yorg = *y;
476  QgsDebugMsg( QString( "[[[[[[ Number of points to transform: %1 ]]]]]]" ).arg( numPoints ) );
477 #endif
478 
479  // use proj4 to do the transform
480 
481  // if the source/destination projection is lat/long, convert the points to radians
482  // prior to transforming
483  QPair<projPJ, projPJ> projData = d->threadLocalProjData();
484  projPJ sourceProj = projData.first;
485  projPJ destProj = projData.second;
486 
487  if ( ( pj_is_latlong( destProj ) && ( direction == ReverseTransform ) )
488  || ( pj_is_latlong( sourceProj ) && ( direction == ForwardTransform ) ) )
489  {
490  for ( int i = 0; i < numPoints; ++i )
491  {
492  x[i] *= DEG_TO_RAD;
493  y[i] *= DEG_TO_RAD;
494  }
495 
496  }
497  int projResult;
498  if ( direction == ReverseTransform )
499  {
500  projResult = pj_transform( destProj, sourceProj, numPoints, 0, x, y, z );
501  }
502  else
503  {
504  Q_ASSERT( sourceProj );
505  Q_ASSERT( destProj );
506  projResult = pj_transform( sourceProj, destProj, numPoints, 0, x, y, z );
507  }
508 
509  if ( projResult != 0 )
510  {
511  //something bad happened....
512  QString points;
513 
514  for ( int i = 0; i < numPoints; ++i )
515  {
516  if ( direction == ForwardTransform )
517  {
518  points += QStringLiteral( "(%1, %2)\n" ).arg( x[i], 0, 'f' ).arg( y[i], 0, 'f' );
519  }
520  else
521  {
522  points += QStringLiteral( "(%1, %2)\n" ).arg( x[i] * RAD_TO_DEG, 0, 'f' ).arg( y[i] * RAD_TO_DEG, 0, 'f' );
523  }
524  }
525 
526  QString dir = ( direction == ForwardTransform ) ? QObject::tr( "forward transform" ) : QObject::tr( "inverse transform" );
527 
528  char *srcdef = pj_get_def( sourceProj, 0 );
529  char *dstdef = pj_get_def( destProj, 0 );
530 
531  QString msg = QObject::tr( "%1 of\n"
532  "%2"
533  "PROJ.4: %3 +to %4\n"
534  "Error: %5" )
535  .arg( dir,
536  points,
537  srcdef, dstdef,
538  QString::fromUtf8( pj_strerrno( projResult ) ) );
539 
540  pj_dalloc( srcdef );
541  pj_dalloc( dstdef );
542 
543  QgsDebugMsg( "Projection failed emitting invalid transform signal: " + msg );
544  QgsDebugMsg( "throwing exception" );
545 
546  throw QgsCsException( msg );
547  }
548 
549  // if the result is lat/long, convert the results from radians back
550  // to degrees
551  if ( ( pj_is_latlong( destProj ) && ( direction == ForwardTransform ) )
552  || ( pj_is_latlong( sourceProj ) && ( direction == ReverseTransform ) ) )
553  {
554  for ( int i = 0; i < numPoints; ++i )
555  {
556  x[i] *= RAD_TO_DEG;
557  y[i] *= RAD_TO_DEG;
558  }
559  }
560 #ifdef COORDINATE_TRANSFORM_VERBOSE
561  QgsDebugMsg( QString( "[[[[[[ Projected %1, %2 to %3, %4 ]]]]]]" )
562  .arg( xorg, 0, 'g', 15 ).arg( yorg, 0, 'g', 15 )
563  .arg( *x, 0, 'g', 15 ).arg( *y, 0, 'g', 15 ) );
564 #endif
565 }
566 
568 {
569  return d->mIsValid;
570 }
571 
573 {
574  return !d->mIsValid || d->mShortCircuit;
575 }
576 
577 bool QgsCoordinateTransform::readXml( const QDomNode &node )
578 {
579  d.detach();
580 
581  QgsDebugMsg( "Reading Coordinate Transform from xml ------------------------!" );
582 
583  QDomNode mySrcNode = node.namedItem( QStringLiteral( "sourcesrs" ) );
584  d->mSourceCRS.readXml( mySrcNode );
585 
586  QDomNode myDestNode = node.namedItem( QStringLiteral( "destinationsrs" ) );
587  d->mDestCRS.readXml( myDestNode );
588 
589  d->mSourceDatumTransform = node.toElement().attribute( QStringLiteral( "sourceDatumTransform" ), QStringLiteral( "-1" ) ).toInt();
590  d->mDestinationDatumTransform = node.toElement().attribute( QStringLiteral( "destinationDatumTransform" ), QStringLiteral( "-1" ) ).toInt();
591 
592  return d->initialize();
593 }
594 
595 bool QgsCoordinateTransform::writeXml( QDomNode &node, QDomDocument &doc ) const
596 {
597  QDomElement myNodeElement = node.toElement();
598  QDomElement myTransformElement = doc.createElement( QStringLiteral( "coordinatetransform" ) );
599  myTransformElement.setAttribute( QStringLiteral( "sourceDatumTransform" ), QString::number( d->mSourceDatumTransform ) );
600  myTransformElement.setAttribute( QStringLiteral( "destinationDatumTransform" ), QString::number( d->mDestinationDatumTransform ) );
601 
602  QDomElement mySourceElement = doc.createElement( QStringLiteral( "sourcesrs" ) );
603  d->mSourceCRS.writeXml( mySourceElement, doc );
604  myTransformElement.appendChild( mySourceElement );
605 
606  QDomElement myDestElement = doc.createElement( QStringLiteral( "destinationsrs" ) );
607  d->mDestCRS.writeXml( myDestElement, doc );
608  myTransformElement.appendChild( myDestElement );
609 
610  myNodeElement.appendChild( myTransformElement );
611 
612  return true;
613 }
614 
615 const char *finder( const char *name )
616 {
617  QString proj;
618 #ifdef Q_OS_WIN
619  proj = QApplication::applicationDirPath()
620  + "/share/proj/" + QString( name );
621 #else
622  Q_UNUSED( name );
623 #endif
624  return proj.toUtf8();
625 }
626 
627 
628 
630 {
631  QList< QList< int > > transformations;
632 
633  QString srcGeoId = srcCRS.geographicCrsAuthId();
634  QString destGeoId = destCRS.geographicCrsAuthId();
635 
636  if ( srcGeoId.isEmpty() || destGeoId.isEmpty() )
637  {
638  return transformations;
639  }
640 
641  QStringList srcSplit = srcGeoId.split( ':' );
642  QStringList destSplit = destGeoId.split( ':' );
643 
644  if ( srcSplit.size() < 2 || destSplit.size() < 2 )
645  {
646  return transformations;
647  }
648 
649  int srcAuthCode = srcSplit.at( 1 ).toInt();
650  int destAuthCode = destSplit.at( 1 ).toInt();
651 
652  if ( srcAuthCode == destAuthCode )
653  {
654  return transformations; //crs have the same datum
655  }
656 
657  QList<int> directTransforms;
658  searchDatumTransform( QStringLiteral( "SELECT coord_op_code FROM tbl_datum_transform WHERE source_crs_code=%1 AND target_crs_code=%2 ORDER BY deprecated ASC,preferred DESC" ).arg( srcAuthCode ).arg( destAuthCode ),
659  directTransforms );
660  QList<int> reverseDirectTransforms;
661  searchDatumTransform( QStringLiteral( "SELECT coord_op_code FROM tbl_datum_transform WHERE source_crs_code = %1 AND target_crs_code=%2 ORDER BY deprecated ASC,preferred DESC" ).arg( destAuthCode ).arg( srcAuthCode ),
662  reverseDirectTransforms );
663  QList<int> srcToWgs84;
664  searchDatumTransform( QStringLiteral( "SELECT coord_op_code FROM tbl_datum_transform WHERE (source_crs_code=%1 AND target_crs_code=%2) OR (source_crs_code=%2 AND target_crs_code=%1) ORDER BY deprecated ASC,preferred DESC" ).arg( srcAuthCode ).arg( 4326 ),
665  srcToWgs84 );
666  QList<int> destToWgs84;
667  searchDatumTransform( QStringLiteral( "SELECT coord_op_code FROM tbl_datum_transform WHERE (source_crs_code=%1 AND target_crs_code=%2) OR (source_crs_code=%2 AND target_crs_code=%1) ORDER BY deprecated ASC,preferred DESC" ).arg( destAuthCode ).arg( 4326 ),
668  destToWgs84 );
669 
670  //add direct datum transformations
671  QList<int>::const_iterator directIt = directTransforms.constBegin();
672  for ( ; directIt != directTransforms.constEnd(); ++directIt )
673  {
674  transformations.push_back( QList<int>() << *directIt << -1 );
675  }
676 
677  //add direct datum transformations
678  directIt = reverseDirectTransforms.constBegin();
679  for ( ; directIt != reverseDirectTransforms.constEnd(); ++directIt )
680  {
681  transformations.push_back( QList<int>() << -1 << *directIt );
682  }
683 
684  QList<int>::const_iterator srcWgsIt = srcToWgs84.constBegin();
685  for ( ; srcWgsIt != srcToWgs84.constEnd(); ++srcWgsIt )
686  {
687  QList<int>::const_iterator dstWgsIt = destToWgs84.constBegin();
688  for ( ; dstWgsIt != destToWgs84.constEnd(); ++dstWgsIt )
689  {
690  transformations.push_back( QList<int>() << *srcWgsIt << *dstWgsIt );
691  }
692  }
693 
694  return transformations;
695 }
696 
697 void QgsCoordinateTransform::searchDatumTransform( const QString &sql, QList< int > &transforms )
698 {
700  int openResult = database.open_v2( QgsApplication::srsDatabaseFilePath(), SQLITE_OPEN_READONLY, nullptr );
701  if ( openResult != SQLITE_OK )
702  {
703  return;
704  }
705 
707  int prepareRes;
708  statement = database.prepare( sql, prepareRes );
709  if ( prepareRes != SQLITE_OK )
710  {
711  return;
712  }
713 
714  QString cOpCode;
715  while ( statement.step() == SQLITE_ROW )
716  {
717  cOpCode = statement.columnAsText( 0 );
718  transforms.push_back( cOpCode.toInt() );
719  }
720 }
721 
722 QString QgsCoordinateTransform::datumTransformString( int datumTransform )
723 {
724  return QgsCoordinateTransformPrivate::datumTransformString( datumTransform );
725 }
726 
727 bool QgsCoordinateTransform::datumTransformCrsInfo( int datumTransform, int &epsgNr, QString &srcProjection, QString &dstProjection, QString &remarks, QString &scope, bool &preferred, bool &deprecated )
728 {
730  int openResult = database.open_v2( QgsApplication::srsDatabaseFilePath(), SQLITE_OPEN_READONLY, nullptr );
731  if ( openResult != SQLITE_OK )
732  {
733  return false;
734  }
735 
737  QString sql = QStringLiteral( "SELECT epsg_nr,source_crs_code,target_crs_code,remarks,scope,preferred,deprecated FROM tbl_datum_transform WHERE coord_op_code=%1" ).arg( datumTransform );
738  int prepareRes;
739  statement = database.prepare( sql, prepareRes );
740  if ( prepareRes != SQLITE_OK )
741  {
742  return false;
743  }
744 
745  int srcCrsId, destCrsId;
746  if ( statement.step() != SQLITE_ROW )
747  {
748  return false;
749  }
750 
751  epsgNr = statement.columnAsInt64( 0 );
752  srcCrsId = statement.columnAsInt64( 1 );
753  destCrsId = statement.columnAsInt64( 2 );
754  remarks = statement.columnAsText( 3 );
755  scope = statement.columnAsText( 4 );
756  preferred = statement.columnAsInt64( 5 ) != 0;
757  deprecated = statement.columnAsInt64( 6 ) != 0;
758 
759  QgsCoordinateReferenceSystem srcCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "EPSG:%1" ).arg( srcCrsId ) );
760  srcProjection = srcCrs.description();
761  QgsCoordinateReferenceSystem destCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "EPSG:%1" ).arg( destCrsId ) );
762  dstProjection = destCrs.description();
763 
764  return true;
765 }
766 
768 {
769  return d->mSourceDatumTransform;
770 }
771 
773 {
774  d.detach();
775  d->mSourceDatumTransform = dt;
776 }
777 
779 {
780  return d->mDestinationDatumTransform;
781 }
782 
784 {
785  d.detach();
786  d->mDestinationDatumTransform = dt;
787 }
788 
790 {
791  d.detach();
792  d->initialize();
793 }
QString geographicCrsAuthId() const
Returns auth id of related geographic CRS.
void transformCoords(int numPoint, double *x, double *y, double *z, TransformDirection direction=ForwardTransform) const
Transform an array of coordinates to the destination CRS.
A rectangle specified with double values.
Definition: qgsrectangle.h:39
void setMinimal()
Set a rectangle so that min corner is at max and max corner is at min.
static QList< QList< int > > datumTransformations(const QgsCoordinateReferenceSystem &srcCRS, const QgsCoordinateReferenceSystem &destinationCrs)
Returns list of datum transformations for the given src and dest CRS.
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:90
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
bool readXml(const QDomNode &node)
Restores state from the given Dom node.
#define QgsDebugMsg(str)
Definition: qgslogger.h:37
double y
Definition: qgspointxy.h:48
A class to represent a 2D point.
Definition: qgspointxy.h:43
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, TransformDirection direction=ForwardTransform, const bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
QgsCoordinateTransform & operator=(const QgsCoordinateTransform &o)
Assignment operator.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
QgsCoordinateTransform()
Default constructor, creates an invalid QgsCoordinateTransform.
QgsCoordinateReferenceSystem sourceCrs() const
Returns the source coordinate reference system, which the transform will transform coordinates from...
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination coordinate reference system.
bool writeXml(QDomNode &node, QDomDocument &document) const
Stores state to the given Dom node in the given document.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:38
bool isEmpty() const
Returns true if the rectangle is empty.
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
bool isShortCircuited() const
Returns true if the transform short circuits because the source and destination are equivalent...
static QString datumTransformString(int datumTransform)
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:138
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
void transformPolygon(QPolygonF &polygon, TransformDirection direction=ForwardTransform) const
Transforms a polygon to the destination coordinate system.
QString description() const
Returns the descriptive name of the CRS, e.g., "WGS 84" or "GDA 94 / Vicgrid94".
QString columnAsText(int column) const
Returns the column value from the current statement row as a string.
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
double x
Definition: qgspointxy.h:47
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:126
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:111
void combineExtentWith(const QgsRectangle &rect)
Expand the rectangle so that covers both the original rectangle and the given rectangle.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
static void logMessage(const QString &message, const QString &tag=QString(), MessageLevel level=QgsMessageLog::WARNING)
add a message to the instance (and create it if necessary)
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
Transform from destination to source CRS.
QgsPointXY transform(const QgsPointXY &point, TransformDirection direction=ForwardTransform) const
Transform the point from the source CRS to the destination CRS.
This class represents a coordinate reference system (CRS).
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Class for doing transforms between two map coordinate systems.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:116
static QString srsDatabaseFilePath()
Returns the path to the srs.db file.
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:121
Transform from source to destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
static bool datumTransformCrsInfo(int datumTransform, int &epsgNr, QString &srcProjection, QString &dstProjection, QString &remarks, QString &scope, bool &preferred, bool &deprecated)
Gets name of source and dest geographical CRS (to show in a tooltip)
void initialize()
initialize is used to actually create the Transformer instance
void transformInPlace(double &x, double &y, double &z, TransformDirection direction=ForwardTransform) const
Transforms an array of x, y and z double coordinates in place, from the source CRS to the destination...
void setSourceCrs(const QgsCoordinateReferenceSystem &crs)
Sets the source coordinate reference system.
qlonglong columnAsInt64(int column) const
Get column value from the current statement row as a long long integer (64 bits). ...
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:85
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:145
const char * finder(const char *name)