QGIS API Documentation  2.99.0-Master (f867b65)
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 = sqrt( ( rect.width() * rect.height() ) / pow( sqrt( static_cast< double >( nPoints ) ) - 1, 2.0 ) );
356  int nXPoints = static_cast< int >( ceil( rect.width() / d ) ) + 1;
357  int nYPoints = static_cast< int >( 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 ( !qIsFinite( x[i] ) || !qIsFinite( 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 ( handle180Crossover )
430  {
431  //subtract temporary addition of 360 degrees from longitudes
432  if ( bb_rect.xMinimum() > 180.0 )
433  bb_rect.setXMinimum( bb_rect.xMinimum() - 360.0 );
434  if ( bb_rect.xMaximum() > 180.0 )
435  bb_rect.setXMaximum( bb_rect.xMaximum() - 360.0 );
436  }
437 
438  QgsDebugMsgLevel( "Projected extent: " + bb_rect.toString(), 4 );
439 
440  if ( bb_rect.isEmpty() )
441  {
442  QgsDebugMsgLevel( "Original extent: " + rect.toString(), 4 );
443  }
444 
445  return bb_rect;
446 }
447 
448 void QgsCoordinateTransform::transformCoords( int numPoints, double *x, double *y, double *z, TransformDirection direction ) const
449 {
450  if ( !d->mIsValid || d->mShortCircuit )
451  return;
452  // Refuse to transform the points if the srs's are invalid
453  if ( !d->mSourceCRS.isValid() )
454  {
455  QgsMessageLog::logMessage( QObject::tr( "The source spatial reference system (CRS) is not valid. "
456  "The coordinates can not be reprojected. The CRS is: %1" )
457  .arg( d->mSourceCRS.toProj4() ), QObject::tr( "CRS" ) );
458  return;
459  }
460  if ( !d->mDestCRS.isValid() )
461  {
462  QgsMessageLog::logMessage( QObject::tr( "The destination spatial reference system (CRS) is not valid. "
463  "The coordinates can not be reprojected. The CRS is: %1" ).arg( d->mDestCRS.toProj4() ), QObject::tr( "CRS" ) );
464  return;
465  }
466 
467 #ifdef COORDINATE_TRANSFORM_VERBOSE
468  double xorg = *x;
469  double yorg = *y;
470  QgsDebugMsg( QString( "[[[[[[ Number of points to transform: %1 ]]]]]]" ).arg( numPoints ) );
471 #endif
472 
473  // use proj4 to do the transform
474 
475  // if the source/destination projection is lat/long, convert the points to radians
476  // prior to transforming
477  QPair<projPJ, projPJ> projData = d->threadLocalProjData();
478  projPJ sourceProj = projData.first;
479  projPJ destProj = projData.second;
480 
481  if ( ( pj_is_latlong( destProj ) && ( direction == ReverseTransform ) )
482  || ( pj_is_latlong( sourceProj ) && ( direction == ForwardTransform ) ) )
483  {
484  for ( int i = 0; i < numPoints; ++i )
485  {
486  x[i] *= DEG_TO_RAD;
487  y[i] *= DEG_TO_RAD;
488  }
489 
490  }
491  int projResult;
492  if ( direction == ReverseTransform )
493  {
494  projResult = pj_transform( destProj, sourceProj, numPoints, 0, x, y, z );
495  }
496  else
497  {
498  Q_ASSERT( sourceProj );
499  Q_ASSERT( destProj );
500  projResult = pj_transform( sourceProj, destProj, numPoints, 0, x, y, z );
501  }
502 
503  if ( projResult != 0 )
504  {
505  //something bad happened....
506  QString points;
507 
508  for ( int i = 0; i < numPoints; ++i )
509  {
510  if ( direction == ForwardTransform )
511  {
512  points += QStringLiteral( "(%1, %2)\n" ).arg( x[i], 0, 'f' ).arg( y[i], 0, 'f' );
513  }
514  else
515  {
516  points += QStringLiteral( "(%1, %2)\n" ).arg( x[i] * RAD_TO_DEG, 0, 'f' ).arg( y[i] * RAD_TO_DEG, 0, 'f' );
517  }
518  }
519 
520  QString dir = ( direction == ForwardTransform ) ? QObject::tr( "forward transform" ) : QObject::tr( "inverse transform" );
521 
522  char *srcdef = pj_get_def( sourceProj, 0 );
523  char *dstdef = pj_get_def( destProj, 0 );
524 
525  QString msg = QObject::tr( "%1 of\n"
526  "%2"
527  "PROJ.4: %3 +to %4\n"
528  "Error: %5" )
529  .arg( dir,
530  points,
531  srcdef, dstdef,
532  QString::fromUtf8( pj_strerrno( projResult ) ) );
533 
534  pj_dalloc( srcdef );
535  pj_dalloc( dstdef );
536 
537  QgsDebugMsg( "Projection failed emitting invalid transform signal: " + msg );
538  QgsDebugMsg( "throwing exception" );
539 
540  throw QgsCsException( msg );
541  }
542 
543  // if the result is lat/long, convert the results from radians back
544  // to degrees
545  if ( ( pj_is_latlong( destProj ) && ( direction == ForwardTransform ) )
546  || ( pj_is_latlong( sourceProj ) && ( direction == ReverseTransform ) ) )
547  {
548  for ( int i = 0; i < numPoints; ++i )
549  {
550  x[i] *= RAD_TO_DEG;
551  y[i] *= RAD_TO_DEG;
552  }
553  }
554 #ifdef COORDINATE_TRANSFORM_VERBOSE
555  QgsDebugMsg( QString( "[[[[[[ Projected %1, %2 to %3, %4 ]]]]]]" )
556  .arg( xorg, 0, 'g', 15 ).arg( yorg, 0, 'g', 15 )
557  .arg( *x, 0, 'g', 15 ).arg( *y, 0, 'g', 15 ) );
558 #endif
559 }
560 
562 {
563  return d->mIsValid;
564 }
565 
567 {
568  return !d->mIsValid || d->mShortCircuit;
569 }
570 
571 bool QgsCoordinateTransform::readXml( const QDomNode &node )
572 {
573  d.detach();
574 
575  QgsDebugMsg( "Reading Coordinate Transform from xml ------------------------!" );
576 
577  QDomNode mySrcNode = node.namedItem( QStringLiteral( "sourcesrs" ) );
578  d->mSourceCRS.readXml( mySrcNode );
579 
580  QDomNode myDestNode = node.namedItem( QStringLiteral( "destinationsrs" ) );
581  d->mDestCRS.readXml( myDestNode );
582 
583  d->mSourceDatumTransform = node.toElement().attribute( QStringLiteral( "sourceDatumTransform" ), QStringLiteral( "-1" ) ).toInt();
584  d->mDestinationDatumTransform = node.toElement().attribute( QStringLiteral( "destinationDatumTransform" ), QStringLiteral( "-1" ) ).toInt();
585 
586  return d->initialize();
587 }
588 
589 bool QgsCoordinateTransform::writeXml( QDomNode &node, QDomDocument &doc ) const
590 {
591  QDomElement myNodeElement = node.toElement();
592  QDomElement myTransformElement = doc.createElement( QStringLiteral( "coordinatetransform" ) );
593  myTransformElement.setAttribute( QStringLiteral( "sourceDatumTransform" ), QString::number( d->mSourceDatumTransform ) );
594  myTransformElement.setAttribute( QStringLiteral( "destinationDatumTransform" ), QString::number( d->mDestinationDatumTransform ) );
595 
596  QDomElement mySourceElement = doc.createElement( QStringLiteral( "sourcesrs" ) );
597  d->mSourceCRS.writeXml( mySourceElement, doc );
598  myTransformElement.appendChild( mySourceElement );
599 
600  QDomElement myDestElement = doc.createElement( QStringLiteral( "destinationsrs" ) );
601  d->mDestCRS.writeXml( myDestElement, doc );
602  myTransformElement.appendChild( myDestElement );
603 
604  myNodeElement.appendChild( myTransformElement );
605 
606  return true;
607 }
608 
609 const char *finder( const char *name )
610 {
611  QString proj;
612 #ifdef Q_OS_WIN
613  proj = QApplication::applicationDirPath()
614  + "/share/proj/" + QString( name );
615 #else
616  Q_UNUSED( name );
617 #endif
618  return proj.toUtf8();
619 }
620 
621 
622 
624 {
625  QList< QList< int > > transformations;
626 
627  QString srcGeoId = srcCRS.geographicCrsAuthId();
628  QString destGeoId = destCRS.geographicCrsAuthId();
629 
630  if ( srcGeoId.isEmpty() || destGeoId.isEmpty() )
631  {
632  return transformations;
633  }
634 
635  QStringList srcSplit = srcGeoId.split( ':' );
636  QStringList destSplit = destGeoId.split( ':' );
637 
638  if ( srcSplit.size() < 2 || destSplit.size() < 2 )
639  {
640  return transformations;
641  }
642 
643  int srcAuthCode = srcSplit.at( 1 ).toInt();
644  int destAuthCode = destSplit.at( 1 ).toInt();
645 
646  if ( srcAuthCode == destAuthCode )
647  {
648  return transformations; //crs have the same datum
649  }
650 
651  QList<int> directTransforms;
652  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 ),
653  directTransforms );
654  QList<int> reverseDirectTransforms;
655  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 ),
656  reverseDirectTransforms );
657  QList<int> srcToWgs84;
658  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 ),
659  srcToWgs84 );
660  QList<int> destToWgs84;
661  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 ),
662  destToWgs84 );
663 
664  //add direct datum transformations
665  QList<int>::const_iterator directIt = directTransforms.constBegin();
666  for ( ; directIt != directTransforms.constEnd(); ++directIt )
667  {
668  transformations.push_back( QList<int>() << *directIt << -1 );
669  }
670 
671  //add direct datum transformations
672  directIt = reverseDirectTransforms.constBegin();
673  for ( ; directIt != reverseDirectTransforms.constEnd(); ++directIt )
674  {
675  transformations.push_back( QList<int>() << -1 << *directIt );
676  }
677 
678  QList<int>::const_iterator srcWgsIt = srcToWgs84.constBegin();
679  for ( ; srcWgsIt != srcToWgs84.constEnd(); ++srcWgsIt )
680  {
681  QList<int>::const_iterator dstWgsIt = destToWgs84.constBegin();
682  for ( ; dstWgsIt != destToWgs84.constEnd(); ++dstWgsIt )
683  {
684  transformations.push_back( QList<int>() << *srcWgsIt << *dstWgsIt );
685  }
686  }
687 
688  return transformations;
689 }
690 
691 void QgsCoordinateTransform::searchDatumTransform( const QString &sql, QList< int > &transforms )
692 {
693  sqlite3 *db = nullptr;
694  int openResult = sqlite3_open_v2( QgsApplication::srsDatabaseFilePath().toUtf8().constData(), &db, SQLITE_OPEN_READONLY, nullptr );
695  if ( openResult != SQLITE_OK )
696  {
697  sqlite3_close( db );
698  return;
699  }
700 
701  sqlite3_stmt *stmt = nullptr;
702  int prepareRes = sqlite3_prepare( db, sql.toLatin1(), sql.size(), &stmt, nullptr );
703  if ( prepareRes != SQLITE_OK )
704  {
705  sqlite3_finalize( stmt );
706  sqlite3_close( db );
707  return;
708  }
709 
710  QString cOpCode;
711  while ( sqlite3_step( stmt ) == SQLITE_ROW )
712  {
713  cOpCode = reinterpret_cast< const char * >( sqlite3_column_text( stmt, 0 ) );
714  transforms.push_back( cOpCode.toInt() );
715  }
716  sqlite3_finalize( stmt );
717  sqlite3_close( db );
718 }
719 
720 QString QgsCoordinateTransform::datumTransformString( int datumTransform )
721 {
722  return QgsCoordinateTransformPrivate::datumTransformString( datumTransform );
723 }
724 
725 bool QgsCoordinateTransform::datumTransformCrsInfo( int datumTransform, int &epsgNr, QString &srcProjection, QString &dstProjection, QString &remarks, QString &scope, bool &preferred, bool &deprecated )
726 {
727  sqlite3 *db = nullptr;
728  int openResult = sqlite3_open_v2( QgsApplication::srsDatabaseFilePath().toUtf8().constData(), &db, SQLITE_OPEN_READONLY, nullptr );
729  if ( openResult != SQLITE_OK )
730  {
731  sqlite3_close( db );
732  return false;
733  }
734 
735  sqlite3_stmt *stmt = nullptr;
736  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 );
737  int prepareRes = sqlite3_prepare( db, sql.toLatin1(), sql.size(), &stmt, nullptr );
738  if ( prepareRes != SQLITE_OK )
739  {
740  sqlite3_finalize( stmt );
741  sqlite3_close( db );
742  return false;
743  }
744 
745  int srcCrsId, destCrsId;
746  if ( sqlite3_step( stmt ) != SQLITE_ROW )
747  {
748  sqlite3_finalize( stmt );
749  sqlite3_close( db );
750  return false;
751  }
752 
753  epsgNr = sqlite3_column_int( stmt, 0 );
754  srcCrsId = sqlite3_column_int( stmt, 1 );
755  destCrsId = sqlite3_column_int( stmt, 2 );
756  remarks = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( stmt, 3 ) ) );
757  scope = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( stmt, 4 ) ) );
758  preferred = sqlite3_column_int( stmt, 5 ) != 0;
759  deprecated = sqlite3_column_int( stmt, 6 ) != 0;
760 
761  QgsCoordinateReferenceSystem srcCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "EPSG:%1" ).arg( srcCrsId ) );
762  srcProjection = srcCrs.description();
763  QgsCoordinateReferenceSystem destCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "EPSG:%1" ).arg( destCrsId ) );
764  dstProjection = destCrs.description();
765 
766  sqlite3_finalize( stmt );
767  sqlite3_close( db );
768  return true;
769 }
770 
772 {
773  return d->mSourceDatumTransform;
774 }
775 
777 {
778  d.detach();
779  d->mSourceDatumTransform = dt;
780 }
781 
783 {
784  return d->mDestinationDatumTransform;
785 }
786 
788 {
789  d.detach();
790  d->mDestinationDatumTransform = dt;
791 }
792 
794 {
795  d.detach();
796  d->initialize();
797 }
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:38
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:70
bool readXml(const QDomNode &node)
Restores state from the given Dom node.
#define QgsDebugMsg(str)
Definition: qgslogger.h:37
double y
Definition: qgspointxy.h:47
A class to represent a 2D point.
Definition: qgspointxy.h:42
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.
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:118
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".
double x
Definition: qgspointxy.h:46
struct sqlite3 sqlite3
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:106
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:91
void combineExtentWith(const QgsRectangle &rect)
Expand the rectangle so that covers both the original rectangle and the given rectangle.
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).
Class for doing transforms between two map coordinate systems.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:96
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:101
Transform from source to destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:64
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.
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:65
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:125
const char * finder(const char *name)