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