QGIS API Documentation  2.0.1-Dufour
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
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"
18 #include "qgscrscache.h"
19 #include "qgsmessagelog.h"
20 #include "qgslogger.h"
21 
22 //qt includes
23 #include <QDomNode>
24 #include <QDomElement>
25 #include <QApplication>
26 #include <QPolygonF>
27 #include <QVector>
28 
29 extern "C"
30 {
31 #include <proj_api.h>
32 }
33 
34 // if defined shows all information about transform to stdout
35 // #define COORDINATE_TRANSFORM_VERBOSE
36 
38  : QObject()
39  , mInitialisedFlag( false )
40  , mSourceProjection( 0 )
41  , mDestinationProjection( 0 )
42 {
43  setFinder();
44 }
45 
47  : QObject()
48  , mInitialisedFlag( false )
49  , mSourceProjection( 0 )
50  , mDestinationProjection( 0 )
51 {
52  setFinder();
53  mSourceCRS = source;
54  mDestCRS = dest;
55  initialise();
56 }
57 
58 QgsCoordinateTransform::QgsCoordinateTransform( long theSourceSrsId, long theDestSrsId )
59  : QObject()
60  , mInitialisedFlag( false )
61  , mSourceCRS( theSourceSrsId, QgsCoordinateReferenceSystem::InternalCrsId )
62  , mDestCRS( theDestSrsId, QgsCoordinateReferenceSystem::InternalCrsId )
63  , mSourceProjection( 0 )
64  , mDestinationProjection( 0 )
65 {
66  initialise();
67 }
68 
69 QgsCoordinateTransform::QgsCoordinateTransform( QString theSourceCRS, QString theDestCRS )
70  : QObject()
71  , mInitialisedFlag( false )
72  , mSourceProjection( 0 )
73  , mDestinationProjection( 0 )
74 {
75  setFinder();
76  mSourceCRS.createFromWkt( theSourceCRS );
77  mDestCRS.createFromWkt( theDestCRS );
78  // initialize the coordinate system data structures
79  //XXX Who spells initialize initialise?
80  //XXX A: Its the queen's english....
81  //XXX : Long live the queen! Lets get on with the initialisation...
82  initialise();
83 }
84 
86  QString theDestWkt,
87  QgsCoordinateReferenceSystem::CrsType theSourceCRSType )
88  : QObject()
89  , mInitialisedFlag( false )
90  , mSourceProjection( 0 )
91  , mDestinationProjection( 0 )
92 {
93  setFinder();
94 
95  mSourceCRS.createFromId( theSourceSrid, theSourceCRSType );
96  mDestCRS.createFromWkt( theDestWkt );
97  // initialize the coordinate system data structures
98  //XXX Who spells initialize initialise?
99  //XXX A: Its the queen's english....
100  //XXX : Long live the queen! Lets get on with the initialisation...
101  initialise();
102 }
103 
105 {
106  // free the proj objects
107  if ( mSourceProjection )
108  {
109  pj_free( mSourceProjection );
110  }
112  {
113  pj_free( mDestinationProjection );
114  }
115 }
116 
118 {
119  mSourceCRS = theCRS;
120  initialise();
121 }
123 {
124  mDestCRS = theCRS;
125  initialise();
126 }
127 
129 {
131  mDestCRS.createFromSrsId( theCRSID );
132  initialise();
133 }
134 
135 // XXX This whole function is full of multiple return statements!!!
136 // And probably shouldn't be a void
138 {
139  // XXX Warning - multiple return paths in this block!!
140  if ( !mSourceCRS.isValid() )
141  {
142  //mSourceCRS = defaultWkt;
143  // Pass through with no projection since we have no idea what the layer
144  // coordinates are and projecting them may not be appropriate
145  mShortCircuit = true;
146  QgsDebugMsg( "SourceCRS seemed invalid!" );
147  return;
148  }
149 
150  if ( !mDestCRS.isValid() )
151  {
152  //No destination projection is set so we set the default output projection to
153  //be the same as input proj.
155  }
156 
157  // init the projections (destination and source)
158  pj_free( mDestinationProjection );
159  mDestinationProjection = pj_init_plus( mDestCRS.toProj4().toUtf8() );
160  pj_free( mSourceProjection );
161  mSourceProjection = pj_init_plus( mSourceCRS.toProj4().toUtf8() );
162 
163 #ifdef COORDINATE_TRANSFORM_VERBOSE
164  QgsDebugMsg( "From proj : " + mSourceCRS.toProj4() );
165  QgsDebugMsg( "To proj : " + mDestCRS.toProj4() );
166 #endif
167 
168  mInitialisedFlag = true;
169  if ( !mDestinationProjection )
170  {
171  mInitialisedFlag = false;
172  }
173  if ( !mSourceProjection )
174  {
175  mInitialisedFlag = false;
176  }
177 #ifdef COORDINATE_TRANSFORM_VERBOSE
178  if ( mInitialisedFlag )
179  {
180  QgsDebugMsg( "------------------------------------------------------------" );
181  QgsDebugMsg( "The OGR Coordinate transformation for this layer was set to" );
182  QgsLogger::debug<QgsCoordinateReferenceSystem>( "Input", mSourceCRS, __FILE__, __FUNCTION__, __LINE__ );
183  QgsLogger::debug<QgsCoordinateReferenceSystem>( "Output", mDestCRS, __FILE__, __FUNCTION__, __LINE__ );
184  QgsDebugMsg( "------------------------------------------------------------" );
185  }
186  else
187  {
188  QgsDebugMsg( "------------------------------------------------------------" );
189  QgsDebugMsg( "The OGR Coordinate transformation FAILED TO INITIALISE!" );
190  QgsDebugMsg( "------------------------------------------------------------" );
191  }
192 #else
193  if ( !mInitialisedFlag )
194  {
195  QgsDebugMsg( "Coordinate transformation failed to initialize!" );
196  }
197 #endif
198 
199  //XXX todo overload == operator for QgsCoordinateReferenceSystem
200  //at the moment srs.parameters contains the whole proj def...soon it wont...
201  //if (mSourceCRS->toProj4() == mDestCRS->toProj4())
202  if ( mSourceCRS == mDestCRS )
203  {
204  // If the source and destination projection are the same, set the short
205  // circuit flag (no transform takes place)
206  mShortCircuit = true;
207  QgsDebugMsgLevel( "Source/Dest CRS equal, shortcircuit is set.", 3 );
208  }
209  else
210  {
211  // Transform must take place
212  mShortCircuit = false;
213  QgsDebugMsgLevel( "Source/Dest CRS UNequal, shortcircuit is NOt set.", 3 );
214  }
215 
216 }
217 
218 //
219 //
220 // TRANSFORMERS BELOW THIS POINT .........
221 //
222 //
223 //
224 
225 
227 {
229  return thePoint;
230  // transform x
231  double x = thePoint.x();
232  double y = thePoint.y();
233  double z = 0.0;
234  try
235  {
236  transformCoords( 1, &x, &y, &z, direction );
237  }
238  catch ( QgsCsException &cse )
239  {
240  // rethrow the exception
241  QgsDebugMsg( "rethrowing exception" );
242  throw cse;
243  }
244 
245  return QgsPoint( x, y );
246 }
247 
248 
249 QgsPoint QgsCoordinateTransform::transform( const double theX, const double theY = 0, TransformDirection direction ) const
250 {
251  try
252  {
253  return transform( QgsPoint( theX, theY ), direction );
254  }
255  catch ( QgsCsException &cse )
256  {
257  // rethrow the exception
258  QgsDebugMsg( "rethrowing exception" );
259  throw cse;
260  }
261 }
262 
264 {
266  return theRect;
267  // transform x
268  double x1 = theRect.xMinimum();
269  double y1 = theRect.yMinimum();
270  double x2 = theRect.xMaximum();
271  double y2 = theRect.yMaximum();
272 
273  // Number of points to reproject------+
274  // |
275  // V
276  try
277  {
278  double z = 0.0;
279  transformCoords( 1, &x1, &y1, &z, direction );
280  transformCoords( 1, &x2, &y2, &z, direction );
281  }
282  catch ( QgsCsException &cse )
283  {
284  // rethrow the exception
285  QgsDebugMsg( "rethrowing exception" );
286  throw cse;
287  }
288 
289 #ifdef COORDINATE_TRANSFORM_VERBOSE
290  QgsDebugMsg( "Rect projection..." );
291  QgsLogger::debug( "Xmin : ", theRect.xMinimum(), 1, __FILE__, __FUNCTION__, __LINE__ );
292  QgsLogger::debug( "-->", x1, 1, __FILE__, __FUNCTION__, __LINE__ );
293  QgsLogger::debug( "Ymin : ", theRect.yMinimum(), 1, __FILE__, __FUNCTION__, __LINE__ );
294  QgsLogger::debug( "-->", y1, 1, __FILE__, __FUNCTION__, __LINE__ );
295  QgsLogger::debug( "Xmax : ", theRect.xMaximum(), 1, __FILE__, __FUNCTION__, __LINE__ );
296  QgsLogger::debug( "-->", x2, 1, __FILE__, __FUNCTION__, __LINE__ );
297  QgsLogger::debug( "Ymax : ", theRect.yMaximum(), 1, __FILE__, __FUNCTION__, __LINE__ );
298  QgsLogger::debug( "-->", y2, 1, __FILE__, __FUNCTION__, __LINE__ );
299 #endif
300  return QgsRectangle( x1, y1, x2, y2 );
301 }
302 
303 void QgsCoordinateTransform::transformInPlace( double& x, double& y, double& z,
304  TransformDirection direction ) const
305 {
307  return;
308 #ifdef QGISDEBUG
309 // QgsDebugMsg(QString("Using transform in place %1 %2").arg(__FILE__).arg(__LINE__));
310 #endif
311  // transform x
312  try
313  {
314  transformCoords( 1, &x, &y, &z, direction );
315  }
316  catch ( QgsCsException &cse )
317  {
318  // rethrow the exception
319  QgsDebugMsg( "rethrowing exception" );
320  throw cse;
321  }
322 }
323 
324 void QgsCoordinateTransform::transformPolygon( QPolygonF& poly, TransformDirection direction ) const
325 {
327  {
328  return;
329  }
330 
331  //create x, y arrays
332  int nVertices = poly.size();
333 
334  QVector<double> x( nVertices );
335  QVector<double> y( nVertices );
336  QVector<double> z( nVertices );
337 
338  for ( int i = 0; i < nVertices; ++i )
339  {
340  const QPointF& pt = poly.at( i );
341  x[i] = pt.x();
342  y[i] = pt.y();
343  z[i] = 0;
344  }
345 
346  try
347  {
348  transformCoords( nVertices, x.data(), y.data(), z.data(), direction );
349  }
350  catch ( QgsCsException &cse )
351  {
352  // rethrow the exception
353  QgsDebugMsg( "rethrowing exception" );
354  throw cse;
355  }
356 
357  for ( int i = 0; i < nVertices; ++i )
358  {
359  QPointF& pt = poly[i];
360  pt.rx() = x[i];
361  pt.ry() = y[i];
362  }
363 }
364 
366  QVector<double>& x, QVector<double>& y, QVector<double>& z,
367  TransformDirection direction ) const
368 {
370  return;
371 
372  Q_ASSERT( x.size() == y.size() );
373 
374  // Apparently, if one has a std::vector, it is valid to use the
375  // address of the first element in the vector as a pointer to an
376  // array of the vectors data, and hence easily interface with code
377  // that wants C-style arrays.
378 
379  try
380  {
381  transformCoords( x.size(), &x[0], &y[0], &z[0], direction );
382  }
383  catch ( QgsCsException &cse )
384  {
385  // rethrow the exception
386  QgsDebugMsg( "rethrowing exception" );
387  throw cse;
388  }
389 }
390 
391 #ifdef ANDROID
392 void QgsCoordinateTransform::transformInPlace( float& x, float& y, float& z,
393  TransformDirection direction ) const
394 {
396  return;
397 #ifdef QGISDEBUG
398 // QgsDebugMsg(QString("Using transform in place %1 %2").arg(__FILE__).arg(__LINE__));
399 #endif
400  // transform x
401  try
402  {
403  double xd = x;
404  double yd = y;
405  double zd = z;
406  transformCoords( 1, &xd, &yd, &zd, direction );
407  x = xd;
408  y = yd;
409  z = zd;
410  }
411  catch ( QgsCsException &cse )
412  {
413  // rethrow the exception
414  QgsDebugMsg( "rethrowing exception" );
415  throw cse;
416  }
417 }
418 
420  QVector<float>& x, QVector<float>& y, QVector<float>& z,
421  TransformDirection direction ) const
422 {
424  return;
425 
426  Q_ASSERT( x.size() == y.size() );
427 
428  // Apparently, if one has a std::vector, it is valid to use the
429  // address of the first element in the vector as a pointer to an
430  // array of the vectors data, and hence easily interface with code
431  // that wants C-style arrays.
432 
433  try
434  {
435  //copy everything to double vectors since proj needs double
436  int vectorSize = x.size();
437  QVector<double> xd( x.size() );
438  QVector<double> yd( y.size() );
439  QVector<double> zd( z.size() );
440  for ( int i = 0; i < vectorSize; ++i )
441  {
442  xd[i] = x[i];
443  yd[i] = y[i];
444  zd[i] = z[i];
445  }
446  transformCoords( x.size(), &xd[0], &yd[0], &zd[0], direction );
447 
448  //copy back
449  for ( int i = 0; i < vectorSize; ++i )
450  {
451  x[i] = xd[i];
452  y[i] = yd[i];
453  z[i] = zd[i];
454  }
455  }
456  catch ( QgsCsException &cse )
457  {
458  // rethrow the exception
459  QgsDebugMsg( "rethrowing exception" );
460  throw cse;
461  }
462 }
463 #endif //ANDROID
464 
465 
467 {
468  // Calculate the bounding box of a QgsRectangle in the source CRS
469  // when projected to the destination CRS (or the inverse).
470  // This is done by looking at a number of points spread evenly
471  // across the rectangle
472 
474  return rect;
475 
476  if ( rect.isEmpty() )
477  {
478  QgsPoint p = transform( rect.xMinimum(), rect.yMinimum(), direction );
479  return QgsRectangle( p, p );
480  }
481 
482  static const int numP = 8;
483 
484  QgsRectangle bb_rect;
485  bb_rect.setMinimal();
486 
487  // We're interfacing with C-style vectors in the
488  // end, so let's do C-style vectors here too.
489 
490  double x[numP * numP];
491  double y[numP * numP];
492  double z[numP * numP];
493 
494  QgsDebugMsg( "Entering transformBoundingBox..." );
495 
496  // Populate the vectors
497 
498  double dx = rect.width() / ( double )( numP - 1 );
499  double dy = rect.height() / ( double )( numP - 1 );
500 
501  double pointY = rect.yMinimum();
502 
503  for ( int i = 0; i < numP ; i++ )
504  {
505 
506  // Start at right edge
507  double pointX = rect.xMinimum();
508 
509  for ( int j = 0; j < numP; j++ )
510  {
511  x[( i*numP ) + j] = pointX;
512  y[( i*numP ) + j] = pointY;
513  // and the height...
514  z[( i*numP ) + j] = 0.0;
515  // QgsDebugMsg(QString("BBox coord: (%1, %2)").arg(x[(i*numP) + j]).arg(y[(i*numP) + j]));
516  pointX += dx;
517  }
518  pointY += dy;
519  }
520 
521  // Do transformation. Any exception generated must
522  // be handled in above layers.
523  try
524  {
525  transformCoords( numP * numP, x, y, z, direction );
526  }
527  catch ( QgsCsException &cse )
528  {
529  // rethrow the exception
530  QgsDebugMsg( "rethrowing exception" );
531  throw cse;
532  }
533 
534  // Calculate the bounding box and use that for the extent
535 
536  for ( int i = 0; i < numP * numP; i++ )
537  {
538  if ( qIsFinite( x[i] ) && qIsFinite( y[i] ) )
539  bb_rect.combineExtentWith( x[i], y[i] );
540  }
541 
542  QgsDebugMsg( "Projected extent: " + bb_rect.toString() );
543 
544  if ( bb_rect.isEmpty() )
545  {
546  QgsDebugMsg( "Original extent: " + rect.toString() );
547  }
548 
549  return bb_rect;
550 }
551 
552 void QgsCoordinateTransform::transformCoords( const int& numPoints, double *x, double *y, double *z, TransformDirection direction ) const
553 {
554  // Refuse to transform the points if the srs's are invalid
555  if ( !mSourceCRS.isValid() )
556  {
557  QgsMessageLog::logMessage( tr( "The source spatial reference system (CRS) is not valid. "
558  "The coordinates can not be reprojected. The CRS is: %1" )
559  .arg( mSourceCRS.toProj4() ), tr( "CRS" ) );
560  return;
561  }
562  if ( !mDestCRS.isValid() )
563  {
564  QgsMessageLog::logMessage( tr( "The destination spatial reference system (CRS) is not valid. "
565  "The coordinates can not be reprojected. The CRS is: %1" ).arg( mDestCRS.toProj4() ), tr( "CRS" ) );
566  return;
567  }
568 
569 #ifdef COORDINATE_TRANSFORM_VERBOSE
570  double xorg = *x;
571  double yorg = *y;
572  QgsDebugMsg( QString( "[[[[[[ Number of points to transform: %1 ]]]]]]" ).arg( numPoints ) );
573 #endif
574 
575  // use proj4 to do the transform
576  QString dir;
577  // if the source/destination projection is lat/long, convert the points to radians
578  // prior to transforming
579  if (( pj_is_latlong( mDestinationProjection ) && ( direction == ReverseTransform ) )
580  || ( pj_is_latlong( mSourceProjection ) && ( direction == ForwardTransform ) ) )
581  {
582  for ( int i = 0; i < numPoints; ++i )
583  {
584  x[i] *= DEG_TO_RAD;
585  y[i] *= DEG_TO_RAD;
586  z[i] *= DEG_TO_RAD;
587  }
588 
589  }
590  int projResult;
591  if ( direction == ReverseTransform )
592  {
593  projResult = pj_transform( mDestinationProjection, mSourceProjection, numPoints, 0, x, y, z );
594  dir = tr( "inverse transform" );
595  }
596  else
597  {
598  Q_ASSERT( mSourceProjection != 0 );
599  Q_ASSERT( mDestinationProjection != 0 );
600  projResult = pj_transform( mSourceProjection, mDestinationProjection, numPoints, 0, x, y, z );
601  dir = tr( "forward transform" );
602  }
603 
604  if ( projResult != 0 )
605  {
606  //something bad happened....
607  QString points;
608 
609  for ( int i = 0; i < numPoints; ++i )
610  {
611  if ( direction == ForwardTransform )
612  {
613  points += QString( "(%1, %2)\n" ).arg( x[i], 0, 'f' ).arg( y[i], 0, 'f' );
614  }
615  else
616  {
617  points += QString( "(%1, %2)\n" ).arg( x[i] * RAD_TO_DEG, 0, 'f' ).arg( y[i] * RAD_TO_DEG, 0, 'f' );
618  }
619  }
620 
621  QString msg = tr( "%1 of\n"
622  "%2"
623  "PROJ.4: %3 +to %4\n"
624  "Error: %5" )
625  .arg( dir )
626  .arg( points )
627  .arg( mSourceCRS.toProj4() ).arg( mDestCRS.toProj4() )
628  .arg( QString::fromUtf8( pj_strerrno( projResult ) ) );
629 
630  QgsDebugMsg( "Projection failed emitting invalid transform signal: " + msg );
631 
632  emit invalidTransformInput();
633 
634  QgsDebugMsg( "throwing exception" );
635 
636  throw QgsCsException( msg );
637  }
638 
639  // if the result is lat/long, convert the results from radians back
640  // to degrees
641  if (( pj_is_latlong( mDestinationProjection ) && ( direction == ForwardTransform ) )
642  || ( pj_is_latlong( mSourceProjection ) && ( direction == ReverseTransform ) ) )
643  {
644  for ( int i = 0; i < numPoints; ++i )
645  {
646  x[i] *= RAD_TO_DEG;
647  y[i] *= RAD_TO_DEG;
648  z[i] *= RAD_TO_DEG;
649  }
650  }
651 #ifdef COORDINATE_TRANSFORM_VERBOSE
652  QgsDebugMsg( QString( "[[[[[[ Projected %1, %2 to %3, %4 ]]]]]]" )
653  .arg( xorg, 0, 'g', 15 ).arg( yorg, 0, 'g', 15 )
654  .arg( *x, 0, 'g', 15 ).arg( *y, 0, 'g', 15 ) );
655 #endif
656 }
657 
658 bool QgsCoordinateTransform::readXML( QDomNode & theNode )
659 {
660 
661  QgsDebugMsg( "Reading Coordinate Transform from xml ------------------------!" );
662 
663  QDomNode mySrcNode = theNode.namedItem( "sourcesrs" );
664  mSourceCRS.readXML( mySrcNode );
665 
666  QDomNode myDestNode = theNode.namedItem( "destinationsrs" );
667  mDestCRS.readXML( myDestNode );
668 
669  initialise();
670 
671  return true;
672 }
673 
674 bool QgsCoordinateTransform::writeXML( QDomNode & theNode, QDomDocument & theDoc )
675 {
676  QDomElement myNodeElement = theNode.toElement();
677  QDomElement myTransformElement = theDoc.createElement( "coordinatetransform" );
678 
679  QDomElement mySourceElement = theDoc.createElement( "sourcesrs" );
680  mSourceCRS.writeXML( mySourceElement, theDoc );
681  myTransformElement.appendChild( mySourceElement );
682 
683  QDomElement myDestElement = theDoc.createElement( "destinationsrs" );
684  mDestCRS.writeXML( myDestElement, theDoc );
685  myTransformElement.appendChild( myDestElement );
686 
687  myNodeElement.appendChild( myTransformElement );
688 
689  return true;
690 }
691 
692 const char *finder( const char *name )
693 {
694  QString proj;
695 #ifdef WIN32
696  proj = QApplication::applicationDirPath()
697  + "/share/proj/" + QString( name );
698 #else
699  Q_UNUSED( name );
700 #endif
701  return proj.toUtf8();
702 }
703 
705 {
706 #if 0
707  // Attention! It should be possible to set PROJ_LIB
708  // but it can happen that it was previously set by installer
709  // (version 0.7) and the old installation was deleted
710 
711  // Another problem: PROJ checks if pj_finder was set before
712  // PROJ_LIB environment variable. pj_finder is probably set in
713  // GRASS gproj library when plugin is loaded, consequently
714  // PROJ_LIB is ignored
715 
716  pj_set_finder( finder );
717 #endif
718 }