QGIS API Documentation  2.99.0-Master (bdf46d7)
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 
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 QgsPoint( x, y );
110 }
111 
112 
113 QgsPoint QgsCoordinateTransform::transform( const double theX, const double theY = 0.0, TransformDirection direction ) const
114 {
115  try
116  {
117  return transform( QgsPoint( 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  QgsPoint 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  if ( ( pj_is_latlong( d->mDestinationProjection ) && ( direction == ReverseTransform ) )
478  || ( pj_is_latlong( d->mSourceProjection ) && ( direction == ForwardTransform ) ) )
479  {
480  for ( int i = 0; i < numPoints; ++i )
481  {
482  x[i] *= DEG_TO_RAD;
483  y[i] *= DEG_TO_RAD;
484  }
485 
486  }
487  int projResult;
488  if ( direction == ReverseTransform )
489  {
490  projResult = pj_transform( d->mDestinationProjection, d->mSourceProjection, numPoints, 0, x, y, z );
491  }
492  else
493  {
494  Q_ASSERT( d->mSourceProjection );
495  Q_ASSERT( d->mDestinationProjection );
496  projResult = pj_transform( d->mSourceProjection, d->mDestinationProjection, numPoints, 0, x, y, z );
497  }
498 
499  if ( projResult != 0 )
500  {
501  //something bad happened....
502  QString points;
503 
504  for ( int i = 0; i < numPoints; ++i )
505  {
506  if ( direction == ForwardTransform )
507  {
508  points += QStringLiteral( "(%1, %2)\n" ).arg( x[i], 0, 'f' ).arg( y[i], 0, 'f' );
509  }
510  else
511  {
512  points += QStringLiteral( "(%1, %2)\n" ).arg( x[i] * RAD_TO_DEG, 0, 'f' ).arg( y[i] * RAD_TO_DEG, 0, 'f' );
513  }
514  }
515 
516  QString dir = ( direction == ForwardTransform ) ? QObject::tr( "forward transform" ) : QObject::tr( "inverse transform" );
517 
518  char *srcdef = pj_get_def( d->mSourceProjection, 0 );
519  char *dstdef = pj_get_def( d->mDestinationProjection, 0 );
520 
521  QString msg = QObject::tr( "%1 of\n"
522  "%2"
523  "PROJ.4: %3 +to %4\n"
524  "Error: %5" )
525  .arg( dir,
526  points,
527  srcdef, dstdef,
528  QString::fromUtf8( pj_strerrno( projResult ) ) );
529 
530  pj_dalloc( srcdef );
531  pj_dalloc( dstdef );
532 
533  QgsDebugMsg( "Projection failed emitting invalid transform signal: " + msg );
534  QgsDebugMsg( "throwing exception" );
535 
536  throw QgsCsException( msg );
537  }
538 
539  // if the result is lat/long, convert the results from radians back
540  // to degrees
541  if ( ( pj_is_latlong( d->mDestinationProjection ) && ( direction == ForwardTransform ) )
542  || ( pj_is_latlong( d->mSourceProjection ) && ( direction == ReverseTransform ) ) )
543  {
544  for ( int i = 0; i < numPoints; ++i )
545  {
546  x[i] *= RAD_TO_DEG;
547  y[i] *= RAD_TO_DEG;
548  }
549  }
550 #ifdef COORDINATE_TRANSFORM_VERBOSE
551  QgsDebugMsg( QString( "[[[[[[ Projected %1, %2 to %3, %4 ]]]]]]" )
552  .arg( xorg, 0, 'g', 15 ).arg( yorg, 0, 'g', 15 )
553  .arg( *x, 0, 'g', 15 ).arg( *y, 0, 'g', 15 ) );
554 #endif
555 }
556 
558 {
559  return d->mIsValid;
560 }
561 
563 {
564  return !d->mIsValid || d->mShortCircuit;
565 }
566 
567 bool QgsCoordinateTransform::readXml( const QDomNode &node )
568 {
569  d.detach();
570 
571  QgsDebugMsg( "Reading Coordinate Transform from xml ------------------------!" );
572 
573  QDomNode mySrcNode = node.namedItem( QStringLiteral( "sourcesrs" ) );
574  d->mSourceCRS.readXml( mySrcNode );
575 
576  QDomNode myDestNode = node.namedItem( QStringLiteral( "destinationsrs" ) );
577  d->mDestCRS.readXml( myDestNode );
578 
579  d->mSourceDatumTransform = node.toElement().attribute( QStringLiteral( "sourceDatumTransform" ), QStringLiteral( "-1" ) ).toInt();
580  d->mDestinationDatumTransform = node.toElement().attribute( QStringLiteral( "destinationDatumTransform" ), QStringLiteral( "-1" ) ).toInt();
581 
582  return d->initialize();
583 }
584 
585 bool QgsCoordinateTransform::writeXml( QDomNode &node, QDomDocument &doc ) const
586 {
587  QDomElement myNodeElement = node.toElement();
588  QDomElement myTransformElement = doc.createElement( QStringLiteral( "coordinatetransform" ) );
589  myTransformElement.setAttribute( QStringLiteral( "sourceDatumTransform" ), QString::number( d->mSourceDatumTransform ) );
590  myTransformElement.setAttribute( QStringLiteral( "destinationDatumTransform" ), QString::number( d->mDestinationDatumTransform ) );
591 
592  QDomElement mySourceElement = doc.createElement( QStringLiteral( "sourcesrs" ) );
593  d->mSourceCRS.writeXml( mySourceElement, doc );
594  myTransformElement.appendChild( mySourceElement );
595 
596  QDomElement myDestElement = doc.createElement( QStringLiteral( "destinationsrs" ) );
597  d->mDestCRS.writeXml( myDestElement, doc );
598  myTransformElement.appendChild( myDestElement );
599 
600  myNodeElement.appendChild( myTransformElement );
601 
602  return true;
603 }
604 
605 const char *finder( const char *name )
606 {
607  QString proj;
608 #ifdef Q_OS_WIN
609  proj = QApplication::applicationDirPath()
610  + "/share/proj/" + QString( name );
611 #else
612  Q_UNUSED( name );
613 #endif
614  return proj.toUtf8();
615 }
616 
617 
618 
620 {
621  QList< QList< int > > transformations;
622 
623  QString srcGeoId = srcCRS.geographicCrsAuthId();
624  QString destGeoId = destCRS.geographicCrsAuthId();
625 
626  if ( srcGeoId.isEmpty() || destGeoId.isEmpty() )
627  {
628  return transformations;
629  }
630 
631  QStringList srcSplit = srcGeoId.split( ':' );
632  QStringList destSplit = destGeoId.split( ':' );
633 
634  if ( srcSplit.size() < 2 || destSplit.size() < 2 )
635  {
636  return transformations;
637  }
638 
639  int srcAuthCode = srcSplit.at( 1 ).toInt();
640  int destAuthCode = destSplit.at( 1 ).toInt();
641 
642  if ( srcAuthCode == destAuthCode )
643  {
644  return transformations; //crs have the same datum
645  }
646 
647  QList<int> directTransforms;
648  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 ),
649  directTransforms );
650  QList<int> reverseDirectTransforms;
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( destAuthCode ).arg( srcAuthCode ),
652  reverseDirectTransforms );
653  QList<int> srcToWgs84;
654  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 ),
655  srcToWgs84 );
656  QList<int> destToWgs84;
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( destAuthCode ).arg( 4326 ),
658  destToWgs84 );
659 
660  //add direct datum transformations
661  QList<int>::const_iterator directIt = directTransforms.constBegin();
662  for ( ; directIt != directTransforms.constEnd(); ++directIt )
663  {
664  transformations.push_back( QList<int>() << *directIt << -1 );
665  }
666 
667  //add direct datum transformations
668  directIt = reverseDirectTransforms.constBegin();
669  for ( ; directIt != reverseDirectTransforms.constEnd(); ++directIt )
670  {
671  transformations.push_back( QList<int>() << -1 << *directIt );
672  }
673 
674  QList<int>::const_iterator srcWgsIt = srcToWgs84.constBegin();
675  for ( ; srcWgsIt != srcToWgs84.constEnd(); ++srcWgsIt )
676  {
677  QList<int>::const_iterator dstWgsIt = destToWgs84.constBegin();
678  for ( ; dstWgsIt != destToWgs84.constEnd(); ++dstWgsIt )
679  {
680  transformations.push_back( QList<int>() << *srcWgsIt << *dstWgsIt );
681  }
682  }
683 
684  return transformations;
685 }
686 
687 void QgsCoordinateTransform::searchDatumTransform( const QString &sql, QList< int > &transforms )
688 {
689  sqlite3 *db = nullptr;
690  int openResult = sqlite3_open_v2( QgsApplication::srsDatabaseFilePath().toUtf8().constData(), &db, SQLITE_OPEN_READONLY, nullptr );
691  if ( openResult != SQLITE_OK )
692  {
693  sqlite3_close( db );
694  return;
695  }
696 
697  sqlite3_stmt *stmt = nullptr;
698  int prepareRes = sqlite3_prepare( db, sql.toLatin1(), sql.size(), &stmt, nullptr );
699  if ( prepareRes != SQLITE_OK )
700  {
701  sqlite3_finalize( stmt );
702  sqlite3_close( db );
703  return;
704  }
705 
706  QString cOpCode;
707  while ( sqlite3_step( stmt ) == SQLITE_ROW )
708  {
709  cOpCode = reinterpret_cast< const char * >( sqlite3_column_text( stmt, 0 ) );
710  transforms.push_back( cOpCode.toInt() );
711  }
712  sqlite3_finalize( stmt );
713  sqlite3_close( db );
714 }
715 
716 QString QgsCoordinateTransform::datumTransformString( int datumTransform )
717 {
718  return QgsCoordinateTransformPrivate::datumTransformString( datumTransform );
719 }
720 
721 bool QgsCoordinateTransform::datumTransformCrsInfo( int datumTransform, int &epsgNr, QString &srcProjection, QString &dstProjection, QString &remarks, QString &scope, bool &preferred, bool &deprecated )
722 {
723  sqlite3 *db = nullptr;
724  int openResult = sqlite3_open_v2( QgsApplication::srsDatabaseFilePath().toUtf8().constData(), &db, SQLITE_OPEN_READONLY, nullptr );
725  if ( openResult != SQLITE_OK )
726  {
727  sqlite3_close( db );
728  return false;
729  }
730 
731  sqlite3_stmt *stmt = nullptr;
732  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 );
733  int prepareRes = sqlite3_prepare( db, sql.toLatin1(), sql.size(), &stmt, nullptr );
734  if ( prepareRes != SQLITE_OK )
735  {
736  sqlite3_finalize( stmt );
737  sqlite3_close( db );
738  return false;
739  }
740 
741  int srcCrsId, destCrsId;
742  if ( sqlite3_step( stmt ) != SQLITE_ROW )
743  {
744  sqlite3_finalize( stmt );
745  sqlite3_close( db );
746  return false;
747  }
748 
749  epsgNr = sqlite3_column_int( stmt, 0 );
750  srcCrsId = sqlite3_column_int( stmt, 1 );
751  destCrsId = sqlite3_column_int( stmt, 2 );
752  remarks = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( stmt, 3 ) ) );
753  scope = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( stmt, 4 ) ) );
754  preferred = sqlite3_column_int( stmt, 5 ) != 0;
755  deprecated = sqlite3_column_int( stmt, 6 ) != 0;
756 
757  QgsCoordinateReferenceSystem srcCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "EPSG:%1" ).arg( srcCrsId ) );
758  srcProjection = srcCrs.description();
759  QgsCoordinateReferenceSystem destCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "EPSG:%1" ).arg( destCrsId ) );
760  dstProjection = destCrs.description();
761 
762  sqlite3_finalize( stmt );
763  sqlite3_close( db );
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:38
double y
Definition: qgspoint.h:42
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:36
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.
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:37
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".
A class to represent a point.
Definition: qgspoint.h:37
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.
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.
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)
double x
Definition: qgspoint.h:41