QGIS API Documentation 4.1.0-Master (60fea48833c)
Loading...
Searching...
No Matches
qgsgcptransformer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsgcptransformer.cpp
3 --------------------------------------
4 Date : 18-Feb-2009
5 Copyright : (c) 2009 by Manuel Massing
6 Email : m.massing at warped-space.de
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsgcptransformer.h"
17
18#include <cassert>
19#include <cmath>
20#include <gdal.h>
21#include <gdal_alg.h>
22#include <limits>
23
24#include "qgsleastsquares.h"
25
26#include "moc_qgsgcptransformer.cpp"
27
28bool QgsGcpTransformerInterface::transform( double &x, double &y, bool inverseTransform ) const
29{
30 GDALTransformerFunc t = GDALTransformer();
31 // Fail if no transformer function was returned
32 if ( !t )
33 return false;
34
35 double z = 0.0;
36 int success = 0;
37
38 // Call GDAL transform function
39 ( *t )( GDALTransformerArgs(), inverseTransform ? 1 : 0, 1, &x, &y, &z, &success );
40 if ( !success )
41 return false;
42
43 return true;
44}
45
47{
48 switch ( method )
49 {
51 return QObject::tr( "Linear" );
53 return QObject::tr( "Helmert" );
55 return QObject::tr( "Polynomial 1" );
57 return QObject::tr( "Polynomial 2" );
59 return QObject::tr( "Polynomial 3" );
61 return QObject::tr( "Thin Plate Spline (TPS)" );
63 return QObject::tr( "Projective" );
64 default:
65 return QObject::tr( "Not set" );
66 }
67}
68
91
93 QgsGcpTransformerInterface::TransformMethod method, const QVector<QgsPointXY> &sourceCoordinates, const QVector<QgsPointXY> &destinationCoordinates
94)
95{
96 std::unique_ptr<QgsGcpTransformerInterface> transformer( create( method ) );
97 if ( !transformer )
98 return nullptr;
99
100 if ( !transformer->updateParametersFromGcps( sourceCoordinates, destinationCoordinates ) )
101 return nullptr;
102
103 return transformer.release();
104}
105
106
107//
108// QgsLinearGeorefTransform
109//
110
111bool QgsLinearGeorefTransform::getOriginScale( QgsPointXY &origin, double &scaleX, double &scaleY ) const
112{
113 origin = mParameters.origin;
114 scaleX = mParameters.scaleX;
115 scaleY = mParameters.scaleY;
116 return true;
117}
118
120{
121 auto res = std::make_unique<QgsLinearGeorefTransform>();
122 res->mParameters = mParameters;
123 return res.release();
124}
125
126bool QgsLinearGeorefTransform::updateParametersFromGcps( const QVector<QgsPointXY> &sourceCoordinates, const QVector<QgsPointXY> &destinationCoordinates, bool invertYAxis )
127{
128 if ( destinationCoordinates.size() < minimumGcpCount() )
129 return false;
130
131 mParameters.invertYAxis = invertYAxis;
132 QgsLeastSquares::linear( sourceCoordinates, destinationCoordinates, mParameters.origin, mParameters.scaleX, mParameters.scaleY );
133 return true;
134}
135
137{
138 return 2;
139}
140
142{
143 return QgsLinearGeorefTransform::linearTransform;
144}
145
147{
148 return ( void * ) &mParameters;
149}
150
155
156int QgsLinearGeorefTransform::linearTransform( void *pTransformerArg, int bDstToSrc, int nPointCount, double *x, double *y, double *z, int *panSuccess )
157{
158 Q_UNUSED( z )
159 LinearParameters *t = static_cast<LinearParameters *>( pTransformerArg );
160 if ( !t )
161 return false;
162
163 if ( !bDstToSrc )
164 {
165 for ( int i = 0; i < nPointCount; ++i )
166 {
167 x[i] = x[i] * t->scaleX + t->origin.x();
168 y[i] = ( t->invertYAxis ? -1 : 1 ) * y[i] * t->scaleY + t->origin.y();
169 panSuccess[i] = true;
170 }
171 }
172 else
173 {
174 // Guard against division by zero
175 if ( std::fabs( t->scaleX ) < std::numeric_limits<double>::epsilon() || std::fabs( t->scaleY ) < std::numeric_limits<double>::epsilon() )
176 {
177 for ( int i = 0; i < nPointCount; ++i )
178 {
179 panSuccess[i] = false;
180 }
181 return false;
182 }
183 for ( int i = 0; i < nPointCount; ++i )
184 {
185 x[i] = ( x[i] - t->origin.x() ) / t->scaleX;
186 y[i] = ( y[i] - t->origin.y() ) / ( ( t->invertYAxis ? -1 : 1 ) * t->scaleY );
187 panSuccess[i] = true;
188 }
189 }
190
191 return true;
192}
193
194//
195// QgsHelmertGeorefTransform
196//
197bool QgsHelmertGeorefTransform::updateParametersFromGcps( const QVector<QgsPointXY> &sourceCoordinates, const QVector<QgsPointXY> &destinationCoordinates, bool invertYAxis )
198{
199 if ( destinationCoordinates.size() < minimumGcpCount() )
200 return false;
201
202 mHelmertParameters.invertYAxis = invertYAxis;
203 QgsLeastSquares::helmert( sourceCoordinates, destinationCoordinates, mHelmertParameters.origin, mHelmertParameters.scale, mHelmertParameters.angle );
204 return true;
205}
206
208{
209 return 2;
210}
211
213{
214 return QgsHelmertGeorefTransform::helmertTransform;
215}
216
218{
219 return ( void * ) &mHelmertParameters;
220}
221
226
227bool QgsHelmertGeorefTransform::getOriginScaleRotation( QgsPointXY &origin, double &scale, double &rotation ) const
228{
229 origin = mHelmertParameters.origin;
230 scale = mHelmertParameters.scale;
231 rotation = mHelmertParameters.angle;
232 return true;
233}
234
236{
237 auto res = std::make_unique<QgsHelmertGeorefTransform>();
238 res->mHelmertParameters = mHelmertParameters;
239 return res.release();
240}
241
242int QgsHelmertGeorefTransform::helmertTransform( void *pTransformerArg, int bDstToSrc, int nPointCount, double *x, double *y, double *z, int *panSuccess )
243{
244 Q_UNUSED( z )
245 const HelmertParameters *t = static_cast<const HelmertParameters *>( pTransformerArg );
246 if ( !t )
247 return false;
248
249 double a = std::cos( t->angle );
250 double b = std::sin( t->angle );
251 const double x0 = t->origin.x();
252 const double y0 = t->origin.y();
253 const double s = t->scale;
254 if ( !bDstToSrc )
255 {
256 a *= s;
257 b *= s;
258 for ( int i = 0; i < nPointCount; ++i )
259 {
260 const double xT = x[i];
261 const double yT = y[i];
262
263 if ( t->invertYAxis )
264 {
265 // Because rotation parameters where estimated in a CS with negative y-axis ^= down.
266 // we need to apply the rotation matrix and a change of base:
267 // |cos a, -sin a | |1, 0| | cos a, sin a|
268 // |sin a, cos a | |0,-1| = | sin a, -cos a|
269 x[i] = x0 + ( a * xT + b * yT );
270 y[i] = y0 + ( b * xT - a * yT );
271 }
272 else
273 {
274 x[i] = x0 + ( a * xT - b * yT );
275 y[i] = y0 + ( b * xT + a * yT );
276 }
277 panSuccess[i] = true;
278 }
279 }
280 else
281 {
282 // Guard against division by zero
283 if ( std::fabs( s ) < std::numeric_limits<double>::epsilon() )
284 {
285 for ( int i = 0; i < nPointCount; ++i )
286 {
287 panSuccess[i] = false;
288 }
289 return false;
290 }
291 a /= s;
292 b /= s;
293 for ( int i = 0; i < nPointCount; ++i )
294 {
295 const double xT = x[i] - x0;
296 const double yT = y[i] - y0;
297 if ( t->invertYAxis )
298 {
299 // | cos a, sin a |^-1 |cos a, sin a|
300 // | sin a, -cos a | = |sin a, -cos a|
301 x[i] = a * xT + b * yT;
302 y[i] = b * xT - a * yT;
303 }
304 else
305 {
306 x[i] = a * xT + b * yT;
307 y[i] = -b * xT + a * yT;
308 }
309 panSuccess[i] = true;
310 }
311 }
312 return true;
313}
314
315//
316// QgsGDALGeorefTransform
317//
318
319QgsGDALGeorefTransform::QgsGDALGeorefTransform( bool useTPS, unsigned int polynomialOrder )
320 : mPolynomialOrder( std::min( 3u, polynomialOrder ) )
321 , mIsTPSTransform( useTPS )
322{}
323
325{
326 destroyGdalArgs();
327}
328
330{
331 auto res = std::make_unique<QgsGDALGeorefTransform>( mIsTPSTransform, mPolynomialOrder );
332 res->updateParametersFromGcps( mSourceCoords, mDestCoordinates, mInvertYAxis );
333 return res.release();
334}
335
336bool QgsGDALGeorefTransform::updateParametersFromGcps( const QVector<QgsPointXY> &sourceCoordinates, const QVector<QgsPointXY> &destinationCoordinates, bool invertYAxis )
337{
338 mSourceCoords = sourceCoordinates;
339 mDestCoordinates = destinationCoordinates;
340 mInvertYAxis = invertYAxis;
341
342 assert( sourceCoordinates.size() == destinationCoordinates.size() );
343 if ( sourceCoordinates.size() != destinationCoordinates.size() )
344 return false;
345 const int n = sourceCoordinates.size();
346
347 GDAL_GCP *GCPList = new GDAL_GCP[n];
348 for ( int i = 0; i < n; i++ )
349 {
350 GCPList[i].pszId = new char[20];
351 snprintf( GCPList[i].pszId, 19, "gcp%i", i );
352 GCPList[i].pszInfo = nullptr;
353 GCPList[i].dfGCPPixel = sourceCoordinates[i].x();
354 GCPList[i].dfGCPLine = ( mInvertYAxis ? -1 : 1 ) * sourceCoordinates[i].y();
355 GCPList[i].dfGCPX = destinationCoordinates[i].x();
356 GCPList[i].dfGCPY = destinationCoordinates[i].y();
357 GCPList[i].dfGCPZ = 0;
358 }
359 destroyGdalArgs();
360
361 if ( mIsTPSTransform )
362 mGDALTransformerArgs = GDALCreateTPSTransformer( n, GCPList, false );
363 else
364 mGDALTransformerArgs = GDALCreateGCPTransformer( n, GCPList, mPolynomialOrder, false );
365
366 for ( int i = 0; i < n; i++ )
367 {
368 delete[] GCPList[i].pszId;
369 }
370 delete[] GCPList;
371
372 return nullptr != mGDALTransformerArgs;
373}
374
376{
377 if ( mIsTPSTransform )
378 return 1;
379 else
380 return ( ( mPolynomialOrder + 2 ) * ( mPolynomialOrder + 1 ) ) / 2;
381}
382
384{
385 // Fail if no arguments were calculated through updateParametersFromGCP
386 if ( !mGDALTransformerArgs )
387 return nullptr;
388
389 if ( mIsTPSTransform )
390 return GDALTPSTransform;
391 else
392 return GDALGCPTransform;
393}
394
396{
397 return mGDALTransformerArgs;
398}
399
401{
402 if ( mIsTPSTransform )
404
405 switch ( mPolynomialOrder )
406 {
407 case 1:
409 case 2:
411 case 3:
413 }
415}
416
417void QgsGDALGeorefTransform::destroyGdalArgs()
418{
419 if ( mGDALTransformerArgs )
420 {
421 if ( mIsTPSTransform )
422 GDALDestroyTPSTransformer( mGDALTransformerArgs );
423 else
424 GDALDestroyGCPTransformer( mGDALTransformerArgs );
425 }
426}
427
428//
429// QgsProjectiveGeorefTransform
430//
431
435
437{
438 auto res = std::make_unique<QgsProjectiveGeorefTransform>();
439 res->mParameters = mParameters;
440 return res.release();
441}
442
443bool QgsProjectiveGeorefTransform::updateParametersFromGcps( const QVector<QgsPointXY> &sourceCoordinates, const QVector<QgsPointXY> &destinationCoordinates, bool invertYAxis )
444{
445 if ( destinationCoordinates.size() < minimumGcpCount() )
446 return false;
447
448 if ( invertYAxis )
449 {
450 // HACK: flip y coordinates, because georeferencer and gdal use different conventions
451 QVector<QgsPointXY> flippedPixelCoords;
452 flippedPixelCoords.reserve( sourceCoordinates.size() );
453 for ( const QgsPointXY &coord : sourceCoordinates )
454 {
455 flippedPixelCoords << QgsPointXY( coord.x(), -coord.y() );
456 }
457
458 QgsLeastSquares::projective( flippedPixelCoords, destinationCoordinates, mParameters.H );
459 }
460 else
461 {
462 QgsLeastSquares::projective( sourceCoordinates, destinationCoordinates, mParameters.H );
463 }
464
465 // Invert the homography matrix using adjoint matrix
466 double *H = mParameters.H;
467
468 double adjoint[9];
469 adjoint[0] = H[4] * H[8] - H[5] * H[7];
470 adjoint[1] = -H[1] * H[8] + H[2] * H[7];
471 adjoint[2] = H[1] * H[5] - H[2] * H[4];
472
473 adjoint[3] = -H[3] * H[8] + H[5] * H[6];
474 adjoint[4] = H[0] * H[8] - H[2] * H[6];
475 adjoint[5] = -H[0] * H[5] + H[2] * H[3];
476
477 adjoint[6] = H[3] * H[7] - H[4] * H[6];
478 adjoint[7] = -H[0] * H[7] + H[1] * H[6];
479 adjoint[8] = H[0] * H[4] - H[1] * H[3];
480
481 const double det = H[0] * adjoint[0] + H[3] * adjoint[1] + H[6] * adjoint[2];
482
483 if ( std::fabs( det ) < 1024.0 * std::numeric_limits<double>::epsilon() )
484 {
485 mParameters.hasInverse = false;
486 }
487 else
488 {
489 mParameters.hasInverse = true;
490 const double oo_det = 1.0 / det;
491 for ( int i = 0; i < 9; i++ )
492 {
493 mParameters.Hinv[i] = adjoint[i] * oo_det;
494 }
495 }
496 return true;
497}
498
500{
501 return 4;
502}
503
505{
506 return QgsProjectiveGeorefTransform::projectiveTransform;
507}
508
510{
511 return ( void * ) &mParameters;
512}
513
518
519int QgsProjectiveGeorefTransform::projectiveTransform( void *pTransformerArg, int bDstToSrc, int nPointCount, double *x, double *y, double *z, int *panSuccess )
520{
521 Q_UNUSED( z )
522 ProjectiveParameters *t = static_cast<ProjectiveParameters *>( pTransformerArg );
523 if ( !t )
524 return false;
525
526 double *H = nullptr;
527 if ( !bDstToSrc )
528 {
529 H = t->H;
530 }
531 else
532 {
533 if ( !t->hasInverse )
534 {
535 for ( int i = 0; i < nPointCount; ++i )
536 {
537 panSuccess[i] = false;
538 }
539 return false;
540 }
541 H = t->Hinv;
542 }
543
544
545 for ( int i = 0; i < nPointCount; ++i )
546 {
547 const double Z = x[i] * H[6] + y[i] * H[7] + H[8];
548 // Projects to infinity?
549 if ( std::fabs( Z ) < 1024.0 * std::numeric_limits<double>::epsilon() )
550 {
551 panSuccess[i] = false;
552 continue;
553 }
554 const double X = ( x[i] * H[0] + y[i] * H[1] + H[2] ) / Z;
555 const double Y = ( x[i] * H[3] + y[i] * H[4] + H[5] ) / Z;
556
557 x[i] = X;
558 y[i] = Y;
559
560 panSuccess[i] = true;
561 }
562
563 return true;
564}
Interface to gdal thin plate splines and 1st/2nd/3rd order polynomials.
int minimumGcpCount() const override
Returns the minimum number of Ground Control Points (GCPs) required for parameter fitting.
TransformMethod method() const override
Returns the transformation method.
QgsGcpTransformerInterface * clone() const override
Clones the transformer, returning a new copy of the transformer with the same parameters as this one.
QgsGDALGeorefTransform(bool useTPS, unsigned int polynomialOrder)
Constructor for QgsGDALGeorefTransform.
GDALTransformerFunc GDALTransformer() const override
Returns function pointer to the GDALTransformer function.
void * GDALTransformerArgs() const override
Returns pointer to the GDALTransformer arguments.
bool updateParametersFromGcps(const QVector< QgsPointXY > &sourceCoordinates, const QVector< QgsPointXY > &destinationCoordinates, bool invertYAxis=false) override
Fits transformation parameters using the specified Ground Control Points (GCPs) lists of source and d...
bool transform(double &x, double &y, bool inverseTransform=false) const
Transforms the point (x, y) from source to destination coordinates.
TransformMethod
Available transformation methods.
static QgsGcpTransformerInterface * create(TransformMethod method)
Creates a new QgsGcpTransformerInterface subclass representing the specified transform method.
QgsGcpTransformerInterface()=default
static QString methodToString(TransformMethod method)
Returns a translated string representing the specified transform method.
virtual void * GDALTransformerArgs() const =0
Returns pointer to the GDALTransformer arguments.
virtual GDALTransformerFunc GDALTransformer() const =0
Returns function pointer to the GDALTransformer function.
virtual TransformMethod method() const =0
Returns the transformation method.
static QgsGcpTransformerInterface * createFromParameters(TransformMethod method, const QVector< QgsPointXY > &sourceCoordinates, const QVector< QgsPointXY > &destinationCoordinates)
Creates a new QgsGcpTransformerInterface subclass representing the specified transform method,...
2-dimensional Helmert transform, parametrised by isotropic scale, rotation angle and translation.
TransformMethod method() const override
Returns the transformation method.
QgsGcpTransformerInterface * clone() const override
Clones the transformer, returning a new copy of the transformer with the same parameters as this one.
bool getOriginScaleRotation(QgsPointXY &origin, double &scale, double &rotation) const
Returns the origin, scale and rotation for the transform.
GDALTransformerFunc GDALTransformer() const override
Returns function pointer to the GDALTransformer function.
int minimumGcpCount() const override
Returns the minimum number of Ground Control Points (GCPs) required for parameter fitting.
bool updateParametersFromGcps(const QVector< QgsPointXY > &sourceCoordinates, const QVector< QgsPointXY > &destinationCoordinates, bool invertYAxis=false) override
Fits transformation parameters using the specified Ground Control Points (GCPs) lists of source and d...
void * GDALTransformerArgs() const override
Returns pointer to the GDALTransformer arguments.
static void helmert(const QVector< QgsPointXY > &sourceCoordinates, const QVector< QgsPointXY > &destinationCoordinates, QgsPointXY &origin, double &pixelSize, double &rotation)
Transforms the point at origin in-place, using a helmert transformation calculated from the list of s...
static void projective(const QVector< QgsPointXY > &sourceCoordinates, const QVector< QgsPointXY > &destinationCoordinates, double H[9])
Calculates projective parameters from the list of source and destination Ground Control Points (GCPs)...
static void linear(const QVector< QgsPointXY > &sourceCoordinates, const QVector< QgsPointXY > &destinationCoordinates, QgsPointXY &origin, double &pixelXSize, double &pixelYSize)
Transforms the point at origin in-place, using a linear transformation calculated from the list of so...
A simple transform which is parametrized by a translation and anistotropic scale.
bool getOriginScale(QgsPointXY &origin, double &scaleX, double &scaleY) const
Returns the origin and scale for the transform.
TransformMethod method() const override
Returns the transformation method.
void * GDALTransformerArgs() const override
Returns pointer to the GDALTransformer arguments.
QgsGcpTransformerInterface * clone() const override
Clones the transformer, returning a new copy of the transformer with the same parameters as this one.
bool updateParametersFromGcps(const QVector< QgsPointXY > &sourceCoordinates, const QVector< QgsPointXY > &destinationCoordinates, bool invertYAxis=false) override
Fits transformation parameters using the specified Ground Control Points (GCPs) lists of source and d...
int minimumGcpCount() const override
Returns the minimum number of Ground Control Points (GCPs) required for parameter fitting.
GDALTransformerFunc GDALTransformer() const override
Returns function pointer to the GDALTransformer function.
Represents a 2D point.
Definition qgspointxy.h:62
A planar projective transform, expressed by a homography.
int minimumGcpCount() const override
Returns the minimum number of Ground Control Points (GCPs) required for parameter fitting.
QgsGcpTransformerInterface * clone() const override
Clones the transformer, returning a new copy of the transformer with the same parameters as this one.
bool updateParametersFromGcps(const QVector< QgsPointXY > &sourceCoordinates, const QVector< QgsPointXY > &destinationCoordinates, bool invertYAxis=false) override
Fits transformation parameters using the specified Ground Control Points (GCPs) lists of source and d...
GDALTransformerFunc GDALTransformer() const override
Returns function pointer to the GDALTransformer function.
void * GDALTransformerArgs() const override
Returns pointer to the GDALTransformer arguments.
TransformMethod method() const override
Returns the transformation method.