From 5953cfda9d2401aa3a30c55d884bde6dd117958a Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 2 Feb 2022 14:01:57 +1000 Subject: [PATCH 01/47] Minor cleanup to rename ambiguous method --- src/app/georeferencer/qgsgcpcanvasitem.cpp | 16 +++++++++------- src/app/georeferencer/qgsgcplistmodel.cpp | 2 +- src/app/georeferencer/qgsgeorefmainwindow.cpp | 6 +++--- src/app/georeferencer/qgsgeoreftransform.h | 4 ++-- src/app/georeferencer/qgsrasterchangecoords.cpp | 5 ++--- src/app/georeferencer/qgsrasterchangecoords.h | 4 ++-- 6 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/app/georeferencer/qgsgcpcanvasitem.cpp b/src/app/georeferencer/qgsgcpcanvasitem.cpp index 846fe942550a..ad7e324040d4 100644 --- a/src/app/georeferencer/qgsgcpcanvasitem.cpp +++ b/src/app/georeferencer/qgsgcpcanvasitem.cpp @@ -164,16 +164,18 @@ void QgsGCPCanvasItem::updatePosition() if ( mIsGCPSource ) { setPos( toCanvasCoordinates( mDataPoint->pixelCoords() ) ); - return; } - if ( mDataPoint->canvasCoords().isEmpty() ) + else { - const QgsCoordinateReferenceSystem mapCrs = mMapCanvas->mapSettings().destinationCrs(); - const QgsCoordinateTransform transf( mDataPoint->crs(), mapCrs, QgsProject::instance() ); - const QgsPointXY mapCoords = transf.transform( mDataPoint->mapCoords() ); - mDataPoint->setCanvasCoords( mapCoords ); + if ( mDataPoint->canvasCoords().isEmpty() ) + { + const QgsCoordinateReferenceSystem mapCrs = mMapCanvas->mapSettings().destinationCrs(); + const QgsCoordinateTransform transf( mDataPoint->crs(), mapCrs, QgsProject::instance() ); + const QgsPointXY mapCoords = transf.transform( mDataPoint->mapCoords() ); + mDataPoint->setCanvasCoords( mapCoords ); + } + setPos( toCanvasCoordinates( mDataPoint->canvasCoords() ) ); } - setPos( toCanvasCoordinates( mDataPoint->canvasCoords() ) ); } void QgsGCPCanvasItem::drawResidualArrow( QPainter *p, const QgsRenderContext &context ) diff --git a/src/app/georeferencer/qgsgcplistmodel.cpp b/src/app/georeferencer/qgsgcplistmodel.cpp index ce457d6445bb..ad455e90a36b 100644 --- a/src/app/georeferencer/qgsgcplistmodel.cpp +++ b/src/app/georeferencer/qgsgcplistmodel.cpp @@ -147,7 +147,7 @@ void QgsGCPListModel::updateModel() if ( mGeorefTransform && bTransformUpdated && mGeorefTransform->parametersInitialized() ) { QgsPointXY dst; - const QgsPointXY pixel = mGeorefTransform->hasCrs() ? mGeorefTransform->toColumnLine( p->pixelCoords() ) : p->pixelCoords(); + const QgsPointXY pixel = mGeorefTransform->hasExistingGeoreference() ? mGeorefTransform->toColumnLine( p->pixelCoords() ) : p->pixelCoords(); if ( unitType == tr( "pixels" ) ) { // Transform from world to raster coordinate: diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index c4285fd0f0a1..5022f20f88bd 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -787,7 +787,7 @@ void QgsGeoreferencerMainWindow::extentsChangedGeorefCanvas() } // Reproject the georeference plugin canvas into world coordinates and fit axis aligned bounding box - QgsRectangle rectMap = mGeorefTransform.hasCrs() ? mGeorefTransform.getBoundingBox( mCanvas->extent(), true ) : mCanvas->extent(); + QgsRectangle rectMap = mGeorefTransform.hasExistingGeoreference() ? mGeorefTransform.getBoundingBox( mCanvas->extent(), true ) : mCanvas->extent(); QgsRectangle boundingBox = transformViewportBoundingBox( rectMap, mGeorefTransform, true ); mExtentsChangedRecursionGuard = true; @@ -818,7 +818,7 @@ void QgsGeoreferencerMainWindow::extentsChangedQGisCanvas() // Reproject the canvas into raster coordinates and fit axis aligned bounding box QgsRectangle boundingBox = transformViewportBoundingBox( QgisApp::instance()->mapCanvas()->extent(), mGeorefTransform, false ); - QgsRectangle rectMap = mGeorefTransform.hasCrs() ? mGeorefTransform.getBoundingBox( boundingBox, false ) : boundingBox; + QgsRectangle rectMap = mGeorefTransform.hasExistingGeoreference() ? mGeorefTransform.getBoundingBox( boundingBox, false ) : boundingBox; mExtentsChangedRecursionGuard = true; // Just set the whole extent for now @@ -1191,7 +1191,7 @@ void QgsGeoreferencerMainWindow::addRaster( const QString &file ) mActionFullHistogramStretch->setEnabled( true ); // Status Bar - if ( mGeorefTransform.hasCrs() ) + if ( mGeorefTransform.hasExistingGeoreference() ) { QString authid = mLayer->crs().authid(); mEPSG->setText( authid ); diff --git a/src/app/georeferencer/qgsgeoreftransform.h b/src/app/georeferencer/qgsgeoreftransform.h index 9d67f118f9e8..9091d1fec74a 100644 --- a/src/app/georeferencer/qgsgeoreftransform.h +++ b/src/app/georeferencer/qgsgeoreftransform.h @@ -51,8 +51,8 @@ class QgsGeorefTransform : public QgsGcpTransformerInterface */ void setRasterChangeCoords( const QString &fileRaster ); - //! \returns Whether has Coordinate Reference Systems in image - bool hasCrs() const { return mRasterChangeCoords.hasCrs(); } + //! \returns Whether has image already has existing georeference + bool hasExistingGeoreference() const { return mRasterChangeCoords.hasExistingGeoreference(); } //! \returns Coordinates of image QgsPointXY toColumnLine( const QgsPointXY &pntMap ) { return mRasterChangeCoords.toColumnLine( pntMap ); } diff --git a/src/app/georeferencer/qgsrasterchangecoords.cpp b/src/app/georeferencer/qgsrasterchangecoords.cpp index 7e12f6cfc16f..cb3b46b1e1f2 100644 --- a/src/app/georeferencer/qgsrasterchangecoords.cpp +++ b/src/app/georeferencer/qgsrasterchangecoords.cpp @@ -28,9 +28,8 @@ void QgsRasterChangeCoords::setRaster( const QString &fileRaster ) const gdal::dataset_unique_ptr hDS( GDALOpen( fileRaster.toUtf8().constData(), GA_ReadOnly ) ); double adfGeoTransform[6]; if ( GDALGetProjectionRef( hDS.get() ) && GDALGetGeoTransform( hDS.get(), adfGeoTransform ) == CE_None ) - //if ( false ) { - mHasCrs = true; + mHasExistingGeoreference = true; mUL_X = adfGeoTransform[0]; mUL_Y = adfGeoTransform[3]; mResX = adfGeoTransform[1]; @@ -38,7 +37,7 @@ void QgsRasterChangeCoords::setRaster( const QString &fileRaster ) } else { - mHasCrs = false; + mHasExistingGeoreference = false; } } diff --git a/src/app/georeferencer/qgsrasterchangecoords.h b/src/app/georeferencer/qgsrasterchangecoords.h index 2622190b10fd..5872d30aef38 100644 --- a/src/app/georeferencer/qgsrasterchangecoords.h +++ b/src/app/georeferencer/qgsrasterchangecoords.h @@ -26,14 +26,14 @@ class QgsRasterChangeCoords public: QgsRasterChangeCoords() = default; void setRaster( const QString &fileRaster ); - bool hasCrs() const { return mHasCrs; } + bool hasExistingGeoreference() const { return mHasExistingGeoreference; } QVector getPixelCoords( const QVector &mapCoords ); QgsRectangle getBoundingBox( const QgsRectangle &rect, bool toPixel ); QgsPointXY toColumnLine( const QgsPointXY &pntMap ); QgsPointXY toXY( const QgsPointXY &pntPixel ); private: - bool mHasCrs = false; + bool mHasExistingGeoreference = false; double mUL_X = 0.; double mUL_Y = 0.; double mResX = 1.; From 2ce753d3532a61f42668b8b2a6259d4d05c2d580 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 2 Feb 2022 14:02:39 +1000 Subject: [PATCH 02/47] Start on test suite for georeferencer --- src/app/georeferencer/qgsgeoreftransform.cpp | 2 +- src/app/georeferencer/qgsgeoreftransform.h | 5 +- src/app/georeferencer/qgsrasterchangecoords.h | 5 +- tests/src/app/CMakeLists.txt | 1 + tests/src/app/testqgsgeoreferencer.cpp | 177 ++++++++++++++++++ 5 files changed, 187 insertions(+), 3 deletions(-) create mode 100644 tests/src/app/testqgsgeoreferencer.cpp diff --git a/src/app/georeferencer/qgsgeoreftransform.cpp b/src/app/georeferencer/qgsgeoreftransform.cpp index df254f1b4d02..d10b7214fe8b 100644 --- a/src/app/georeferencer/qgsgeoreftransform.cpp +++ b/src/app/georeferencer/qgsgeoreftransform.cpp @@ -95,7 +95,7 @@ bool QgsGeorefTransform::updateParametersFromGcps( const QVector &so { return false; } - if ( mRasterChangeCoords.hasCrs() ) + if ( mRasterChangeCoords.hasExistingGeoreference() ) { const QVector pixelCoordsCorrect = mRasterChangeCoords.getPixelCoords( sourceCoordinates ); mParametersInitialized = mGeorefTransformImplementation->updateParametersFromGcps( sourceCoordinates, pixelCoordsCorrect, invertYAxis ); diff --git a/src/app/georeferencer/qgsgeoreftransform.h b/src/app/georeferencer/qgsgeoreftransform.h index 9091d1fec74a..fb523b021bb0 100644 --- a/src/app/georeferencer/qgsgeoreftransform.h +++ b/src/app/georeferencer/qgsgeoreftransform.h @@ -18,6 +18,7 @@ #define QGSGEOREFTRANSFORM_H #include +#include "qgis_app.h" #include "qgspoint.h" #include "qgsgcptransformer.h" #include "qgsrasterchangecoords.h" @@ -33,7 +34,7 @@ * Delegates to concrete implementations of \ref QgsGeorefInterface. For exception safety, * this is preferred over using the subclasses directly. */ -class QgsGeorefTransform : public QgsGcpTransformerInterface +class APP_EXPORT QgsGeorefTransform : public QgsGcpTransformerInterface { public: @@ -121,6 +122,8 @@ class QgsGeorefTransform : public QgsGcpTransformerInterface TransformMethod mTransformParametrisation = TransformMethod::InvalidTransform; bool mParametersInitialized = false; QgsRasterChangeCoords mRasterChangeCoords; + + friend class TestQgsGeoreferencer; }; #endif //QGSGEOREFTRANSFORM_H diff --git a/src/app/georeferencer/qgsrasterchangecoords.h b/src/app/georeferencer/qgsrasterchangecoords.h index 5872d30aef38..d7e56dc860a1 100644 --- a/src/app/georeferencer/qgsrasterchangecoords.h +++ b/src/app/georeferencer/qgsrasterchangecoords.h @@ -18,10 +18,11 @@ #include +#include "qgis_app.h" #include "qgspointxy.h" #include "qgsrectangle.h" -class QgsRasterChangeCoords +class APP_EXPORT QgsRasterChangeCoords { public: QgsRasterChangeCoords() = default; @@ -38,6 +39,8 @@ class QgsRasterChangeCoords double mUL_Y = 0.; double mResX = 1.; double mResY = 1.; + + friend class TestQgsGeoreferencer; }; #endif // QGSRASTERCHANGECOORDS_H diff --git a/tests/src/app/CMakeLists.txt b/tests/src/app/CMakeLists.txt index 33589d1b3aa8..9da78d504ab7 100644 --- a/tests/src/app/CMakeLists.txt +++ b/tests/src/app/CMakeLists.txt @@ -17,6 +17,7 @@ set(TESTS testqgsapplocatorfilters.cpp testqgsdecorationscalebar.cpp testqgsfieldcalculator.cpp + testqgsgeoreferencer.cpp testqgsmaptooleditannotation.cpp testqgsmaptoolidentifyaction.cpp testqgsmaptoollabel.cpp diff --git a/tests/src/app/testqgsgeoreferencer.cpp b/tests/src/app/testqgsgeoreferencer.cpp new file mode 100644 index 000000000000..14c5ccfca993 --- /dev/null +++ b/tests/src/app/testqgsgeoreferencer.cpp @@ -0,0 +1,177 @@ +/*************************************************************************** + testqgsgeorefencer.cpp + -------------------------- + Date : 2022-02-02 + Copyright : (C) 2022 by Nyall Dawson + Email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include "qgstest.h" +#include "qgisapp.h" +#include "qgsapplication.h" +#include "qgsvectorlayer.h" +#include "qgsfeature.h" +#include "qgsfeatureiterator.h" +#include "qgsgeometry.h" +#include "qgsvectordataprovider.h" +#include "qgsfieldcalculator.h" +#include "qgsproject.h" +#include "qgsmapcanvas.h" +#include "georeferencer/qgsgeoreftransform.h" + +/** + * \ingroup UnitTests + * This is a unit test for georeferencer + */ +class TestQgsGeoreferencer : public QObject +{ + Q_OBJECT + public: + TestQgsGeoreferencer(); + + private slots: + void initTestCase();// will be called before the first testfunction is executed. + void cleanupTestCase();// will be called after the last testfunction was executed. + void init() {} // will be called before each testfunction is executed. + void cleanup() {} // will be called after every testfunction. + void testTransformNoExistingImage(); + void testTransformImageWithExistingGeoreference(); + + private: + QgisApp *mQgisApp = nullptr; +}; + +TestQgsGeoreferencer::TestQgsGeoreferencer() = default; + +//runs before all tests +void TestQgsGeoreferencer::initTestCase() +{ + qDebug() << "TestQgisAppClipboard::initTestCase()"; + // init QGIS's paths - true means that all path will be inited from prefix + QgsApplication::init(); + QgsApplication::initQgis(); + mQgisApp = new QgisApp(); +} + +//runs after all tests +void TestQgsGeoreferencer::cleanupTestCase() +{ + QgsApplication::exitQgis(); +} + +void TestQgsGeoreferencer::testTransformNoExistingImage() +{ + QgsGeorefTransform transform( QgsGcpTransformerInterface::TransformMethod::Linear ); + + QVERIFY( !transform.hasExistingGeoreference() ); + + QVERIFY( transform.updateParametersFromGcps( {QgsPointXY( 0, 0 ), QgsPointXY( 10, 0 ), QgsPointXY( 0, 30 ), QgsPointXY( 10, 30 )}, + {QgsPointXY( 10, 5 ), QgsPointXY( 16, 5 ), QgsPointXY( 10, 8 ), QgsPointXY( 16, 8 )}, true ) ); + + QgsPointXY res; + QVERIFY( transform.transform( QgsPointXY( 0, 5 ), res, true ) ); + QCOMPARE( res.x(), 10 ); + QCOMPARE( res.y(), 5.5 ); + QVERIFY( transform.transform( QgsPointXY( 9, 25 ), res, true ) ); + QCOMPARE( res.x(), 15.4 ); + QCOMPARE( res.y(), 7.5 ); + // reverse transform + QVERIFY( transform.transform( QgsPointXY( 10, 5.5 ), res, false ) ); + QCOMPARE( res.x(), 0.0 ); + QCOMPARE( res.y(), 5.0 ); + QVERIFY( transform.transform( QgsPointXY( 15.4, 7.5 ), res, false ) ); + QCOMPARE( res.x(), 9.0 ); + QCOMPARE( res.y(), 25.0 ); +} + +void TestQgsGeoreferencer::testTransformImageWithExistingGeoreference() +{ + // load an image which is already georeferenced + QgsGeorefTransform transform( QgsGcpTransformerInterface::TransformMethod::Linear ); + transform.setRasterChangeCoords( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/landsat.tif" ) ); + + QVERIFY( transform.mRasterChangeCoords.mHasExistingGeoreference ); + QGSCOMPARENEAR( transform.mRasterChangeCoords.mResX, 57, 0.00001 ); + QGSCOMPARENEAR( transform.mRasterChangeCoords.mResY, -57, 0.00001 ); + QGSCOMPARENEAR( transform.mRasterChangeCoords.mUL_X, 781662.375, 0.01 ); + QGSCOMPARENEAR( transform.mRasterChangeCoords.mUL_Y, 3350923.125, 0.01 ); + + QgsPointXY res = transform.mRasterChangeCoords.toColumnLine( QgsPointXY( 783414, 3350122 ) ); + QGSCOMPARENEAR( res.x(), 30.7302631579, 0.01 ); + QGSCOMPARENEAR( res.y(), -14.0548245614, 0.01 ); + res = transform.mRasterChangeCoords.toXY( QgsPointXY( 30.7302631579, -14.0548245614 ) ); + QGSCOMPARENEAR( res.x(), 783414, 10 ); + QGSCOMPARENEAR( res.y(), 3350122, 10 ); + + QVector pixelCoords = transform.mRasterChangeCoords.getPixelCoords( + { + QgsPointXY( 783414, 3350122 ), + QgsPointXY( 791344, 3349795 ), + QgsPointXY( 783077, 3340937 ), + QgsPointXY( 791134, 3341401 ) + } ) ; + + QCOMPARE( pixelCoords.size(), 4 ); + QGSCOMPARENEAR( pixelCoords.at( 0 ).x(), 30.7302631579, 0.01 ); + QGSCOMPARENEAR( pixelCoords.at( 0 ).y(), -14.0548245614, 0.01 ); + QGSCOMPARENEAR( pixelCoords.at( 1 ).x(), 169.8530701754, 0.01 ); + QGSCOMPARENEAR( pixelCoords.at( 1 ).y(), -19.7916666667, 0.01 ); + QGSCOMPARENEAR( pixelCoords.at( 2 ).x(), 24.8179824561, 0.01 ); + QGSCOMPARENEAR( pixelCoords.at( 2 ).y(), -175.1951754386, 0.01 ); + QGSCOMPARENEAR( pixelCoords.at( 3 ).x(), 166.168859649, 0.01 ); + QGSCOMPARENEAR( pixelCoords.at( 3 ).y(), -167.0548245614, 0.01 ); + + QVERIFY( transform.hasExistingGeoreference() ); + + // currently disabled -- it is ambiguous whether calling updateParametersFromGcps should be using PIXEL coordinates as source or layer CRS coordinates, and is quite broken either + // way. Here the disabled tests assume layer CRS coordinates, but in the actual georeferencer a mix of pixel/layer coordinates are used. + // it would be better if all calls to updateParametersFromGcps ALWAYS use pixel coordinates to avoid the confusion, with the caller transforming from layer CRS coordinates + // back to pixel coordinates before calling this method. +#if 0 + // source coordinates here should be raster CRS - ie. 32633 + // first use a "null" transform + QVERIFY( transform.updateParametersFromGcps( {QgsPointXY( 783414, 3350122 ), QgsPointXY( 791344, 3349795 ), QgsPointXY( 783077, 334093 ), QgsPointXY( 791134, 3341401 )}, + {QgsPointXY( 783414, 3350122 ), QgsPointXY( 791344, 3349795 ), QgsPointXY( 783077, 334093 ), QgsPointXY( 791134, 3341401 )}, true ) ); + + QVERIFY( transform.transform( QgsPointXY( 30.7302631579, -14.0548245614 ), res, true ) ); + QGSCOMPARENEAR( res.x(), 783414, 1 ); + QGSCOMPARENEAR( res.y(), 3350122, 1 ); + QVERIFY( transform.transform( QgsPointXY( 166.168859649, -167.0548245614 ), res, true ) ); + QGSCOMPARENEAR( res.x(), 791134, 1 ); + QGSCOMPARENEAR( res.y(), 3341401, 1 ); + // reverse transform + QVERIFY( transform.transform( QgsPointXY( 783414, 3350122 ), res, false ) ); + QGSCOMPARENEAR( res.x(), 30.7302631579, 0.1 ); + QGSCOMPARENEAR( res.y(), -14.0548245614, 0.1 ); + QVERIFY( transform.transform( QgsPointXY( 791134, 3341401 ), res, false ) ); + QGSCOMPARENEAR( res.x(), 166.168859649, 0.1 ); + QGSCOMPARENEAR( res.y(), -167.0548245614, 0.1 ); + + // with shift of 100, 200 + QVERIFY( transform.updateParametersFromGcps( {QgsPointXY( 783414, 3350122 ), QgsPointXY( 791344, 3349795 ), QgsPointXY( 783077, 334093 ), QgsPointXY( 791134, 3341401 )}, + {QgsPointXY( 783514, 3350322 ), QgsPointXY( 791444, 3349995 ), QgsPointXY( 783177, 334293 ), QgsPointXY( 791234, 3341601 )}, true ) ); + + QVERIFY( transform.transform( QgsPointXY( 30.7302631579, -14.0548245614 ), res, true ) ); + QGSCOMPARENEAR( res.x(), 783514, 1 ); + QGSCOMPARENEAR( res.y(), 3350322, 1 ); + QVERIFY( transform.transform( QgsPointXY( 166.168859649, -167.0548245614 ), res, true ) ); + QGSCOMPARENEAR( res.x(), 791234, 1 ); + QGSCOMPARENEAR( res.y(), 3341601, 1 ); + // reverse transform + QVERIFY( transform.transform( QgsPointXY( 783514, 3350322 ), res, false ) ); + QGSCOMPARENEAR( res.x(), 30.7302631579, 0.1 ); + QGSCOMPARENEAR( res.y(), -14.0548245614, 0.1 ); + QVERIFY( transform.transform( QgsPointXY( 791234, 3341601 ), res, false ) ); + QGSCOMPARENEAR( res.x(), 166.168859649, 0.1 ); + QGSCOMPARENEAR( res.y(), -167.0548245614, 0.1 ); +#endif +} + +QGSTEST_MAIN( TestQgsGeoreferencer ) +#include "testqgsgeoreferencer.moc" From bac71a9ca29fab7f3f310eda7aed969fa2c1e152 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 2 Feb 2022 14:06:17 +1000 Subject: [PATCH 03/47] Spelling --- tests/src/app/testqgsgeoreferencer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/app/testqgsgeoreferencer.cpp b/tests/src/app/testqgsgeoreferencer.cpp index 14c5ccfca993..bb1d672c947b 100644 --- a/tests/src/app/testqgsgeoreferencer.cpp +++ b/tests/src/app/testqgsgeoreferencer.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - testqgsgeorefencer.cpp + testqgsgeoreferencer.cpp -------------------------- Date : 2022-02-02 Copyright : (C) 2022 by Nyall Dawson From 2fc2812ec98d455d0ac6dd82aef2fe38006d5d28 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 2 Feb 2022 16:08:31 +1000 Subject: [PATCH 04/47] Rename method for clarity --- src/app/georeferencer/qgsgeoreftransform.cpp | 8 ++++---- src/app/georeferencer/qgsgeoreftransform.h | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/app/georeferencer/qgsgeoreftransform.cpp b/src/app/georeferencer/qgsgeoreftransform.cpp index d10b7214fe8b..ee9a94d545bc 100644 --- a/src/app/georeferencer/qgsgeoreftransform.cpp +++ b/src/app/georeferencer/qgsgeoreftransform.cpp @@ -131,12 +131,12 @@ bool QgsGeorefTransform::transformRasterToWorld( const QgsPointXY &raster, QgsPo { // flip y coordinate due to different CS orientation const QgsPointXY raster_flipped( raster.x(), -raster.y() ); - return gdal_transform( raster_flipped, world, 0 ); + return transformPrivate( raster_flipped, world, false ); } bool QgsGeorefTransform::transformWorldToRaster( const QgsPointXY &world, QgsPointXY &raster ) { - const bool success = gdal_transform( world, raster, 1 ); + const bool success = transformPrivate( world, raster, true ); // flip y coordinate due to different CS orientation raster.setY( -raster.y() ); return success; @@ -186,13 +186,13 @@ bool QgsGeorefTransform::getOriginScaleRotation( QgsPointXY &origin, double &sca } -bool QgsGeorefTransform::gdal_transform( const QgsPointXY &src, QgsPointXY &dst, int dstToSrc ) const +bool QgsGeorefTransform::transformPrivate( const QgsPointXY &src, QgsPointXY &dst, bool inverseTransform ) const { // Copy the source coordinate for inplace transform double x = src.x(); double y = src.y(); - if ( !QgsGcpTransformerInterface::transform( x, y, dstToSrc == 1 ) ) + if ( !QgsGcpTransformerInterface::transform( x, y, inverseTransform ) ) return false; dst.setX( x ); diff --git a/src/app/georeferencer/qgsgeoreftransform.h b/src/app/georeferencer/qgsgeoreftransform.h index fb523b021bb0..d9b2507912d2 100644 --- a/src/app/georeferencer/qgsgeoreftransform.h +++ b/src/app/georeferencer/qgsgeoreftransform.h @@ -110,8 +110,7 @@ class APP_EXPORT QgsGeorefTransform : public QgsGcpTransformerInterface QgsGeorefTransform( const QgsGeorefTransform &other ); QgsGeorefTransform &operator= ( const QgsGeorefTransform & ) = delete; - // convenience wrapper around GDALTransformerFunc - bool gdal_transform( const QgsPointXY &src, QgsPointXY &dst, int dstToSrc ) const; + bool transformPrivate( const QgsPointXY &src, QgsPointXY &dst, bool inverseTransform ) const; QVector mSourceCoordinates; QVector mDestinationCoordinates; From 7f079c27e1cdef7f554b37cf5a3af4cc0db4e22e Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 2 Feb 2022 17:02:33 +1000 Subject: [PATCH 05/47] Cleanup some georeferencer API to disambiguate if points are source/ destination coordinates, and whether they are in pixels/map coordinates And make some TODO notes flagging errors identified along the way --- src/app/georeferencer/qgsgcpcanvasitem.cpp | 17 +++--- src/app/georeferencer/qgsgcplist.cpp | 17 +++--- src/app/georeferencer/qgsgcplist.h | 2 +- src/app/georeferencer/qgsgcplistmodel.cpp | 6 +- src/app/georeferencer/qgsgcplistwidget.cpp | 13 ++-- src/app/georeferencer/qgsgeorefdatapoint.cpp | 59 +++++++++---------- src/app/georeferencer/qgsgeorefdatapoint.h | 45 +++++++++----- src/app/georeferencer/qgsgeorefmainwindow.cpp | 36 ++++++----- src/app/georeferencer/qgsgeorefmainwindow.h | 6 +- src/app/georeferencer/qgsmapcoordsdialog.h | 2 +- src/app/georeferencer/qgsresidualplotitem.cpp | 4 +- 11 files changed, 113 insertions(+), 94 deletions(-) diff --git a/src/app/georeferencer/qgsgcpcanvasitem.cpp b/src/app/georeferencer/qgsgcpcanvasitem.cpp index ad7e324040d4..76fbf1c676b6 100644 --- a/src/app/georeferencer/qgsgcpcanvasitem.cpp +++ b/src/app/georeferencer/qgsgcpcanvasitem.cpp @@ -54,7 +54,7 @@ void QgsGCPCanvasItem::paint( QPainter *p ) if ( mDataPoint ) { enabled = mDataPoint->isEnabled(); - worldCoords = mDataPoint->canvasCoords(); + worldCoords = mDataPoint->destinationMapCoords(); id = mDataPoint->id(); } p->setOpacity( enabled ? 1.0 : 0.3 ); @@ -163,18 +163,19 @@ void QgsGCPCanvasItem::updatePosition() if ( mIsGCPSource ) { - setPos( toCanvasCoordinates( mDataPoint->pixelCoords() ) ); + setPos( toCanvasCoordinates( mDataPoint->sourceCoords() ) ); } else { - if ( mDataPoint->canvasCoords().isEmpty() ) + if ( mDataPoint->destinationInCanvasPixels().isEmpty() ) { - const QgsCoordinateReferenceSystem mapCrs = mMapCanvas->mapSettings().destinationCrs(); - const QgsCoordinateTransform transf( mDataPoint->crs(), mapCrs, QgsProject::instance() ); - const QgsPointXY mapCoords = transf.transform( mDataPoint->mapCoords() ); - mDataPoint->setCanvasCoords( mapCoords ); + const QgsCoordinateReferenceSystem canvasCrs = mMapCanvas->mapSettings().destinationCrs(); + const QgsCoordinateTransform pointToCanvasTransform( mDataPoint->destinationCrs(), canvasCrs, QgsProject::instance() ); + const QgsPointXY canvasMapCoords = pointToCanvasTransform.transform( mDataPoint->destinationMapCoords() ); + const QPointF canvasCoordinatesInPixels = toCanvasCoordinates( canvasMapCoords ); + mDataPoint->setDestinationInCanvasPixels( canvasCoordinatesInPixels ); } - setPos( toCanvasCoordinates( mDataPoint->canvasCoords() ) ); + setPos( mDataPoint->destinationInCanvasPixels().toQPointF() ); } } diff --git a/src/app/georeferencer/qgsgcplist.cpp b/src/app/georeferencer/qgsgcplist.cpp index c607fd85d078..df199de0c8c9 100644 --- a/src/app/georeferencer/qgsgcplist.cpp +++ b/src/app/georeferencer/qgsgcplist.cpp @@ -33,7 +33,7 @@ QgsGCPList::QgsGCPList( const QgsGCPList &list ) } } -void QgsGCPList::createGCPVectors( QVector &mapCoords, QVector &pixelCoords, const QgsCoordinateReferenceSystem targetCrs ) +void QgsGCPList::createGCPVectors( QVector &mapCoords, QVector &pixelCoords, const QgsCoordinateReferenceSystem &targetCrs ) { mapCoords = QVector( size() ); pixelCoords = QVector( size() ); @@ -47,20 +47,21 @@ void QgsGCPList::createGCPVectors( QVector &mapCoords, QVectorcrs(), targetCrs, - QgsProject::instance() ).transform( pt->mapCoords() ); + transCoords = QgsCoordinateTransform( pt->destinationCrs(), targetCrs, + QgsProject::instance() ).transform( pt->destinationMapCoords() ); mapCoords[j] = transCoords; pt->setTransCoords( transCoords ); } - catch ( const QgsException &e ) + catch ( const QgsException & ) { - Q_UNUSED( e ); - mapCoords[j] = pt->mapCoords(); + mapCoords[j] = pt->destinationMapCoords(); } } else - mapCoords[j] = pt->mapCoords(); - pixelCoords[j] = pt->pixelCoords(); + mapCoords[j] = pt->destinationMapCoords(); + + // TODO -- this must be converted to pixels!!! + pixelCoords[j] = pt->sourceCoords(); j++; } } diff --git a/src/app/georeferencer/qgsgcplist.h b/src/app/georeferencer/qgsgcplist.h index 8b913993d272..8e6a565eab8c 100644 --- a/src/app/georeferencer/qgsgcplist.h +++ b/src/app/georeferencer/qgsgcplist.h @@ -30,7 +30,7 @@ class QgsGCPList : public QList QgsGCPList() = default; QgsGCPList( const QgsGCPList &list ); - void createGCPVectors( QVector &mapCoords, QVector &pixelCoords, const QgsCoordinateReferenceSystem targetCrs ); + void createGCPVectors( QVector &mapCoords, QVector &pixelCoords, const QgsCoordinateReferenceSystem &targetCrs ); int size() const; int sizeAll() const; diff --git a/src/app/georeferencer/qgsgcplistmodel.cpp b/src/app/georeferencer/qgsgcplistmodel.cpp index ad455e90a36b..2d584322e268 100644 --- a/src/app/georeferencer/qgsgcplistmodel.cpp +++ b/src/app/georeferencer/qgsgcplistmodel.cpp @@ -135,8 +135,8 @@ void QgsGCPListModel::updateModel() setItem( i, j++, si ); setItem( i, j++, new QgsStandardItem( i ) ); - setItem( i, j++, new QgsStandardItem( p->pixelCoords().x() ) ); - setItem( i, j++, new QgsStandardItem( p->pixelCoords().y() ) ); + setItem( i, j++, new QgsStandardItem( p->sourceCoords().x() ) ); + setItem( i, j++, new QgsStandardItem( p->sourceCoords().y() ) ); setItem( i, j++, new QgsStandardItem( p->transCoords().x() ) ); setItem( i, j++, new QgsStandardItem( p->transCoords().y() ) ); @@ -147,7 +147,7 @@ void QgsGCPListModel::updateModel() if ( mGeorefTransform && bTransformUpdated && mGeorefTransform->parametersInitialized() ) { QgsPointXY dst; - const QgsPointXY pixel = mGeorefTransform->hasExistingGeoreference() ? mGeorefTransform->toColumnLine( p->pixelCoords() ) : p->pixelCoords(); + const QgsPointXY pixel = mGeorefTransform->hasExistingGeoreference() ? mGeorefTransform->toColumnLine( p->sourceCoords() ) : p->sourceCoords(); if ( unitType == tr( "pixels" ) ) { // Transform from world to raster coordinate: diff --git a/src/app/georeferencer/qgsgcplistwidget.cpp b/src/app/georeferencer/qgsgcplistwidget.cpp index 23bdcbdf1ee2..6bb2d80efccb 100644 --- a/src/app/georeferencer/qgsgcplistwidget.cpp +++ b/src/app/georeferencer/qgsgcplistwidget.cpp @@ -214,15 +214,16 @@ void QgsGCPListWidget::updateItemCoords( QWidget *editor ) if ( lineEdit ) { const double value = lineEdit->text().toDouble(); - QgsPointXY newMapCoords( dataPoint->mapCoords() ); - QgsPointXY newPixelCoords( dataPoint->pixelCoords() ); + QgsPointXY newMapCoords( dataPoint->destinationMapCoords() ); + + QgsPointXY newSourceCoords( dataPoint->sourceCoords() ); if ( mPrevColumn == 2 ) // srcX { - newPixelCoords.setX( value ); + newSourceCoords.setX( value ); } else if ( mPrevColumn == 3 ) // srcY { - newPixelCoords.setY( value ); + newSourceCoords.setY( value ); } else if ( mPrevColumn == 4 ) // dstX { @@ -237,8 +238,8 @@ void QgsGCPListWidget::updateItemCoords( QWidget *editor ) return; } - dataPoint->setPixelCoords( newPixelCoords ); - dataPoint->setMapCoords( newMapCoords ); + dataPoint->setSourceCoords( newSourceCoords ); + dataPoint->setDestinationMapCoords( newMapCoords ); } dataPoint->updateCoords(); diff --git a/src/app/georeferencer/qgsgeorefdatapoint.cpp b/src/app/georeferencer/qgsgeorefdatapoint.cpp index eeda622827da..d679556554ba 100644 --- a/src/app/georeferencer/qgsgeorefdatapoint.cpp +++ b/src/app/georeferencer/qgsgeorefdatapoint.cpp @@ -22,18 +22,17 @@ #include "qgsgeorefdatapoint.h" QgsGeorefDataPoint::QgsGeorefDataPoint( QgsMapCanvas *srcCanvas, QgsMapCanvas *dstCanvas, - const QgsPointXY &pixelCoords, const QgsPointXY &mapCoords, - const QgsCoordinateReferenceSystem proj, bool enable ) + const QgsPointXY &sourceCoordinates, const QgsPointXY &destinationMapCoords, + const QgsCoordinateReferenceSystem &destinationCrs, bool enable ) : mSrcCanvas( srcCanvas ) , mDstCanvas( dstCanvas ) - , mPixelCoords( pixelCoords ) - , mMapCoords( mapCoords ) + , mSourceCoords( sourceCoordinates ) + , mDestinationMapCoords( destinationMapCoords ) , mId( -1 ) - , mCrs( proj ) + , mDestinationCrs( destinationCrs ) , mEnabled( enable ) { - mTransCoords = QgsPointXY( mapCoords ); - mCanvasCoords = QgsPointXY(); + mTransCoords = QgsPointXY( destinationMapCoords ); mGCPSourceItem = new QgsGCPCanvasItem( srcCanvas, this, true ); mGCPDestinationItem = new QgsGCPCanvasItem( dstCanvas, this, false ); mGCPSourceItem->setEnabled( enable ); @@ -48,13 +47,13 @@ QgsGeorefDataPoint::QgsGeorefDataPoint( const QgsGeorefDataPoint &p ) // we share item representation on canvas between all points // mGCPSourceItem = new QgsGCPCanvasItem(p.srcCanvas(), p.pixelCoords(), p.mapCoords(), p.isEnabled()); // mGCPDestinationItem = new QgsGCPCanvasItem(p.dstCanvas(), p.pixelCoords(), p.mapCoords(), p.isEnabled()); - mPixelCoords = p.pixelCoords(); - mMapCoords = p.mapCoords(); + mSourceCoords = p.sourceCoords(); + mDestinationMapCoords = p.destinationMapCoords(); mTransCoords = p.transCoords(); mEnabled = p.isEnabled(); mResidual = p.residual(); - mCanvasCoords = p.canvasCoords(); - mCrs = p.crs(); + mDestinationInCanvasPixels = p.destinationInCanvasPixels(); + mDestinationCrs = p.destinationCrs(); mId = p.id(); } @@ -64,16 +63,16 @@ QgsGeorefDataPoint::~QgsGeorefDataPoint() delete mGCPDestinationItem; } -void QgsGeorefDataPoint::setPixelCoords( const QgsPointXY &p ) +void QgsGeorefDataPoint::setSourceCoords( const QgsPointXY &p ) { - mPixelCoords = p; + mSourceCoords = p; mGCPSourceItem->update(); mGCPDestinationItem->update(); } -void QgsGeorefDataPoint::setMapCoords( const QgsPointXY &p ) +void QgsGeorefDataPoint::setDestinationMapCoords( const QgsPointXY &p ) { - mMapCoords = p; + mDestinationMapCoords = p; if ( mGCPSourceItem ) { mGCPSourceItem->update(); @@ -99,18 +98,17 @@ void QgsGeorefDataPoint::setTransCoords( const QgsPointXY &p ) QgsPointXY QgsGeorefDataPoint::transCoords() const { - return mTransCoords.isEmpty() ? mMapCoords : mTransCoords; + return mTransCoords.isEmpty() ? mDestinationMapCoords : mTransCoords; } - -void QgsGeorefDataPoint::setCanvasCoords( const QgsPointXY &p ) +void QgsGeorefDataPoint::setDestinationInCanvasPixels( const QgsPointXY &p ) { - mCanvasCoords = p; + mDestinationInCanvasPixels = p; } -QgsPointXY QgsGeorefDataPoint::canvasCoords() const +QgsPointXY QgsGeorefDataPoint::destinationInCanvasPixels() const { - return mCanvasCoords; + return mDestinationInCanvasPixels; } void QgsGeorefDataPoint::setEnabled( bool enabled ) @@ -172,25 +170,24 @@ bool QgsGeorefDataPoint::contains( QPoint p, bool isMapPlugin ) } } -void QgsGeorefDataPoint::moveTo( QPoint p, bool isMapPlugin ) +void QgsGeorefDataPoint::moveTo( QPoint canvasPixels, bool isMapPlugin ) { if ( isMapPlugin ) { - const QgsPointXY pnt = mGCPSourceItem->toMapCoordinates( p ); - mPixelCoords = pnt; + const QgsPointXY pnt = mGCPSourceItem->toMapCoordinates( canvasPixels ); + mSourceCoords = pnt; } else { - const QgsPointXY pnt = mGCPDestinationItem->toMapCoordinates( p ); - setCanvasCoords( pnt ); - mMapCoords = pnt; + mDestinationInCanvasPixels = canvasPixels; + mDestinationMapCoords = mGCPDestinationItem->toMapCoordinates( canvasPixels ); if ( mSrcCanvas && mSrcCanvas->mapSettings().destinationCrs().isValid() ) - mCrs = mSrcCanvas->mapSettings().destinationCrs(); + mDestinationCrs = mSrcCanvas->mapSettings().destinationCrs(); else - mCrs = mGCPDestinationItem->canvas()->mapSettings().destinationCrs(); + mDestinationCrs = mGCPDestinationItem->canvas()->mapSettings().destinationCrs(); } - if ( !mCrs.isValid() ) - mCrs = QgsProject::instance()->crs(); + if ( !mDestinationCrs.isValid() ) + mDestinationCrs = QgsProject::instance()->crs(); mGCPSourceItem->update(); mGCPDestinationItem->update(); updateCoords(); diff --git a/src/app/georeferencer/qgsgeorefdatapoint.h b/src/app/georeferencer/qgsgeorefdatapoint.h index d7ba0d1266cc..d440c503cdee 100644 --- a/src/app/georeferencer/qgsgeorefdatapoint.h +++ b/src/app/georeferencer/qgsgeorefdatapoint.h @@ -28,23 +28,36 @@ class QgsGeorefDataPoint : public QObject public: //! constructor QgsGeorefDataPoint( QgsMapCanvas *srcCanvas, QgsMapCanvas *dstCanvas, - const QgsPointXY &pixelCoords, const QgsPointXY &mapCoords, - const QgsCoordinateReferenceSystem proj, bool enable ); + const QgsPointXY &sourceCoordinates, const QgsPointXY &destinationMapCoords, + const QgsCoordinateReferenceSystem &destinationCrs, bool enable ); QgsGeorefDataPoint( const QgsGeorefDataPoint &p ); ~QgsGeorefDataPoint() override; - //! returns coordinates of the point - QgsPointXY pixelCoords() const { return mPixelCoords; } - void setPixelCoords( const QgsPointXY &p ); + /** + * Returns source coordinates of the point. + * + * This may either be in pixels (for completely non-referenced images) OR in the source layer CRS. + */ + QgsPointXY sourceCoords() const { return mSourceCoords; } + void setSourceCoords( const QgsPointXY &p ); - QgsPointXY mapCoords() const { return mMapCoords; } - void setMapCoords( const QgsPointXY &p ); + QgsPointXY destinationMapCoords() const { return mDestinationMapCoords; } + void setDestinationMapCoords( const QgsPointXY &p ); QgsPointXY transCoords() const; void setTransCoords( const QgsPointXY &p ); - QgsPointXY canvasCoords() const; - void setCanvasCoords( const QgsPointXY &p ); + /** + * Returns the destination point in canvas coordinates (i.e. pixels). + * + * May be an empty point if not yet calculated. + */ + QgsPointXY destinationInCanvasPixels() const; + + /** + * Sets the destination point in canvas coordinates (i.e. pixels). + */ + void setDestinationInCanvasPixels( const QgsPointXY &p ); bool isEnabled() const { return mEnabled; } void setEnabled( bool enabled ); @@ -60,10 +73,10 @@ class QgsGeorefDataPoint : public QObject QPointF residual() const { return mResidual; } void setResidual( QPointF r ); - QgsCoordinateReferenceSystem crs() const { return mCrs; } + QgsCoordinateReferenceSystem destinationCrs() const { return mDestinationCrs; } public slots: - void moveTo( QPoint, bool isMapPlugin ); + void moveTo( QPoint canvasPixels, bool isMapPlugin ); void updateCoords(); private: @@ -71,13 +84,15 @@ class QgsGeorefDataPoint : public QObject QgsMapCanvas *mDstCanvas = nullptr; QgsGCPCanvasItem *mGCPSourceItem = nullptr; QgsGCPCanvasItem *mGCPDestinationItem = nullptr; - QgsPointXY mPixelCoords; - QgsPointXY mMapCoords; + QgsPointXY mSourceCoords; + QgsPointXY mDestinationMapCoords; QgsPointXY mTransCoords; - QgsPointXY mCanvasCoords; + + // destination point converted to canvas coordinates (i.e. pixels) + QgsPointXY mDestinationInCanvasPixels; int mId; - QgsCoordinateReferenceSystem mCrs; + QgsCoordinateReferenceSystem mDestinationCrs; bool mEnabled; QPointF mResidual; diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index 5022f20f88bd..df558e9f0b83 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -534,14 +534,16 @@ void QgsGeoreferencerMainWindow::linkGeorefToQgis( bool link ) } // GCPs slots -void QgsGeoreferencerMainWindow::addPoint( const QgsPointXY &pixelCoords, const QgsPointXY &mapCoords, const QgsCoordinateReferenceSystem &crs, +void QgsGeoreferencerMainWindow::addPoint( const QgsPointXY &sourceCoords, const QgsPointXY &destinationMapCoords, const QgsCoordinateReferenceSystem &destinationCrs, bool enable, bool finalize ) { - QgsGeorefDataPoint *pnt = new QgsGeorefDataPoint( mCanvas, QgisApp::instance()->mapCanvas(), pixelCoords, mapCoords, crs, enable ); + QgsGeorefDataPoint *pnt = new QgsGeorefDataPoint( mCanvas, QgisApp::instance()->mapCanvas(), sourceCoords, destinationMapCoords, destinationCrs, enable ); mPoints.append( pnt ); - if ( !mLastGCPProjection.isValid() || mLastGCPProjection != crs ) - mLastGCPProjection = QgsCoordinateReferenceSystem( crs ); + + if ( !mLastGCPProjection.isValid() || mLastGCPProjection != destinationCrs ) + mLastGCPProjection = destinationCrs; mGCPsDirty = true; + if ( finalize ) { mGCPListWidget->setGCPList( &mPoints ); @@ -598,7 +600,7 @@ void QgsGeoreferencerMainWindow::selectPoint( QPoint p ) } } -void QgsGeoreferencerMainWindow::movePoint( QPoint p ) +void QgsGeoreferencerMainWindow::movePoint( QPoint canvasPixels ) { // Get Map Sender bool isMapPlugin = sender() == mToolMovePoint; @@ -606,7 +608,7 @@ void QgsGeoreferencerMainWindow::movePoint( QPoint p ) if ( mvPoint ) { - mvPoint->moveTo( p, isMapPlugin ); + mvPoint->moveTo( canvasPixels, isMapPlugin ); } } @@ -640,9 +642,9 @@ void QgsGeoreferencerMainWindow::showCoordDialog( const QgsPointXY &pixelCoords if ( mLayer && !mMapCoordsDialog ) { mMapCoordsDialog = new QgsMapCoordsDialog( QgisApp::instance()->mapCanvas(), pixelCoords, lastProjection, this ); - connect( mMapCoordsDialog, &QgsMapCoordsDialog::pointAdded, this, [ = ]( const QgsPointXY & a, const QgsPointXY & b, const QgsCoordinateReferenceSystem & crs ) + connect( mMapCoordsDialog, &QgsMapCoordsDialog::pointAdded, this, [ = ]( const QgsPointXY & a, const QgsPointXY & destination, const QgsCoordinateReferenceSystem & destinationCrs ) { - addPoint( a, b, crs ); + addPoint( a, destination, destinationCrs ); } ); connect( mMapCoordsDialog, &QObject::destroyed, this, [ = ] { @@ -761,7 +763,7 @@ void QgsGeoreferencerMainWindow::jumpToGCP( uint theGCPIndex ) QgsRectangle ext = mCanvas->extent(); QgsPointXY center = ext.center(); - QgsPointXY new_center = mPoints[theGCPIndex]->pixelCoords(); + QgsPointXY new_center = mPoints[theGCPIndex]->sourceCoords(); QgsPointXY diff( new_center.x() - center.x(), new_center.y() - center.y() ); QgsRectangle new_extent( ext.xMinimum() + diff.x(), ext.yMinimum() + diff.y(), @@ -1273,6 +1275,8 @@ bool QgsGeoreferencerMainWindow::loadGCPs( /*bool verbose*/ ) QgsPointXY mapCoords( ls.at( 0 ).toDouble(), ls.at( 1 ).toDouble() ); // map x,y QgsPointXY pixelCoords( ls.at( 2 ).toDouble(), ls.at( 3 ).toDouble() ); // pixel x,y + // TO FIX -- pixelCoord needs to be converted to sourceCoord, i.e. if source is georeferenced we need to convert to layer coordinates from pixels!!!! + if ( ls.count() == 5 ) { bool enable = ls.at( 4 ).toInt(); @@ -1310,8 +1314,8 @@ void QgsGeoreferencerMainWindow::saveGCPs() points << QStringLiteral( "%1,%2,%3,%4,%5,%6,%7,%8" ) .arg( qgsDoubleToString( pt->transCoords().x() ), qgsDoubleToString( pt->transCoords().y() ), - qgsDoubleToString( pt->pixelCoords().x() ), - qgsDoubleToString( pt->pixelCoords().y() ) ) + qgsDoubleToString( pt->sourceCoords().x() ), + qgsDoubleToString( pt->sourceCoords().y() ) ) .arg( pt->isEnabled() ) .arg( qgsDoubleToString( pt->residual().x() ), qgsDoubleToString( pt->residual().y() ), @@ -1786,7 +1790,7 @@ bool QgsGeoreferencerMainWindow::writePDFReportFile( const QString &fileName, co { currentGCPStrings << tr( "no" ); } - currentGCPStrings << QString::number( ( *gcpIt )->pixelCoords().x(), 'f', 0 ) << QString::number( ( *gcpIt )->pixelCoords().y(), 'f', 0 ) << QString::number( ( *gcpIt )->transCoords().x(), 'f', 3 ) + currentGCPStrings << QString::number( ( *gcpIt )->sourceCoords().x(), 'f', 0 ) << QString::number( ( *gcpIt )->sourceCoords().y(), 'f', 0 ) << QString::number( ( *gcpIt )->transCoords().x(), 'f', 3 ) << QString::number( ( *gcpIt )->transCoords().y(), 'f', 3 ) << QString::number( residual.x() ) << QString::number( residual.y() ) << QString::number( residualTot ); gcpTableContents << currentGCPStrings; } @@ -1892,7 +1896,7 @@ QString QgsGeoreferencerMainWindow::generateGDALtranslateCommand( bool generateT for ( QgsGeorefDataPoint *pt : std::as_const( mPoints ) ) { - gdalCommand << QStringLiteral( "-gcp %1 %2 %3 %4" ).arg( pt->pixelCoords().x() ).arg( -pt->pixelCoords().y() ) + gdalCommand << QStringLiteral( "-gcp %1 %2 %3 %4" ).arg( pt->sourceCoords().x() ).arg( -pt->sourceCoords().y() ) .arg( pt->transCoords().x() ).arg( pt->transCoords().y() ); } @@ -2138,10 +2142,10 @@ bool QgsGeoreferencerMainWindow::equalGCPlists( const QgsGCPList &list1, const Q { QgsGeorefDataPoint *p1 = list1.at( i ); QgsGeorefDataPoint *p2 = list2.at( j ); - if ( p1->pixelCoords() != p2->pixelCoords() ) + if ( p1->sourceCoords() != p2->sourceCoords() ) return false; - if ( p1->mapCoords() != p2->mapCoords() ) + if ( p1->destinationMapCoords() != p2->destinationMapCoords() ) return false; } @@ -2191,7 +2195,7 @@ void QgsGeoreferencerMainWindow::invalidateCanvasCoords() for ( int i = 0; i < count; ++i, ++j ) { QgsGeorefDataPoint *p = mPoints.at( i ); - p->setCanvasCoords( QgsPointXY() ); + p->setDestinationInCanvasPixels( QgsPointXY() ); p->updateCoords(); } } diff --git a/src/app/georeferencer/qgsgeorefmainwindow.h b/src/app/georeferencer/qgsgeorefmainwindow.h index 47952d91f841..b89cd4994c53 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.h +++ b/src/app/georeferencer/qgsgeorefmainwindow.h @@ -90,14 +90,14 @@ class QgsGeoreferencerMainWindow : public QMainWindow, private Ui::QgsGeorefPlug void linkQGisToGeoref( bool link ); // gcps - void addPoint( const QgsPointXY &pixelCoords, const QgsPointXY &mapCoords, - const QgsCoordinateReferenceSystem &crs, bool enable = true, bool finalize = true ); + void addPoint( const QgsPointXY &sourceCoords, const QgsPointXY &destinationMapCoords, + const QgsCoordinateReferenceSystem &destinationCrs, bool enable = true, bool finalize = true ); void deleteDataPoint( QPoint pixelCoords ); void deleteDataPoint( int index ); void showCoordDialog( const QgsPointXY &pixelCoords ); void selectPoint( QPoint ); - void movePoint( QPoint ); + void movePoint( QPoint canvasPixels ); void releasePoint( QPoint ); void loadGCPsDialog(); diff --git a/src/app/georeferencer/qgsmapcoordsdialog.h b/src/app/georeferencer/qgsmapcoordsdialog.h index 8f942fd554e7..5e82c094bff4 100644 --- a/src/app/georeferencer/qgsmapcoordsdialog.h +++ b/src/app/georeferencer/qgsmapcoordsdialog.h @@ -73,7 +73,7 @@ class QgsMapCoordsDialog : public QDialog, private Ui::QgsMapCoordsDialogBase void setPrevTool(); signals: - void pointAdded( const QgsPointXY &a, const QgsPointXY &b, const QgsCoordinateReferenceSystem &crs ); + void pointAdded( const QgsPointXY &a, const QgsPointXY &destination, const QgsCoordinateReferenceSystem &destinationCrs ); private: double dmsToDD( const QString &dms ); diff --git a/src/app/georeferencer/qgsresidualplotitem.cpp b/src/app/georeferencer/qgsresidualplotitem.cpp index 826e955c6d7d..b402909290ef 100644 --- a/src/app/georeferencer/qgsresidualplotitem.cpp +++ b/src/app/georeferencer/qgsresidualplotitem.cpp @@ -58,7 +58,7 @@ void QgsResidualPlotItem::paint( QPainter *painter, const QStyleOptionGraphicsIt QgsGCPList::const_iterator gcpIt = mGCPList.constBegin(); for ( ; gcpIt != mGCPList.constEnd(); ++gcpIt ) { - const QgsPointXY gcpCoords = ( *gcpIt )->pixelCoords(); + const QgsPointXY gcpCoords = ( *gcpIt )->sourceCoords(); const double gcpItemMMX = ( gcpCoords.x() - mExtent.xMinimum() ) / mExtent.width() * widthMM; const double gcpItemMMY = ( 1 - ( gcpCoords.y() - mExtent.yMinimum() ) / mExtent.height() ) * heightMM; @@ -86,7 +86,7 @@ void QgsResidualPlotItem::paint( QPainter *painter, const QStyleOptionGraphicsIt gcpIt = mGCPList.constBegin(); for ( ; gcpIt != mGCPList.constEnd(); ++gcpIt ) { - const QgsPointXY gcpCoords = ( *gcpIt )->pixelCoords(); + const QgsPointXY gcpCoords = ( *gcpIt )->sourceCoords(); const double gcpItemMMX = ( gcpCoords.x() - mExtent.xMinimum() ) / mExtent.width() * widthMM; const double gcpItemMMY = ( 1 - ( gcpCoords.y() - mExtent.yMinimum() ) / mExtent.height() ) * heightMM; if ( ( *gcpIt )->isEnabled() ) From 0d02d78ae498a306c4640ef53bbef286f1004b97 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 3 Feb 2022 13:36:04 +1000 Subject: [PATCH 06/47] [georeferencer] Fix incorrect pixel to layer coordinate update causes issues when a second raster is loaded into the georeferencer --- .../georeferencer/qgsrasterchangecoords.cpp | 6 +++ tests/src/app/testqgsgeoreferencer.cpp | 41 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/app/georeferencer/qgsrasterchangecoords.cpp b/src/app/georeferencer/qgsrasterchangecoords.cpp index cb3b46b1e1f2..3991d1642ad0 100644 --- a/src/app/georeferencer/qgsrasterchangecoords.cpp +++ b/src/app/georeferencer/qgsrasterchangecoords.cpp @@ -67,6 +67,9 @@ QgsRectangle QgsRasterChangeCoords::getBoundingBox( const QgsRectangle &rect, bo QgsPointXY QgsRasterChangeCoords::toColumnLine( const QgsPointXY &pntMap ) { + if ( ! mHasExistingGeoreference ) + return QgsPointXY( pntMap.x(), pntMap.y() ); + const double col = ( pntMap.x() - mUL_X ) / mResX; const double line = ( mUL_Y - pntMap.y() ) / mResY; return QgsPointXY( col, line ); @@ -74,6 +77,9 @@ QgsPointXY QgsRasterChangeCoords::toColumnLine( const QgsPointXY &pntMap ) QgsPointXY QgsRasterChangeCoords::toXY( const QgsPointXY &pntPixel ) { + if ( ! mHasExistingGeoreference ) + return QgsPointXY( pntPixel.x(), pntPixel.y() ); + const double x = mUL_X + ( pntPixel.x() * mResX ); const double y = mUL_Y + ( pntPixel.y() * -mResY ); return QgsPointXY( x, y ); diff --git a/tests/src/app/testqgsgeoreferencer.cpp b/tests/src/app/testqgsgeoreferencer.cpp index bb1d672c947b..5b5bc0a2cf58 100644 --- a/tests/src/app/testqgsgeoreferencer.cpp +++ b/tests/src/app/testqgsgeoreferencer.cpp @@ -42,6 +42,7 @@ class TestQgsGeoreferencer : public QObject void cleanup() {} // will be called after every testfunction. void testTransformNoExistingImage(); void testTransformImageWithExistingGeoreference(); + void testRasterChangeCoords(); private: QgisApp *mQgisApp = nullptr; @@ -173,5 +174,45 @@ void TestQgsGeoreferencer::testTransformImageWithExistingGeoreference() #endif } +void TestQgsGeoreferencer::testRasterChangeCoords() +{ + QgsRasterChangeCoords transform; + QVERIFY( !transform.hasExistingGeoreference() ); + + transform.setRaster( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/landsat.tif" ) ); + QVERIFY( transform.hasExistingGeoreference() ); + QGSCOMPARENEAR( transform.toXY( QgsPointXY( 0, 0 ) ).x(), 781662.375, 0.001 ); + QGSCOMPARENEAR( transform.toXY( QgsPointXY( 0, 0 ) ).y(), 3350923.125, 0.001 ); + QGSCOMPARENEAR( transform.toXY( QgsPointXY( 100, 0 ) ).x(), 787362.375, 0.001 ); + QGSCOMPARENEAR( transform.toXY( QgsPointXY( 100, 0 ) ).y(), 3350923.125, 0.001 ); + QGSCOMPARENEAR( transform.toXY( QgsPointXY( 100, 200 ) ).x(), 787362.375, 0.001 ); + QGSCOMPARENEAR( transform.toXY( QgsPointXY( 100, 200 ) ).y(), 3362323.125, 0.001 ); + + QGSCOMPARENEAR( transform.toColumnLine( QgsPointXY( 781662.375, 3350923.125 ) ).x(), 0.0, 0.0001 ); + QGSCOMPARENEAR( transform.toColumnLine( QgsPointXY( 781662.375, 3350923.125 ) ).y(), 0.0, 0.0001 ); + QGSCOMPARENEAR( transform.toColumnLine( QgsPointXY( 787362.375, 3350923.125 ) ).x(), 100.0, 0.0001 ); + QGSCOMPARENEAR( transform.toColumnLine( QgsPointXY( 787362.375, 3350923.125 ) ).y(), 0.0, 0.0001 ); + QGSCOMPARENEAR( transform.toColumnLine( QgsPointXY( 787362.375, 3362323.125 ) ).x(), 100.0, 0.0001 ); + QGSCOMPARENEAR( transform.toColumnLine( QgsPointXY( 787362.375, 3362323.125 ) ).y(), 200.0, 0.0001 ); + + // load a raster with no georeferencing + transform.setRaster( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/rgb256x256.png" ) ); + QVERIFY( !transform.hasExistingGeoreference() ); + // should be treat layer coordinates and pixels as identical + QCOMPARE( transform.toXY( QgsPointXY( 0, 0 ) ).x(), 0.0 ); + QCOMPARE( transform.toXY( QgsPointXY( 0, 0 ) ).y(), 0.0 ); + QCOMPARE( transform.toXY( QgsPointXY( 100, 0 ) ).x(), 100.0 ); + QCOMPARE( transform.toXY( QgsPointXY( 100, 0 ) ).y(), 0.0 ); + QCOMPARE( transform.toXY( QgsPointXY( 100, 200 ) ).x(), 100.0 ); + QCOMPARE( transform.toXY( QgsPointXY( 100, 200 ) ).y(), 200.0 ); + + QCOMPARE( transform.toColumnLine( QgsPointXY( 0, 0 ) ).x(), 0.0 ); + QCOMPARE( transform.toColumnLine( QgsPointXY( 0, 0 ) ).y(), 0.0 ); + QCOMPARE( transform.toColumnLine( QgsPointXY( 100, 0 ) ).x(), 100.0 ); + QCOMPARE( transform.toColumnLine( QgsPointXY( 100, 0 ) ).y(), 0.0 ); + QCOMPARE( transform.toColumnLine( QgsPointXY( 100, 200 ) ).x(), 100.0 ); + QCOMPARE( transform.toColumnLine( QgsPointXY( 100, 200 ) ).y(), 200.0 ); +} + QGSTEST_MAIN( TestQgsGeoreferencer ) #include "testqgsgeoreferencer.moc" From 457141874e3679afb552f07d5eb0604a621e1445 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 3 Feb 2022 13:42:26 +1000 Subject: [PATCH 07/47] Add tests for conversion of source coordinate to pixel --- src/app/georeferencer/qgsgeoreftransform.cpp | 5 +++ src/app/georeferencer/qgsgeoreftransform.h | 13 ++++++- tests/src/app/testqgsgeoreferencer.cpp | 40 ++++++++++++++++++-- 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/app/georeferencer/qgsgeoreftransform.cpp b/src/app/georeferencer/qgsgeoreftransform.cpp index ee9a94d545bc..98614aa2f2d3 100644 --- a/src/app/georeferencer/qgsgeoreftransform.cpp +++ b/src/app/georeferencer/qgsgeoreftransform.cpp @@ -58,6 +58,11 @@ void QgsGeorefTransform::setRasterChangeCoords( const QString &fileRaster ) mRasterChangeCoords.setRaster( fileRaster ); } +QgsPointXY QgsGeorefTransform::toSourceCoordinate( const QgsPointXY &pixel ) +{ + return mRasterChangeCoords.toXY( pixel ); +} + bool QgsGeorefTransform::providesAccurateInverseTransformation() const { return ( mTransformParametrisation == TransformMethod::Linear diff --git a/src/app/georeferencer/qgsgeoreftransform.h b/src/app/georeferencer/qgsgeoreftransform.h index d9b2507912d2..b0f4d3242f26 100644 --- a/src/app/georeferencer/qgsgeoreftransform.h +++ b/src/app/georeferencer/qgsgeoreftransform.h @@ -55,8 +55,17 @@ class APP_EXPORT QgsGeorefTransform : public QgsGcpTransformerInterface //! \returns Whether has image already has existing georeference bool hasExistingGeoreference() const { return mRasterChangeCoords.hasExistingGeoreference(); } - //! \returns Coordinates of image - QgsPointXY toColumnLine( const QgsPointXY &pntMap ) { return mRasterChangeCoords.toColumnLine( pntMap ); } + /** + * Returns the pixel coordinate from the source image given a layer coordinate from the source image. + * \see toSourceCoordinate() + */ + QgsPointXY toSourcePixel( const QgsPointXY &pntMap ) { return mRasterChangeCoords.toColumnLine( pntMap ); } + + /** + * Returns the layer coordinate from the source image given a pixel coordinate from the source image. + * \see toSourcePixel() + */ + QgsPointXY toSourceCoordinate( const QgsPointXY &pixel ); //! \returns Bounding box of image(transform to coordinate of Map or Image ) QgsRectangle getBoundingBox( const QgsRectangle &rect, bool toPixel ) { return mRasterChangeCoords.getBoundingBox( rect, toPixel ); } diff --git a/tests/src/app/testqgsgeoreferencer.cpp b/tests/src/app/testqgsgeoreferencer.cpp index 5b5bc0a2cf58..ad3d19b8ae37 100644 --- a/tests/src/app/testqgsgeoreferencer.cpp +++ b/tests/src/app/testqgsgeoreferencer.cpp @@ -40,7 +40,7 @@ class TestQgsGeoreferencer : public QObject void cleanupTestCase();// will be called after the last testfunction was executed. void init() {} // will be called before each testfunction is executed. void cleanup() {} // will be called after every testfunction. - void testTransformNoExistingImage(); + void testTransformImageNoGeoference(); void testTransformImageWithExistingGeoreference(); void testRasterChangeCoords(); @@ -66,16 +66,33 @@ void TestQgsGeoreferencer::cleanupTestCase() QgsApplication::exitQgis(); } -void TestQgsGeoreferencer::testTransformNoExistingImage() +void TestQgsGeoreferencer::testTransformImageNoGeoference() { QgsGeorefTransform transform( QgsGcpTransformerInterface::TransformMethod::Linear ); + // this image has no georeferencing set + transform.setRasterChangeCoords( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/rgb256x256.png" ) ); QVERIFY( !transform.hasExistingGeoreference() ); + QgsPointXY res; + // should be treating source coordinates and source pixels as identical + res = transform.toSourceCoordinate( QgsPointXY( 0, 0 ) ); + QCOMPARE( res.x(), 0.0 ); + QCOMPARE( res.y(), 0.0 ); + res = transform.toSourceCoordinate( QgsPointXY( 100, 200 ) ); + QCOMPARE( res.x(), 100.0 ); + QCOMPARE( res.y(), 200.0 ); + + res = transform.toSourcePixel( QgsPointXY( 0, 0 ) ); + QCOMPARE( res.x(), 0.0 ); + QCOMPARE( res.y(), 0.0 ); + res = transform.toSourcePixel( QgsPointXY( 100, 200 ) ); + QCOMPARE( res.x(), 100.0 ); + QCOMPARE( res.y(), 200.0 ); + QVERIFY( transform.updateParametersFromGcps( {QgsPointXY( 0, 0 ), QgsPointXY( 10, 0 ), QgsPointXY( 0, 30 ), QgsPointXY( 10, 30 )}, {QgsPointXY( 10, 5 ), QgsPointXY( 16, 5 ), QgsPointXY( 10, 8 ), QgsPointXY( 16, 8 )}, true ) ); - QgsPointXY res; QVERIFY( transform.transform( QgsPointXY( 0, 5 ), res, true ) ); QCOMPARE( res.x(), 10 ); QCOMPARE( res.y(), 5.5 ); @@ -103,7 +120,22 @@ void TestQgsGeoreferencer::testTransformImageWithExistingGeoreference() QGSCOMPARENEAR( transform.mRasterChangeCoords.mUL_X, 781662.375, 0.01 ); QGSCOMPARENEAR( transform.mRasterChangeCoords.mUL_Y, 3350923.125, 0.01 ); - QgsPointXY res = transform.mRasterChangeCoords.toColumnLine( QgsPointXY( 783414, 3350122 ) ); + QgsPointXY res; + res = transform.toSourceCoordinate( QgsPointXY( 0, 0 ) ); + QGSCOMPARENEAR( res.x(), 781662.375, 0.1 ); + QGSCOMPARENEAR( res.y(), 3350923.125, 0.1 ); + res = transform.toSourceCoordinate( QgsPointXY( 100, 200 ) ); + QGSCOMPARENEAR( res.x(), 787362.375, 0.1 ); + QGSCOMPARENEAR( res.y(), 3362323.125, 0.1 ); + + res = transform.toSourcePixel( QgsPointXY( 781662.375, 3350923.125 ) ); + QGSCOMPARENEAR( res.x(), 0.0, 0.1 ); + QGSCOMPARENEAR( res.y(), 0.0, 0.1 ); + res = transform.toSourcePixel( QgsPointXY( 787362.375, 3362323.125 ) ); + QGSCOMPARENEAR( res.x(), 100.0, 0.1 ); + QGSCOMPARENEAR( res.y(), 200.0, 0.1 ); + + res = transform.mRasterChangeCoords.toColumnLine( QgsPointXY( 783414, 3350122 ) ); QGSCOMPARENEAR( res.x(), 30.7302631579, 0.01 ); QGSCOMPARENEAR( res.y(), -14.0548245614, 0.01 ); res = transform.mRasterChangeCoords.toXY( QgsPointXY( 30.7302631579, -14.0548245614 ) ); From d51b6bcbdc19335d89e7b402536bf91a74dd5eec Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 3 Feb 2022 13:43:59 +1000 Subject: [PATCH 08/47] Rename method for clarity --- src/app/georeferencer/qgsgeorefmainwindow.cpp | 2 +- src/app/georeferencer/qgsgeoreftransform.cpp | 4 ++-- src/app/georeferencer/qgsgeoreftransform.h | 5 +++-- src/app/georeferencer/qgsrasterchangecoords.cpp | 2 +- src/app/georeferencer/qgsrasterchangecoords.h | 2 +- tests/src/app/testqgsgeoreferencer.cpp | 8 ++++---- 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index df558e9f0b83..9d7b74866d8a 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -261,7 +261,7 @@ void QgsGeoreferencerMainWindow::openRaster( const QString &fileName ) s.setValue( QStringLiteral( "/Plugin-GeoReferencer/rasterdirectory" ), fileInfo.path() ); mGeorefTransform.selectTransformParametrisation( mTransformParam ); - mGeorefTransform.setRasterChangeCoords( mRasterFileName ); + mGeorefTransform.loadRaster( mRasterFileName ); statusBar()->showMessage( tr( "Raster loaded: %1" ).arg( mRasterFileName ) ); setWindowTitle( tr( "Georeferencer - %1" ).arg( fileInfo.fileName() ) ); diff --git a/src/app/georeferencer/qgsgeoreftransform.cpp b/src/app/georeferencer/qgsgeoreftransform.cpp index 98614aa2f2d3..c64d660de4f8 100644 --- a/src/app/georeferencer/qgsgeoreftransform.cpp +++ b/src/app/georeferencer/qgsgeoreftransform.cpp @@ -53,9 +53,9 @@ void QgsGeorefTransform::selectTransformParametrisation( TransformMethod paramet } } -void QgsGeorefTransform::setRasterChangeCoords( const QString &fileRaster ) +void QgsGeorefTransform::loadRaster( const QString &fileRaster ) { - mRasterChangeCoords.setRaster( fileRaster ); + mRasterChangeCoords.loadRaster( fileRaster ); } QgsPointXY QgsGeorefTransform::toSourceCoordinate( const QgsPointXY &pixel ) diff --git a/src/app/georeferencer/qgsgeoreftransform.h b/src/app/georeferencer/qgsgeoreftransform.h index b0f4d3242f26..f8a935dafd4e 100644 --- a/src/app/georeferencer/qgsgeoreftransform.h +++ b/src/app/georeferencer/qgsgeoreftransform.h @@ -48,9 +48,10 @@ class APP_EXPORT QgsGeorefTransform : public QgsGcpTransformerInterface void selectTransformParametrisation( TransformMethod parametrisation ); /** - * Setting the mRasterChangeCoords for change type coordinate(map for pixel). + * Loads an existing raster image so that the source pixel to source layer conversion + * can be correctly initialized. */ - void setRasterChangeCoords( const QString &fileRaster ); + void loadRaster( const QString &fileRaster ); //! \returns Whether has image already has existing georeference bool hasExistingGeoreference() const { return mRasterChangeCoords.hasExistingGeoreference(); } diff --git a/src/app/georeferencer/qgsrasterchangecoords.cpp b/src/app/georeferencer/qgsrasterchangecoords.cpp index 3991d1642ad0..fc0257ffcbf4 100644 --- a/src/app/georeferencer/qgsrasterchangecoords.cpp +++ b/src/app/georeferencer/qgsrasterchangecoords.cpp @@ -22,7 +22,7 @@ #include -void QgsRasterChangeCoords::setRaster( const QString &fileRaster ) +void QgsRasterChangeCoords::loadRaster( const QString &fileRaster ) { GDALAllRegister(); const gdal::dataset_unique_ptr hDS( GDALOpen( fileRaster.toUtf8().constData(), GA_ReadOnly ) ); diff --git a/src/app/georeferencer/qgsrasterchangecoords.h b/src/app/georeferencer/qgsrasterchangecoords.h index d7e56dc860a1..87e74fb1b2c9 100644 --- a/src/app/georeferencer/qgsrasterchangecoords.h +++ b/src/app/georeferencer/qgsrasterchangecoords.h @@ -26,7 +26,7 @@ class APP_EXPORT QgsRasterChangeCoords { public: QgsRasterChangeCoords() = default; - void setRaster( const QString &fileRaster ); + void loadRaster( const QString &fileRaster ); bool hasExistingGeoreference() const { return mHasExistingGeoreference; } QVector getPixelCoords( const QVector &mapCoords ); QgsRectangle getBoundingBox( const QgsRectangle &rect, bool toPixel ); diff --git a/tests/src/app/testqgsgeoreferencer.cpp b/tests/src/app/testqgsgeoreferencer.cpp index ad3d19b8ae37..18d0b26bb888 100644 --- a/tests/src/app/testqgsgeoreferencer.cpp +++ b/tests/src/app/testqgsgeoreferencer.cpp @@ -70,7 +70,7 @@ void TestQgsGeoreferencer::testTransformImageNoGeoference() { QgsGeorefTransform transform( QgsGcpTransformerInterface::TransformMethod::Linear ); // this image has no georeferencing set - transform.setRasterChangeCoords( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/rgb256x256.png" ) ); + transform.loadRaster( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/rgb256x256.png" ) ); QVERIFY( !transform.hasExistingGeoreference() ); @@ -112,7 +112,7 @@ void TestQgsGeoreferencer::testTransformImageWithExistingGeoreference() { // load an image which is already georeferenced QgsGeorefTransform transform( QgsGcpTransformerInterface::TransformMethod::Linear ); - transform.setRasterChangeCoords( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/landsat.tif" ) ); + transform.loadRaster( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/landsat.tif" ) ); QVERIFY( transform.mRasterChangeCoords.mHasExistingGeoreference ); QGSCOMPARENEAR( transform.mRasterChangeCoords.mResX, 57, 0.00001 ); @@ -211,7 +211,7 @@ void TestQgsGeoreferencer::testRasterChangeCoords() QgsRasterChangeCoords transform; QVERIFY( !transform.hasExistingGeoreference() ); - transform.setRaster( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/landsat.tif" ) ); + transform.loadRaster( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/landsat.tif" ) ); QVERIFY( transform.hasExistingGeoreference() ); QGSCOMPARENEAR( transform.toXY( QgsPointXY( 0, 0 ) ).x(), 781662.375, 0.001 ); QGSCOMPARENEAR( transform.toXY( QgsPointXY( 0, 0 ) ).y(), 3350923.125, 0.001 ); @@ -228,7 +228,7 @@ void TestQgsGeoreferencer::testRasterChangeCoords() QGSCOMPARENEAR( transform.toColumnLine( QgsPointXY( 787362.375, 3362323.125 ) ).y(), 200.0, 0.0001 ); // load a raster with no georeferencing - transform.setRaster( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/rgb256x256.png" ) ); + transform.loadRaster( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/rgb256x256.png" ) ); QVERIFY( !transform.hasExistingGeoreference() ); // should be treat layer coordinates and pixels as identical QCOMPARE( transform.toXY( QgsPointXY( 0, 0 ) ).x(), 0.0 ); From 0f963eaf2c9cd0556cd9638b8b6f3c4d81e3f3d7 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 3 Feb 2022 14:00:33 +1000 Subject: [PATCH 09/47] Simplify call --- src/app/georeferencer/qgsgcplistmodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/georeferencer/qgsgcplistmodel.cpp b/src/app/georeferencer/qgsgcplistmodel.cpp index 2d584322e268..b73bfc38e031 100644 --- a/src/app/georeferencer/qgsgcplistmodel.cpp +++ b/src/app/georeferencer/qgsgcplistmodel.cpp @@ -147,7 +147,7 @@ void QgsGCPListModel::updateModel() if ( mGeorefTransform && bTransformUpdated && mGeorefTransform->parametersInitialized() ) { QgsPointXY dst; - const QgsPointXY pixel = mGeorefTransform->hasExistingGeoreference() ? mGeorefTransform->toColumnLine( p->sourceCoords() ) : p->sourceCoords(); + const QgsPointXY pixel = mGeorefTransform->toSourcePixel( p->sourceCoords() ); if ( unitType == tr( "pixels" ) ) { // Transform from world to raster coordinate: From 9c33bc70258cf0755dabbbb1fb33dcc8bc514b4f Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 3 Feb 2022 14:01:09 +1000 Subject: [PATCH 10/47] Add tests for source pixel to coord rect conversion, rename for clarity --- src/app/georeferencer/qgsgeorefmainwindow.cpp | 4 ++-- src/app/georeferencer/qgsgeoreftransform.h | 6 +++-- .../georeferencer/qgsrasterchangecoords.cpp | 5 ++++- src/app/georeferencer/qgsrasterchangecoords.h | 7 +++++- tests/src/app/testqgsgeoreferencer.cpp | 22 +++++++++++++++++++ 5 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index 9d7b74866d8a..e6f02bf7936b 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -789,7 +789,7 @@ void QgsGeoreferencerMainWindow::extentsChangedGeorefCanvas() } // Reproject the georeference plugin canvas into world coordinates and fit axis aligned bounding box - QgsRectangle rectMap = mGeorefTransform.hasExistingGeoreference() ? mGeorefTransform.getBoundingBox( mCanvas->extent(), true ) : mCanvas->extent(); + QgsRectangle rectMap = mGeorefTransform.transformSourceExtent( mCanvas->extent(), true ); QgsRectangle boundingBox = transformViewportBoundingBox( rectMap, mGeorefTransform, true ); mExtentsChangedRecursionGuard = true; @@ -820,7 +820,7 @@ void QgsGeoreferencerMainWindow::extentsChangedQGisCanvas() // Reproject the canvas into raster coordinates and fit axis aligned bounding box QgsRectangle boundingBox = transformViewportBoundingBox( QgisApp::instance()->mapCanvas()->extent(), mGeorefTransform, false ); - QgsRectangle rectMap = mGeorefTransform.hasExistingGeoreference() ? mGeorefTransform.getBoundingBox( boundingBox, false ) : boundingBox; + QgsRectangle rectMap = mGeorefTransform.transformSourceExtent( boundingBox, false ); mExtentsChangedRecursionGuard = true; // Just set the whole extent for now diff --git a/src/app/georeferencer/qgsgeoreftransform.h b/src/app/georeferencer/qgsgeoreftransform.h index f8a935dafd4e..b0e91f7ddc3a 100644 --- a/src/app/georeferencer/qgsgeoreftransform.h +++ b/src/app/georeferencer/qgsgeoreftransform.h @@ -68,8 +68,10 @@ class APP_EXPORT QgsGeorefTransform : public QgsGcpTransformerInterface */ QgsPointXY toSourceCoordinate( const QgsPointXY &pixel ); - //! \returns Bounding box of image(transform to coordinate of Map or Image ) - QgsRectangle getBoundingBox( const QgsRectangle &rect, bool toPixel ) { return mRasterChangeCoords.getBoundingBox( rect, toPixel ); } + /** + * Transforms a bounding box of the source image from source coordinates to source pixels or vice versa. + */ + QgsRectangle transformSourceExtent( const QgsRectangle &rect, bool toPixel ) { return mRasterChangeCoords.transformExtent( rect, toPixel ); } //! \brief The transform parametrisation currently in use. TransformMethod transformParametrisation() const; diff --git a/src/app/georeferencer/qgsrasterchangecoords.cpp b/src/app/georeferencer/qgsrasterchangecoords.cpp index fc0257ffcbf4..695712b6fc7d 100644 --- a/src/app/georeferencer/qgsrasterchangecoords.cpp +++ b/src/app/georeferencer/qgsrasterchangecoords.cpp @@ -52,8 +52,11 @@ QVector QgsRasterChangeCoords::getPixelCoords( const QVector getPixelCoords( const QVector &mapCoords ); - QgsRectangle getBoundingBox( const QgsRectangle &rect, bool toPixel ); + + /** + * Transforms a rectangle extent of the source image from source coordinates to source pixels or vice versa. + */ + QgsRectangle transformExtent( const QgsRectangle &rect, bool toPixel ); + QgsPointXY toColumnLine( const QgsPointXY &pntMap ); QgsPointXY toXY( const QgsPointXY &pntPixel ); diff --git a/tests/src/app/testqgsgeoreferencer.cpp b/tests/src/app/testqgsgeoreferencer.cpp index 18d0b26bb888..6df91dab14b5 100644 --- a/tests/src/app/testqgsgeoreferencer.cpp +++ b/tests/src/app/testqgsgeoreferencer.cpp @@ -90,6 +90,17 @@ void TestQgsGeoreferencer::testTransformImageNoGeoference() QCOMPARE( res.x(), 100.0 ); QCOMPARE( res.y(), 200.0 ); + QgsRectangle rect = transform.transformSourceExtent( QgsRectangle( 0, 0, 100, 200 ), true ); + QCOMPARE( rect.xMinimum(), 0.0 ); + QCOMPARE( rect.yMinimum(), 0.0 ); + QCOMPARE( rect.xMaximum(), 100.0 ); + QCOMPARE( rect.yMaximum(), 200.0 ); + rect = transform.transformSourceExtent( QgsRectangle( 0, 0, 100, 200 ), false ); + QCOMPARE( rect.xMinimum(), 0.0 ); + QCOMPARE( rect.yMinimum(), 0.0 ); + QCOMPARE( rect.xMaximum(), 100.0 ); + QCOMPARE( rect.yMaximum(), 200.0 ); + QVERIFY( transform.updateParametersFromGcps( {QgsPointXY( 0, 0 ), QgsPointXY( 10, 0 ), QgsPointXY( 0, 30 ), QgsPointXY( 10, 30 )}, {QgsPointXY( 10, 5 ), QgsPointXY( 16, 5 ), QgsPointXY( 10, 8 ), QgsPointXY( 16, 8 )}, true ) ); @@ -142,6 +153,17 @@ void TestQgsGeoreferencer::testTransformImageWithExistingGeoreference() QGSCOMPARENEAR( res.x(), 783414, 10 ); QGSCOMPARENEAR( res.y(), 3350122, 10 ); + QgsRectangle rect = transform.transformSourceExtent( QgsRectangle( 781662.375, 3350923.125, 787362.375, 3362323.125 ), true ); + QGSCOMPARENEAR( rect.xMinimum(), 0.0, 0.1 ); + QGSCOMPARENEAR( rect.yMinimum(), 0.0, 0.1 ); + QGSCOMPARENEAR( rect.xMaximum(), 100.0, 0.1 ); + QGSCOMPARENEAR( rect.yMaximum(), 200.0, 0.1 ); + rect = transform.transformSourceExtent( QgsRectangle( 0, 0, 100, 200 ), false ); + QGSCOMPARENEAR( rect.xMinimum(), 781662.375, 0.1 ); + QGSCOMPARENEAR( rect.yMinimum(), 3350923.125, 0.1 ); + QGSCOMPARENEAR( rect.xMaximum(), 787362.375, 0.1 ); + QGSCOMPARENEAR( rect.yMaximum(), 3362323.125, 0.1 ); + QVector pixelCoords = transform.mRasterChangeCoords.getPixelCoords( { QgsPointXY( 783414, 3350122 ), From 1e0f1c240a6369c99bd0ad1d6d8709e0b2e0c3ed Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 3 Feb 2022 14:21:54 +1000 Subject: [PATCH 11/47] Rename things to clarify that they aren't always pixel values --- src/app/georeferencer/qgsgeorefdatapoint.h | 11 +++++++++- src/app/georeferencer/qgsgeorefmainwindow.cpp | 18 +++++++-------- src/app/georeferencer/qgsgeorefmainwindow.h | 12 +++++++++- src/app/georeferencer/qgsgeoreftooladdpoint.h | 2 +- src/app/georeferencer/qgsmapcoordsdialog.cpp | 6 ++--- src/app/georeferencer/qgsmapcoordsdialog.h | 22 ++++++++++++++++--- 6 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/app/georeferencer/qgsgeorefdatapoint.h b/src/app/georeferencer/qgsgeorefdatapoint.h index d440c503cdee..80dea55c2bb2 100644 --- a/src/app/georeferencer/qgsgeorefdatapoint.h +++ b/src/app/georeferencer/qgsgeorefdatapoint.h @@ -26,7 +26,16 @@ class QgsGeorefDataPoint : public QObject Q_OBJECT public: - //! constructor + + /** + * Constructor for QgsGeorefDataPoint + * \param srcCanvas + * \param dstCanvas + * \param sourceCoordinates must be in source layer coordinates, NOT pixels (unless source image is completely non-referenced)! + * \param destinationMapCoords + * \param destinationCrs + * \param enable + */ QgsGeorefDataPoint( QgsMapCanvas *srcCanvas, QgsMapCanvas *dstCanvas, const QgsPointXY &sourceCoordinates, const QgsPointXY &destinationMapCoords, const QgsCoordinateReferenceSystem &destinationCrs, bool enable ); diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index e6f02bf7936b..60901c04f150 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -628,7 +628,7 @@ void QgsGeoreferencerMainWindow::releasePoint( QPoint p ) } } -void QgsGeoreferencerMainWindow::showCoordDialog( const QgsPointXY &pixelCoords ) +void QgsGeoreferencerMainWindow::showCoordDialog( const QgsPointXY &sourceCoordinates ) { delete mNewlyAddedPointItem; mNewlyAddedPointItem = nullptr; @@ -636,15 +636,15 @@ void QgsGeoreferencerMainWindow::showCoordDialog( const QgsPointXY &pixelCoords // show a temporary marker at the clicked source point on the raster while we show the coordinate dialog. mNewlyAddedPointItem = new QgsGCPCanvasItem( mCanvas, nullptr, true ); mNewlyAddedPointItem->setPointColor( QColor( 0, 200, 0 ) ); - mNewlyAddedPointItem->setPos( mNewlyAddedPointItem->toCanvasCoordinates( pixelCoords ) ); + mNewlyAddedPointItem->setPos( mNewlyAddedPointItem->toCanvasCoordinates( sourceCoordinates ) ); QgsCoordinateReferenceSystem lastProjection = mLastGCPProjection.isValid() ? mLastGCPProjection : mProjection; if ( mLayer && !mMapCoordsDialog ) { - mMapCoordsDialog = new QgsMapCoordsDialog( QgisApp::instance()->mapCanvas(), pixelCoords, lastProjection, this ); - connect( mMapCoordsDialog, &QgsMapCoordsDialog::pointAdded, this, [ = ]( const QgsPointXY & a, const QgsPointXY & destination, const QgsCoordinateReferenceSystem & destinationCrs ) + mMapCoordsDialog = new QgsMapCoordsDialog( QgisApp::instance()->mapCanvas(), sourceCoordinates, lastProjection, this ); + connect( mMapCoordsDialog, &QgsMapCoordsDialog::pointAdded, this, [ = ]( const QgsPointXY & sourceLayerCoordinate, const QgsPointXY & destinationCoordinate, const QgsCoordinateReferenceSystem & destinationCrs ) { - addPoint( a, destination, destinationCrs ); + addPoint( sourceLayerCoordinate, destinationCoordinate, destinationCrs ); } ); connect( mMapCoordsDialog, &QObject::destroyed, this, [ = ] { @@ -1251,14 +1251,14 @@ bool QgsGeoreferencerMainWindow::loadGCPs( /*bool verbose*/ ) QTextStream points( &pointFile ); QString line = points.readLine(); int i = 0; - QgsCoordinateReferenceSystem proj; - if ( line.contains( "#CRS: " ) ) + QgsCoordinateReferenceSystem destinationCrs; + if ( line.contains( QLatin1String( "#CRS: " ) ) ) { - proj = QgsCoordinateReferenceSystem( line.remove( "#CRS: " ) ); + destinationCrs = QgsCoordinateReferenceSystem( line.remove( QStringLiteral( "#CRS: " ) ) ); line = points.readLine(); } else - proj = QgsProject::instance()->crs(); + destinationCrs = QgsProject::instance()->crs(); while ( !points.atEnd() ) { diff --git a/src/app/georeferencer/qgsgeorefmainwindow.h b/src/app/georeferencer/qgsgeorefmainwindow.h index b89cd4994c53..2dc4b52cd67b 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.h +++ b/src/app/georeferencer/qgsgeorefmainwindow.h @@ -90,11 +90,21 @@ class QgsGeoreferencerMainWindow : public QMainWindow, private Ui::QgsGeorefPlug void linkQGisToGeoref( bool link ); // gcps + + /** + * Adds a new reference point. + * \param sourceCoords MUST be in source layer coordinates, e.g. if source is already georeferenced then it is in layer coordinates NOT pixels + * \param destinationMapCoords + * \param destinationCrs + * \param enable + * \param finalize + */ void addPoint( const QgsPointXY &sourceCoords, const QgsPointXY &destinationMapCoords, const QgsCoordinateReferenceSystem &destinationCrs, bool enable = true, bool finalize = true ); + void deleteDataPoint( QPoint pixelCoords ); void deleteDataPoint( int index ); - void showCoordDialog( const QgsPointXY &pixelCoords ); + void showCoordDialog( const QgsPointXY &sourceCoordinates ); void selectPoint( QPoint ); void movePoint( QPoint canvasPixels ); diff --git a/src/app/georeferencer/qgsgeoreftooladdpoint.h b/src/app/georeferencer/qgsgeoreftooladdpoint.h index 1bf4bc608ec2..1be56cbeb717 100644 --- a/src/app/georeferencer/qgsgeoreftooladdpoint.h +++ b/src/app/georeferencer/qgsgeoreftooladdpoint.h @@ -33,7 +33,7 @@ class QgsGeorefToolAddPoint : public QgsMapToolEmitPoint void canvasPressEvent( QgsMapMouseEvent *e ) override; signals: - void showCoordDialog( const QgsPointXY & ); + void showCoordDialog( const QgsPointXY &sourceCoordinates ); }; #endif // QGSGEOREFTOOLADDPOINT_H diff --git a/src/app/georeferencer/qgsmapcoordsdialog.cpp b/src/app/georeferencer/qgsmapcoordsdialog.cpp index ac9269d25c52..a0363d439ac7 100644 --- a/src/app/georeferencer/qgsmapcoordsdialog.cpp +++ b/src/app/georeferencer/qgsmapcoordsdialog.cpp @@ -26,11 +26,11 @@ #include "qgsproject.h" #include "qgsgcpcanvasitem.h" -QgsMapCoordsDialog::QgsMapCoordsDialog( QgsMapCanvas *qgisCanvas, const QgsPointXY &pixelCoords, QgsCoordinateReferenceSystem &rasterCrs, QWidget *parent ) +QgsMapCoordsDialog::QgsMapCoordsDialog( QgsMapCanvas *qgisCanvas, const QgsPointXY &sourceLayerCoordinates, QgsCoordinateReferenceSystem &rasterCrs, QWidget *parent ) : QDialog( parent, Qt::Dialog ) , mQgisCanvas( qgisCanvas ) , mRasterCrs( rasterCrs ) - , mPixelCoords( pixelCoords ) + , mSourceLayerCoordinates( sourceLayerCoordinates ) { setupUi( this ); QgsGui::enableAutoGeometryRestore( this ); @@ -103,7 +103,7 @@ void QgsMapCoordsDialog::buttonBox_accepted() if ( !ok ) y = dmsToDD( leYCoord->text() ); - emit pointAdded( mPixelCoords, QgsPointXY( x, y ), mProjectionSelector->crs().isValid() ? mProjectionSelector->crs() : mRasterCrs ); + emit pointAdded( mSourceLayerCoordinates, QgsPointXY( x, y ), mProjectionSelector->crs().isValid() ? mProjectionSelector->crs() : mRasterCrs ); close(); } diff --git a/src/app/georeferencer/qgsmapcoordsdialog.h b/src/app/georeferencer/qgsmapcoordsdialog.h index 5e82c094bff4..e72ba08ade36 100644 --- a/src/app/georeferencer/qgsmapcoordsdialog.h +++ b/src/app/georeferencer/qgsmapcoordsdialog.h @@ -60,7 +60,15 @@ class QgsMapCoordsDialog : public QDialog, private Ui::QgsMapCoordsDialogBase Q_OBJECT public: - QgsMapCoordsDialog( QgsMapCanvas *qgisCanvas, const QgsPointXY &pixelCoords, QgsCoordinateReferenceSystem &rasterCrs, QWidget *parent = nullptr ); + + /** + * Constructor for QgsMapCoordsDialog. + * \param qgisCanvas + * \param sourceCoordinates must be in source layer coordinates, NOT pixels (unless source image is completely non-referenced)! + * \param rasterCrs + * \param parent + */ + QgsMapCoordsDialog( QgsMapCanvas *qgisCanvas, const QgsPointXY &sourceCoordinates, QgsCoordinateReferenceSystem &rasterCrs, QWidget *parent = nullptr ); ~QgsMapCoordsDialog() override; private slots: @@ -73,7 +81,14 @@ class QgsMapCoordsDialog : public QDialog, private Ui::QgsMapCoordsDialogBase void setPrevTool(); signals: - void pointAdded( const QgsPointXY &a, const QgsPointXY &destination, const QgsCoordinateReferenceSystem &destinationCrs ); + + /** + * Emitted when a point should be added through the dialog. + * \param sourceCoordinate source point, which MUST be in source layer coordinates not pixels + * \param destinationCoordinate + * \param destinationCrs + */ + void pointAdded( const QgsPointXY &sourceCoordinate, const QgsPointXY &destinationCoordinate, const QgsCoordinateReferenceSystem &destinationCrs ); private: double dmsToDD( const QString &dms ); @@ -90,7 +105,8 @@ class QgsMapCoordsDialog : public QDialog, private Ui::QgsMapCoordsDialogBase QgsCoordinateReferenceSystem mRasterCrs; - QgsPointXY mPixelCoords; + //! Source layer coordinates -- must be in source layer coordinates, not pixels (unless source image is completely non-referenced) + QgsPointXY mSourceLayerCoordinates; }; #endif From 0640d001d895c46f57f72c9eaf460540518e0f2f Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 3 Feb 2022 14:22:32 +1000 Subject: [PATCH 12/47] Fix loading existing GCPs for images which are already referenced results in incorrect placement of source points --- src/app/georeferencer/qgsgeorefmainwindow.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index 60901c04f150..4c7c34e66141 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -1272,18 +1272,16 @@ bool QgsGeoreferencerMainWindow::loadGCPs( /*bool verbose*/ ) if ( ls.count() < 4 ) return false; - QgsPointXY mapCoords( ls.at( 0 ).toDouble(), ls.at( 1 ).toDouble() ); // map x,y - QgsPointXY pixelCoords( ls.at( 2 ).toDouble(), ls.at( 3 ).toDouble() ); // pixel x,y - - // TO FIX -- pixelCoord needs to be converted to sourceCoord, i.e. if source is georeferenced we need to convert to layer coordinates from pixels!!!! - + const QgsPointXY destinationCoordinate( ls.at( 0 ).toDouble(), ls.at( 1 ).toDouble() ); // map x,y + const QgsPointXY pixelCoords( ls.at( 2 ).toDouble(), ls.at( 3 ).toDouble() ); // pixel x,y + const QgsPointXY sourceLayerCoordinate = mGeorefTransform.toSourceCoordinate( pixelCoords ); if ( ls.count() == 5 ) { bool enable = ls.at( 4 ).toInt(); - addPoint( pixelCoords, mapCoords, proj, enable, false ); + addPoint( sourceLayerCoordinate, destinationCoordinate, destinationCrs, enable, false ); } else - addPoint( pixelCoords, mapCoords, proj, true, false ); + addPoint( sourceLayerCoordinate, destinationCoordinate, destinationCrs, true, false ); ++i; } From fbd3cface07729e57e26a9cc9cea73b87b18c21b Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 3 Feb 2022 14:44:26 +1000 Subject: [PATCH 13/47] Remove fragile cached transformed destination point -- this needlessly complicates the code --- src/app/georeferencer/qgsgcpcanvasitem.cpp | 11 ++++++----- src/app/georeferencer/qgsgeorefdatapoint.cpp | 12 ------------ src/app/georeferencer/qgsgeorefdatapoint.h | 15 --------------- src/app/georeferencer/qgsgeorefmainwindow.cpp | 1 - 4 files changed, 6 insertions(+), 33 deletions(-) diff --git a/src/app/georeferencer/qgsgcpcanvasitem.cpp b/src/app/georeferencer/qgsgcpcanvasitem.cpp index 76fbf1c676b6..c61db7ada8d2 100644 --- a/src/app/georeferencer/qgsgcpcanvasitem.cpp +++ b/src/app/georeferencer/qgsgcpcanvasitem.cpp @@ -167,15 +167,16 @@ void QgsGCPCanvasItem::updatePosition() } else { - if ( mDataPoint->destinationInCanvasPixels().isEmpty() ) + const QgsCoordinateTransform pointToCanvasTransform( mDataPoint->destinationCrs(), mMapCanvas->mapSettings().destinationCrs(), QgsProject::instance() ); + try { - const QgsCoordinateReferenceSystem canvasCrs = mMapCanvas->mapSettings().destinationCrs(); - const QgsCoordinateTransform pointToCanvasTransform( mDataPoint->destinationCrs(), canvasCrs, QgsProject::instance() ); const QgsPointXY canvasMapCoords = pointToCanvasTransform.transform( mDataPoint->destinationMapCoords() ); const QPointF canvasCoordinatesInPixels = toCanvasCoordinates( canvasMapCoords ); - mDataPoint->setDestinationInCanvasPixels( canvasCoordinatesInPixels ); + + setPos( canvasCoordinatesInPixels ); } - setPos( mDataPoint->destinationInCanvasPixels().toQPointF() ); + catch ( QgsCsException & ) + {} } } diff --git a/src/app/georeferencer/qgsgeorefdatapoint.cpp b/src/app/georeferencer/qgsgeorefdatapoint.cpp index d679556554ba..180e74bd6964 100644 --- a/src/app/georeferencer/qgsgeorefdatapoint.cpp +++ b/src/app/georeferencer/qgsgeorefdatapoint.cpp @@ -52,7 +52,6 @@ QgsGeorefDataPoint::QgsGeorefDataPoint( const QgsGeorefDataPoint &p ) mTransCoords = p.transCoords(); mEnabled = p.isEnabled(); mResidual = p.residual(); - mDestinationInCanvasPixels = p.destinationInCanvasPixels(); mDestinationCrs = p.destinationCrs(); mId = p.id(); } @@ -101,16 +100,6 @@ QgsPointXY QgsGeorefDataPoint::transCoords() const return mTransCoords.isEmpty() ? mDestinationMapCoords : mTransCoords; } -void QgsGeorefDataPoint::setDestinationInCanvasPixels( const QgsPointXY &p ) -{ - mDestinationInCanvasPixels = p; -} - -QgsPointXY QgsGeorefDataPoint::destinationInCanvasPixels() const -{ - return mDestinationInCanvasPixels; -} - void QgsGeorefDataPoint::setEnabled( bool enabled ) { mEnabled = enabled; @@ -179,7 +168,6 @@ void QgsGeorefDataPoint::moveTo( QPoint canvasPixels, bool isMapPlugin ) } else { - mDestinationInCanvasPixels = canvasPixels; mDestinationMapCoords = mGCPDestinationItem->toMapCoordinates( canvasPixels ); if ( mSrcCanvas && mSrcCanvas->mapSettings().destinationCrs().isValid() ) mDestinationCrs = mSrcCanvas->mapSettings().destinationCrs(); diff --git a/src/app/georeferencer/qgsgeorefdatapoint.h b/src/app/georeferencer/qgsgeorefdatapoint.h index 80dea55c2bb2..5600e73cdb43 100644 --- a/src/app/georeferencer/qgsgeorefdatapoint.h +++ b/src/app/georeferencer/qgsgeorefdatapoint.h @@ -56,18 +56,6 @@ class QgsGeorefDataPoint : public QObject QgsPointXY transCoords() const; void setTransCoords( const QgsPointXY &p ); - /** - * Returns the destination point in canvas coordinates (i.e. pixels). - * - * May be an empty point if not yet calculated. - */ - QgsPointXY destinationInCanvasPixels() const; - - /** - * Sets the destination point in canvas coordinates (i.e. pixels). - */ - void setDestinationInCanvasPixels( const QgsPointXY &p ); - bool isEnabled() const { return mEnabled; } void setEnabled( bool enabled ); @@ -97,9 +85,6 @@ class QgsGeorefDataPoint : public QObject QgsPointXY mDestinationMapCoords; QgsPointXY mTransCoords; - // destination point converted to canvas coordinates (i.e. pixels) - QgsPointXY mDestinationInCanvasPixels; - int mId; QgsCoordinateReferenceSystem mDestinationCrs; bool mEnabled; diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index 4c7c34e66141..8532c052a550 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -2193,7 +2193,6 @@ void QgsGeoreferencerMainWindow::invalidateCanvasCoords() for ( int i = 0; i < count; ++i, ++j ) { QgsGeorefDataPoint *p = mPoints.at( i ); - p->setDestinationInCanvasPixels( QgsPointXY() ); p->updateCoords(); } } From 9acb8ec7f4e3ebd3c9fa1bbf3b3aec41f2779f86 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 3 Feb 2022 14:45:48 +1000 Subject: [PATCH 14/47] Const --- src/app/georeferencer/qgsgeoreftransform.cpp | 2 +- src/app/georeferencer/qgsgeoreftransform.h | 6 +++--- src/app/georeferencer/qgsrasterchangecoords.cpp | 11 +++++------ src/app/georeferencer/qgsrasterchangecoords.h | 8 ++++---- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/app/georeferencer/qgsgeoreftransform.cpp b/src/app/georeferencer/qgsgeoreftransform.cpp index c64d660de4f8..a06d008d61ff 100644 --- a/src/app/georeferencer/qgsgeoreftransform.cpp +++ b/src/app/georeferencer/qgsgeoreftransform.cpp @@ -58,7 +58,7 @@ void QgsGeorefTransform::loadRaster( const QString &fileRaster ) mRasterChangeCoords.loadRaster( fileRaster ); } -QgsPointXY QgsGeorefTransform::toSourceCoordinate( const QgsPointXY &pixel ) +QgsPointXY QgsGeorefTransform::toSourceCoordinate( const QgsPointXY &pixel ) const { return mRasterChangeCoords.toXY( pixel ); } diff --git a/src/app/georeferencer/qgsgeoreftransform.h b/src/app/georeferencer/qgsgeoreftransform.h index b0e91f7ddc3a..2bbf93cf6a76 100644 --- a/src/app/georeferencer/qgsgeoreftransform.h +++ b/src/app/georeferencer/qgsgeoreftransform.h @@ -60,18 +60,18 @@ class APP_EXPORT QgsGeorefTransform : public QgsGcpTransformerInterface * Returns the pixel coordinate from the source image given a layer coordinate from the source image. * \see toSourceCoordinate() */ - QgsPointXY toSourcePixel( const QgsPointXY &pntMap ) { return mRasterChangeCoords.toColumnLine( pntMap ); } + QgsPointXY toSourcePixel( const QgsPointXY &pntMap ) const { return mRasterChangeCoords.toColumnLine( pntMap ); } /** * Returns the layer coordinate from the source image given a pixel coordinate from the source image. * \see toSourcePixel() */ - QgsPointXY toSourceCoordinate( const QgsPointXY &pixel ); + QgsPointXY toSourceCoordinate( const QgsPointXY &pixel ) const; /** * Transforms a bounding box of the source image from source coordinates to source pixels or vice versa. */ - QgsRectangle transformSourceExtent( const QgsRectangle &rect, bool toPixel ) { return mRasterChangeCoords.transformExtent( rect, toPixel ); } + QgsRectangle transformSourceExtent( const QgsRectangle &rect, bool toPixel ) const { return mRasterChangeCoords.transformExtent( rect, toPixel ); } //! \brief The transform parametrisation currently in use. TransformMethod transformParametrisation() const; diff --git a/src/app/georeferencer/qgsrasterchangecoords.cpp b/src/app/georeferencer/qgsrasterchangecoords.cpp index 695712b6fc7d..8b2fe2d229c2 100644 --- a/src/app/georeferencer/qgsrasterchangecoords.cpp +++ b/src/app/georeferencer/qgsrasterchangecoords.cpp @@ -41,7 +41,7 @@ void QgsRasterChangeCoords::loadRaster( const QString &fileRaster ) } } -QVector QgsRasterChangeCoords::getPixelCoords( const QVector &mapCoords ) +QVector QgsRasterChangeCoords::getPixelCoords( const QVector &mapCoords ) const { const int size = mapCoords.size(); QVector pixelCoords( size ); @@ -52,7 +52,7 @@ QVector QgsRasterChangeCoords::getPixelCoords( const QVector*func )( p1 ), ( this->*func )( p2 ) ); return rectReturn; } -QgsPointXY QgsRasterChangeCoords::toColumnLine( const QgsPointXY &pntMap ) +QgsPointXY QgsRasterChangeCoords::toColumnLine( const QgsPointXY &pntMap ) const { if ( ! mHasExistingGeoreference ) return QgsPointXY( pntMap.x(), pntMap.y() ); @@ -78,7 +77,7 @@ QgsPointXY QgsRasterChangeCoords::toColumnLine( const QgsPointXY &pntMap ) return QgsPointXY( col, line ); } -QgsPointXY QgsRasterChangeCoords::toXY( const QgsPointXY &pntPixel ) +QgsPointXY QgsRasterChangeCoords::toXY( const QgsPointXY &pntPixel ) const { if ( ! mHasExistingGeoreference ) return QgsPointXY( pntPixel.x(), pntPixel.y() ); diff --git a/src/app/georeferencer/qgsrasterchangecoords.h b/src/app/georeferencer/qgsrasterchangecoords.h index 8924267e758e..668aff8ff81e 100644 --- a/src/app/georeferencer/qgsrasterchangecoords.h +++ b/src/app/georeferencer/qgsrasterchangecoords.h @@ -28,15 +28,15 @@ class APP_EXPORT QgsRasterChangeCoords QgsRasterChangeCoords() = default; void loadRaster( const QString &fileRaster ); bool hasExistingGeoreference() const { return mHasExistingGeoreference; } - QVector getPixelCoords( const QVector &mapCoords ); + QVector getPixelCoords( const QVector &mapCoords ) const; /** * Transforms a rectangle extent of the source image from source coordinates to source pixels or vice versa. */ - QgsRectangle transformExtent( const QgsRectangle &rect, bool toPixel ); + QgsRectangle transformExtent( const QgsRectangle &rect, bool toPixel ) const; - QgsPointXY toColumnLine( const QgsPointXY &pntMap ); - QgsPointXY toXY( const QgsPointXY &pntPixel ); + QgsPointXY toColumnLine( const QgsPointXY &pntMap ) const; + QgsPointXY toXY( const QgsPointXY &pntPixel ) const; private: bool mHasExistingGeoreference = false; From d5575b690214e47bed4848157fa6cbbceca33897 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 3 Feb 2022 15:03:20 +1000 Subject: [PATCH 15/47] When calling QgsGeorefTransform::updateParametersFromGcps the source points must ALWAYS be in source layer coordinates --- src/app/georeferencer/qgsgcplist.cpp | 15 +++++++-------- src/app/georeferencer/qgsgcplist.h | 2 +- src/app/georeferencer/qgsgcplistmodel.cpp | 7 ++++--- src/app/georeferencer/qgsgeorefmainwindow.cpp | 7 ++++--- src/app/georeferencer/qgsgeoreftransform.cpp | 4 ++-- tests/src/app/testqgsgeoreferencer.cpp | 9 +-------- 6 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/app/georeferencer/qgsgcplist.cpp b/src/app/georeferencer/qgsgcplist.cpp index df199de0c8c9..2701d8d6cba2 100644 --- a/src/app/georeferencer/qgsgcplist.cpp +++ b/src/app/georeferencer/qgsgcplist.cpp @@ -33,10 +33,10 @@ QgsGCPList::QgsGCPList( const QgsGCPList &list ) } } -void QgsGCPList::createGCPVectors( QVector &mapCoords, QVector &pixelCoords, const QgsCoordinateReferenceSystem &targetCrs ) +void QgsGCPList::createGCPVectors( QVector &sourceCoordinates, QVector &destinationCoordinates, const QgsCoordinateReferenceSystem &targetCrs ) { - mapCoords = QVector( size() ); - pixelCoords = QVector( size() ); + sourceCoordinates = QVector( size() ); + destinationCoordinates = QVector( size() ); QgsPointXY transCoords; for ( int i = 0, j = 0; i < sizeAll(); i++ ) { @@ -49,19 +49,18 @@ void QgsGCPList::createGCPVectors( QVector &mapCoords, QVectordestinationCrs(), targetCrs, QgsProject::instance() ).transform( pt->destinationMapCoords() ); - mapCoords[j] = transCoords; + destinationCoordinates[j] = transCoords; pt->setTransCoords( transCoords ); } catch ( const QgsException & ) { - mapCoords[j] = pt->destinationMapCoords(); + destinationCoordinates[j] = pt->destinationMapCoords(); } } else - mapCoords[j] = pt->destinationMapCoords(); + destinationCoordinates[j] = pt->destinationMapCoords(); - // TODO -- this must be converted to pixels!!! - pixelCoords[j] = pt->sourceCoords(); + sourceCoordinates[j] = pt->sourceCoords(); j++; } } diff --git a/src/app/georeferencer/qgsgcplist.h b/src/app/georeferencer/qgsgcplist.h index 8e6a565eab8c..81c393003a14 100644 --- a/src/app/georeferencer/qgsgcplist.h +++ b/src/app/georeferencer/qgsgcplist.h @@ -30,7 +30,7 @@ class QgsGCPList : public QList QgsGCPList() = default; QgsGCPList( const QgsGCPList &list ); - void createGCPVectors( QVector &mapCoords, QVector &pixelCoords, const QgsCoordinateReferenceSystem &targetCrs ); + void createGCPVectors( QVector &sourceCoordinates, QVector &destinationCoordinates, const QgsCoordinateReferenceSystem &targetCrs ); int size() const; int sizeAll() const; diff --git a/src/app/georeferencer/qgsgcplistmodel.cpp b/src/app/georeferencer/qgsgcplistmodel.cpp index b73bfc38e031..058f06a48d42 100644 --- a/src/app/georeferencer/qgsgcplistmodel.cpp +++ b/src/app/georeferencer/qgsgcplistmodel.cpp @@ -82,14 +82,15 @@ void QgsGCPListModel::updateModel() QString unitType; const QgsSettings s; bool mapUnitsPossible = false; - QVector mapCoords, pixelCoords; + QVector sourceCoordinates; + QVector destinationCoordinates; - mGCPList->createGCPVectors( mapCoords, pixelCoords, + mGCPList->createGCPVectors( sourceCoordinates, destinationCoordinates, QgsCoordinateReferenceSystem( s.value( QStringLiteral( "/Plugin-GeoReferencer/targetsrs" ) ).toString() ) ); if ( mGeorefTransform ) { - bTransformUpdated = mGeorefTransform->updateParametersFromGcps( pixelCoords, mapCoords, true ); + bTransformUpdated = mGeorefTransform->updateParametersFromGcps( sourceCoordinates, destinationCoordinates, true ); mapUnitsPossible = mGeorefTransform->providesAccurateInverseTransformation(); } diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index 8532c052a550..fcb42a95e992 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -2000,14 +2000,15 @@ bool QgsGeoreferencerMainWindow::checkReadyGeoref() bool QgsGeoreferencerMainWindow::updateGeorefTransform() { - QVector mapCoords, pixelCoords; + QVector sourceCoordinates; + QVector destinationCoords; if ( mGCPListWidget->gcpList() ) - mGCPListWidget->gcpList()->createGCPVectors( mapCoords, pixelCoords, mProjection ); + mGCPListWidget->gcpList()->createGCPVectors( sourceCoordinates, destinationCoords, mProjection ); else return false; // Parametrize the transform with GCPs - if ( !mGeorefTransform.updateParametersFromGcps( pixelCoords, mapCoords, true ) ) + if ( !mGeorefTransform.updateParametersFromGcps( sourceCoordinates, destinationCoords, true ) ) { return false; } diff --git a/src/app/georeferencer/qgsgeoreftransform.cpp b/src/app/georeferencer/qgsgeoreftransform.cpp index a06d008d61ff..aba6313c8914 100644 --- a/src/app/georeferencer/qgsgeoreftransform.cpp +++ b/src/app/georeferencer/qgsgeoreftransform.cpp @@ -102,8 +102,8 @@ bool QgsGeorefTransform::updateParametersFromGcps( const QVector &so } if ( mRasterChangeCoords.hasExistingGeoreference() ) { - const QVector pixelCoordsCorrect = mRasterChangeCoords.getPixelCoords( sourceCoordinates ); - mParametersInitialized = mGeorefTransformImplementation->updateParametersFromGcps( sourceCoordinates, pixelCoordsCorrect, invertYAxis ); + const QVector sourcePixelCoordinates = mRasterChangeCoords.getPixelCoords( sourceCoordinates ); + mParametersInitialized = mGeorefTransformImplementation->updateParametersFromGcps( sourcePixelCoordinates, destinationCoordinates, invertYAxis ); } else { diff --git a/tests/src/app/testqgsgeoreferencer.cpp b/tests/src/app/testqgsgeoreferencer.cpp index 6df91dab14b5..1554a7d2bdae 100644 --- a/tests/src/app/testqgsgeoreferencer.cpp +++ b/tests/src/app/testqgsgeoreferencer.cpp @@ -184,13 +184,7 @@ void TestQgsGeoreferencer::testTransformImageWithExistingGeoreference() QVERIFY( transform.hasExistingGeoreference() ); - // currently disabled -- it is ambiguous whether calling updateParametersFromGcps should be using PIXEL coordinates as source or layer CRS coordinates, and is quite broken either - // way. Here the disabled tests assume layer CRS coordinates, but in the actual georeferencer a mix of pixel/layer coordinates are used. - // it would be better if all calls to updateParametersFromGcps ALWAYS use pixel coordinates to avoid the confusion, with the caller transforming from layer CRS coordinates - // back to pixel coordinates before calling this method. -#if 0 - // source coordinates here should be raster CRS - ie. 32633 - // first use a "null" transform + // when calling updateParametersFromGcps the source list MUST be in source layer CRS, not pixels! QVERIFY( transform.updateParametersFromGcps( {QgsPointXY( 783414, 3350122 ), QgsPointXY( 791344, 3349795 ), QgsPointXY( 783077, 334093 ), QgsPointXY( 791134, 3341401 )}, {QgsPointXY( 783414, 3350122 ), QgsPointXY( 791344, 3349795 ), QgsPointXY( 783077, 334093 ), QgsPointXY( 791134, 3341401 )}, true ) ); @@ -225,7 +219,6 @@ void TestQgsGeoreferencer::testTransformImageWithExistingGeoreference() QVERIFY( transform.transform( QgsPointXY( 791234, 3341601 ), res, false ) ); QGSCOMPARENEAR( res.x(), 166.168859649, 0.1 ); QGSCOMPARENEAR( res.y(), -167.0548245614, 0.1 ); -#endif } void TestQgsGeoreferencer::testRasterChangeCoords() From a198bf4158f2d80c06cae773349ee15a0a7f455f Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 3 Feb 2022 15:09:24 +1000 Subject: [PATCH 16/47] Saving gcps should ALWAYS use source pixel coordinates --- src/app/georeferencer/qgsgeorefmainwindow.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index fcb42a95e992..149543921a54 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -1309,11 +1309,13 @@ void QgsGeoreferencerMainWindow::saveGCPs() points << "mapX,mapY,pixelX,pixelY,enable,dX,dY,residual" << endl; for ( QgsGeorefDataPoint *pt : std::as_const( mPoints ) ) { + const QgsPointXY sourcePixel = mGeorefTransform.toSourcePixel( pt->sourceCoords() ); + points << QStringLiteral( "%1,%2,%3,%4,%5,%6,%7,%8" ) .arg( qgsDoubleToString( pt->transCoords().x() ), qgsDoubleToString( pt->transCoords().y() ), - qgsDoubleToString( pt->sourceCoords().x() ), - qgsDoubleToString( pt->sourceCoords().y() ) ) + qgsDoubleToString( sourcePixel.x() ), + qgsDoubleToString( sourcePixel.y() ) ) .arg( pt->isEnabled() ) .arg( qgsDoubleToString( pt->residual().x() ), qgsDoubleToString( pt->residual().y() ), From 2300217045e0681d1df2715e9a11a128ee3164a1 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 3 Feb 2022 15:24:33 +1000 Subject: [PATCH 17/47] Don't misuse a string as a bool --- src/app/georeferencer/qgsgeorefmainwindow.cpp | 2 +- src/app/georeferencer/qgsgeorefmainwindow.h | 2 +- src/app/georeferencer/qgstransformsettingsdialog.cpp | 7 ++----- src/app/georeferencer/qgstransformsettingsdialog.h | 2 +- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index 149543921a54..b0757b8f118e 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -1442,7 +1442,7 @@ bool QgsGeoreferencerMainWindow::georeference() { writePDFMapFile( mPdfOutputMapFile, mGeorefTransform ); } - if ( !mSaveGcp.isEmpty() ) + if ( mSaveGcp ) { mGCPpointsFileName = mModifiedRasterFileName + QLatin1String( ".points" ); saveGCPs(); diff --git a/src/app/georeferencer/qgsgeorefmainwindow.h b/src/app/georeferencer/qgsgeorefmainwindow.h index 2dc4b52cd67b..237b4ee34ccf 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.h +++ b/src/app/georeferencer/qgsgeorefmainwindow.h @@ -236,7 +236,7 @@ class QgsGeoreferencerMainWindow : public QMainWindow, private Ui::QgsGeorefPlug QgsCoordinateReferenceSystem mLastGCPProjection; QString mPdfOutputFile; QString mPdfOutputMapFile; - QString mSaveGcp; + bool mSaveGcp = false; double mUserResX, mUserResY; // User specified target scale QgsGcpTransformerInterface::TransformMethod mTransformParam = QgsGcpTransformerInterface::TransformMethod::InvalidTransform; diff --git a/src/app/georeferencer/qgstransformsettingsdialog.cpp b/src/app/georeferencer/qgstransformsettingsdialog.cpp index 2134437fde14..78f66f6ab0a9 100644 --- a/src/app/georeferencer/qgstransformsettingsdialog.cpp +++ b/src/app/georeferencer/qgstransformsettingsdialog.cpp @@ -131,7 +131,7 @@ QgsTransformSettingsDialog::QgsTransformSettingsDialog( const QString &raster, c void QgsTransformSettingsDialog::getTransformSettings( QgsGeorefTransform::TransformMethod &tp, QgsImageWarper::ResamplingMethod &rm, QString &comprMethod, QString &raster, - QgsCoordinateReferenceSystem &proj, QString &pdfMapFile, QString &pdfReportFile, QString &gcpPoints, bool &zt, bool &loadInQgis, + QgsCoordinateReferenceSystem &proj, QString &pdfMapFile, QString &pdfReportFile, bool &saveGcpPoints, bool &zt, bool &loadInQgis, double &resX, double &resY ) { if ( cmbTransformType->currentIndex() == -1 ) @@ -161,10 +161,7 @@ void QgsTransformSettingsDialog::getTransformSettings( QgsGeorefTransform::Trans resX = dsbHorizRes->value(); resY = dsbVerticalRes->value(); } - if ( saveGcpCheckBox->isChecked() ) - { - gcpPoints = mOutputRaster->filePath(); - } + saveGcpPoints = saveGcpCheckBox->isChecked(); } void QgsTransformSettingsDialog::resetSettings() diff --git a/src/app/georeferencer/qgstransformsettingsdialog.h b/src/app/georeferencer/qgstransformsettingsdialog.h index 41b52599fd9e..d9dbf376d7da 100644 --- a/src/app/georeferencer/qgstransformsettingsdialog.h +++ b/src/app/georeferencer/qgstransformsettingsdialog.h @@ -32,7 +32,7 @@ class QgsTransformSettingsDialog : public QDialog, private Ui::QgsTransformSetti void getTransformSettings( QgsGeorefTransform::TransformMethod &tp, QgsImageWarper::ResamplingMethod &rm, QString &comprMethod, - QString &raster, QgsCoordinateReferenceSystem &proj, QString &pdfMapFile, QString &pdfReportFile, QString &gcpPoints, bool &zt, bool &loadInQgis, + QString &raster, QgsCoordinateReferenceSystem &proj, QString &pdfMapFile, QString &pdfReportFile, bool &saveGcpPoints, bool &zt, bool &loadInQgis, double &resX, double &resY ); static void resetSettings(); From 7a2389065cc280ea328df73b225b4bec3b00885c Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 3 Feb 2022 15:24:40 +1000 Subject: [PATCH 18/47] When autosaving the georeferencer gcp points, we MUST use the original raster file name for these and not the output raster file name The GCPs relate specifically to the ORIGINAL image, not the warped output exported after georeferencing. If we use the output file name for the .points file then when THIS georeferenced output is loaded into the georeferencer we get misleading location of source GCP points, as the pixel coordinates are from the ORIGINAL image, not the warped one. --- src/app/georeferencer/qgsgeorefmainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index b0757b8f118e..624e99b08de8 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -1444,7 +1444,7 @@ bool QgsGeoreferencerMainWindow::georeference() } if ( mSaveGcp ) { - mGCPpointsFileName = mModifiedRasterFileName + QLatin1String( ".points" ); + mGCPpointsFileName = mRasterFileName + QLatin1String( ".points" ); saveGCPs(); } return true; From f852443205c69916d220e0b55a3549f16c0c89cb Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 3 Feb 2022 16:32:51 +1000 Subject: [PATCH 19/47] Fix incorrect display of residual lines for previously georeferenced images --- src/app/georeferencer/qgsgcpcanvasitem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/georeferencer/qgsgcpcanvasitem.cpp b/src/app/georeferencer/qgsgcpcanvasitem.cpp index c61db7ada8d2..9d07aa7dd974 100644 --- a/src/app/georeferencer/qgsgcpcanvasitem.cpp +++ b/src/app/georeferencer/qgsgcpcanvasitem.cpp @@ -220,7 +220,7 @@ double QgsGCPCanvasItem::residualToScreenFactor() const } } - return 1.0 / ( mapUnitsPerScreenPixel * mapUnitsPerRasterPixel ); + return mapUnitsPerRasterPixel / mapUnitsPerScreenPixel; } void QgsGCPCanvasItem::checkBoundingRectChange() From 0ce7c5f6ab0edb26b686dbd10ffc8139fe90f8b9 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 8 Feb 2022 09:21:24 +1000 Subject: [PATCH 20/47] Remove unused method/member --- src/app/georeferencer/qgsgeorefmainwindow.cpp | 2 +- src/app/georeferencer/qgstransformsettingsdialog.cpp | 12 +----------- src/app/georeferencer/qgstransformsettingsdialog.h | 6 +----- 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index 624e99b08de8..51c9de777802 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -370,7 +370,7 @@ void QgsGeoreferencerMainWindow::doGeoreference() bool QgsGeoreferencerMainWindow::getTransformSettings() { - QgsTransformSettingsDialog d( mRasterFileName, mModifiedRasterFileName, mPoints.size() ); + QgsTransformSettingsDialog d( mRasterFileName, mModifiedRasterFileName ); if ( !d.exec() ) { return false; diff --git a/src/app/georeferencer/qgstransformsettingsdialog.cpp b/src/app/georeferencer/qgstransformsettingsdialog.cpp index 78f66f6ab0a9..c9fa88d2e2fc 100644 --- a/src/app/georeferencer/qgstransformsettingsdialog.cpp +++ b/src/app/georeferencer/qgstransformsettingsdialog.cpp @@ -27,11 +27,9 @@ #include "qgsgui.h" #include "qgshelp.h" -QgsTransformSettingsDialog::QgsTransformSettingsDialog( const QString &raster, const QString &output, - int countGCPpoints, QWidget *parent ) +QgsTransformSettingsDialog::QgsTransformSettingsDialog( const QString &raster, const QString &output, QWidget *parent ) : QDialog( parent ) , mSourceRasterFile( raster ) - , mCountGCPpoints( countGCPpoints ) { setupUi( this ); QgsSettings settings; @@ -259,14 +257,6 @@ void QgsTransformSettingsDialog::mWorldFileCheckBox_stateChanged( int state ) mOutputRaster->setEnabled( enableOutputRaster ); } -bool QgsTransformSettingsDialog::checkGCPpoints( int count, int &minGCPpoints ) -{ - QgsGeorefTransform georefTransform; - georefTransform.selectTransformParametrisation( ( QgsGeorefTransform::TransformMethod )count ); - minGCPpoints = georefTransform.minimumGcpCount(); - return ( mCountGCPpoints >= minGCPpoints ); -} - QString QgsTransformSettingsDialog::generateModifiedRasterFileName( const QString &raster ) { if ( raster.isEmpty() ) diff --git a/src/app/georeferencer/qgstransformsettingsdialog.h b/src/app/georeferencer/qgstransformsettingsdialog.h index d9dbf376d7da..0d456db0236a 100644 --- a/src/app/georeferencer/qgstransformsettingsdialog.h +++ b/src/app/georeferencer/qgstransformsettingsdialog.h @@ -27,8 +27,7 @@ class QgsTransformSettingsDialog : public QDialog, private Ui::QgsTransformSetti Q_OBJECT public: - QgsTransformSettingsDialog( const QString &raster, const QString &output, - int countGCPpoints, QWidget *parent = nullptr ); + QgsTransformSettingsDialog( const QString &raster, const QString &output, QWidget *parent = nullptr ); void getTransformSettings( QgsGeorefTransform::TransformMethod &tp, QgsImageWarper::ResamplingMethod &rm, QString &comprMethod, @@ -47,13 +46,10 @@ class QgsTransformSettingsDialog : public QDialog, private Ui::QgsTransformSetti void showHelp(); private: - bool checkGCPpoints( int count, int &minGCPpoints ); QString generateModifiedRasterFileName( const QString &raster ); QString mSourceRasterFile; - int mCountGCPpoints; - QStringList mListCompression; }; From 2127c0cd868f88ce24ee3050ccb84db605a5d82a Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 8 Feb 2022 09:28:25 +1000 Subject: [PATCH 21/47] Fix misleading override of QList::size --- src/app/georeferencer/qgsgcplist.cpp | 55 +++++++++---------- src/app/georeferencer/qgsgcplist.h | 13 ++++- src/app/georeferencer/qgsgcplistmodel.cpp | 2 +- src/app/georeferencer/qgsgeorefmainwindow.cpp | 2 +- 4 files changed, 37 insertions(+), 35 deletions(-) diff --git a/src/app/georeferencer/qgsgcplist.cpp b/src/app/georeferencer/qgsgcplist.cpp index 2701d8d6cba2..7124a0369912 100644 --- a/src/app/georeferencer/qgsgcplist.cpp +++ b/src/app/georeferencer/qgsgcplist.cpp @@ -35,40 +35,40 @@ QgsGCPList::QgsGCPList( const QgsGCPList &list ) void QgsGCPList::createGCPVectors( QVector &sourceCoordinates, QVector &destinationCoordinates, const QgsCoordinateReferenceSystem &targetCrs ) { - sourceCoordinates = QVector( size() ); - destinationCoordinates = QVector( size() ); - QgsPointXY transCoords; - for ( int i = 0, j = 0; i < sizeAll(); i++ ) + const int targetSize = countEnabledPoints(); + sourceCoordinates.clear(); + sourceCoordinates.reserve( targetSize ); + destinationCoordinates.clear(); + destinationCoordinates.reserve( targetSize ); + + for ( QgsGeorefDataPoint *pt : std::as_const( *this ) ) { - QgsGeorefDataPoint *pt = at( i ); - if ( pt->isEnabled() ) + if ( !pt->isEnabled() ) + continue; + + sourceCoordinates.push_back( pt->sourceCoords() ); + if ( targetCrs.isValid() ) { - if ( targetCrs.isValid() ) + try { - try - { - transCoords = QgsCoordinateTransform( pt->destinationCrs(), targetCrs, - QgsProject::instance() ).transform( pt->destinationMapCoords() ); - destinationCoordinates[j] = transCoords; - pt->setTransCoords( transCoords ); - } - catch ( const QgsException & ) - { - destinationCoordinates[j] = pt->destinationMapCoords(); - } + QgsPointXY transCoords = QgsCoordinateTransform( pt->destinationCrs(), targetCrs, + QgsProject::instance() ).transform( pt->destinationMapCoords() ); + destinationCoordinates.push_back( transCoords ); + pt->setTransCoords( transCoords ); + } + catch ( const QgsException & ) + { + destinationCoordinates.push_back( pt->destinationMapCoords() ); } - else - destinationCoordinates[j] = pt->destinationMapCoords(); - - sourceCoordinates[j] = pt->sourceCoords(); - j++; } + else + destinationCoordinates.push_back( pt->destinationMapCoords() ); } } -int QgsGCPList::size() const +int QgsGCPList::countEnabledPoints() const { - if ( QList::isEmpty() ) + if ( isEmpty() ) return 0; int s = 0; @@ -82,11 +82,6 @@ int QgsGCPList::size() const return s; } -int QgsGCPList::sizeAll() const -{ - return QList::size(); -} - QgsGCPList &QgsGCPList::operator =( const QgsGCPList &list ) { clear(); diff --git a/src/app/georeferencer/qgsgcplist.h b/src/app/georeferencer/qgsgcplist.h index 81c393003a14..5857d7582221 100644 --- a/src/app/georeferencer/qgsgcplist.h +++ b/src/app/georeferencer/qgsgcplist.h @@ -23,7 +23,11 @@ class QgsGeorefDataPoint; class QgsPointXY; class QgsCoordinateReferenceSystem; -// what is better use inherid or agrigate QList? +/** + * A container for GCP data points. + * + * The container does NOT own the points! + */ class QgsGCPList : public QList { public: @@ -31,8 +35,11 @@ class QgsGCPList : public QList QgsGCPList( const QgsGCPList &list ); void createGCPVectors( QVector &sourceCoordinates, QVector &destinationCoordinates, const QgsCoordinateReferenceSystem &targetCrs ); - int size() const; - int sizeAll() const; + + /** + * Returns the count of currently enabled data points. + */ + int countEnabledPoints() const; QgsGCPList &operator =( const QgsGCPList &list ); }; diff --git a/src/app/georeferencer/qgsgcplistmodel.cpp b/src/app/georeferencer/qgsgcplistmodel.cpp index 058f06a48d42..80c5858d5ef1 100644 --- a/src/app/georeferencer/qgsgcplistmodel.cpp +++ b/src/app/georeferencer/qgsgcplistmodel.cpp @@ -116,7 +116,7 @@ void QgsGCPListModel::updateModel() setHorizontalHeaderLabels( itemLabels ); setRowCount( mGCPList->size() ); - for ( int i = 0; i < mGCPList->sizeAll(); ++i ) + for ( int i = 0; i < mGCPList->size(); ++i ) { int j = 0; QgsGeorefDataPoint *p = mGCPList->at( i ); diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index 51c9de777802..e9f7951432ca 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -754,7 +754,7 @@ void QgsGeoreferencerMainWindow::localHistogramStretch() // Comfort slots void QgsGeoreferencerMainWindow::jumpToGCP( uint theGCPIndex ) { - if ( static_cast( theGCPIndex ) >= mPoints.size() ) + if ( static_cast( theGCPIndex ) >= mPoints.numberEnabledPoints() ) { return; } From 4c0bf94befce3e722eb32a9e226dc63be9986e7c Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 8 Feb 2022 09:50:42 +1000 Subject: [PATCH 22/47] Cleanup georeferencer GCP data class by separating data point from class which stores graphical item representing point --- src/app/georeferencer/qgsgcpcanvasitem.cpp | 8 +- src/app/georeferencer/qgsgcplist.cpp | 10 +- src/app/georeferencer/qgsgcplistmodel.cpp | 6 +- src/app/georeferencer/qgsgcplistwidget.cpp | 8 +- src/app/georeferencer/qgsgeorefdatapoint.cpp | 72 +++++--- src/app/georeferencer/qgsgeorefdatapoint.h | 162 ++++++++++++++++-- src/app/georeferencer/qgsgeorefmainwindow.cpp | 15 +- src/app/georeferencer/qgsresidualplotitem.cpp | 4 +- tests/src/app/testqgsgeoreferencer.cpp | 23 +++ 9 files changed, 241 insertions(+), 67 deletions(-) diff --git a/src/app/georeferencer/qgsgcpcanvasitem.cpp b/src/app/georeferencer/qgsgcpcanvasitem.cpp index 9d07aa7dd974..2175d0f97454 100644 --- a/src/app/georeferencer/qgsgcpcanvasitem.cpp +++ b/src/app/georeferencer/qgsgcpcanvasitem.cpp @@ -54,7 +54,7 @@ void QgsGCPCanvasItem::paint( QPainter *p ) if ( mDataPoint ) { enabled = mDataPoint->isEnabled(); - worldCoords = mDataPoint->destinationMapCoords(); + worldCoords = mDataPoint->destinationPoint(); id = mDataPoint->id(); } p->setOpacity( enabled ? 1.0 : 0.3 ); @@ -163,14 +163,14 @@ void QgsGCPCanvasItem::updatePosition() if ( mIsGCPSource ) { - setPos( toCanvasCoordinates( mDataPoint->sourceCoords() ) ); + setPos( toCanvasCoordinates( mDataPoint->sourcePoint() ) ); } else { - const QgsCoordinateTransform pointToCanvasTransform( mDataPoint->destinationCrs(), mMapCanvas->mapSettings().destinationCrs(), QgsProject::instance() ); + const QgsCoordinateTransform pointToCanvasTransform( mDataPoint->destinationPointCrs(), mMapCanvas->mapSettings().destinationCrs(), QgsProject::instance() ); try { - const QgsPointXY canvasMapCoords = pointToCanvasTransform.transform( mDataPoint->destinationMapCoords() ); + const QgsPointXY canvasMapCoords = pointToCanvasTransform.transform( mDataPoint->destinationPoint() ); const QPointF canvasCoordinatesInPixels = toCanvasCoordinates( canvasMapCoords ); setPos( canvasCoordinatesInPixels ); diff --git a/src/app/georeferencer/qgsgcplist.cpp b/src/app/georeferencer/qgsgcplist.cpp index 7124a0369912..b6b08d197bd2 100644 --- a/src/app/georeferencer/qgsgcplist.cpp +++ b/src/app/georeferencer/qgsgcplist.cpp @@ -46,23 +46,23 @@ void QgsGCPList::createGCPVectors( QVector &sourceCoordinates, QVect if ( !pt->isEnabled() ) continue; - sourceCoordinates.push_back( pt->sourceCoords() ); + sourceCoordinates.push_back( pt->sourcePoint() ); if ( targetCrs.isValid() ) { try { - QgsPointXY transCoords = QgsCoordinateTransform( pt->destinationCrs(), targetCrs, - QgsProject::instance() ).transform( pt->destinationMapCoords() ); + QgsPointXY transCoords = QgsCoordinateTransform( pt->destinationPointCrs(), targetCrs, + QgsProject::instance() ).transform( pt->destinationPoint() ); destinationCoordinates.push_back( transCoords ); pt->setTransCoords( transCoords ); } catch ( const QgsException & ) { - destinationCoordinates.push_back( pt->destinationMapCoords() ); + destinationCoordinates.push_back( pt->destinationPoint() ); } } else - destinationCoordinates.push_back( pt->destinationMapCoords() ); + destinationCoordinates.push_back( pt->destinationPoint() ); } } diff --git a/src/app/georeferencer/qgsgcplistmodel.cpp b/src/app/georeferencer/qgsgcplistmodel.cpp index 80c5858d5ef1..d1949b40308c 100644 --- a/src/app/georeferencer/qgsgcplistmodel.cpp +++ b/src/app/georeferencer/qgsgcplistmodel.cpp @@ -136,8 +136,8 @@ void QgsGCPListModel::updateModel() setItem( i, j++, si ); setItem( i, j++, new QgsStandardItem( i ) ); - setItem( i, j++, new QgsStandardItem( p->sourceCoords().x() ) ); - setItem( i, j++, new QgsStandardItem( p->sourceCoords().y() ) ); + setItem( i, j++, new QgsStandardItem( p->sourcePoint().x() ) ); + setItem( i, j++, new QgsStandardItem( p->sourcePoint().y() ) ); setItem( i, j++, new QgsStandardItem( p->transCoords().x() ) ); setItem( i, j++, new QgsStandardItem( p->transCoords().y() ) ); @@ -148,7 +148,7 @@ void QgsGCPListModel::updateModel() if ( mGeorefTransform && bTransformUpdated && mGeorefTransform->parametersInitialized() ) { QgsPointXY dst; - const QgsPointXY pixel = mGeorefTransform->toSourcePixel( p->sourceCoords() ); + const QgsPointXY pixel = mGeorefTransform->toSourcePixel( p->sourcePoint() ); if ( unitType == tr( "pixels" ) ) { // Transform from world to raster coordinate: diff --git a/src/app/georeferencer/qgsgcplistwidget.cpp b/src/app/georeferencer/qgsgcplistwidget.cpp index 6bb2d80efccb..e7693bb59a7c 100644 --- a/src/app/georeferencer/qgsgcplistwidget.cpp +++ b/src/app/georeferencer/qgsgcplistwidget.cpp @@ -214,9 +214,9 @@ void QgsGCPListWidget::updateItemCoords( QWidget *editor ) if ( lineEdit ) { const double value = lineEdit->text().toDouble(); - QgsPointXY newMapCoords( dataPoint->destinationMapCoords() ); + QgsPointXY newMapCoords( dataPoint->destinationPoint() ); - QgsPointXY newSourceCoords( dataPoint->sourceCoords() ); + QgsPointXY newSourceCoords( dataPoint->sourcePoint() ); if ( mPrevColumn == 2 ) // srcX { newSourceCoords.setX( value ); @@ -238,8 +238,8 @@ void QgsGCPListWidget::updateItemCoords( QWidget *editor ) return; } - dataPoint->setSourceCoords( newSourceCoords ); - dataPoint->setDestinationMapCoords( newMapCoords ); + dataPoint->setSourcePoint( newSourceCoords ); + dataPoint->setDestinationPoint( newMapCoords ); } dataPoint->updateCoords(); diff --git a/src/app/georeferencer/qgsgeorefdatapoint.cpp b/src/app/georeferencer/qgsgeorefdatapoint.cpp index 180e74bd6964..e5485948bb9c 100644 --- a/src/app/georeferencer/qgsgeorefdatapoint.cpp +++ b/src/app/georeferencer/qgsgeorefdatapoint.cpp @@ -21,38 +21,60 @@ #include "qgsgeorefdatapoint.h" +// +// QgsGcpPoint +// + +QgsGcpPoint::QgsGcpPoint( const QgsPointXY &sourcePoint, const QgsPointXY &destinationPoint, const QgsCoordinateReferenceSystem &destinationPointCrs, bool enabled ) + : mSourcePoint( sourcePoint ) + , mDestinationPoint( destinationPoint ) + , mDestinationCrs( destinationPointCrs ) + , mEnabled( enabled ) +{ + +} + +QgsCoordinateReferenceSystem QgsGcpPoint::destinationPointCrs() const +{ + return mDestinationCrs; +} + +void QgsGcpPoint::setDestinationPointCrs( const QgsCoordinateReferenceSystem &crs ) +{ + mDestinationCrs = crs; +} + + +// +// QgsGeorefDataPoint +// + QgsGeorefDataPoint::QgsGeorefDataPoint( QgsMapCanvas *srcCanvas, QgsMapCanvas *dstCanvas, - const QgsPointXY &sourceCoordinates, const QgsPointXY &destinationMapCoords, - const QgsCoordinateReferenceSystem &destinationCrs, bool enable ) + const QgsPointXY &sourceCoordinates, const QgsPointXY &destinationPoint, + const QgsCoordinateReferenceSystem &destinationPointCrs, bool enabled ) : mSrcCanvas( srcCanvas ) , mDstCanvas( dstCanvas ) - , mSourceCoords( sourceCoordinates ) - , mDestinationMapCoords( destinationMapCoords ) + , mGcpPoint( sourceCoordinates, destinationPoint, destinationPointCrs, enabled ) , mId( -1 ) - , mDestinationCrs( destinationCrs ) - , mEnabled( enable ) { - mTransCoords = QgsPointXY( destinationMapCoords ); + mTransCoords = QgsPointXY( destinationPoint ); mGCPSourceItem = new QgsGCPCanvasItem( srcCanvas, this, true ); mGCPDestinationItem = new QgsGCPCanvasItem( dstCanvas, this, false ); - mGCPSourceItem->setEnabled( enable ); - mGCPDestinationItem->setEnabled( enable ); + mGCPSourceItem->setEnabled( enabled ); + mGCPDestinationItem->setEnabled( enabled ); mGCPSourceItem->show(); mGCPDestinationItem->show(); } QgsGeorefDataPoint::QgsGeorefDataPoint( const QgsGeorefDataPoint &p ) : QObject( nullptr ) + , mGcpPoint( p.mGcpPoint ) { // we share item representation on canvas between all points // mGCPSourceItem = new QgsGCPCanvasItem(p.srcCanvas(), p.pixelCoords(), p.mapCoords(), p.isEnabled()); // mGCPDestinationItem = new QgsGCPCanvasItem(p.dstCanvas(), p.pixelCoords(), p.mapCoords(), p.isEnabled()); - mSourceCoords = p.sourceCoords(); - mDestinationMapCoords = p.destinationMapCoords(); mTransCoords = p.transCoords(); - mEnabled = p.isEnabled(); mResidual = p.residual(); - mDestinationCrs = p.destinationCrs(); mId = p.id(); } @@ -62,16 +84,16 @@ QgsGeorefDataPoint::~QgsGeorefDataPoint() delete mGCPDestinationItem; } -void QgsGeorefDataPoint::setSourceCoords( const QgsPointXY &p ) +void QgsGeorefDataPoint::setSourcePoint( const QgsPointXY &p ) { - mSourceCoords = p; + mGcpPoint.setSourcePoint( p ); mGCPSourceItem->update(); mGCPDestinationItem->update(); } -void QgsGeorefDataPoint::setDestinationMapCoords( const QgsPointXY &p ) +void QgsGeorefDataPoint::setDestinationPoint( const QgsPointXY &p ) { - mDestinationMapCoords = p; + mGcpPoint.setDestinationPoint( p ); if ( mGCPSourceItem ) { mGCPSourceItem->update(); @@ -97,12 +119,12 @@ void QgsGeorefDataPoint::setTransCoords( const QgsPointXY &p ) QgsPointXY QgsGeorefDataPoint::transCoords() const { - return mTransCoords.isEmpty() ? mDestinationMapCoords : mTransCoords; + return mTransCoords.isEmpty() ? mGcpPoint.destinationPoint() : mTransCoords; } void QgsGeorefDataPoint::setEnabled( bool enabled ) { - mEnabled = enabled; + mGcpPoint.setEnabled( enabled ); if ( mGCPSourceItem ) { mGCPSourceItem->update(); @@ -164,18 +186,18 @@ void QgsGeorefDataPoint::moveTo( QPoint canvasPixels, bool isMapPlugin ) if ( isMapPlugin ) { const QgsPointXY pnt = mGCPSourceItem->toMapCoordinates( canvasPixels ); - mSourceCoords = pnt; + mGcpPoint.setSourcePoint( pnt ); } else { - mDestinationMapCoords = mGCPDestinationItem->toMapCoordinates( canvasPixels ); + mGcpPoint.setDestinationPoint( mGCPDestinationItem->toMapCoordinates( canvasPixels ) ); if ( mSrcCanvas && mSrcCanvas->mapSettings().destinationCrs().isValid() ) - mDestinationCrs = mSrcCanvas->mapSettings().destinationCrs(); + mGcpPoint.setDestinationPointCrs( mSrcCanvas->mapSettings().destinationCrs() ); else - mDestinationCrs = mGCPDestinationItem->canvas()->mapSettings().destinationCrs(); + mGcpPoint.setDestinationPointCrs( mGCPDestinationItem->canvas()->mapSettings().destinationCrs() ); } - if ( !mDestinationCrs.isValid() ) - mDestinationCrs = QgsProject::instance()->crs(); + if ( !mGcpPoint.destinationPointCrs().isValid() ) + mGcpPoint.setDestinationPointCrs( QgsProject::instance()->crs() ); mGCPSourceItem->update(); mGCPDestinationItem->update(); updateCoords(); diff --git a/src/app/georeferencer/qgsgeorefdatapoint.h b/src/app/georeferencer/qgsgeorefdatapoint.h index 5600e73cdb43..1121e8f7d8ea 100644 --- a/src/app/georeferencer/qgsgeorefdatapoint.h +++ b/src/app/georeferencer/qgsgeorefdatapoint.h @@ -16,11 +16,103 @@ #ifndef QGSGEOREFDATAPOINT_H #define QGSGEOREFDATAPOINT_H +#include "qgis_app.h" #include "qgsmapcanvasitem.h" #include "qgscoordinatereferencesystem.h" class QgsGCPCanvasItem; +/** + * Contains properties of a ground control point (GCP). + */ +class APP_EXPORT QgsGcpPoint +{ + public: + + /** + * Constructor for QgsGcpPoint. + * + * \param sourceCoordinates source coordinates. This may either be in pixels (for completely non-referenced images) OR in the source layer CRS. + * \param destinationPoint destination coordinates + * \param destinationPointCrs CRS of destination point + * \param enabled whether the point is currently enabled + */ + QgsGcpPoint( const QgsPointXY &sourcePoint, const QgsPointXY &destinationPoint, + const QgsCoordinateReferenceSystem &destinationPointCrs, bool enabled ); + + /** + * Returns the source coordinates. + * + * This may either be in pixels (for completely non-referenced images) OR in the source layer CRS. + * + * \see setSourcePoint() + */ + QgsPointXY sourcePoint() const { return mSourcePoint; } + + /** + * Sets the source coordinates. + * + * This may either be in pixels (for completely non-referenced images) OR in the source layer CRS. + * + * \see sourcePoint() + */ + void setSourcePoint( QgsPointXY point ) { mSourcePoint = point; } + + /** + * Returns the destination coordinates. + * + * \see setDestinationPoint() + */ + QgsPointXY destinationPoint() const { return mDestinationPoint; } + + /** + * Sets the destination coordinates. + * + * \see destinationPoint() + */ + void setDestinationPoint( QgsPointXY point ) { mDestinationPoint = point; } + + /** + * Returns the CRS of the destination point. + * + * \see setDestinationCrs() + */ + QgsCoordinateReferenceSystem destinationPointCrs() const; + + /** + * Sets the \a crs of the destination point. + * + * \see destinationCrs() + */ + void setDestinationPointCrs( const QgsCoordinateReferenceSystem &crs ); + + /** + * Returns TRUE if the point is currently enabled. + * + * \see setEnabled() + */ + bool isEnabled() const { return mEnabled; } + + /** + * Sets whether the point is currently enabled. + * + * \see enabled() + */ + void setEnabled( bool enabled ) { mEnabled = enabled; } + + private: + + QgsPointXY mSourcePoint; + QgsPointXY mDestinationPoint; + QgsCoordinateReferenceSystem mDestinationCrs; + bool mEnabled = true; + +}; + + +/** + * Container for a GCP point and the graphical objects which represent it on the map canvas. + */ class QgsGeorefDataPoint : public QObject { Q_OBJECT @@ -31,32 +123,64 @@ class QgsGeorefDataPoint : public QObject * Constructor for QgsGeorefDataPoint * \param srcCanvas * \param dstCanvas - * \param sourceCoordinates must be in source layer coordinates, NOT pixels (unless source image is completely non-referenced)! - * \param destinationMapCoords - * \param destinationCrs - * \param enable + * \param sourceCoordinates source coordinates. This may either be in pixels (for completely non-referenced images) OR in the source layer CRS. + * \param destinationPoint destination coordinates + * \param destinationPointCrs CRS of destination point + * \param enabled whether the point is currently enabled */ QgsGeorefDataPoint( QgsMapCanvas *srcCanvas, QgsMapCanvas *dstCanvas, - const QgsPointXY &sourceCoordinates, const QgsPointXY &destinationMapCoords, - const QgsCoordinateReferenceSystem &destinationCrs, bool enable ); + const QgsPointXY &sourceCoordinates, const QgsPointXY &destinationPoint, + const QgsCoordinateReferenceSystem &destinationPointCrs, bool enabled ); QgsGeorefDataPoint( const QgsGeorefDataPoint &p ); ~QgsGeorefDataPoint() override; /** - * Returns source coordinates of the point. + * Returns the source coordinates. * * This may either be in pixels (for completely non-referenced images) OR in the source layer CRS. + * + * \see setSourcePoint() */ - QgsPointXY sourceCoords() const { return mSourceCoords; } - void setSourceCoords( const QgsPointXY &p ); + QgsPointXY sourcePoint() const { return mGcpPoint.sourcePoint(); } - QgsPointXY destinationMapCoords() const { return mDestinationMapCoords; } - void setDestinationMapCoords( const QgsPointXY &p ); + /** + * Sets the source coordinates. + * + * This may either be in pixels (for completely non-referenced images) OR in the source layer CRS. + * + * \see sourcePoint() + */ + void setSourcePoint( const QgsPointXY &p ); + + /** + * Returns the destination coordinates. + * + * \see setDestinationPoint() + */ + QgsPointXY destinationPoint() const { return mGcpPoint.destinationPoint(); } + + /** + * Sets the destination coordinates. + * + * \see destinationPoint() + */ + void setDestinationPoint( const QgsPointXY &p ); QgsPointXY transCoords() const; void setTransCoords( const QgsPointXY &p ); - bool isEnabled() const { return mEnabled; } + /** + * Returns TRUE if the point is currently enabled. + * + * \see setEnabled() + */ + bool isEnabled() const { return mGcpPoint.isEnabled(); } + + /** + * Sets whether the point is currently enabled. + * + * \see enabled() + */ void setEnabled( bool enabled ); int id() const { return mId; } @@ -70,7 +194,12 @@ class QgsGeorefDataPoint : public QObject QPointF residual() const { return mResidual; } void setResidual( QPointF r ); - QgsCoordinateReferenceSystem destinationCrs() const { return mDestinationCrs; } + /** + * Returns the CRS of the destination point. + * + * \see setDestinationCrs() + */ + QgsCoordinateReferenceSystem destinationPointCrs() const { return mGcpPoint.destinationPointCrs(); } public slots: void moveTo( QPoint canvasPixels, bool isMapPlugin ); @@ -81,13 +210,12 @@ class QgsGeorefDataPoint : public QObject QgsMapCanvas *mDstCanvas = nullptr; QgsGCPCanvasItem *mGCPSourceItem = nullptr; QgsGCPCanvasItem *mGCPDestinationItem = nullptr; - QgsPointXY mSourceCoords; - QgsPointXY mDestinationMapCoords; + + QgsGcpPoint mGcpPoint; + QgsPointXY mTransCoords; int mId; - QgsCoordinateReferenceSystem mDestinationCrs; - bool mEnabled; QPointF mResidual; QgsGeorefDataPoint &operator=( const QgsGeorefDataPoint & ) = delete; diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index e9f7951432ca..3cd21bbf6189 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -754,7 +754,8 @@ void QgsGeoreferencerMainWindow::localHistogramStretch() // Comfort slots void QgsGeoreferencerMainWindow::jumpToGCP( uint theGCPIndex ) { - if ( static_cast( theGCPIndex ) >= mPoints.numberEnabledPoints() ) + // TODO -- probably a bug here!!!! re enabled/not enabled points + if ( static_cast( theGCPIndex ) >= mPoints.countEnabledPoints() ) { return; } @@ -763,7 +764,7 @@ void QgsGeoreferencerMainWindow::jumpToGCP( uint theGCPIndex ) QgsRectangle ext = mCanvas->extent(); QgsPointXY center = ext.center(); - QgsPointXY new_center = mPoints[theGCPIndex]->sourceCoords(); + QgsPointXY new_center = mPoints[theGCPIndex]->sourcePoint(); QgsPointXY diff( new_center.x() - center.x(), new_center.y() - center.y() ); QgsRectangle new_extent( ext.xMinimum() + diff.x(), ext.yMinimum() + diff.y(), @@ -1309,7 +1310,7 @@ void QgsGeoreferencerMainWindow::saveGCPs() points << "mapX,mapY,pixelX,pixelY,enable,dX,dY,residual" << endl; for ( QgsGeorefDataPoint *pt : std::as_const( mPoints ) ) { - const QgsPointXY sourcePixel = mGeorefTransform.toSourcePixel( pt->sourceCoords() ); + const QgsPointXY sourcePixel = mGeorefTransform.toSourcePixel( pt->sourcePoint() ); points << QStringLiteral( "%1,%2,%3,%4,%5,%6,%7,%8" ) .arg( qgsDoubleToString( pt->transCoords().x() ), @@ -1790,7 +1791,7 @@ bool QgsGeoreferencerMainWindow::writePDFReportFile( const QString &fileName, co { currentGCPStrings << tr( "no" ); } - currentGCPStrings << QString::number( ( *gcpIt )->sourceCoords().x(), 'f', 0 ) << QString::number( ( *gcpIt )->sourceCoords().y(), 'f', 0 ) << QString::number( ( *gcpIt )->transCoords().x(), 'f', 3 ) + currentGCPStrings << QString::number( ( *gcpIt )->sourcePoint().x(), 'f', 0 ) << QString::number( ( *gcpIt )->sourcePoint().y(), 'f', 0 ) << QString::number( ( *gcpIt )->transCoords().x(), 'f', 3 ) << QString::number( ( *gcpIt )->transCoords().y(), 'f', 3 ) << QString::number( residual.x() ) << QString::number( residual.y() ) << QString::number( residualTot ); gcpTableContents << currentGCPStrings; } @@ -1896,7 +1897,7 @@ QString QgsGeoreferencerMainWindow::generateGDALtranslateCommand( bool generateT for ( QgsGeorefDataPoint *pt : std::as_const( mPoints ) ) { - gdalCommand << QStringLiteral( "-gcp %1 %2 %3 %4" ).arg( pt->sourceCoords().x() ).arg( -pt->sourceCoords().y() ) + gdalCommand << QStringLiteral( "-gcp %1 %2 %3 %4" ).arg( pt->sourcePoint().x() ).arg( -pt->sourcePoint().y() ) .arg( pt->transCoords().x() ).arg( pt->transCoords().y() ); } @@ -2143,10 +2144,10 @@ bool QgsGeoreferencerMainWindow::equalGCPlists( const QgsGCPList &list1, const Q { QgsGeorefDataPoint *p1 = list1.at( i ); QgsGeorefDataPoint *p2 = list2.at( j ); - if ( p1->sourceCoords() != p2->sourceCoords() ) + if ( p1->sourcePoint() != p2->sourcePoint() ) return false; - if ( p1->destinationMapCoords() != p2->destinationMapCoords() ) + if ( p1->destinationPoint() != p2->destinationPoint() ) return false; } diff --git a/src/app/georeferencer/qgsresidualplotitem.cpp b/src/app/georeferencer/qgsresidualplotitem.cpp index b402909290ef..bffbe7325d6f 100644 --- a/src/app/georeferencer/qgsresidualplotitem.cpp +++ b/src/app/georeferencer/qgsresidualplotitem.cpp @@ -58,7 +58,7 @@ void QgsResidualPlotItem::paint( QPainter *painter, const QStyleOptionGraphicsIt QgsGCPList::const_iterator gcpIt = mGCPList.constBegin(); for ( ; gcpIt != mGCPList.constEnd(); ++gcpIt ) { - const QgsPointXY gcpCoords = ( *gcpIt )->sourceCoords(); + const QgsPointXY gcpCoords = ( *gcpIt )->sourcePoint(); const double gcpItemMMX = ( gcpCoords.x() - mExtent.xMinimum() ) / mExtent.width() * widthMM; const double gcpItemMMY = ( 1 - ( gcpCoords.y() - mExtent.yMinimum() ) / mExtent.height() ) * heightMM; @@ -86,7 +86,7 @@ void QgsResidualPlotItem::paint( QPainter *painter, const QStyleOptionGraphicsIt gcpIt = mGCPList.constBegin(); for ( ; gcpIt != mGCPList.constEnd(); ++gcpIt ) { - const QgsPointXY gcpCoords = ( *gcpIt )->sourceCoords(); + const QgsPointXY gcpCoords = ( *gcpIt )->sourcePoint(); const double gcpItemMMX = ( gcpCoords.x() - mExtent.xMinimum() ) / mExtent.width() * widthMM; const double gcpItemMMY = ( 1 - ( gcpCoords.y() - mExtent.yMinimum() ) / mExtent.height() ) * heightMM; if ( ( *gcpIt )->isEnabled() ) diff --git a/tests/src/app/testqgsgeoreferencer.cpp b/tests/src/app/testqgsgeoreferencer.cpp index 1554a7d2bdae..9ad0475ba836 100644 --- a/tests/src/app/testqgsgeoreferencer.cpp +++ b/tests/src/app/testqgsgeoreferencer.cpp @@ -24,6 +24,7 @@ #include "qgsproject.h" #include "qgsmapcanvas.h" #include "georeferencer/qgsgeoreftransform.h" +#include "georeferencer/qgsgeorefdatapoint.h" /** * \ingroup UnitTests @@ -40,6 +41,7 @@ class TestQgsGeoreferencer : public QObject void cleanupTestCase();// will be called after the last testfunction was executed. void init() {} // will be called before each testfunction is executed. void cleanup() {} // will be called after every testfunction. + void testGcpPoint(); void testTransformImageNoGeoference(); void testTransformImageWithExistingGeoreference(); void testRasterChangeCoords(); @@ -66,6 +68,27 @@ void TestQgsGeoreferencer::cleanupTestCase() QgsApplication::exitQgis(); } +void TestQgsGeoreferencer::testGcpPoint() +{ + QgsGcpPoint p( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ), QgsCoordinateReferenceSystem( "EPSG:3111" ), false ); + + QCOMPARE( p.sourcePoint(), QgsPointXY( 1, 2 ) ); + p.setSourcePoint( QgsPointXY( 11, 22 ) ); + QCOMPARE( p.sourcePoint(), QgsPointXY( 11, 22 ) ); + + QCOMPARE( p.destinationPoint(), QgsPointXY( 3, 4 ) ); + p.setDestinationPoint( QgsPointXY( 33, 44 ) ); + QCOMPARE( p.destinationPoint(), QgsPointXY( 33, 44 ) ); + + QCOMPARE( p.destinationPointCrs().authid(), QStringLiteral( "EPSG:3111" ) ); + p.setDestinationPointCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:28356" ) ) ); + QCOMPARE( p.destinationPointCrs().authid(), QStringLiteral( "EPSG:28356" ) ); + + QVERIFY( !p.isEnabled() ); + p.setEnabled( true ); + QVERIFY( p.isEnabled() ); +} + void TestQgsGeoreferencer::testTransformImageNoGeoference() { QgsGeorefTransform transform( QgsGcpTransformerInterface::TransformMethod::Linear ); From 80b58f7f51b1cc4b1e54eeeea89d0b58db554c23 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 8 Feb 2022 10:16:39 +1000 Subject: [PATCH 23/47] Fix memory leaks and make gcp container class safer to use --- src/app/georeferencer/qgsgcplist.cpp | 25 ++---- src/app/georeferencer/qgsgcplist.h | 15 +++- src/app/georeferencer/qgsgeorefdatapoint.h | 18 +++- src/app/georeferencer/qgsgeorefmainwindow.cpp | 17 ++-- src/app/georeferencer/qgsgeorefmainwindow.h | 6 +- src/app/georeferencer/qgsresidualplotitem.cpp | 17 ++++ src/app/georeferencer/qgsresidualplotitem.h | 5 +- tests/src/app/testqgsgeoreferencer.cpp | 90 ++++++++++++++++++- 8 files changed, 154 insertions(+), 39 deletions(-) diff --git a/src/app/georeferencer/qgsgcplist.cpp b/src/app/georeferencer/qgsgcplist.cpp index b6b08d197bd2..f7a68104639e 100644 --- a/src/app/georeferencer/qgsgcplist.cpp +++ b/src/app/georeferencer/qgsgcplist.cpp @@ -21,18 +21,6 @@ #include "qgsgcplist.h" -QgsGCPList::QgsGCPList( const QgsGCPList &list ) - : QList() -{ - clear(); - QgsGCPList::const_iterator it = list.constBegin(); - for ( ; it != list.constEnd(); ++it ) - { - QgsGeorefDataPoint *pt = new QgsGeorefDataPoint( **it ); - append( pt ); - } -} - void QgsGCPList::createGCPVectors( QVector &sourceCoordinates, QVector &destinationCoordinates, const QgsCoordinateReferenceSystem &targetCrs ) { const int targetSize = countEnabledPoints(); @@ -82,14 +70,13 @@ int QgsGCPList::countEnabledPoints() const return s; } -QgsGCPList &QgsGCPList::operator =( const QgsGCPList &list ) +QList QgsGCPList::asPoints() const { - clear(); - QgsGCPList::const_iterator it = list.constBegin(); - for ( ; it != list.constEnd(); ++it ) + QList res; + res.reserve( size() ); + for ( QgsGeorefDataPoint *pt : *this ) { - QgsGeorefDataPoint *pt = new QgsGeorefDataPoint( **it ); - append( pt ); + res.append( QgsGcpPoint( pt->point() ) ); } - return *this; + return res; } diff --git a/src/app/georeferencer/qgsgcplist.h b/src/app/georeferencer/qgsgcplist.h index 5857d7582221..6fb99ce463ed 100644 --- a/src/app/georeferencer/qgsgcplist.h +++ b/src/app/georeferencer/qgsgcplist.h @@ -18,21 +18,24 @@ #include #include +#include "qgis_app.h" class QgsGeorefDataPoint; +class QgsGcpPoint; class QgsPointXY; class QgsCoordinateReferenceSystem; /** * A container for GCP data points. * - * The container does NOT own the points! + * The container does NOT own the points -- they have to be manually deleted elsewhere!! */ -class QgsGCPList : public QList +class APP_EXPORT QgsGCPList : public QList { public: QgsGCPList() = default; - QgsGCPList( const QgsGCPList &list ); + QgsGCPList( const QgsGCPList &list ) = delete; + QgsGCPList &operator =( const QgsGCPList &list ) = delete; void createGCPVectors( QVector &sourceCoordinates, QVector &destinationCoordinates, const QgsCoordinateReferenceSystem &targetCrs ); @@ -41,7 +44,11 @@ class QgsGCPList : public QList */ int countEnabledPoints() const; - QgsGCPList &operator =( const QgsGCPList &list ); + /** + * Returns the container as a list of GCP points. + */ + QList< QgsGcpPoint > asPoints() const; + }; #endif diff --git a/src/app/georeferencer/qgsgeorefdatapoint.h b/src/app/georeferencer/qgsgeorefdatapoint.h index 1121e8f7d8ea..044dad385dc8 100644 --- a/src/app/georeferencer/qgsgeorefdatapoint.h +++ b/src/app/georeferencer/qgsgeorefdatapoint.h @@ -100,6 +100,20 @@ class APP_EXPORT QgsGcpPoint */ void setEnabled( bool enabled ) { mEnabled = enabled; } + // TODO c++20 - replace with = default + bool operator==( const QgsGcpPoint &other ) const + { + return mEnabled == other.mEnabled + && mSourcePoint == other.mSourcePoint + && mDestinationPoint == other.mDestinationPoint + && mDestinationCrs == other.mDestinationCrs; + } + + bool operator!=( const QgsGcpPoint &other ) const + { + return !( *this == other ); + } + private: QgsPointXY mSourcePoint; @@ -113,7 +127,7 @@ class APP_EXPORT QgsGcpPoint /** * Container for a GCP point and the graphical objects which represent it on the map canvas. */ -class QgsGeorefDataPoint : public QObject +class APP_EXPORT QgsGeorefDataPoint : public QObject { Q_OBJECT @@ -201,6 +215,8 @@ class QgsGeorefDataPoint : public QObject */ QgsCoordinateReferenceSystem destinationPointCrs() const { return mGcpPoint.destinationPointCrs(); } + QgsGcpPoint point() const { return mGcpPoint; } + public slots: void moveTo( QPoint canvasPixels, bool isMapPlugin ); void updateCoords(); diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index 3cd21bbf6189..5a1a381fc399 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -1287,7 +1287,7 @@ bool QgsGeoreferencerMainWindow::loadGCPs( /*bool verbose*/ ) ++i; } - mInitialPoints = mPoints; + mSavedPoints = mPoints.asPoints(); // showMessageInLog(tr("GCP points loaded from"), mGCPpointsFileName); if ( mGCPsDirty ) { @@ -1324,7 +1324,7 @@ void QgsGeoreferencerMainWindow::saveGCPs() << endl; } - mInitialPoints = mPoints; + mSavedPoints = mPoints.asPoints(); } else { @@ -1340,7 +1340,7 @@ QgsGeoreferencerMainWindow::SaveGCPs QgsGeoreferencerMainWindow::checkNeedGCPSav if ( 0 == mPoints.count() ) return QgsGeoreferencerMainWindow::GCPDISCARD; - if ( !equalGCPlists( mInitialPoints, mPoints ) ) + if ( !equalGCPlists( mSavedPoints, mPoints ) ) { QMessageBox::StandardButton a = QMessageBox::question( this, tr( "Save GCPs" ), tr( "Save GCP points?" ), @@ -2133,7 +2133,7 @@ bool QgsGeoreferencerMainWindow::checkFileExisting( const QString &fileName, con return true; } -bool QgsGeoreferencerMainWindow::equalGCPlists( const QgsGCPList &list1, const QgsGCPList &list2 ) +bool QgsGeoreferencerMainWindow::equalGCPlists( const QList< QgsGcpPoint > &list1, const QgsGCPList &list2 ) { if ( list1.count() != list2.count() ) return false; @@ -2142,12 +2142,9 @@ bool QgsGeoreferencerMainWindow::equalGCPlists( const QgsGCPList &list1, const Q int j = 0; for ( int i = 0; i < count; ++i, ++j ) { - QgsGeorefDataPoint *p1 = list1.at( i ); - QgsGeorefDataPoint *p2 = list2.at( j ); - if ( p1->sourcePoint() != p2->sourcePoint() ) - return false; - - if ( p1->destinationPoint() != p2->destinationPoint() ) + const QgsGcpPoint p1 = list1.at( i ); + const QgsGcpPoint p2 = list2.at( j )->point(); + if ( p1 != p2 ) return false; } diff --git a/src/app/georeferencer/qgsgeorefmainwindow.h b/src/app/georeferencer/qgsgeorefmainwindow.h index 237b4ee34ccf..3aa1c6a3dcb3 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.h +++ b/src/app/georeferencer/qgsgeorefmainwindow.h @@ -45,6 +45,7 @@ class QgsGeorefToolDeletePoint; class QgsGeorefToolMovePoint; class QgsGeorefToolMovePoint; class QgsGCPCanvasItem; +class QgsGcpPoint; class QgsGeorefDockWidget : public QgsDockWidget { @@ -193,7 +194,7 @@ class QgsGeoreferencerMainWindow : public QMainWindow, private Ui::QgsGeorefPlug int polynomialOrder( QgsGeorefTransform::TransformMethod transform ); QString guessWorldFileName( const QString &rasterFileName ); bool checkFileExisting( const QString &fileName, const QString &title, const QString &question ); - bool equalGCPlists( const QgsGCPList &list1, const QgsGCPList &list2 ); + bool equalGCPlists( const QList &list1, const QgsGCPList &list2 ); void logTransformOptions(); void logRequaredGCPs(); void clearGCPData(); @@ -245,7 +246,8 @@ class QgsGeoreferencerMainWindow : public QMainWindow, private Ui::QgsGeorefPlug QString mCompressionMethod; QgsGCPList mPoints; - QgsGCPList mInitialPoints; + QList< QgsGcpPoint > mSavedPoints; + QgsMapCanvas *mCanvas = nullptr; std::unique_ptr< QgsRasterLayer > mLayer; diff --git a/src/app/georeferencer/qgsresidualplotitem.cpp b/src/app/georeferencer/qgsresidualplotitem.cpp index bffbe7325d6f..bc2ba1a71f81 100644 --- a/src/app/georeferencer/qgsresidualplotitem.cpp +++ b/src/app/georeferencer/qgsresidualplotitem.cpp @@ -27,6 +27,12 @@ QgsResidualPlotItem::QgsResidualPlotItem( QgsLayout *layout ) setBackgroundEnabled( false ); } +QgsResidualPlotItem::~QgsResidualPlotItem() +{ + qDeleteAll( mGCPList ); + mGCPList.clear(); +} + QgsLayoutItem::Flags QgsResidualPlotItem::itemFlags() const { return QgsLayoutItem::FlagOverridesPaint; @@ -154,6 +160,17 @@ void QgsResidualPlotItem::paint( QPainter *painter, const QStyleOptionGraphicsIt } } +void QgsResidualPlotItem::setGCPList( const QgsGCPList &list ) +{ + qDeleteAll( mGCPList ); + mGCPList.clear(); + + for ( const QgsGeorefDataPoint *pt : list ) + { + mGCPList.append( new QgsGeorefDataPoint( *pt ) ); + } +} + void QgsResidualPlotItem::draw( QgsLayoutItemRenderContext & ) { diff --git a/src/app/georeferencer/qgsresidualplotitem.h b/src/app/georeferencer/qgsresidualplotitem.h index 80e98f57eb1d..793dfc96fd06 100644 --- a/src/app/georeferencer/qgsresidualplotitem.h +++ b/src/app/georeferencer/qgsresidualplotitem.h @@ -30,14 +30,15 @@ class QgsResidualPlotItem: public QgsLayoutItem public: explicit QgsResidualPlotItem( QgsLayout *layout ); + ~QgsResidualPlotItem() override; QgsLayoutItem::Flags itemFlags() const override; //! \brief Reimplementation of QCanvasItem::paint void paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget ) override; - void setGCPList( const QgsGCPList &list ) { mGCPList = list; } - QgsGCPList GCPList() const { return mGCPList; } + void setGCPList( const QgsGCPList &list ); + const QgsGCPList &GCPList() const { return mGCPList; } void setExtent( const QgsRectangle &rect ) { mExtent = rect;} QgsRectangle extent() const { return mExtent; } diff --git a/tests/src/app/testqgsgeoreferencer.cpp b/tests/src/app/testqgsgeoreferencer.cpp index 9ad0475ba836..6b097366dcea 100644 --- a/tests/src/app/testqgsgeoreferencer.cpp +++ b/tests/src/app/testqgsgeoreferencer.cpp @@ -25,6 +25,7 @@ #include "qgsmapcanvas.h" #include "georeferencer/qgsgeoreftransform.h" #include "georeferencer/qgsgeorefdatapoint.h" +#include "georeferencer/qgsgcplist.h" /** * \ingroup UnitTests @@ -42,6 +43,8 @@ class TestQgsGeoreferencer : public QObject void init() {} // will be called before each testfunction is executed. void cleanup() {} // will be called after every testfunction. void testGcpPoint(); + void testGeorefDataPoint(); + void testGcpList(); void testTransformImageNoGeoference(); void testTransformImageWithExistingGeoreference(); void testRasterChangeCoords(); @@ -70,7 +73,7 @@ void TestQgsGeoreferencer::cleanupTestCase() void TestQgsGeoreferencer::testGcpPoint() { - QgsGcpPoint p( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ), QgsCoordinateReferenceSystem( "EPSG:3111" ), false ); + QgsGcpPoint p( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), false ); QCOMPARE( p.sourcePoint(), QgsPointXY( 1, 2 ) ); p.setSourcePoint( QgsPointXY( 11, 22 ) ); @@ -87,6 +90,91 @@ void TestQgsGeoreferencer::testGcpPoint() QVERIFY( !p.isEnabled() ); p.setEnabled( true ); QVERIFY( p.isEnabled() ); + + // equality operator + QVERIFY( QgsGcpPoint( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), false ) + == QgsGcpPoint( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), false ) ); + QVERIFY( QgsGcpPoint( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), false ) + != QgsGcpPoint( QgsPointXY( 11, 22 ), QgsPointXY( 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), false ) ); + QVERIFY( QgsGcpPoint( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), false ) + != QgsGcpPoint( QgsPointXY( 1, 2 ), QgsPointXY( 33, 44 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), false ) ); + QVERIFY( QgsGcpPoint( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), false ) + != QgsGcpPoint( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:28356" ) ), false ) ); + QVERIFY( QgsGcpPoint( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), false ) + != QgsGcpPoint( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), true ) ); + +} + +void TestQgsGeoreferencer::testGeorefDataPoint() +{ + QgsMapCanvas c1; + QgsMapCanvas c2; + QgsGeorefDataPoint p( &c1, &c2, + QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), + false ); + + QCOMPARE( p.sourcePoint(), QgsPointXY( 1, 2 ) ); + p.setSourcePoint( QgsPointXY( 11, 22 ) ); + QCOMPARE( p.sourcePoint(), QgsPointXY( 11, 22 ) ); + + QCOMPARE( p.destinationPoint(), QgsPointXY( 3, 4 ) ); + p.setDestinationPoint( QgsPointXY( 33, 44 ) ); + QCOMPARE( p.destinationPoint(), QgsPointXY( 33, 44 ) ); + + QCOMPARE( p.destinationPointCrs().authid(), QStringLiteral( "EPSG:3111" ) ); + + QVERIFY( !p.isEnabled() ); + p.setEnabled( true ); + QVERIFY( p.isEnabled() ); + + QCOMPARE( p.point(), QgsGcpPoint( QgsPointXY( 11, 22 ), + QgsPointXY( 33, 44 ), + QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), + true ) ); +} + +void TestQgsGeoreferencer::testGcpList() +{ + QgsGCPList list; + QCOMPARE( list.countEnabledPoints(), 0 ); + QVERIFY( list.asPoints().isEmpty() ); + + QgsMapCanvas c1; + QgsMapCanvas c2; + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), + false ) ); + QCOMPARE( list.countEnabledPoints(), 0 ); + QCOMPARE( list.asPoints(), + { + QgsGcpPoint( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), false ) + } + ); + + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 11, 22 ), QgsPointXY( 33, 44 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:28356" ) ), + true ) ); + QCOMPARE( list.countEnabledPoints(), 1 ); + QCOMPARE( list.asPoints(), + QList< QgsGcpPoint >( + { + QgsGcpPoint( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), false ), + QgsGcpPoint( QgsPointXY( 11, 22 ), QgsPointXY( 33, 44 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:28356" ) ), true ) + } ) ); + + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 111, 222 ), QgsPointXY( 333, 444 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:28356" ) ), + true ) ); + QCOMPARE( list.countEnabledPoints(), 2 ); + QCOMPARE( list.asPoints(), + QList< QgsGcpPoint >( + { + QgsGcpPoint( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), false ), + QgsGcpPoint( QgsPointXY( 11, 22 ), QgsPointXY( 33, 44 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:28356" ) ), true ), + QgsGcpPoint( QgsPointXY( 111, 222 ), QgsPointXY( 333, 444 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:28356" ) ), true ) + } ) ); + + qDeleteAll( list ); } void TestQgsGeoreferencer::testTransformImageNoGeoference() From f63cb67889982a3b06dd8d17ce5484bc5e727c19 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 8 Feb 2022 10:43:08 +1000 Subject: [PATCH 24/47] Don't cache transformed destination points This is too fragile, there's too many different situations which should lead us to invalidate the cached point which are not being caught. --- src/app/georeferencer/qgsgcplist.cpp | 30 +++++++---------- src/app/georeferencer/qgsgcplist.h | 8 +++-- src/app/georeferencer/qgsgcplistmodel.cpp | 16 +++++---- src/app/georeferencer/qgsgeorefdatapoint.cpp | 33 +++++++++---------- src/app/georeferencer/qgsgeorefdatapoint.h | 14 +++++--- src/app/georeferencer/qgsgeorefmainwindow.cpp | 15 ++++++--- tests/src/app/testqgsgeoreferencer.cpp | 13 ++++++++ 7 files changed, 75 insertions(+), 54 deletions(-) diff --git a/src/app/georeferencer/qgsgcplist.cpp b/src/app/georeferencer/qgsgcplist.cpp index f7a68104639e..c973d53433a1 100644 --- a/src/app/georeferencer/qgsgcplist.cpp +++ b/src/app/georeferencer/qgsgcplist.cpp @@ -21,36 +21,28 @@ #include "qgsgcplist.h" -void QgsGCPList::createGCPVectors( QVector &sourceCoordinates, QVector &destinationCoordinates, const QgsCoordinateReferenceSystem &targetCrs ) +void QgsGCPList::createGCPVectors( QVector &sourcePoints, QVector &destinationPoints, const QgsCoordinateReferenceSystem &targetCrs ) const { const int targetSize = countEnabledPoints(); - sourceCoordinates.clear(); - sourceCoordinates.reserve( targetSize ); - destinationCoordinates.clear(); - destinationCoordinates.reserve( targetSize ); + sourcePoints.clear(); + sourcePoints.reserve( targetSize ); + destinationPoints.clear(); + destinationPoints.reserve( targetSize ); - for ( QgsGeorefDataPoint *pt : std::as_const( *this ) ) + for ( const QgsGeorefDataPoint *pt : std::as_const( *this ) ) { if ( !pt->isEnabled() ) continue; - sourceCoordinates.push_back( pt->sourcePoint() ); + sourcePoints.push_back( pt->sourcePoint() ); if ( targetCrs.isValid() ) { - try - { - QgsPointXY transCoords = QgsCoordinateTransform( pt->destinationPointCrs(), targetCrs, - QgsProject::instance() ).transform( pt->destinationPoint() ); - destinationCoordinates.push_back( transCoords ); - pt->setTransCoords( transCoords ); - } - catch ( const QgsException & ) - { - destinationCoordinates.push_back( pt->destinationPoint() ); - } + destinationPoints.push_back( pt->transformedDestinationPoint( targetCrs, QgsProject::instance()->transformContext() ) ); } else - destinationCoordinates.push_back( pt->destinationPoint() ); + { + destinationPoints.push_back( pt->destinationPoint() ); + } } } diff --git a/src/app/georeferencer/qgsgcplist.h b/src/app/georeferencer/qgsgcplist.h index 6fb99ce463ed..516637aa3e95 100644 --- a/src/app/georeferencer/qgsgcplist.h +++ b/src/app/georeferencer/qgsgcplist.h @@ -30,14 +30,18 @@ class QgsCoordinateReferenceSystem; * * The container does NOT own the points -- they have to be manually deleted elsewhere!! */ -class APP_EXPORT QgsGCPList : public QList +class APP_EXPORT QgsGCPList : public QList { public: QgsGCPList() = default; QgsGCPList( const QgsGCPList &list ) = delete; QgsGCPList &operator =( const QgsGCPList &list ) = delete; - void createGCPVectors( QVector &sourceCoordinates, QVector &destinationCoordinates, const QgsCoordinateReferenceSystem &targetCrs ); + /** + * Creates vectors of source and destination points, where the destination points are all transformed to the + * specified \a targetCrs. + */ + void createGCPVectors( QVector &sourcePoints, QVector &destinationPoints, const QgsCoordinateReferenceSystem &targetCrs ) const; /** * Returns the count of currently enabled data points. diff --git a/src/app/georeferencer/qgsgcplistmodel.cpp b/src/app/georeferencer/qgsgcplistmodel.cpp index d1949b40308c..31d72742f8db 100644 --- a/src/app/georeferencer/qgsgcplistmodel.cpp +++ b/src/app/georeferencer/qgsgcplistmodel.cpp @@ -19,6 +19,7 @@ #include "qgsgeorefdatapoint.h" #include "qgsgeoreftransform.h" #include "qgssettings.h" +#include "qgsproject.h" #include @@ -85,8 +86,8 @@ void QgsGCPListModel::updateModel() QVector sourceCoordinates; QVector destinationCoordinates; - mGCPList->createGCPVectors( sourceCoordinates, destinationCoordinates, - QgsCoordinateReferenceSystem( s.value( QStringLiteral( "/Plugin-GeoReferencer/targetsrs" ) ).toString() ) ); + const QgsCoordinateReferenceSystem targetCrs( s.value( QStringLiteral( "/Plugin-GeoReferencer/targetsrs" ) ).toString() ); + mGCPList->createGCPVectors( sourceCoordinates, destinationCoordinates, targetCrs ); if ( mGeorefTransform ) { @@ -138,8 +139,9 @@ void QgsGCPListModel::updateModel() setItem( i, j++, new QgsStandardItem( i ) ); setItem( i, j++, new QgsStandardItem( p->sourcePoint().x() ) ); setItem( i, j++, new QgsStandardItem( p->sourcePoint().y() ) ); - setItem( i, j++, new QgsStandardItem( p->transCoords().x() ) ); - setItem( i, j++, new QgsStandardItem( p->transCoords().y() ) ); + const QgsPointXY transformedDestinationPoint = p->transformedDestinationPoint( targetCrs, QgsProject::instance()->transformContext() ); + setItem( i, j++, new QgsStandardItem( transformedDestinationPoint.x() ) ); + setItem( i, j++, new QgsStandardItem( transformedDestinationPoint.y() ) ); double residual; double dX = 0; @@ -155,7 +157,7 @@ void QgsGCPListModel::updateModel() // This is the transform direction used by the warp operation. // As transforms of order >=2 are not invertible, we are only // interested in the residual in this direction - if ( mGeorefTransform->transformWorldToRaster( p->transCoords(), dst ) ) + if ( mGeorefTransform->transformWorldToRaster( transformedDestinationPoint, dst ) ) { dX = ( dst.x() - pixel.x() ); dY = -( dst.y() - pixel.y() ); @@ -165,8 +167,8 @@ void QgsGCPListModel::updateModel() { if ( mGeorefTransform->transformRasterToWorld( pixel, dst ) ) { - dX = ( dst.x() - p->transCoords().x() ); - dY = ( dst.y() - p->transCoords().y() ); + dX = ( dst.x() - transformedDestinationPoint.x() ); + dY = ( dst.y() - transformedDestinationPoint.y() ); } } } diff --git a/src/app/georeferencer/qgsgeorefdatapoint.cpp b/src/app/georeferencer/qgsgeorefdatapoint.cpp index e5485948bb9c..1e1d029bc3df 100644 --- a/src/app/georeferencer/qgsgeorefdatapoint.cpp +++ b/src/app/georeferencer/qgsgeorefdatapoint.cpp @@ -44,6 +44,20 @@ void QgsGcpPoint::setDestinationPointCrs( const QgsCoordinateReferenceSystem &cr mDestinationCrs = crs; } +QgsPointXY QgsGcpPoint::transformedDestinationPoint( const QgsCoordinateReferenceSystem &targetCrs, const QgsCoordinateTransformContext &context ) const +{ + const QgsCoordinateTransform transform( mDestinationCrs, targetCrs, context ); + try + { + return transform.transform( mDestinationPoint ); + } + catch ( QgsCsException & ) + { + QgsDebugMsg( QStringLiteral( "Error transforming destination point" ) ); + return mDestinationPoint; + } +} + // // QgsGeorefDataPoint @@ -57,7 +71,6 @@ QgsGeorefDataPoint::QgsGeorefDataPoint( QgsMapCanvas *srcCanvas, QgsMapCanvas *d , mGcpPoint( sourceCoordinates, destinationPoint, destinationPointCrs, enabled ) , mId( -1 ) { - mTransCoords = QgsPointXY( destinationPoint ); mGCPSourceItem = new QgsGCPCanvasItem( srcCanvas, this, true ); mGCPDestinationItem = new QgsGCPCanvasItem( dstCanvas, this, false ); mGCPSourceItem->setEnabled( enabled ); @@ -73,7 +86,6 @@ QgsGeorefDataPoint::QgsGeorefDataPoint( const QgsGeorefDataPoint &p ) // we share item representation on canvas between all points // mGCPSourceItem = new QgsGCPCanvasItem(p.srcCanvas(), p.pixelCoords(), p.mapCoords(), p.isEnabled()); // mGCPDestinationItem = new QgsGCPCanvasItem(p.dstCanvas(), p.pixelCoords(), p.mapCoords(), p.isEnabled()); - mTransCoords = p.transCoords(); mResidual = p.residual(); mId = p.id(); } @@ -104,22 +116,9 @@ void QgsGeorefDataPoint::setDestinationPoint( const QgsPointXY &p ) } } -void QgsGeorefDataPoint::setTransCoords( const QgsPointXY &p ) -{ - mTransCoords = p; - if ( mGCPSourceItem ) - { - mGCPSourceItem->update(); - } - if ( mGCPDestinationItem ) - { - mGCPDestinationItem->update(); - } -} - -QgsPointXY QgsGeorefDataPoint::transCoords() const +QgsPointXY QgsGeorefDataPoint::transformedDestinationPoint( const QgsCoordinateReferenceSystem &targetCrs, const QgsCoordinateTransformContext &context ) const { - return mTransCoords.isEmpty() ? mGcpPoint.destinationPoint() : mTransCoords; + return mGcpPoint.transformedDestinationPoint( targetCrs, context ); } void QgsGeorefDataPoint::setEnabled( bool enabled ) diff --git a/src/app/georeferencer/qgsgeorefdatapoint.h b/src/app/georeferencer/qgsgeorefdatapoint.h index 044dad385dc8..be3755dd3538 100644 --- a/src/app/georeferencer/qgsgeorefdatapoint.h +++ b/src/app/georeferencer/qgsgeorefdatapoint.h @@ -21,6 +21,7 @@ #include "qgscoordinatereferencesystem.h" class QgsGCPCanvasItem; +class QgsCoordinateTransformContext; /** * Contains properties of a ground control point (GCP). @@ -86,6 +87,11 @@ class APP_EXPORT QgsGcpPoint */ void setDestinationPointCrs( const QgsCoordinateReferenceSystem &crs ); + /** + * Returns the destionationPoint() transformed to the given target CRS. + */ + QgsPointXY transformedDestinationPoint( const QgsCoordinateReferenceSystem &targetCrs, const QgsCoordinateTransformContext &context ) const; + /** * Returns TRUE if the point is currently enabled. * @@ -180,8 +186,10 @@ class APP_EXPORT QgsGeorefDataPoint : public QObject */ void setDestinationPoint( const QgsPointXY &p ); - QgsPointXY transCoords() const; - void setTransCoords( const QgsPointXY &p ); + /** + * Returns the destionationPoint() transformed to the given target CRS. + */ + QgsPointXY transformedDestinationPoint( const QgsCoordinateReferenceSystem &targetCrs, const QgsCoordinateTransformContext &context ) const; /** * Returns TRUE if the point is currently enabled. @@ -229,8 +237,6 @@ class APP_EXPORT QgsGeorefDataPoint : public QObject QgsGcpPoint mGcpPoint; - QgsPointXY mTransCoords; - int mId; QPointF mResidual; diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index 5a1a381fc399..09f25afe2ef5 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -1312,9 +1312,10 @@ void QgsGeoreferencerMainWindow::saveGCPs() { const QgsPointXY sourcePixel = mGeorefTransform.toSourcePixel( pt->sourcePoint() ); + const QgsPointXY transformedDestinationPoint = pt->transformedDestinationPoint( mProjection, QgsProject::instance()->transformContext() ); points << QStringLiteral( "%1,%2,%3,%4,%5,%6,%7,%8" ) - .arg( qgsDoubleToString( pt->transCoords().x() ), - qgsDoubleToString( pt->transCoords().y() ), + .arg( qgsDoubleToString( transformedDestinationPoint.x() ), + qgsDoubleToString( transformedDestinationPoint.y() ), qgsDoubleToString( sourcePixel.x() ), qgsDoubleToString( sourcePixel.y() ) ) .arg( pt->isEnabled() ) @@ -1791,8 +1792,11 @@ bool QgsGeoreferencerMainWindow::writePDFReportFile( const QString &fileName, co { currentGCPStrings << tr( "no" ); } - currentGCPStrings << QString::number( ( *gcpIt )->sourcePoint().x(), 'f', 0 ) << QString::number( ( *gcpIt )->sourcePoint().y(), 'f', 0 ) << QString::number( ( *gcpIt )->transCoords().x(), 'f', 3 ) - << QString::number( ( *gcpIt )->transCoords().y(), 'f', 3 ) << QString::number( residual.x() ) << QString::number( residual.y() ) << QString::number( residualTot ); + + const QgsPointXY transformedDestinationPoint = ( *gcpIt )->transformedDestinationPoint( mProjection, QgsProject::instance()->transformContext() ); + + currentGCPStrings << QString::number( ( *gcpIt )->sourcePoint().x(), 'f', 0 ) << QString::number( ( *gcpIt )->sourcePoint().y(), 'f', 0 ) << QString::number( transformedDestinationPoint.x(), 'f', 3 ) + << QString::number( transformedDestinationPoint.y(), 'f', 3 ) << QString::number( residual.x() ) << QString::number( residual.y() ) << QString::number( residualTot ); gcpTableContents << currentGCPStrings; } @@ -1897,8 +1901,9 @@ QString QgsGeoreferencerMainWindow::generateGDALtranslateCommand( bool generateT for ( QgsGeorefDataPoint *pt : std::as_const( mPoints ) ) { + const QgsPointXY transformedDestinationPoint = pt->transformedDestinationPoint( mProjection, QgsProject::instance()->transformContext() ); gdalCommand << QStringLiteral( "-gcp %1 %2 %3 %4" ).arg( pt->sourcePoint().x() ).arg( -pt->sourcePoint().y() ) - .arg( pt->transCoords().x() ).arg( pt->transCoords().y() ); + .arg( transformedDestinationPoint.x() ).arg( transformedDestinationPoint.y() ); } QFileInfo rasterFileInfo( mRasterFileName ); diff --git a/tests/src/app/testqgsgeoreferencer.cpp b/tests/src/app/testqgsgeoreferencer.cpp index 6b097366dcea..cb85130a9784 100644 --- a/tests/src/app/testqgsgeoreferencer.cpp +++ b/tests/src/app/testqgsgeoreferencer.cpp @@ -103,6 +103,12 @@ void TestQgsGeoreferencer::testGcpPoint() QVERIFY( QgsGcpPoint( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), false ) != QgsGcpPoint( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), true ) ); + + // transform destination point + QgsGcpPoint p2( QgsPointXY( 1, 2 ), QgsPointXY( 150, -30 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), false ); + const QgsPointXY res = p2.transformedDestinationPoint( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), QgsProject::instance()->transformContext() ); + QGSCOMPARENEAR( res.x(), 16697923, 10000 ); + QGSCOMPARENEAR( res.y(), -3503549, 10000 ); } void TestQgsGeoreferencer::testGeorefDataPoint() @@ -131,6 +137,13 @@ void TestQgsGeoreferencer::testGeorefDataPoint() QgsPointXY( 33, 44 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), true ) ); + + + // transform destination point + QgsGeorefDataPoint p2( &c1, &c2, QgsPointXY( 1, 2 ), QgsPointXY( 150, -30 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), false ); + const QgsPointXY res = p2.transformedDestinationPoint( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), QgsProject::instance()->transformContext() ); + QGSCOMPARENEAR( res.x(), 16697923, 10000 ); + QGSCOMPARENEAR( res.y(), -3503549, 10000 ); } void TestQgsGeoreferencer::testGcpList() From e35c05797be9a63d0e25fedf18ae5a89560b5603 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 8 Feb 2022 10:51:51 +1000 Subject: [PATCH 25/47] Add test for createGCPVectors --- src/app/georeferencer/qgsgcplist.cpp | 4 +-- src/app/georeferencer/qgsgcplist.h | 4 ++- src/app/georeferencer/qgsgcplistmodel.cpp | 2 +- src/app/georeferencer/qgsgeorefmainwindow.cpp | 2 +- tests/src/app/testqgsgeoreferencer.cpp | 30 +++++++++++++++++++ 5 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/app/georeferencer/qgsgcplist.cpp b/src/app/georeferencer/qgsgcplist.cpp index c973d53433a1..2df38b580baf 100644 --- a/src/app/georeferencer/qgsgcplist.cpp +++ b/src/app/georeferencer/qgsgcplist.cpp @@ -21,7 +21,7 @@ #include "qgsgcplist.h" -void QgsGCPList::createGCPVectors( QVector &sourcePoints, QVector &destinationPoints, const QgsCoordinateReferenceSystem &targetCrs ) const +void QgsGCPList::createGCPVectors( QVector &sourcePoints, QVector &destinationPoints, const QgsCoordinateReferenceSystem &targetCrs, const QgsCoordinateTransformContext &context ) const { const int targetSize = countEnabledPoints(); sourcePoints.clear(); @@ -37,7 +37,7 @@ void QgsGCPList::createGCPVectors( QVector &sourcePoints, QVectorsourcePoint() ); if ( targetCrs.isValid() ) { - destinationPoints.push_back( pt->transformedDestinationPoint( targetCrs, QgsProject::instance()->transformContext() ) ); + destinationPoints.push_back( pt->transformedDestinationPoint( targetCrs, context ) ); } else { diff --git a/src/app/georeferencer/qgsgcplist.h b/src/app/georeferencer/qgsgcplist.h index 516637aa3e95..fc93476c22fa 100644 --- a/src/app/georeferencer/qgsgcplist.h +++ b/src/app/georeferencer/qgsgcplist.h @@ -24,6 +24,7 @@ class QgsGeorefDataPoint; class QgsGcpPoint; class QgsPointXY; class QgsCoordinateReferenceSystem; +class QgsCoordinateTransformContext; /** * A container for GCP data points. @@ -41,7 +42,8 @@ class APP_EXPORT QgsGCPList : public QList * Creates vectors of source and destination points, where the destination points are all transformed to the * specified \a targetCrs. */ - void createGCPVectors( QVector &sourcePoints, QVector &destinationPoints, const QgsCoordinateReferenceSystem &targetCrs ) const; + void createGCPVectors( QVector &sourcePoints, QVector &destinationPoints, + const QgsCoordinateReferenceSystem &targetCrs, const QgsCoordinateTransformContext &context ) const; /** * Returns the count of currently enabled data points. diff --git a/src/app/georeferencer/qgsgcplistmodel.cpp b/src/app/georeferencer/qgsgcplistmodel.cpp index 31d72742f8db..5fcd4322a2f8 100644 --- a/src/app/georeferencer/qgsgcplistmodel.cpp +++ b/src/app/georeferencer/qgsgcplistmodel.cpp @@ -87,7 +87,7 @@ void QgsGCPListModel::updateModel() QVector destinationCoordinates; const QgsCoordinateReferenceSystem targetCrs( s.value( QStringLiteral( "/Plugin-GeoReferencer/targetsrs" ) ).toString() ); - mGCPList->createGCPVectors( sourceCoordinates, destinationCoordinates, targetCrs ); + mGCPList->createGCPVectors( sourceCoordinates, destinationCoordinates, targetCrs, QgsProject::instance()->transformContext() ); if ( mGeorefTransform ) { diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index 09f25afe2ef5..bc4df2692d19 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -2011,7 +2011,7 @@ bool QgsGeoreferencerMainWindow::updateGeorefTransform() QVector sourceCoordinates; QVector destinationCoords; if ( mGCPListWidget->gcpList() ) - mGCPListWidget->gcpList()->createGCPVectors( sourceCoordinates, destinationCoords, mProjection ); + mGCPListWidget->gcpList()->createGCPVectors( sourceCoordinates, destinationCoords, mProjection, QgsProject::instance()->transformContext() ); else return false; diff --git a/tests/src/app/testqgsgeoreferencer.cpp b/tests/src/app/testqgsgeoreferencer.cpp index cb85130a9784..652b673cc586 100644 --- a/tests/src/app/testqgsgeoreferencer.cpp +++ b/tests/src/app/testqgsgeoreferencer.cpp @@ -187,7 +187,37 @@ void TestQgsGeoreferencer::testGcpList() QgsGcpPoint( QgsPointXY( 111, 222 ), QgsPointXY( 333, 444 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:28356" ) ), true ) } ) ); + qDeleteAll( list ); + list.clear(); + + // create gcp vectors + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 111, 222 ), QgsPointXY( -30, 40 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), + true ) ); + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 11, 22 ), QgsPointXY( 16697923, -3503549 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), + true ) ); + // disabled! + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 33, 44 ), QgsPointXY( 100, 200 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), + false ) ); + + QVector< QgsPointXY > sourcePoints; + QVector< QgsPointXY > destinationPoints; + list.createGCPVectors( sourcePoints, destinationPoints, QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), QgsProject::instance()->transformContext() ); + QCOMPARE( sourcePoints.size(), 2 ); + QCOMPARE( sourcePoints.at( 0 ).x(), 111 ); + QCOMPARE( sourcePoints.at( 0 ).y(), 222 ); + QCOMPARE( sourcePoints.at( 1 ).x(), 11 ); + QCOMPARE( sourcePoints.at( 1 ).y(), 22 ); + + QCOMPARE( destinationPoints.size(), 2 ); + QGSCOMPARENEAR( destinationPoints.at( 0 ).x(), -3339584, 10000 ); + QGSCOMPARENEAR( destinationPoints.at( 0 ).y(), 4865942, 10000 ); + QCOMPARE( destinationPoints.at( 1 ).x(), 16697923 ); + QCOMPARE( destinationPoints.at( 1 ).y(), -3503549 ); + } void TestQgsGeoreferencer::testTransformImageNoGeoference() From 4c1bae1ee398f4fc0f179dc275fef52221c4f149 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 8 Feb 2022 11:09:16 +1000 Subject: [PATCH 26/47] [georeferencer] Fix recenter on points fails when some points in list are disabled --- src/app/georeferencer/qgsgcplistmodel.cpp | 2 ++ src/app/georeferencer/qgsgcplistmodel.h | 5 ++++ src/app/georeferencer/qgsgcplistwidget.cpp | 26 ++++++++++++------- src/app/georeferencer/qgsgcplistwidget.h | 4 +-- src/app/georeferencer/qgsgeorefmainwindow.cpp | 22 +++------------- src/app/georeferencer/qgsgeorefmainwindow.h | 2 +- 6 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/app/georeferencer/qgsgcplistmodel.cpp b/src/app/georeferencer/qgsgcplistmodel.cpp index 5fcd4322a2f8..51acb25009ac 100644 --- a/src/app/georeferencer/qgsgcplistmodel.cpp +++ b/src/app/georeferencer/qgsgcplistmodel.cpp @@ -135,7 +135,9 @@ void QgsGCPListModel::updateModel() else si->setCheckState( Qt::Unchecked ); + si->setData( p->sourcePoint(), SourcePointRole ); setItem( i, j++, si ); + setItem( i, j++, new QgsStandardItem( i ) ); setItem( i, j++, new QgsStandardItem( p->sourcePoint().x() ) ); setItem( i, j++, new QgsStandardItem( p->sourcePoint().y() ) ); diff --git a/src/app/georeferencer/qgsgcplistmodel.h b/src/app/georeferencer/qgsgcplistmodel.h index 435527698d7b..523a3677b1f2 100644 --- a/src/app/georeferencer/qgsgcplistmodel.h +++ b/src/app/georeferencer/qgsgcplistmodel.h @@ -28,6 +28,11 @@ class QgsGCPListModel : public QStandardItemModel Q_OBJECT public: + enum Role + { + SourcePointRole = Qt::UserRole + 1, + }; + explicit QgsGCPListModel( QObject *parent = nullptr ); void setGCPList( QgsGCPList *theGCPList ); diff --git a/src/app/georeferencer/qgsgcplistwidget.cpp b/src/app/georeferencer/qgsgcplistwidget.cpp index e7693bb59a7c..02c790b68adc 100644 --- a/src/app/georeferencer/qgsgcplistwidget.cpp +++ b/src/app/georeferencer/qgsgcplistwidget.cpp @@ -99,16 +99,16 @@ void QgsGCPListWidget::closeEditors() } } -void QgsGCPListWidget::itemDoubleClicked( QModelIndex index ) +void QgsGCPListWidget::itemDoubleClicked( const QModelIndex &index ) { - index = static_cast( model() )->mapToSource( index ); - QStandardItem *item = mGCPListModel->item( index.row(), 1 ); - bool ok; - const int id = item->text().toInt( &ok ); - - if ( ok ) + const QModelIndex sourceIndex = static_cast( model() )->mapToSource( index ); + if ( QStandardItem *item = mGCPListModel->item( sourceIndex.row(), 0 ) ) { - emit jumpToGCP( id ); + const QgsPointXY sourcePoint = item->data( QgsGCPListModel::Role::SourcePointRole ).value< QgsPointXY >(); + if ( !sourcePoint.isEmpty() ) + { + emit jumpToGCP( sourcePoint ); + } } } @@ -285,7 +285,15 @@ void QgsGCPListWidget::jumpToPoint() const QModelIndex index = static_cast( model() )->mapToSource( currentIndex() ); mPrevRow = index.row(); mPrevColumn = index.column(); - emit jumpToGCP( index.row() ); + + if ( QStandardItem *item = mGCPListModel->item( index.row(), 0 ) ) + { + const QgsPointXY sourcePoint = item->data( QgsGCPListModel::Role::SourcePointRole ).value< QgsPointXY >(); + if ( !sourcePoint.isEmpty() ) + { + emit jumpToGCP( sourcePoint ); + } + } } void QgsGCPListWidget::adjustTableContent() diff --git a/src/app/georeferencer/qgsgcplistwidget.h b/src/app/georeferencer/qgsgcplistwidget.h index b3a3acc19389..4e38d28c3fff 100644 --- a/src/app/georeferencer/qgsgcplistwidget.h +++ b/src/app/georeferencer/qgsgcplistwidget.h @@ -44,11 +44,11 @@ class QgsGCPListWidget : public QTableView public slots: // This slot is called by the list view if an item is double-clicked - void itemDoubleClicked( QModelIndex index ); + void itemDoubleClicked( const QModelIndex &index ); void itemClicked( QModelIndex index ); signals: - void jumpToGCP( uint theGCPIndex ); + void jumpToGCP( const QgsPointXY &point ); void pointEnabled( QgsGeorefDataPoint *pnt, int i ); void deleteDataPoint( int index ); diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index bc4df2692d19..1b12e8432c30 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -751,25 +751,9 @@ void QgsGeoreferencerMainWindow::localHistogramStretch() mCanvas->refresh(); } -// Comfort slots -void QgsGeoreferencerMainWindow::jumpToGCP( uint theGCPIndex ) +void QgsGeoreferencerMainWindow::recenterOnPoint( const QgsPointXY &point ) { - // TODO -- probably a bug here!!!! re enabled/not enabled points - if ( static_cast( theGCPIndex ) >= mPoints.countEnabledPoints() ) - { - return; - } - - // qgsmapcanvas doesn't seem to have a method for recentering the map - QgsRectangle ext = mCanvas->extent(); - - QgsPointXY center = ext.center(); - QgsPointXY new_center = mPoints[theGCPIndex]->sourcePoint(); - - QgsPointXY diff( new_center.x() - center.x(), new_center.y() - center.y() ); - QgsRectangle new_extent( ext.xMinimum() + diff.x(), ext.yMinimum() + diff.y(), - ext.xMaximum() + diff.x(), ext.yMaximum() + diff.y() ); - mCanvas->setExtent( new_extent ); + mCanvas->setCenter( point ); mCanvas->refresh(); } @@ -1094,7 +1078,7 @@ void QgsGeoreferencerMainWindow::createDockWidgets() mGCPListWidget->setGeorefTransform( &mGeorefTransform ); dockWidgetGCPpoints->setWidget( mGCPListWidget ); - connect( mGCPListWidget, &QgsGCPListWidget::jumpToGCP, this, &QgsGeoreferencerMainWindow::jumpToGCP ); + connect( mGCPListWidget, &QgsGCPListWidget::jumpToGCP, this, &QgsGeoreferencerMainWindow::recenterOnPoint ); #if 0 connect( mGCPListWidget, SIGNAL( replaceDataPoint( QgsGeorefDataPoint *, int ) ), this, SLOT( replaceDataPoint( QgsGeorefDataPoint *, int ) ) ); diff --git a/src/app/georeferencer/qgsgeorefmainwindow.h b/src/app/georeferencer/qgsgeorefmainwindow.h index 3aa1c6a3dcb3..31987d744e74 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.h +++ b/src/app/georeferencer/qgsgeorefmainwindow.h @@ -119,7 +119,7 @@ class QgsGeoreferencerMainWindow : public QMainWindow, private Ui::QgsGeorefPlug void showGeorefConfigDialog(); // comfort - void jumpToGCP( uint theGCPIndex ); + void recenterOnPoint( const QgsPointXY &point ); void extentsChangedGeorefCanvas(); void extentsChangedQGisCanvas(); void updateCanvasRotation(); From 38cab6b5556fe63d97a4beb3cb9163c25dc4875d Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 8 Feb 2022 11:48:48 +1000 Subject: [PATCH 27/47] When saving/loading GCP files, use source layer coordinates for source points instead of always using source pixel row/columns The "always use pixels" approach does not work well when a source image already has some georeferencing information attached. Also add tests for this, and fix loading of "enabled" status for points when loading points from a file --- src/app/georeferencer/qgsgcplist.cpp | 91 +++++++++++++++++++ src/app/georeferencer/qgsgcplist.h | 27 ++++++ src/app/georeferencer/qgsgeorefmainwindow.cpp | 85 +++-------------- src/app/georeferencer/qgsgeorefmainwindow.h | 2 +- tests/src/app/testqgsgeoreferencer.cpp | 63 +++++++++++++ 5 files changed, 197 insertions(+), 71 deletions(-) diff --git a/src/app/georeferencer/qgsgcplist.cpp b/src/app/georeferencer/qgsgcplist.cpp index 2df38b580baf..fecf9f62370f 100644 --- a/src/app/georeferencer/qgsgcplist.cpp +++ b/src/app/georeferencer/qgsgcplist.cpp @@ -20,6 +20,7 @@ #include "qgsproject.h" #include "qgsgcplist.h" +#include void QgsGCPList::createGCPVectors( QVector &sourcePoints, QVector &destinationPoints, const QgsCoordinateReferenceSystem &targetCrs, const QgsCoordinateTransformContext &context ) const { @@ -72,3 +73,93 @@ QList QgsGCPList::asPoints() const } return res; } + +bool QgsGCPList::saveGcps( const QString &filePath, const QgsCoordinateReferenceSystem &targetCrs, const QgsCoordinateTransformContext &context, QString &error ) const +{ + error.clear(); + + QFile pointFile( filePath ); + if ( pointFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) + { + QTextStream points( &pointFile ); + points << QStringLiteral( "#CRS: %1" ).arg( targetCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) ) << endl; + points << "mapX,mapY,sourceX,sourceY,enable,dX,dY,residual" << endl; + for ( QgsGeorefDataPoint *pt : *this ) + { + const QgsPointXY transformedDestinationPoint = pt->transformedDestinationPoint( targetCrs, context ); + points << QStringLiteral( "%1,%2,%3,%4,%5,%6,%7,%8" ) + .arg( qgsDoubleToString( transformedDestinationPoint.x() ), + qgsDoubleToString( transformedDestinationPoint.y() ), + qgsDoubleToString( pt->sourcePoint().x() ), + qgsDoubleToString( pt->sourcePoint().y() ) ) + .arg( pt->isEnabled() ) + .arg( qgsDoubleToString( pt->residual().x() ), + qgsDoubleToString( pt->residual().y() ), + qgsDoubleToString( std::sqrt( pt->residual().x() * pt->residual().x() + pt->residual().y() * pt->residual().y() ) ) ) + << endl; + } + return true; + } + else + { + error = QObject::tr( "Could not write to GCP points file %1." ).arg( QDir::toNativeSeparators( filePath ) ); + return false; + } +} + +QList QgsGCPList::loadGcps( const QString &filePath, const QgsCoordinateReferenceSystem &defaultDestinationCrs, QString &error ) +{ + error.clear(); + QFile pointFile( filePath ); + if ( !pointFile.open( QIODevice::ReadOnly ) ) + { + error = QObject::tr( "Could not open GCP points file %1." ).arg( QDir::toNativeSeparators( filePath ) ); + return {}; + } + + QTextStream points( &pointFile ); + int lineNumber = 0; + QString line = points.readLine(); + lineNumber++; + + int i = 0; + QgsCoordinateReferenceSystem destinationCrs; + if ( line.contains( QLatin1String( "#CRS: " ) ) ) + { + destinationCrs = QgsCoordinateReferenceSystem( line.remove( QStringLiteral( "#CRS: " ) ) ); + line = points.readLine(); + lineNumber++; + } + else + destinationCrs = defaultDestinationCrs; + + QList res; + while ( !points.atEnd() ) + { + line = points.readLine(); + lineNumber++; + QStringList ls; + if ( line.contains( ',' ) ) // in previous format "\t" is delimiter of points in new - "," + ls = line.split( ',' ); // points from new georeferencer + else + ls = line.split( '\t' ); // points from prev georeferencer + + if ( ls.count() < 4 ) + { + error = QObject::tr( "Malformed content at line %1" ).arg( lineNumber ); + return {}; + } + + const QgsPointXY destinationPoint( ls.at( 0 ).toDouble(), ls.at( 1 ).toDouble() ); // map x,y + const QgsPointXY sourcePoint( ls.at( 2 ).toDouble(), ls.at( 3 ).toDouble() ); // source x,y + bool enable = true; + if ( ls.count() >= 5 ) + { + enable = ls.at( 4 ).toInt(); + } + res.append( QgsGcpPoint( sourcePoint, destinationPoint, destinationCrs, enable ) ); + + ++i; + } + return res; +} diff --git a/src/app/georeferencer/qgsgcplist.h b/src/app/georeferencer/qgsgcplist.h index fc93476c22fa..5ddd1bac2d7e 100644 --- a/src/app/georeferencer/qgsgcplist.h +++ b/src/app/georeferencer/qgsgcplist.h @@ -55,6 +55,33 @@ class APP_EXPORT QgsGCPList : public QList */ QList< QgsGcpPoint > asPoints() const; + /** + * Saves the GCPs to a text file. + * + * \param filePath destination file path + * \param targetCrs target CRS for destination points + * \param context transform context + * \param error will be set to a descriptive error message if saving fails + * + * \returns TRUE on success + */ + bool saveGcps( const QString &filePath, + const QgsCoordinateReferenceSystem &targetCrs, const QgsCoordinateTransformContext &context, + QString &error ) const; + + /** + * Loads GCPs from a text file. + * + * \param filePath source file path + * \param defaultDestinationCrs default CRS to use for destination points if no destination CRS information is present in text file. + * \param error will be set to a descriptive error message if loading fails + * + * \returns TRUE on success + */ + static QList< QgsGcpPoint > loadGcps( const QString &filePath, + const QgsCoordinateReferenceSystem &defaultDestinationCrs, + QString &error ); + }; #endif diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index 1b12e8432c30..1b91f29c9c2d 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -278,7 +278,8 @@ void QgsGeoreferencerMainWindow::openRaster( const QString &fileName ) // load previously added points mGCPpointsFileName = mRasterFileName + ".points"; - ( void )loadGCPs(); + QString error; + ( void )loadGCPs( error ); if ( mLayer ) mCanvas->setExtent( mLayer->extent() ); @@ -663,9 +664,10 @@ void QgsGeoreferencerMainWindow::loadGCPsDialog() if ( mGCPpointsFileName.isEmpty() ) return; - if ( !loadGCPs() ) + QString error; + if ( !loadGCPs( error ) ) { - mMessageBar->pushMessage( tr( "Load GCP Points" ), tr( "Invalid GCP file. File could not be read." ), Qgis::MessageLevel::Critical ); + mMessageBar->pushMessage( tr( "Load GCP Points" ), error, Qgis::MessageLevel::Critical ); } else { @@ -1223,52 +1225,18 @@ void QgsGeoreferencerMainWindow::writeSettings() } // GCP points -bool QgsGeoreferencerMainWindow::loadGCPs( /*bool verbose*/ ) +bool QgsGeoreferencerMainWindow::loadGCPs( QString &error ) { - QFile pointFile( mGCPpointsFileName ); - if ( !pointFile.open( QIODevice::ReadOnly ) ) - { + const QList< QgsGcpPoint > points = QgsGCPList::loadGcps( mGCPpointsFileName, + QgsProject::instance()->crs(), + error ); + if ( !error.isEmpty() ) return false; - } clearGCPData(); - - QTextStream points( &pointFile ); - QString line = points.readLine(); - int i = 0; - QgsCoordinateReferenceSystem destinationCrs; - if ( line.contains( QLatin1String( "#CRS: " ) ) ) - { - destinationCrs = QgsCoordinateReferenceSystem( line.remove( QStringLiteral( "#CRS: " ) ) ); - line = points.readLine(); - } - else - destinationCrs = QgsProject::instance()->crs(); - - while ( !points.atEnd() ) + for ( const QgsGcpPoint &point : points ) { - line = points.readLine(); - QStringList ls; - if ( line.contains( ',' ) ) // in previous format "\t" is delimiter of points in new - "," - ls = line.split( ',' ); // points from new georeferencer - else - ls = line.split( '\t' ); // points from prev georeferencer - - if ( ls.count() < 4 ) - return false; - - const QgsPointXY destinationCoordinate( ls.at( 0 ).toDouble(), ls.at( 1 ).toDouble() ); // map x,y - const QgsPointXY pixelCoords( ls.at( 2 ).toDouble(), ls.at( 3 ).toDouble() ); // pixel x,y - const QgsPointXY sourceLayerCoordinate = mGeorefTransform.toSourceCoordinate( pixelCoords ); - if ( ls.count() == 5 ) - { - bool enable = ls.at( 4 ).toInt(); - addPoint( sourceLayerCoordinate, destinationCoordinate, destinationCrs, enable, false ); - } - else - addPoint( sourceLayerCoordinate, destinationCoordinate, destinationCrs, true, false ); - - ++i; + addPoint( point.sourcePoint(), point.destinationPoint(), point.destinationPointCrs(), point.isEnabled(), false ); } mSavedPoints = mPoints.asPoints(); @@ -1286,38 +1254,15 @@ bool QgsGeoreferencerMainWindow::loadGCPs( /*bool verbose*/ ) void QgsGeoreferencerMainWindow::saveGCPs() { - QFile pointFile( mGCPpointsFileName ); - if ( pointFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) + QString error; + if ( mPoints.saveGcps( mGCPpointsFileName, mProjection, QgsProject::instance()->transformContext(), error ) ) { - QTextStream points( &pointFile ); - points << QStringLiteral( "#CRS: %1" ).arg( mProjection.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) ) << endl; - points << "mapX,mapY,pixelX,pixelY,enable,dX,dY,residual" << endl; - for ( QgsGeorefDataPoint *pt : std::as_const( mPoints ) ) - { - const QgsPointXY sourcePixel = mGeorefTransform.toSourcePixel( pt->sourcePoint() ); - - const QgsPointXY transformedDestinationPoint = pt->transformedDestinationPoint( mProjection, QgsProject::instance()->transformContext() ); - points << QStringLiteral( "%1,%2,%3,%4,%5,%6,%7,%8" ) - .arg( qgsDoubleToString( transformedDestinationPoint.x() ), - qgsDoubleToString( transformedDestinationPoint.y() ), - qgsDoubleToString( sourcePixel.x() ), - qgsDoubleToString( sourcePixel.y() ) ) - .arg( pt->isEnabled() ) - .arg( qgsDoubleToString( pt->residual().x() ), - qgsDoubleToString( pt->residual().y() ), - qgsDoubleToString( std::sqrt( pt->residual().x() * pt->residual().x() + pt->residual().y() * pt->residual().y() ) ) ) - << endl; - } - mSavedPoints = mPoints.asPoints(); } else { - mMessageBar->pushMessage( tr( "Write Error" ), tr( "Could not write to GCP points file %1." ).arg( mGCPpointsFileName ), Qgis::MessageLevel::Critical ); - return; + mMessageBar->pushMessage( tr( "Write Error" ), error, Qgis::MessageLevel::Critical ); } - - // showMessageInLog(tr("GCP points saved in"), mGCPpointsFileName); } QgsGeoreferencerMainWindow::SaveGCPs QgsGeoreferencerMainWindow::checkNeedGCPSave() diff --git a/src/app/georeferencer/qgsgeorefmainwindow.h b/src/app/georeferencer/qgsgeorefmainwindow.h index 31987d744e74..71194a3680b1 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.h +++ b/src/app/georeferencer/qgsgeorefmainwindow.h @@ -163,7 +163,7 @@ class QgsGeoreferencerMainWindow : public QMainWindow, private Ui::QgsGeorefPlug void writeSettings(); // gcp points - bool loadGCPs( /*bool verbose = true*/ ); + bool loadGCPs( QString &error ); void saveGCPs(); QgsGeoreferencerMainWindow::SaveGCPs checkNeedGCPSave(); diff --git a/tests/src/app/testqgsgeoreferencer.cpp b/tests/src/app/testqgsgeoreferencer.cpp index 652b673cc586..411250eb6889 100644 --- a/tests/src/app/testqgsgeoreferencer.cpp +++ b/tests/src/app/testqgsgeoreferencer.cpp @@ -45,6 +45,7 @@ class TestQgsGeoreferencer : public QObject void testGcpPoint(); void testGeorefDataPoint(); void testGcpList(); + void testSaveLoadGcps(); void testTransformImageNoGeoference(); void testTransformImageWithExistingGeoreference(); void testRasterChangeCoords(); @@ -220,6 +221,68 @@ void TestQgsGeoreferencer::testGcpList() } +void TestQgsGeoreferencer::testSaveLoadGcps() +{ + // test saving and loading GCPs + QgsGCPList list; + QgsMapCanvas c1; + QgsMapCanvas c2; + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 111, 222 ), QgsPointXY( -30, 40 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), + true ) ); + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 11, 22 ), QgsPointXY( 16697923, -3503549 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), + true ) ); + // disabled! + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 33, 44 ), QgsPointXY( 100, 200 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), + false ) ); + + QTemporaryDir dir; + QVERIFY( dir.isValid() ); + const QString tempFilename = dir.filePath( QStringLiteral( "test.points" ) ); + + QString error; + + QVERIFY( list.saveGcps( tempFilename, QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), + QgsProject::instance()->transformContext(), error ) ); + QVERIFY( error.isEmpty() ); + + + // load + QList res = QgsGCPList::loadGcps( QStringLiteral( "not real" ), + QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), + error ); + QVERIFY( !error.isEmpty() ); + + res = QgsGCPList::loadGcps( tempFilename, + QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), + error ); + QVERIFY( error.isEmpty() ); + QCOMPARE( res.size(), 3 ); + + QCOMPARE( res.at( 0 ).sourcePoint().x(), 111 ); + QCOMPARE( res.at( 0 ).sourcePoint().y(), 222 ); + QGSCOMPARENEAR( res.at( 0 ).destinationPoint().x(), -3339584, 10000 ); + QGSCOMPARENEAR( res.at( 0 ).destinationPoint().y(), 4865942, 10000 ); + QVERIFY( res.at( 0 ).isEnabled() ); + QCOMPARE( res.at( 0 ).destinationPointCrs().authid(), QStringLiteral( "EPSG:3857" ) ); + + QCOMPARE( res.at( 1 ).sourcePoint().x(), 11 ); + QCOMPARE( res.at( 1 ).sourcePoint().y(), 22 ); + QCOMPARE( res.at( 1 ).destinationPoint().x(), 16697923 ); + QCOMPARE( res.at( 1 ).destinationPoint().y(), -3503549 ); + QVERIFY( res.at( 1 ).isEnabled() ); + QCOMPARE( res.at( 1 ).destinationPointCrs().authid(), QStringLiteral( "EPSG:3857" ) ); + + QCOMPARE( res.at( 2 ).sourcePoint().x(), 33 ); + QCOMPARE( res.at( 2 ).sourcePoint().y(), 44 ); + QCOMPARE( res.at( 2 ).destinationPoint().x(), 100 ); + QCOMPARE( res.at( 2 ).destinationPoint().y(), 200 ); + QVERIFY( !res.at( 2 ).isEnabled() ); + QCOMPARE( res.at( 2 ).destinationPointCrs().authid(), QStringLiteral( "EPSG:3857" ) ); +} + void TestQgsGeoreferencer::testTransformImageNoGeoference() { QgsGeorefTransform transform( QgsGcpTransformerInterface::TransformMethod::Linear ); From 1a96f299d4fb4040eff535df3e09151e5f72f92d Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 8 Feb 2022 12:02:04 +1000 Subject: [PATCH 28/47] Fix logic determining when link georef to qgis canvas actions are enabled And partially fix this functionality when using an already georeferenced image (not fully) --- src/app/georeferencer/qgsgeorefmainwindow.cpp | 56 +++++++------------ 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index 1b91f29c9c2d..0ad0aafc8a96 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -287,10 +287,14 @@ void QgsGeoreferencerMainWindow::openRaster( const QString &fileName ) mCanvas->refresh(); QgisApp::instance()->mapCanvas()->refresh(); - mActionLinkGeorefToQgis->setChecked( false ); - mActionLinkQGisToGeoref->setChecked( false ); - mActionLinkGeorefToQgis->setEnabled( false ); - mActionLinkQGisToGeoref->setEnabled( false ); + const bool hasExistingReference = mLayer->crs().isValid(); + mActionLinkGeorefToQgis->setEnabled( hasExistingReference ); + mActionLinkQGisToGeoref->setEnabled( hasExistingReference ); + if ( !hasExistingReference ) + { + mActionLinkGeorefToQgis->setChecked( false ); + mActionLinkQGisToGeoref->setChecked( false ); + } mCanvas->clearExtentHistory(); // reset zoomnext/zoomlast mWorldFileName = guessWorldFileName( mRasterFileName ); @@ -362,9 +366,6 @@ void QgsGeoreferencerMainWindow::doGeoreference() // mGeorefTransform.selectTransformParametrisation(mTransformParam); // mGCPListWidget->setGeorefTransform(&mGeorefTransform); // mTransformParamLabel->setText(tr("Transform: ") + convertTransformEnumToString(mTransformParam)); - - mActionLinkGeorefToQgis->setEnabled( false ); - mActionLinkQGisToGeoref->setEnabled( false ); } } } @@ -389,15 +390,14 @@ bool QgsGeoreferencerMainWindow::getTransformSettings() // logTransformOptions(); // logRequaredGCPs(); - if ( QgsGcpTransformerInterface::TransformMethod::InvalidTransform != mTransformParam ) + const bool hasReferencing = QgsGcpTransformerInterface::TransformMethod::InvalidTransform != mTransformParam + || mLayer->crs().isValid(); + mActionLinkGeorefToQgis->setEnabled( hasReferencing ); + mActionLinkQGisToGeoref->setEnabled( hasReferencing ); + if ( !hasReferencing ) { - mActionLinkGeorefToQgis->setEnabled( true ); - mActionLinkQGisToGeoref->setEnabled( true ); - } - else - { - mActionLinkGeorefToQgis->setEnabled( false ); - mActionLinkQGisToGeoref->setEnabled( false ); + mActionLinkGeorefToQgis->setChecked( false ); + mActionLinkQGisToGeoref->setChecked( false ); } updateTransformParamLabel(); @@ -506,15 +506,8 @@ void QgsGeoreferencerMainWindow::linkQGisToGeoref( bool link ) { if ( link ) { - if ( QgsGcpTransformerInterface::TransformMethod::InvalidTransform != mTransformParam ) - { - // Indicate that georeferencer canvas extent has changed - extentsChangedGeorefCanvas(); - } - else - { - mActionLinkGeorefToQgis->setEnabled( false ); - } + // Indicate that georeferencer canvas extent has changed + extentsChangedGeorefCanvas(); } } @@ -522,15 +515,8 @@ void QgsGeoreferencerMainWindow::linkGeorefToQgis( bool link ) { if ( link ) { - if ( QgsGcpTransformerInterface::TransformMethod::InvalidTransform != mTransformParam ) - { - // Indicate that qgis main canvas extent has changed - extentsChangedQGisCanvas(); - } - else - { - mActionLinkQGisToGeoref->setEnabled( false ); - } + // Indicate that qgis main canvas extent has changed + extentsChangedQGisCanvas(); } } @@ -920,10 +906,10 @@ void QgsGeoreferencerMainWindow::createActions() connect( mActionZoomNext, &QAction::triggered, this, &QgsGeoreferencerMainWindow::zoomToNext ); mActionLinkGeorefToQgis->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/georeferencer/mActionLinkGeorefToQgis.png" ) ) ); - connect( mActionLinkGeorefToQgis, &QAction::triggered, this, &QgsGeoreferencerMainWindow::linkGeorefToQgis ); + connect( mActionLinkGeorefToQgis, &QAction::toggled, this, &QgsGeoreferencerMainWindow::linkGeorefToQgis ); mActionLinkQGisToGeoref->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/georeferencer/mActionLinkQGisToGeoref.png" ) ) ); - connect( mActionLinkQGisToGeoref, &QAction::triggered, this, &QgsGeoreferencerMainWindow::linkQGisToGeoref ); + connect( mActionLinkQGisToGeoref, &QAction::toggled, this, &QgsGeoreferencerMainWindow::linkQGisToGeoref ); // Settings actions mActionRasterProperties->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionRasterProperties.png" ) ) ); From fdb41bd1a4fc04f72a14cf25e15913072a01b2f6 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 8 Feb 2022 12:02:46 +1000 Subject: [PATCH 29/47] Fix shortcut --- src/app/georeferencer/qgsgeorefmainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index 0ad0aafc8a96..ae71308144d0 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -927,7 +927,7 @@ void QgsGeoreferencerMainWindow::createActions() connect( mActionFullHistogramStretch, &QAction::triggered, this, &QgsGeoreferencerMainWindow::fullHistogramStretch ); mActionFullHistogramStretch->setEnabled( false ); - mActionQuit->setShortcuts( QList() << QKeySequence( Qt::CTRL + Qt::Key_Q ) + mActionQuit->setShortcuts( QList() << QKeySequence( QStringLiteral( "CTRL+Q" ) ) << QKeySequence( Qt::Key_Escape ) ); connect( mActionQuit, &QAction::triggered, this, &QWidget::close ); } From 5584eaadda84eda915c348e39d28ceea54ed6160 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 8 Feb 2022 12:34:42 +1000 Subject: [PATCH 30/47] Fix build on older Qt --- src/app/georeferencer/qgsgcplist.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/app/georeferencer/qgsgcplist.cpp b/src/app/georeferencer/qgsgcplist.cpp index fecf9f62370f..79b192e37140 100644 --- a/src/app/georeferencer/qgsgcplist.cpp +++ b/src/app/georeferencer/qgsgcplist.cpp @@ -21,6 +21,7 @@ #include "qgsgcplist.h" #include +#include void QgsGCPList::createGCPVectors( QVector &sourcePoints, QVector &destinationPoints, const QgsCoordinateReferenceSystem &targetCrs, const QgsCoordinateTransformContext &context ) const { @@ -82,8 +83,20 @@ bool QgsGCPList::saveGcps( const QString &filePath, const QgsCoordinateReference if ( pointFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) { QTextStream points( &pointFile ); - points << QStringLiteral( "#CRS: %1" ).arg( targetCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) ) << endl; - points << "mapX,mapY,sourceX,sourceY,enable,dX,dY,residual" << endl; + points << QStringLiteral( "#CRS: %1" ).arg( targetCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) ); +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + points << endl; +#else + points << Qt::endl; +#endif + + points << "mapX,mapY,sourceX,sourceY,enable,dX,dY,residual"; +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + points << endl; +#else + points << Qt::endl; +#endif + for ( QgsGeorefDataPoint *pt : *this ) { const QgsPointXY transformedDestinationPoint = pt->transformedDestinationPoint( targetCrs, context ); @@ -95,8 +108,12 @@ bool QgsGCPList::saveGcps( const QString &filePath, const QgsCoordinateReference .arg( pt->isEnabled() ) .arg( qgsDoubleToString( pt->residual().x() ), qgsDoubleToString( pt->residual().y() ), - qgsDoubleToString( std::sqrt( pt->residual().x() * pt->residual().x() + pt->residual().y() * pt->residual().y() ) ) ) - << endl; + qgsDoubleToString( std::sqrt( pt->residual().x() * pt->residual().x() + pt->residual().y() * pt->residual().y() ) ) ); +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + points << endl; +#else + points << Qt::endl; +#endif } return true; } From c4610adf326301982c02f3981490e0d940c23679 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 9 Feb 2022 10:13:44 +1000 Subject: [PATCH 31/47] wip (cherry picked from commit 74d7e13ff9a137b2ec479f35ab7703691aacb28e) --- src/app/georeferencer/qgsgcplistmodel.cpp | 267 +++++++++++++++------ src/app/georeferencer/qgsgcplistmodel.h | 35 ++- src/app/georeferencer/qgsgcplistwidget.cpp | 49 ++-- src/app/georeferencer/qgsgcplistwidget.h | 4 +- 4 files changed, 250 insertions(+), 105 deletions(-) diff --git a/src/app/georeferencer/qgsgcplistmodel.cpp b/src/app/georeferencer/qgsgcplistmodel.cpp index 51acb25009ac..99f03ccc1c20 100644 --- a/src/app/georeferencer/qgsgcplistmodel.cpp +++ b/src/app/georeferencer/qgsgcplistmodel.cpp @@ -22,6 +22,7 @@ #include "qgsproject.h" #include +#include class QgsStandardItem : public QStandardItem { @@ -51,75 +52,234 @@ class QgsStandardItem : public QStandardItem }; QgsGCPListModel::QgsGCPListModel( QObject *parent ) - : QStandardItemModel( parent ) + : QAbstractTableModel( parent ) { - // Use data provided by Qt::UserRole as sorting key (needed for numerical sorting). - setSortRole( Qt::UserRole ); } void QgsGCPListModel::setGCPList( QgsGCPList *theGCPList ) { + beginResetModel(); mGCPList = theGCPList; - updateModel(); + endResetModel(); } // ------------------------------- public ---------------------------------- // void QgsGCPListModel::setGeorefTransform( QgsGeorefTransform *georefTransform ) { mGeorefTransform = georefTransform; - updateModel(); + emit dataChanged( index( 0, 0 ), index( rowCount() - 1, columnCount() - 1 ) ); } -void QgsGCPListModel::updateModel() +int QgsGCPListModel::rowCount( const QModelIndex & ) const { + return mGCPList ? mGCPList->size() : 0; +} - //clear(); - if ( !mGCPList ) - return; +int QgsGCPListModel::columnCount( const QModelIndex & ) const +{ + return static_cast< int >( Column::LastColumn ) + 1; +} - bool bTransformUpdated = false; - // // Setup table header - QStringList itemLabels; - QString unitType; - const QgsSettings s; - bool mapUnitsPossible = false; - QVector sourceCoordinates; - QVector destinationCoordinates; +QVariant QgsGCPListModel::data( const QModelIndex &index, int role ) const +{ + if ( !mGCPList + || index.row() < 0 + || index.row() >= mGCPList->size() + || index.column() < 0 + || index.column() >= columnCount() ) + return QVariant(); - const QgsCoordinateReferenceSystem targetCrs( s.value( QStringLiteral( "/Plugin-GeoReferencer/targetsrs" ) ).toString() ); - mGCPList->createGCPVectors( sourceCoordinates, destinationCoordinates, targetCrs, QgsProject::instance()->transformContext() ); + // TODO don't read this from settings!! + const QgsCoordinateReferenceSystem targetCrs( QgsSettings().value( QStringLiteral( "/Plugin-GeoReferencer/targetsrs" ) ).toString() ); + + const Column column = static_cast< Column >( index.column() ); + + const QgsGeorefDataPoint *point = mGCPList->at( index.row() ); + switch ( role ) + { + case Qt::DisplayRole: + switch ( column ) + { + case QgsGCPListModel::Column::Enabled: + break; + case QgsGCPListModel::Column::ID: + return index.row(); + case QgsGCPListModel::Column::SourceX: + return point->sourcePoint().x(); // TODO formatting!! + case QgsGCPListModel::Column::SourceY: + return point->sourcePoint().y(); + case QgsGCPListModel::Column::DestinationX: + { + const QgsPointXY transformedDestinationPoint = point->transformedDestinationPoint( targetCrs, QgsProject::instance()->transformContext() ); + return transformedDestinationPoint.x(); + } + case QgsGCPListModel::Column::DestinationY: + { + const QgsPointXY transformedDestinationPoint = point->transformedDestinationPoint( targetCrs, QgsProject::instance()->transformContext() ); + return transformedDestinationPoint.y(); + } + case QgsGCPListModel::Column::ResidualDx: + break; + case QgsGCPListModel::Column::ResidualDy: + break; + case QgsGCPListModel::Column::TotalResidual: + break; + case QgsGCPListModel::Column::LastColumn: + break; + } + break; + + case Qt::CheckStateRole: + if ( column == Column::Enabled ) + { + return point->isEnabled() ? Qt::Checked : Qt::Unchecked; + } + break; + + case static_cast< int >( Role::SourcePointRole ): + return point->sourcePoint(); + + } + return QVariant(); +} + +Qt::ItemFlags QgsGCPListModel::flags( const QModelIndex &index ) const +{ + if ( !mGCPList + || index.row() < 0 + || index.row() >= mGCPList->size() + || index.column() < 0 + || index.column() >= columnCount() ) + return QAbstractTableModel::flags( index ); + + const Column column = static_cast< Column >( index.column() ); + switch ( column ) + { + case QgsGCPListModel::Column::Enabled: + return Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsUserCheckable | Qt::ItemFlag::ItemIsSelectable; + + case QgsGCPListModel::Column::ID: + case QgsGCPListModel::Column::SourceX: + case QgsGCPListModel::Column::SourceY: + case QgsGCPListModel::Column::DestinationX: + case QgsGCPListModel::Column::DestinationY: + return Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable; + + case QgsGCPListModel::Column::ResidualDx: + case QgsGCPListModel::Column::ResidualDy: + case QgsGCPListModel::Column::TotalResidual: + case QgsGCPListModel::Column::LastColumn: + return Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable; + } + return QAbstractTableModel::flags( index ); +} + +QVariant QgsGCPListModel::headerData( int section, Qt::Orientation orientation, int role ) const +{ + switch ( orientation ) + { + case Qt::Horizontal: + switch ( role ) + { + case Qt::DisplayRole: + { + QString residualUnitType; + switch ( residualUnit() ) + { + case QgsUnitTypes::RenderMapUnits: + residualUnitType = tr( "map units" ); + break; + case QgsUnitTypes::RenderPixels: + residualUnitType = tr( "pixels" ); + break; + + case QgsUnitTypes::RenderMillimeters: + case QgsUnitTypes::RenderPercentage: + case QgsUnitTypes::RenderPoints: + case QgsUnitTypes::RenderInches: + case QgsUnitTypes::RenderUnknownUnit: + case QgsUnitTypes::RenderMetersInMapUnits: + break; + } + + switch ( static_cast< Column >( section ) ) + { + case QgsGCPListModel::Column::Enabled: + return tr( "Enabled" ); + case QgsGCPListModel::Column::ID: + return tr( "ID" ); + case QgsGCPListModel::Column::SourceX: + return tr( "Source X" ); + case QgsGCPListModel::Column::SourceY: + return tr( "Source Y" ); + case QgsGCPListModel::Column::DestinationX: + return tr( "Dest. X" ); + case QgsGCPListModel::Column::DestinationY: + return tr( "Dest. Y" ); + case QgsGCPListModel::Column::ResidualDx: + return tr( "dX (%1)" ).arg( residualUnitType ); + case QgsGCPListModel::Column::ResidualDy: + return tr( "dY (%1)" ).arg( residualUnitType ); + case QgsGCPListModel::Column::TotalResidual: + return tr( "Residual (%1)" ).arg( residualUnitType ); + case QgsGCPListModel::Column::LastColumn: + break; + } + } + break; + default: + break; + } + + break; + case Qt::Vertical: + break; + } + return QVariant(); +} + +QgsUnitTypes::RenderUnit QgsGCPListModel::residualUnit() const +{ + bool mapUnitsPossible = false; if ( mGeorefTransform ) { - bTransformUpdated = mGeorefTransform->updateParametersFromGcps( sourceCoordinates, destinationCoordinates, true ); mapUnitsPossible = mGeorefTransform->providesAccurateInverseTransformation(); } - if ( s.value( QStringLiteral( "/Plugin-GeoReferencer/Config/ResidualUnits" ) ) == "mapUnits" && mapUnitsPossible ) + if ( mapUnitsPossible && QgsSettings().value( QStringLiteral( "/Plugin-GeoReferencer/Config/ResidualUnits" ) ) == "mapUnits" ) { - unitType = tr( "map units" ); + return QgsUnitTypes::RenderUnit::RenderMapUnits; } else { - unitType = tr( "pixels" ); + return QgsUnitTypes::RenderUnit::RenderPixels; } +} - itemLabels << tr( "Visible" ) - << tr( "ID" ) - << tr( "Source X" ) - << tr( "Source Y" ) - << tr( "Dest. X" ) - << tr( "Dest. Y" ) - << tr( "dX (%1)" ).arg( unitType ) - << tr( "dY (%1)" ).arg( unitType ) - << tr( "Residual (%1)" ).arg( unitType ); - setHorizontalHeaderLabels( itemLabels ); - setRowCount( mGCPList->size() ); +void QgsGCPListModel::updateModel() +{ + if ( !mGCPList ) + return; + bool bTransformUpdated = false; + QVector sourceCoordinates; + QVector destinationCoordinates; + + // TODO - don't read from settings + const QgsCoordinateReferenceSystem targetCrs( QgsSettings().value( QStringLiteral( "/Plugin-GeoReferencer/targetsrs" ) ).toString() ); + mGCPList->createGCPVectors( sourceCoordinates, destinationCoordinates, targetCrs, QgsProject::instance()->transformContext() ); + + if ( mGeorefTransform ) + { + bTransformUpdated = mGeorefTransform->updateParametersFromGcps( sourceCoordinates, destinationCoordinates, true ); + } + const QgsUnitTypes::RenderUnit unitType = residualUnit(); + + // update residuals for ( int i = 0; i < mGCPList->size(); ++i ) { - int j = 0; QgsGeorefDataPoint *p = mGCPList->at( i ); if ( !p ) @@ -127,25 +287,9 @@ void QgsGCPListModel::updateModel() p->setId( i ); - QStandardItem *si = new QStandardItem(); - si->setTextAlignment( Qt::AlignCenter ); - si->setCheckable( true ); - if ( p->isEnabled() ) - si->setCheckState( Qt::Checked ); - else - si->setCheckState( Qt::Unchecked ); - - si->setData( p->sourcePoint(), SourcePointRole ); - setItem( i, j++, si ); - - setItem( i, j++, new QgsStandardItem( i ) ); - setItem( i, j++, new QgsStandardItem( p->sourcePoint().x() ) ); - setItem( i, j++, new QgsStandardItem( p->sourcePoint().y() ) ); const QgsPointXY transformedDestinationPoint = p->transformedDestinationPoint( targetCrs, QgsProject::instance()->transformContext() ); - setItem( i, j++, new QgsStandardItem( transformedDestinationPoint.x() ) ); - setItem( i, j++, new QgsStandardItem( transformedDestinationPoint.y() ) ); - double residual; + double residual = 0; double dX = 0; double dY = 0; // Calculate residual if transform is available and up-to-date @@ -153,7 +297,7 @@ void QgsGCPListModel::updateModel() { QgsPointXY dst; const QgsPointXY pixel = mGeorefTransform->toSourcePixel( p->sourcePoint() ); - if ( unitType == tr( "pixels" ) ) + if ( unitType == QgsUnitTypes::RenderPixels ) { // Transform from world to raster coordinate: // This is the transform direction used by the warp operation. @@ -165,7 +309,7 @@ void QgsGCPListModel::updateModel() dY = -( dst.y() - pixel.y() ); } } - else if ( unitType == tr( "map units" ) ) + else if ( unitType == QgsUnitTypes::RenderMapUnits ) { if ( mGeorefTransform->transformRasterToWorld( pixel, dst ) ) { @@ -177,7 +321,7 @@ void QgsGCPListModel::updateModel() residual = std::sqrt( dX * dX + dY * dY ); p->setResidual( QPointF( dX, dY ) ); - +#if 0 if ( residual >= 0.f ) { setItem( i, j++, new QgsStandardItem( dX ) ); @@ -190,19 +334,8 @@ void QgsGCPListModel::updateModel() setItem( i, j++, new QgsStandardItem( QStringLiteral( "n/a" ) ) ); setItem( i, j++, new QgsStandardItem( QStringLiteral( "n/a" ) ) ); } +#endif } } -// --------------------------- public slots -------------------------------- // -void QgsGCPListModel::replaceDataPoint( QgsGeorefDataPoint *newDataPoint, int i ) -{ - mGCPList->replace( i, newDataPoint ); -} -void QgsGCPListModel::onGCPListModified() -{ -} - -void QgsGCPListModel::onTransformationModified() -{ -} diff --git a/src/app/georeferencer/qgsgcplistmodel.h b/src/app/georeferencer/qgsgcplistmodel.h index 523a3677b1f2..e683f034f32c 100644 --- a/src/app/georeferencer/qgsgcplistmodel.h +++ b/src/app/georeferencer/qgsgcplistmodel.h @@ -17,18 +17,33 @@ #define QGSGCP_LIST_TABLE_VIEW_H #include -#include +#include +#include "qgsunittypes.h" class QgsGeorefDataPoint; class QgsGeorefTransform; class QgsGCPList; -class QgsGCPListModel : public QStandardItemModel +class QgsGCPListModel : public QAbstractTableModel { Q_OBJECT public: - enum Role + enum class Column : int + { + Enabled, + ID, + SourceX, + SourceY, + DestinationX, + DestinationY, + ResidualDx, + ResidualDy, + TotalResidual, + LastColumn + }; + + enum class Role : int { SourcePointRole = Qt::UserRole + 1, }; @@ -37,15 +52,17 @@ class QgsGCPListModel : public QStandardItemModel void setGCPList( QgsGCPList *theGCPList ); void setGeorefTransform( QgsGeorefTransform *georefTransform ); - void updateModel(); - public slots: - void replaceDataPoint( QgsGeorefDataPoint *newDataPoint, int i ); - - void onGCPListModified(); - void onTransformationModified(); + int rowCount( const QModelIndex &parent = QModelIndex() ) const override; + int columnCount( const QModelIndex &parent = QModelIndex() ) const override; + QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const override; + Qt::ItemFlags flags( const QModelIndex &index ) const override; + QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const override; + void updateModel(); private: + QgsUnitTypes::RenderUnit residualUnit() const; + QgsGCPList *mGCPList = nullptr; QgsGeorefTransform *mGeorefTransform = nullptr; }; diff --git a/src/app/georeferencer/qgsgcplistwidget.cpp b/src/app/georeferencer/qgsgcplistwidget.cpp index 02c790b68adc..78a9eb6ea4f0 100644 --- a/src/app/georeferencer/qgsgcplistwidget.cpp +++ b/src/app/georeferencer/qgsgcplistwidget.cpp @@ -102,23 +102,18 @@ void QgsGCPListWidget::closeEditors() void QgsGCPListWidget::itemDoubleClicked( const QModelIndex &index ) { const QModelIndex sourceIndex = static_cast( model() )->mapToSource( index ); - if ( QStandardItem *item = mGCPListModel->item( sourceIndex.row(), 0 ) ) - { - const QgsPointXY sourcePoint = item->data( QgsGCPListModel::Role::SourcePointRole ).value< QgsPointXY >(); - if ( !sourcePoint.isEmpty() ) - { - emit jumpToGCP( sourcePoint ); - } - } + jumpToSourcePoint( sourceIndex ); } -void QgsGCPListWidget::itemClicked( QModelIndex index ) +void QgsGCPListWidget::itemClicked( const QModelIndex &index ) { - index = static_cast( model() )->mapToSource( index ); - QStandardItem *item = mGCPListModel->item( index.row(), index.column() ); + const QModelIndex sourceIndex = static_cast( model() )->mapToSource( index ); + +#if 0 + QStandardItem *item = mGCPListModel->item( sourceIndex.row(), sourceIndex.column() ); if ( item->isCheckable() ) { - QgsGeorefDataPoint *p = mGCPList->at( index.row() ); + QgsGeorefDataPoint *p = mGCPList->at( sourceIndex.row() ); if ( item->checkState() == Qt::Checked ) { p->setEnabled( true ); @@ -129,12 +124,13 @@ void QgsGCPListWidget::itemClicked( QModelIndex index ) } mGCPListModel->updateModel(); - emit pointEnabled( p, index.row() ); + emit pointEnabled( p, sourceIndex.row() ); adjustTableContent(); } +#endif - mPrevRow = index.row(); - mPrevColumn = index.column(); + mPrevRow = sourceIndex.row(); + mPrevColumn = sourceIndex.column(); } void QgsGCPListWidget::keyPressEvent( QKeyEvent *e ) @@ -260,7 +256,13 @@ void QgsGCPListWidget::showContextMenu( QPoint p ) setCurrentIndex( index ); QAction *jumpToPointAction = new QAction( tr( "Recenter" ), this ); - connect( jumpToPointAction, &QAction::triggered, this, &QgsGCPListWidget::jumpToPoint ); + connect( jumpToPointAction, &QAction::triggered, this, [ = ] + { + const QModelIndex sourceIndex = static_cast( model() )->mapToSource( currentIndex() ); + mPrevRow = sourceIndex.row(); + mPrevColumn = sourceIndex.column(); + jumpToSourcePoint( sourceIndex ); + } ); m.addAction( jumpToPointAction ); QAction *removeAction = new QAction( tr( "Remove" ), this ); @@ -280,19 +282,12 @@ void QgsGCPListWidget::editCell() edit( currentIndex() ); } -void QgsGCPListWidget::jumpToPoint() +void QgsGCPListWidget::jumpToSourcePoint( const QModelIndex &modelIndex ) { - const QModelIndex index = static_cast( model() )->mapToSource( currentIndex() ); - mPrevRow = index.row(); - mPrevColumn = index.column(); - - if ( QStandardItem *item = mGCPListModel->item( index.row(), 0 ) ) + const QgsPointXY sourcePoint = mGCPListModel->data( modelIndex, static_cast< int >( QgsGCPListModel::Role::SourcePointRole ) ).value< QgsPointXY >(); + if ( !sourcePoint.isEmpty() ) { - const QgsPointXY sourcePoint = item->data( QgsGCPListModel::Role::SourcePointRole ).value< QgsPointXY >(); - if ( !sourcePoint.isEmpty() ) - { - emit jumpToGCP( sourcePoint ); - } + emit jumpToGCP( sourcePoint ); } } diff --git a/src/app/georeferencer/qgsgcplistwidget.h b/src/app/georeferencer/qgsgcplistwidget.h index 4e38d28c3fff..cf85e4e0fea8 100644 --- a/src/app/georeferencer/qgsgcplistwidget.h +++ b/src/app/georeferencer/qgsgcplistwidget.h @@ -45,7 +45,7 @@ class QgsGCPListWidget : public QTableView public slots: // This slot is called by the list view if an item is double-clicked void itemDoubleClicked( const QModelIndex &index ); - void itemClicked( QModelIndex index ); + void itemClicked( const QModelIndex &index ); signals: void jumpToGCP( const QgsPointXY &point ); @@ -57,7 +57,7 @@ class QgsGCPListWidget : public QTableView void showContextMenu( QPoint ); void removeRow(); void editCell(); - void jumpToPoint(); + void jumpToSourcePoint( const QModelIndex &modelIndex ); private: void createActions(); From f7b85732bfd0f08bd5912f320508d59cca43f295 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 9 Feb 2022 11:25:08 +1000 Subject: [PATCH 32/47] Fix reading destination CRS from .points files (cherry picked from commit 23402c8c7015908f775d5c9f4d4a1b19165a81f1) --- src/app/georeferencer/qgsgcplist.cpp | 9 ++++----- src/app/georeferencer/qgsgcplist.h | 2 ++ src/app/georeferencer/qgsgeorefmainwindow.cpp | 4 ++++ tests/src/app/testqgsgeoreferencer.cpp | 5 +++++ 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/app/georeferencer/qgsgcplist.cpp b/src/app/georeferencer/qgsgcplist.cpp index 79b192e37140..7ff6d159127a 100644 --- a/src/app/georeferencer/qgsgcplist.cpp +++ b/src/app/georeferencer/qgsgcplist.cpp @@ -124,7 +124,7 @@ bool QgsGCPList::saveGcps( const QString &filePath, const QgsCoordinateReference } } -QList QgsGCPList::loadGcps( const QString &filePath, const QgsCoordinateReferenceSystem &defaultDestinationCrs, QString &error ) +QList QgsGCPList::loadGcps( const QString &filePath, const QgsCoordinateReferenceSystem &defaultDestinationCrs, QgsCoordinateReferenceSystem &actualDestinationCrs, QString &error ) { error.clear(); QFile pointFile( filePath ); @@ -140,15 +140,14 @@ QList QgsGCPList::loadGcps( const QString &filePath, const QgsCoord lineNumber++; int i = 0; - QgsCoordinateReferenceSystem destinationCrs; if ( line.contains( QLatin1String( "#CRS: " ) ) ) { - destinationCrs = QgsCoordinateReferenceSystem( line.remove( QStringLiteral( "#CRS: " ) ) ); + actualDestinationCrs = QgsCoordinateReferenceSystem( line.remove( QStringLiteral( "#CRS: " ) ) ); line = points.readLine(); lineNumber++; } else - destinationCrs = defaultDestinationCrs; + actualDestinationCrs = defaultDestinationCrs; QList res; while ( !points.atEnd() ) @@ -174,7 +173,7 @@ QList QgsGCPList::loadGcps( const QString &filePath, const QgsCoord { enable = ls.at( 4 ).toInt(); } - res.append( QgsGcpPoint( sourcePoint, destinationPoint, destinationCrs, enable ) ); + res.append( QgsGcpPoint( sourcePoint, destinationPoint, actualDestinationCrs, enable ) ); ++i; } diff --git a/src/app/georeferencer/qgsgcplist.h b/src/app/georeferencer/qgsgcplist.h index 5ddd1bac2d7e..8513dd8282c8 100644 --- a/src/app/georeferencer/qgsgcplist.h +++ b/src/app/georeferencer/qgsgcplist.h @@ -74,12 +74,14 @@ class APP_EXPORT QgsGCPList : public QList * * \param filePath source file path * \param defaultDestinationCrs default CRS to use for destination points if no destination CRS information is present in text file. + * \param actualDestinationCrs will be set to actual destination CRS for points, which is either the CRS information from the text file OR the defaultDestinationCrs * \param error will be set to a descriptive error message if loading fails * * \returns TRUE on success */ static QList< QgsGcpPoint > loadGcps( const QString &filePath, const QgsCoordinateReferenceSystem &defaultDestinationCrs, + QgsCoordinateReferenceSystem &actualDestinationCrs, QString &error ); }; diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index ae71308144d0..d2340269af10 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -1213,12 +1213,16 @@ void QgsGeoreferencerMainWindow::writeSettings() // GCP points bool QgsGeoreferencerMainWindow::loadGCPs( QString &error ) { + QgsCoordinateReferenceSystem actualDestinationCrs; const QList< QgsGcpPoint > points = QgsGCPList::loadGcps( mGCPpointsFileName, QgsProject::instance()->crs(), + actualDestinationCrs, error ); if ( !error.isEmpty() ) return false; + QgsSettings().setValue( QStringLiteral( "/Plugin-GeoReferencer/targetsrs" ), actualDestinationCrs.authid() ); + clearGCPData(); for ( const QgsGcpPoint &point : points ) { diff --git a/tests/src/app/testqgsgeoreferencer.cpp b/tests/src/app/testqgsgeoreferencer.cpp index 411250eb6889..c9a6ad1090de 100644 --- a/tests/src/app/testqgsgeoreferencer.cpp +++ b/tests/src/app/testqgsgeoreferencer.cpp @@ -250,16 +250,21 @@ void TestQgsGeoreferencer::testSaveLoadGcps() // load + QgsCoordinateReferenceSystem actualDestinationCrs; QList res = QgsGCPList::loadGcps( QStringLiteral( "not real" ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), + actualDestinationCrs, error ); QVERIFY( !error.isEmpty() ); res = QgsGCPList::loadGcps( tempFilename, QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), + actualDestinationCrs, error ); QVERIFY( error.isEmpty() ); QCOMPARE( res.size(), 3 ); + // should be loaded from txt + QCOMPARE( actualDestinationCrs.authid(), QStringLiteral( "EPSG:3857" ) ); QCOMPARE( res.at( 0 ).sourcePoint().x(), 111 ); QCOMPARE( res.at( 0 ).sourcePoint().y(), 222 ); From 1b0260bfc3eed6bbacc59150358e995d5554194f Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 9 Feb 2022 12:26:47 +1000 Subject: [PATCH 33/47] Never silently overwrite gcp point files without prompting user (cherry picked from commit d76fe7939f8d89a5bdedf7f48ba9a063b809fd53) --- src/app/georeferencer/qgsgeorefmainwindow.cpp | 18 +----------------- src/app/georeferencer/qgsgeorefmainwindow.h | 1 - 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index d2340269af10..eda6394d73cd 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -164,13 +164,6 @@ void QgsGeoreferencerMainWindow::closeEvent( QCloseEvent *e ) mRasterFileName.clear(); e->accept(); return; - case QgsGeoreferencerMainWindow::GCPSILENTSAVE: - if ( !mGCPpointsFileName.isEmpty() ) - saveGCPs(); - clearGCPData(); - removeOldLayer(); - mRasterFileName.clear(); - return; case QgsGeoreferencerMainWindow::GCPDISCARD: writeSettings(); clearGCPData(); @@ -213,10 +206,6 @@ void QgsGeoreferencerMainWindow::openRaster( const QString &fileName ) case QgsGeoreferencerMainWindow::GCPSAVE: saveGCPsDialog(); break; - case QgsGeoreferencerMainWindow::GCPSILENTSAVE: - if ( !mGCPpointsFileName.isEmpty() ) - saveGCPs(); - break; case QgsGeoreferencerMainWindow::GCPDISCARD: break; case QgsGeoreferencerMainWindow::GCPCANCEL: @@ -1274,13 +1263,8 @@ QgsGeoreferencerMainWindow::SaveGCPs QgsGeoreferencerMainWindow::checkNeedGCPSav { return QgsGeoreferencerMainWindow::GCPCANCEL; } - else if ( a == QMessageBox::Discard ) - { - return QgsGeoreferencerMainWindow::GCPDISCARD; - } } - - return QgsGeoreferencerMainWindow::GCPSILENTSAVE; + return QgsGeoreferencerMainWindow::GCPDISCARD; } // Georeference diff --git a/src/app/georeferencer/qgsgeorefmainwindow.h b/src/app/georeferencer/qgsgeorefmainwindow.h index 71194a3680b1..897ac636b570 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.h +++ b/src/app/georeferencer/qgsgeorefmainwindow.h @@ -139,7 +139,6 @@ class QgsGeoreferencerMainWindow : public QMainWindow, private Ui::QgsGeorefPlug enum SaveGCPs { GCPSAVE, - GCPSILENTSAVE, GCPDISCARD, GCPCANCEL }; From 3d1bff55f6d6b9c84c4219c511f1f5333c9559f5 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 9 Feb 2022 12:39:00 +1000 Subject: [PATCH 34/47] Move code for calculating residuals to QgsGCPList out of model (cherry picked from commit 977585ce3daaa09da83fd40e11a3fe9cfc133481) --- src/app/georeferencer/qgsgcplist.cpp | 57 ++++++++++++++++++++++++ src/app/georeferencer/qgsgcplist.h | 14 ++++++ tests/src/app/testqgsgeoreferencer.cpp | 61 ++++++++++++++++++++++++++ 3 files changed, 132 insertions(+) diff --git a/src/app/georeferencer/qgsgcplist.cpp b/src/app/georeferencer/qgsgcplist.cpp index 7ff6d159127a..3498ad3b8f7f 100644 --- a/src/app/georeferencer/qgsgcplist.cpp +++ b/src/app/georeferencer/qgsgcplist.cpp @@ -18,6 +18,7 @@ #include "qgscoordinatereferencesystem.h" #include "qgscoordinatetransform.h" #include "qgsproject.h" +#include "qgsgeoreftransform.h" #include "qgsgcplist.h" #include @@ -64,6 +65,62 @@ int QgsGCPList::countEnabledPoints() const return s; } +void QgsGCPList::updateResiduals( QgsGeorefTransform *georefTransform, const QgsCoordinateReferenceSystem &targetCrs, const QgsCoordinateTransformContext &context, QgsUnitTypes::RenderUnit residualUnit ) +{ + bool bTransformUpdated = false; + QVector sourceCoordinates; + QVector destinationCoordinates; + createGCPVectors( sourceCoordinates, destinationCoordinates, targetCrs, context ); + + if ( georefTransform ) + { + bTransformUpdated = georefTransform->updateParametersFromGcps( sourceCoordinates, destinationCoordinates, true ); + } + + // update residuals + for ( int i = 0; i < size(); ++i ) + { + QgsGeorefDataPoint *p = at( i ); + + if ( !p ) + continue; + + p->setId( i ); + + const QgsPointXY transformedDestinationPoint = p->transformedDestinationPoint( targetCrs, QgsProject::instance()->transformContext() ); + + double dX = 0; + double dY = 0; + // Calculate residual if transform is available and up-to-date + if ( georefTransform && bTransformUpdated && georefTransform->parametersInitialized() ) + { + QgsPointXY dst; + const QgsPointXY pixel = georefTransform->toSourcePixel( p->sourcePoint() ); + if ( residualUnit == QgsUnitTypes::RenderPixels ) + { + // Transform from world to raster coordinate: + // This is the transform direction used by the warp operation. + // As transforms of order >=2 are not invertible, we are only + // interested in the residual in this direction + if ( georefTransform->transformWorldToRaster( transformedDestinationPoint, dst ) ) + { + dX = ( dst.x() - pixel.x() ); + dY = -( dst.y() - pixel.y() ); + } + } + else if ( residualUnit == QgsUnitTypes::RenderMapUnits ) + { + if ( georefTransform->transformRasterToWorld( pixel, dst ) ) + { + dX = ( dst.x() - transformedDestinationPoint.x() ); + dY = ( dst.y() - transformedDestinationPoint.y() ); + } + } + } + p->setResidual( QPointF( dX, dY ) ); + } +} + QList QgsGCPList::asPoints() const { QList res; diff --git a/src/app/georeferencer/qgsgcplist.h b/src/app/georeferencer/qgsgcplist.h index 8513dd8282c8..6662a95e97e2 100644 --- a/src/app/georeferencer/qgsgcplist.h +++ b/src/app/georeferencer/qgsgcplist.h @@ -19,12 +19,14 @@ #include #include #include "qgis_app.h" +#include "qgsunittypes.h" class QgsGeorefDataPoint; class QgsGcpPoint; class QgsPointXY; class QgsCoordinateReferenceSystem; class QgsCoordinateTransformContext; +class QgsGeorefTransform; /** * A container for GCP data points. @@ -50,6 +52,18 @@ class APP_EXPORT QgsGCPList : public QList */ int countEnabledPoints() const; + /** + * Updates the stored residual sizes for points in the list. + * + * \param georefTransform transformation to use for residual calculation + * \param targetCrs georeference output CRS + * \param context transform context + * \param residualUnit units for residual calculation. Supported values are QgsUnitTypes::RenderPixels or QgsUnitTypes::RenderMapUnits + */ + void updateResiduals( QgsGeorefTransform *georefTransform, + const QgsCoordinateReferenceSystem &targetCrs, const QgsCoordinateTransformContext &context, + QgsUnitTypes::RenderUnit residualUnit ); + /** * Returns the container as a list of GCP points. */ diff --git a/tests/src/app/testqgsgeoreferencer.cpp b/tests/src/app/testqgsgeoreferencer.cpp index c9a6ad1090de..d42b76770696 100644 --- a/tests/src/app/testqgsgeoreferencer.cpp +++ b/tests/src/app/testqgsgeoreferencer.cpp @@ -49,6 +49,7 @@ class TestQgsGeoreferencer : public QObject void testTransformImageNoGeoference(); void testTransformImageWithExistingGeoreference(); void testRasterChangeCoords(); + void testUpdateResiduals(); private: QgisApp *mQgisApp = nullptr; @@ -483,5 +484,65 @@ void TestQgsGeoreferencer::testRasterChangeCoords() QCOMPARE( transform.toColumnLine( QgsPointXY( 100, 200 ) ).y(), 200.0 ); } +void TestQgsGeoreferencer::testUpdateResiduals() +{ + // test updating residuals + QgsGeorefTransform transform( QgsGcpTransformerInterface::TransformMethod::Linear ); + transform.loadRaster( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/landsat.tif" ) ); + QVERIFY( transform.hasExistingGeoreference() ); + + QgsGCPList list; + QgsMapCanvas c1; + QgsMapCanvas c2; + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 781662.375, 3350923.125 ), QgsPointXY( -30, 40 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), + true ) ); + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 787362.375, 3350923.125 ), QgsPointXY( 16697923, -3503549 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), + true ) ); + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 787362.375, 3362323.125 ), QgsPointXY( -35, 42 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), + true ) ); + + list.updateResiduals( &transform, QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), QgsProject::instance()->transformContext(), QgsUnitTypes::RenderPixels ); + QGSCOMPARENEAR( list.at( 0 )->residual().x(), 0, 0.00001 ); + QGSCOMPARENEAR( list.at( 0 )->residual().y(), -189.189, 0.1 ); + QGSCOMPARENEAR( list.at( 1 )->residual().x(), 105.7142, 0.1 ); + QGSCOMPARENEAR( list.at( 1 )->residual().y(), 189.189, 0.1 ); + QGSCOMPARENEAR( list.at( 2 )->residual().x(), -105.7142, 0.1 ); + QGSCOMPARENEAR( list.at( 2 )->residual().y(), 0, 0.00001 ); + + // in map units + list.updateResiduals( &transform, QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), QgsProject::instance()->transformContext(), QgsUnitTypes::RenderMapUnits ); + QGSCOMPARENEAR( list.at( 0 )->residual().x(), 0, 0.00001 ); + QGSCOMPARENEAR( list.at( 0 )->residual().y(), -34.999, 0.1 ); + QGSCOMPARENEAR( list.at( 1 )->residual().x(), -92.499, 0.1 ); + QGSCOMPARENEAR( list.at( 1 )->residual().y(), 34.99999, 0.1 ); + QGSCOMPARENEAR( list.at( 2 )->residual().x(), 92.4999972, 0.1 ); + QGSCOMPARENEAR( list.at( 2 )->residual().y(), 0, 0.00001 ); + + // different target CRS + list.updateResiduals( &transform, QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), QgsProject::instance()->transformContext(), QgsUnitTypes::RenderPixels ); + QGSCOMPARENEAR( list.at( 0 )->residual().x(), 0, 0.00001 ); + QGSCOMPARENEAR( list.at( 0 )->residual().y(), -186.828, 0.1 ); + QGSCOMPARENEAR( list.at( 1 )->residual().x(), 105.7142, 0.1 ); + QGSCOMPARENEAR( list.at( 1 )->residual().y(), 186.828, 0.1 ); + QGSCOMPARENEAR( list.at( 2 )->residual().x(), -105.7142, 0.1 ); + QGSCOMPARENEAR( list.at( 2 )->residual().y(), 0, 0.00001 ); + + // projective transform -- except 0 residuals here + QgsGeorefTransform projective( QgsGcpTransformerInterface::TransformMethod::Projective ); + projective.loadRaster( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/landsat.tif" ) ); + QVERIFY( projective.hasExistingGeoreference() ); + + list.updateResiduals( &projective, QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), QgsProject::instance()->transformContext(), QgsUnitTypes::RenderPixels ); + QGSCOMPARENEAR( list.at( 0 )->residual().x(), 0, 0.00001 ); + QGSCOMPARENEAR( list.at( 0 )->residual().y(), 0, 0.00001 ); + QGSCOMPARENEAR( list.at( 1 )->residual().x(), 0, 0.00001 ); + QGSCOMPARENEAR( list.at( 1 )->residual().y(), 0, 0.00001 ); + QGSCOMPARENEAR( list.at( 2 )->residual().x(), 0, 0.00001 ); + QGSCOMPARENEAR( list.at( 2 )->residual().y(), 0, 0.00001 ); +} + QGSTEST_MAIN( TestQgsGeoreferencer ) #include "testqgsgeoreferencer.moc" From b380a0c7fc19525b9b9920a8f13a6cba262f45c7 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 9 Feb 2022 12:55:27 +1000 Subject: [PATCH 35/47] Use a proper model for gcp list, fix use of delegates, fix gcp points don't move on canvas when edited in list (cherry picked from commit ca2dbef4300c65814850afdc7a4a64cf9cd3a0e2) --- src/app/georeferencer/qgsgcplistmodel.cpp | 311 +++++++++++------- src/app/georeferencer/qgsgcplistmodel.h | 25 +- src/app/georeferencer/qgsgcplistwidget.cpp | 105 +----- src/app/georeferencer/qgsgcplistwidget.h | 19 +- src/app/georeferencer/qgsgeorefdatapoint.cpp | 12 +- src/app/georeferencer/qgsgeorefdelegates.cpp | 10 - src/app/georeferencer/qgsgeorefdelegates.h | 17 - src/app/georeferencer/qgsgeorefmainwindow.cpp | 16 +- tests/src/app/testqgsgeoreferencer.cpp | 105 ++++++ 9 files changed, 358 insertions(+), 262 deletions(-) diff --git a/src/app/georeferencer/qgsgcplistmodel.cpp b/src/app/georeferencer/qgsgcplistmodel.cpp index 99f03ccc1c20..077e0ccbdc12 100644 --- a/src/app/georeferencer/qgsgcplistmodel.cpp +++ b/src/app/georeferencer/qgsgcplistmodel.cpp @@ -22,34 +22,6 @@ #include "qgsproject.h" #include -#include - -class QgsStandardItem : public QStandardItem -{ - public: - explicit QgsStandardItem( const QString &text ) : QStandardItem( text ) - { - // In addition to the DisplayRole, also set the user role, which is used for sorting. - // This is needed for numerical sorting to work correctly (otherwise sorting is lexicographic). - setData( QVariant( text ), Qt::UserRole ); - setTextAlignment( Qt::AlignRight ); - } - - explicit QgsStandardItem( int value ) : QStandardItem( QString::number( value ) ) - { - setData( QVariant( value ), Qt::UserRole ); - setTextAlignment( Qt::AlignCenter ); - } - - explicit QgsStandardItem( double value ) : QStandardItem( QString::number( value, 'f', 4 ) ) - { - setData( QVariant( value ), Qt::UserRole ); - //show the full precision when editing points - setData( QVariant( value ), Qt::EditRole ); - setData( QVariant( value ), Qt::ToolTipRole ); - setTextAlignment( Qt::AlignRight ); - } -}; QgsGCPListModel::QgsGCPListModel( QObject *parent ) : QAbstractTableModel( parent ) @@ -61,13 +33,23 @@ void QgsGCPListModel::setGCPList( QgsGCPList *theGCPList ) beginResetModel(); mGCPList = theGCPList; endResetModel(); + updateResiduals(); } // ------------------------------- public ---------------------------------- // void QgsGCPListModel::setGeorefTransform( QgsGeorefTransform *georefTransform ) { mGeorefTransform = georefTransform; - emit dataChanged( index( 0, 0 ), index( rowCount() - 1, columnCount() - 1 ) ); + updateResiduals(); +} + +void QgsGCPListModel::setTargetCrs( const QgsCoordinateReferenceSystem &targetCrs, const QgsCoordinateTransformContext &context ) +{ + mTargetCrs = targetCrs; + mTransformContext = context; + updateResiduals(); + emit dataChanged( index( 0, static_cast< int >( Column::DestinationX ) ), + index( rowCount() - 1, static_cast< int >( Column::DestinationY ) ) ); } int QgsGCPListModel::rowCount( const QModelIndex & ) const @@ -77,7 +59,7 @@ int QgsGCPListModel::rowCount( const QModelIndex & ) const int QgsGCPListModel::columnCount( const QModelIndex & ) const { - return static_cast< int >( Column::LastColumn ) + 1; + return static_cast< int >( Column::LastColumn ); } QVariant QgsGCPListModel::data( const QModelIndex &index, int role ) const @@ -89,44 +71,117 @@ QVariant QgsGCPListModel::data( const QModelIndex &index, int role ) const || index.column() >= columnCount() ) return QVariant(); - // TODO don't read this from settings!! - const QgsCoordinateReferenceSystem targetCrs( QgsSettings().value( QStringLiteral( "/Plugin-GeoReferencer/targetsrs" ) ).toString() ); - const Column column = static_cast< Column >( index.column() ); const QgsGeorefDataPoint *point = mGCPList->at( index.row() ); switch ( role ) { case Qt::DisplayRole: + case Qt::EditRole: + case Qt::UserRole: + case Qt::ToolTipRole: switch ( column ) { case QgsGCPListModel::Column::Enabled: break; case QgsGCPListModel::Column::ID: return index.row(); + case QgsGCPListModel::Column::SourceX: - return point->sourcePoint().x(); // TODO formatting!! case QgsGCPListModel::Column::SourceY: - return point->sourcePoint().y(); - case QgsGCPListModel::Column::DestinationX: { - const QgsPointXY transformedDestinationPoint = point->transformedDestinationPoint( targetCrs, QgsProject::instance()->transformContext() ); - return transformedDestinationPoint.x(); + switch ( role ) + { + case Qt::ToolTipRole: + case Qt::EditRole: + case Qt::UserRole: + // use full precision + return column == QgsGCPListModel::Column::SourceX ? point->sourcePoint().x() : point->sourcePoint().y(); + + case Qt::DisplayRole: + // truncate decimals for display + return QString::number( + column == QgsGCPListModel::Column::SourceX ? point->sourcePoint().x() : point->sourcePoint().y(), + 'f', 4 ); + default: + break; + } + break; } + + case QgsGCPListModel::Column::DestinationX: case QgsGCPListModel::Column::DestinationY: { - const QgsPointXY transformedDestinationPoint = point->transformedDestinationPoint( targetCrs, QgsProject::instance()->transformContext() ); - return transformedDestinationPoint.y(); + const QgsPointXY transformedDestinationPoint = point->transformedDestinationPoint( mTargetCrs, mTransformContext ); + + switch ( role ) + { + case Qt::ToolTipRole: + case Qt::EditRole: + case Qt::UserRole: + // use full precision + return column == QgsGCPListModel::Column::DestinationX ? transformedDestinationPoint.x() : transformedDestinationPoint.y(); + + case Qt::DisplayRole: + // truncate decimals for display + return QString::number( + column == QgsGCPListModel::Column::DestinationX ? transformedDestinationPoint.x() : transformedDestinationPoint.y(), + 'f', 4 ); + default: + break; + } + break; } + case QgsGCPListModel::Column::ResidualDx: - break; case QgsGCPListModel::Column::ResidualDy: - break; case QgsGCPListModel::Column::TotalResidual: + { + const double dX = point->residual().x(); + const double dY = point->residual().y(); + const double residual = std::sqrt( dX * dX + dY * dY ); + if ( !qgsDoubleNear( residual, 0 ) ) + { + double value = 0; + switch ( column ) + { + case QgsGCPListModel::Column::ResidualDx: + value = dX; + break; + case QgsGCPListModel::Column::ResidualDy: + value = dY; + break; + case QgsGCPListModel::Column::TotalResidual: + value = residual; + break; + default: + break; + } + + switch ( role ) + { + case Qt::ToolTipRole: + case Qt::EditRole: + case Qt::UserRole: + // use full precision + return value; + + case Qt::DisplayRole: + // truncate decimals for display + return QString::number( value, 'f', 4 ); + default: + break; + } + } + else + { + return tr( "n/a" ); + } + BUILTIN_UNREACHABLE break; + } case QgsGCPListModel::Column::LastColumn: break; - } break; @@ -137,6 +192,29 @@ QVariant QgsGCPListModel::data( const QModelIndex &index, int role ) const } break; + case Qt::TextAlignmentRole: + { + switch ( column ) + { + case QgsGCPListModel::Column::Enabled: + return Qt::AlignHCenter; + + case QgsGCPListModel::Column::LastColumn: + break; + + case QgsGCPListModel::Column::ID: + case QgsGCPListModel::Column::SourceX: + case QgsGCPListModel::Column::SourceY: + case QgsGCPListModel::Column::DestinationX: + case QgsGCPListModel::Column::DestinationY: + case QgsGCPListModel::Column::ResidualDx: + case QgsGCPListModel::Column::ResidualDy: + case QgsGCPListModel::Column::TotalResidual: + return Qt::AlignRight; + } + break; + } + case static_cast< int >( Role::SourcePointRole ): return point->sourcePoint(); @@ -144,6 +222,70 @@ QVariant QgsGCPListModel::data( const QModelIndex &index, int role ) const return QVariant(); } +bool QgsGCPListModel::setData( const QModelIndex &index, const QVariant &value, int role ) +{ + if ( !mGCPList + || index.row() < 0 + || index.row() >= mGCPList->size() + || index.column() < 0 + || index.column() >= columnCount() ) + return false; + + QgsGeorefDataPoint *point = mGCPList->at( index.row() ); + const Column column = static_cast< Column >( index.column() ); + switch ( column ) + { + case QgsGCPListModel::Column::Enabled: + if ( role == Qt::CheckStateRole ) + { + const bool checked = static_cast< Qt::CheckState >( value.toInt() ) == Qt::Checked; + point->setEnabled( checked ); + emit dataChanged( index, index ); + updateResiduals(); + emit pointEnabled( point, index.row() ); + return true; + } + break; + + case QgsGCPListModel::Column::SourceX: + case QgsGCPListModel::Column::SourceY: + { + QgsPointXY sourcePoint = point->sourcePoint(); + if ( column == QgsGCPListModel::Column::SourceX ) + sourcePoint.setX( value.toDouble() ); + else + sourcePoint.setY( value.toDouble() ); + point->setSourcePoint( sourcePoint ); + emit dataChanged( index, index ); + updateResiduals(); + return true; + } + + case QgsGCPListModel::Column::DestinationX: + case QgsGCPListModel::Column::DestinationY: + { + QgsPointXY destinationPoint = point->destinationPoint(); + if ( column == QgsGCPListModel::Column::DestinationX ) + destinationPoint.setX( value.toDouble() ); + else + destinationPoint.setY( value.toDouble() ); + point->setDestinationPoint( destinationPoint ); + emit dataChanged( index, index ); + updateResiduals(); + return true; + } + + case QgsGCPListModel::Column::ID: + case QgsGCPListModel::Column::ResidualDx: + case QgsGCPListModel::Column::ResidualDy: + case QgsGCPListModel::Column::TotalResidual: + case QgsGCPListModel::Column::LastColumn: + return false; + } + + return false; +} + Qt::ItemFlags QgsGCPListModel::flags( const QModelIndex &index ) const { if ( !mGCPList @@ -159,13 +301,13 @@ Qt::ItemFlags QgsGCPListModel::flags( const QModelIndex &index ) const case QgsGCPListModel::Column::Enabled: return Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsUserCheckable | Qt::ItemFlag::ItemIsSelectable; - case QgsGCPListModel::Column::ID: case QgsGCPListModel::Column::SourceX: case QgsGCPListModel::Column::SourceY: case QgsGCPListModel::Column::DestinationX: case QgsGCPListModel::Column::DestinationY: - return Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable; + return Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsEditable; + case QgsGCPListModel::Column::ID: case QgsGCPListModel::Column::ResidualDx: case QgsGCPListModel::Column::ResidualDy: case QgsGCPListModel::Column::TotalResidual: @@ -257,85 +399,14 @@ QgsUnitTypes::RenderUnit QgsGCPListModel::residualUnit() const } } - -void QgsGCPListModel::updateModel() +void QgsGCPListModel::updateResiduals() { if ( !mGCPList ) return; - bool bTransformUpdated = false; - QVector sourceCoordinates; - QVector destinationCoordinates; - - // TODO - don't read from settings - const QgsCoordinateReferenceSystem targetCrs( QgsSettings().value( QStringLiteral( "/Plugin-GeoReferencer/targetsrs" ) ).toString() ); - mGCPList->createGCPVectors( sourceCoordinates, destinationCoordinates, targetCrs, QgsProject::instance()->transformContext() ); - - if ( mGeorefTransform ) - { - bTransformUpdated = mGeorefTransform->updateParametersFromGcps( sourceCoordinates, destinationCoordinates, true ); - } - const QgsUnitTypes::RenderUnit unitType = residualUnit(); - - // update residuals - for ( int i = 0; i < mGCPList->size(); ++i ) - { - QgsGeorefDataPoint *p = mGCPList->at( i ); - - if ( !p ) - continue; - - p->setId( i ); - - const QgsPointXY transformedDestinationPoint = p->transformedDestinationPoint( targetCrs, QgsProject::instance()->transformContext() ); - - double residual = 0; - double dX = 0; - double dY = 0; - // Calculate residual if transform is available and up-to-date - if ( mGeorefTransform && bTransformUpdated && mGeorefTransform->parametersInitialized() ) - { - QgsPointXY dst; - const QgsPointXY pixel = mGeorefTransform->toSourcePixel( p->sourcePoint() ); - if ( unitType == QgsUnitTypes::RenderPixels ) - { - // Transform from world to raster coordinate: - // This is the transform direction used by the warp operation. - // As transforms of order >=2 are not invertible, we are only - // interested in the residual in this direction - if ( mGeorefTransform->transformWorldToRaster( transformedDestinationPoint, dst ) ) - { - dX = ( dst.x() - pixel.x() ); - dY = -( dst.y() - pixel.y() ); - } - } - else if ( unitType == QgsUnitTypes::RenderMapUnits ) - { - if ( mGeorefTransform->transformRasterToWorld( pixel, dst ) ) - { - dX = ( dst.x() - transformedDestinationPoint.x() ); - dY = ( dst.y() - transformedDestinationPoint.y() ); - } - } - } - residual = std::sqrt( dX * dX + dY * dY ); - - p->setResidual( QPointF( dX, dY ) ); -#if 0 - if ( residual >= 0.f ) - { - setItem( i, j++, new QgsStandardItem( dX ) ); - setItem( i, j++, new QgsStandardItem( dY ) ); - setItem( i, j++, new QgsStandardItem( residual ) ); - } - else - { - setItem( i, j++, new QgsStandardItem( QStringLiteral( "n/a" ) ) ); - setItem( i, j++, new QgsStandardItem( QStringLiteral( "n/a" ) ) ); - setItem( i, j++, new QgsStandardItem( QStringLiteral( "n/a" ) ) ); - } -#endif - } + mGCPList->updateResiduals( mGeorefTransform, mTargetCrs, mTransformContext, residualUnit() ); + emit dataChanged( index( 0, static_cast< int >( Column::ResidualDx ) ), + index( rowCount() - 1, static_cast< int >( Column::TotalResidual ) ) ); } diff --git a/src/app/georeferencer/qgsgcplistmodel.h b/src/app/georeferencer/qgsgcplistmodel.h index e683f034f32c..312ded4efa5c 100644 --- a/src/app/georeferencer/qgsgcplistmodel.h +++ b/src/app/georeferencer/qgsgcplistmodel.h @@ -16,15 +16,17 @@ #ifndef QGSGCP_LIST_TABLE_VIEW_H #define QGSGCP_LIST_TABLE_VIEW_H -#include #include +#include "qgis_app.h" #include "qgsunittypes.h" +#include "qgscoordinatereferencesystem.h" +#include "qgscoordinatetransformcontext.h" class QgsGeorefDataPoint; class QgsGeorefTransform; class QgsGCPList; -class QgsGCPListModel : public QAbstractTableModel +class APP_EXPORT QgsGCPListModel : public QAbstractTableModel { Q_OBJECT @@ -53,16 +55,33 @@ class QgsGCPListModel : public QAbstractTableModel void setGCPList( QgsGCPList *theGCPList ); void setGeorefTransform( QgsGeorefTransform *georefTransform ); + /** + * Sets the target (output) CRS for the georeferencing. + */ + void setTargetCrs( const QgsCoordinateReferenceSystem &targetCrs, const QgsCoordinateTransformContext &context ); + int rowCount( const QModelIndex &parent = QModelIndex() ) const override; int columnCount( const QModelIndex &parent = QModelIndex() ) const override; QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const override; + bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole ) override; Qt::ItemFlags flags( const QModelIndex &index ) const override; QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const override; - void updateModel(); + /** + * Recalculates the residual values. + */ + void updateResiduals(); + + signals: + + void pointEnabled( QgsGeorefDataPoint *pnt, int i ); + private: QgsUnitTypes::RenderUnit residualUnit() const; + QgsCoordinateReferenceSystem mTargetCrs; + QgsCoordinateTransformContext mTransformContext; + QgsGCPList *mGCPList = nullptr; QgsGeorefTransform *mGeorefTransform = nullptr; }; diff --git a/src/app/georeferencer/qgsgcplistwidget.cpp b/src/app/georeferencer/qgsgcplistwidget.cpp index 78a9eb6ea4f0..54c9e2e8e1f3 100644 --- a/src/app/georeferencer/qgsgcplistwidget.cpp +++ b/src/app/georeferencer/qgsgcplistwidget.cpp @@ -29,7 +29,6 @@ QgsGCPListWidget::QgsGCPListWidget( QWidget *parent ) : QTableView( parent ) , mGCPListModel( new QgsGCPListModel( this ) ) - , mNonEditableDelegate( new QgsNonEditableDelegate( this ) ) , mDmsAndDdDelegate( new QgsDmsAndDdDelegate( this ) ) , mCoordDelegate( new QgsCoordDelegate( this ) ) { @@ -48,14 +47,10 @@ QgsGCPListWidget::QgsGCPListWidget( QWidget *parent ) setAlternatingRowColors( true ); // set delegates for items - setItemDelegateForColumn( 1, mNonEditableDelegate ); // id setItemDelegateForColumn( 2, mCoordDelegate ); // srcX setItemDelegateForColumn( 3, mCoordDelegate ); // srcY setItemDelegateForColumn( 4, mDmsAndDdDelegate ); // dstX setItemDelegateForColumn( 5, mDmsAndDdDelegate ); // dstY - setItemDelegateForColumn( 6, mNonEditableDelegate ); // dX - setItemDelegateForColumn( 7, mNonEditableDelegate ); // dY - setItemDelegateForColumn( 8, mNonEditableDelegate ); // residual connect( this, &QAbstractItemView::doubleClicked, this, &QgsGCPListWidget::itemDoubleClicked ); @@ -64,10 +59,12 @@ QgsGCPListWidget::QgsGCPListWidget( QWidget *parent ) connect( this, &QWidget::customContextMenuRequested, this, &QgsGCPListWidget::showContextMenu ); - connect( mDmsAndDdDelegate, &QAbstractItemDelegate::closeEditor, - this, &QgsGCPListWidget::updateItemCoords ); - connect( mCoordDelegate, &QAbstractItemDelegate::closeEditor, - this, &QgsGCPListWidget::updateItemCoords ); + connect( mGCPListModel, &QgsGCPListModel::pointEnabled, this, [ = ]( QgsGeorefDataPoint * point, int row ) + { + emit pointEnabled( point, row ); + adjustTableContent(); + return; + } ); } void QgsGCPListWidget::setGCPList( QgsGCPList *theGCPList ) @@ -84,9 +81,15 @@ void QgsGCPListWidget::setGeorefTransform( QgsGeorefTransform *georefTransform ) adjustTableContent(); } -void QgsGCPListWidget::updateGCPList() +void QgsGCPListWidget::setTargetCrs( const QgsCoordinateReferenceSystem &targetCrs, const QgsCoordinateTransformContext &context ) +{ + mGCPListModel->setTargetCrs( targetCrs, context ); + adjustTableContent(); +} + +void QgsGCPListWidget::updateResiduals() { - mGCPListModel->updateModel(); + mGCPListModel->updateResiduals(); adjustTableContent(); } @@ -109,26 +112,6 @@ void QgsGCPListWidget::itemClicked( const QModelIndex &index ) { const QModelIndex sourceIndex = static_cast( model() )->mapToSource( index ); -#if 0 - QStandardItem *item = mGCPListModel->item( sourceIndex.row(), sourceIndex.column() ); - if ( item->isCheckable() ) - { - QgsGeorefDataPoint *p = mGCPList->at( sourceIndex.row() ); - if ( item->checkState() == Qt::Checked ) - { - p->setEnabled( true ); - } - else // Qt::Unchecked - { - p->setEnabled( false ); - } - - mGCPListModel->updateModel(); - emit pointEnabled( p, sourceIndex.row() ); - adjustTableContent(); - } -#endif - mPrevRow = sourceIndex.row(); mPrevColumn = sourceIndex.column(); } @@ -148,22 +131,6 @@ void QgsGCPListWidget::keyPressEvent( QKeyEvent *e ) } } } - else if ( e->key() == Qt::Key_Space ) - { - const QModelIndex index = currentIndex(); - if ( index.isValid() ) - { - const QModelIndex sourceIndex = static_cast( model() )->mapToSource( index ); - QgsGeorefDataPoint *p = mGCPList->at( sourceIndex.row() ); - p->setEnabled( !p->isEnabled() ); - - mGCPListModel->updateModel(); - emit pointEnabled( p, sourceIndex.row() ); - adjustTableContent(); - setCurrentIndex( model()->index( index.row(), index.column() ) ); - return; - } - } else if ( e->key() == Qt::Key_Up ) { const QModelIndex index = currentIndex(); @@ -203,45 +170,6 @@ void QgsGCPListWidget::keyPressEvent( QKeyEvent *e ) e->ignore(); } -void QgsGCPListWidget::updateItemCoords( QWidget *editor ) -{ - QLineEdit *lineEdit = qobject_cast( editor ); - QgsGeorefDataPoint *dataPoint = mGCPList->at( mPrevRow ); - if ( lineEdit ) - { - const double value = lineEdit->text().toDouble(); - QgsPointXY newMapCoords( dataPoint->destinationPoint() ); - - QgsPointXY newSourceCoords( dataPoint->sourcePoint() ); - if ( mPrevColumn == 2 ) // srcX - { - newSourceCoords.setX( value ); - } - else if ( mPrevColumn == 3 ) // srcY - { - newSourceCoords.setY( value ); - } - else if ( mPrevColumn == 4 ) // dstX - { - newMapCoords.setX( value ); - } - else if ( mPrevColumn == 5 ) // dstY - { - newMapCoords.setY( value ); - } - else - { - return; - } - - dataPoint->setSourcePoint( newSourceCoords ); - dataPoint->setDestinationPoint( newMapCoords ); - } - - dataPoint->updateCoords(); - updateGCPList(); -} - void QgsGCPListWidget::showContextMenu( QPoint p ) { if ( !mGCPList || 0 == mGCPList->count() ) @@ -277,11 +205,6 @@ void QgsGCPListWidget::removeRow() emit deleteDataPoint( index.row() ); } -void QgsGCPListWidget::editCell() -{ - edit( currentIndex() ); -} - void QgsGCPListWidget::jumpToSourcePoint( const QModelIndex &modelIndex ) { const QgsPointXY sourcePoint = mGCPListModel->data( modelIndex, static_cast< int >( QgsGCPListModel::Role::SourcePointRole ) ).value< QgsPointXY >(); diff --git a/src/app/georeferencer/qgsgcplistwidget.h b/src/app/georeferencer/qgsgcplistwidget.h index cf85e4e0fea8..572658bb9ca3 100644 --- a/src/app/georeferencer/qgsgcplistwidget.h +++ b/src/app/georeferencer/qgsgcplistwidget.h @@ -18,7 +18,6 @@ #include class QgsDoubleSpinBoxDelegate; -class QgsNonEditableDelegate; class QgsDmsAndDdDelegate; class QgsCoordDelegate; @@ -27,6 +26,8 @@ class QgsGCPListModel; class QgsGeorefTransform; class QgsGeorefDataPoint; class QgsPointXY; +class QgsCoordinateReferenceSystem; +class QgsCoordinateTransformContext; class QgsGCPListWidget : public QTableView { @@ -36,8 +37,19 @@ class QgsGCPListWidget : public QTableView void setGCPList( QgsGCPList *theGCPList ); void setGeorefTransform( QgsGeorefTransform *georefTransform ); + + /** + * Sets the target (output) CRS for the georeferencing. + */ + void setTargetCrs( const QgsCoordinateReferenceSystem &targetCrs, const QgsCoordinateTransformContext &context ); + QgsGCPList *gcpList() { return mGCPList; } - void updateGCPList(); + + /** + * Recalculates the residual values. + */ + void updateResiduals(); + void closeEditors(); void keyPressEvent( QKeyEvent *e ) override; @@ -53,10 +65,8 @@ class QgsGCPListWidget : public QTableView void deleteDataPoint( int index ); private slots: - void updateItemCoords( QWidget *editor ); void showContextMenu( QPoint ); void removeRow(); - void editCell(); void jumpToSourcePoint( const QModelIndex &modelIndex ); private: @@ -67,7 +77,6 @@ class QgsGCPListWidget : public QTableView QgsGCPList *mGCPList = nullptr; QgsGCPListModel *mGCPListModel = nullptr; - QgsNonEditableDelegate *mNonEditableDelegate = nullptr; QgsDmsAndDdDelegate *mDmsAndDdDelegate = nullptr; QgsCoordDelegate *mCoordDelegate = nullptr; diff --git a/src/app/georeferencer/qgsgeorefdatapoint.cpp b/src/app/georeferencer/qgsgeorefdatapoint.cpp index 1e1d029bc3df..43dcde1ebb70 100644 --- a/src/app/georeferencer/qgsgeorefdatapoint.cpp +++ b/src/app/georeferencer/qgsgeorefdatapoint.cpp @@ -99,21 +99,13 @@ QgsGeorefDataPoint::~QgsGeorefDataPoint() void QgsGeorefDataPoint::setSourcePoint( const QgsPointXY &p ) { mGcpPoint.setSourcePoint( p ); - mGCPSourceItem->update(); - mGCPDestinationItem->update(); + updateCoords(); } void QgsGeorefDataPoint::setDestinationPoint( const QgsPointXY &p ) { mGcpPoint.setDestinationPoint( p ); - if ( mGCPSourceItem ) - { - mGCPSourceItem->update(); - } - if ( mGCPDestinationItem ) - { - mGCPDestinationItem->update(); - } + updateCoords(); } QgsPointXY QgsGeorefDataPoint::transformedDestinationPoint( const QgsCoordinateReferenceSystem &targetCrs, const QgsCoordinateTransformContext &context ) const diff --git a/src/app/georeferencer/qgsgeorefdelegates.cpp b/src/app/georeferencer/qgsgeorefdelegates.cpp index 79d0627658c4..4bf96cb48db9 100644 --- a/src/app/georeferencer/qgsgeorefdelegates.cpp +++ b/src/app/georeferencer/qgsgeorefdelegates.cpp @@ -21,12 +21,6 @@ #include "qgsgeorefdelegates.h" #include -// ------------------------ QgsNonEditableDelegate ------------------------- // -QgsNonEditableDelegate::QgsNonEditableDelegate( QWidget *parent ) - : QStyledItemDelegate( parent ) -{ -} - // ------------------------- QgsDmsAndDdDelegate --------------------------- // QgsDmsAndDdDelegate::QgsDmsAndDdDelegate( QWidget *parent ) : QStyledItemDelegate( parent ) @@ -63,8 +57,6 @@ void QgsDmsAndDdDelegate::setModelData( QWidget *editor, QAbstractItemModel *mod value = stringValue.toDouble(); model->setData( index, value, Qt::EditRole ); - model->setData( index, value, Qt::DisplayRole ); - model->setData( index, value, Qt::ToolTipRole ); } void QgsDmsAndDdDelegate::updateEditorGeometry( QWidget *editor, const QStyleOptionViewItem &option, @@ -121,8 +113,6 @@ void QgsCoordDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QString stringValue = lineEdit->text(); const double value = stringValue.toDouble(); model->setData( index, value, Qt::EditRole ); - model->setData( index, value, Qt::DisplayRole ); - model->setData( index, value, Qt::ToolTipRole ); } void QgsCoordDelegate::updateEditorGeometry( QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index ) const diff --git a/src/app/georeferencer/qgsgeorefdelegates.h b/src/app/georeferencer/qgsgeorefdelegates.h index 87b730e063e7..dd82515dddc3 100644 --- a/src/app/georeferencer/qgsgeorefdelegates.h +++ b/src/app/georeferencer/qgsgeorefdelegates.h @@ -17,23 +17,6 @@ #include -class QgsNonEditableDelegate : public QStyledItemDelegate -{ - Q_OBJECT - - public: - explicit QgsNonEditableDelegate( QWidget *parent = nullptr ); - - QWidget *createEditor( QWidget *parent, const QStyleOptionViewItem &option, - const QModelIndex &index ) const override - { - Q_UNUSED( parent ) - Q_UNUSED( option ) - Q_UNUSED( index ) - return nullptr; - } -}; - class QgsDmsAndDdDelegate : public QStyledItemDelegate { Q_OBJECT diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index eda6394d73cd..50dfa965d825 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -543,7 +543,7 @@ void QgsGeoreferencerMainWindow::deleteDataPoint( QPoint coords ) { delete *it; mPoints.erase( it ); - mGCPListWidget->updateGCPList(); + mGCPListWidget->updateResiduals(); mCanvas->refresh(); break; @@ -556,7 +556,8 @@ void QgsGeoreferencerMainWindow::deleteDataPoint( int theGCPIndex ) { Q_ASSERT( theGCPIndex >= 0 ); delete mPoints.takeAt( theGCPIndex ); - mGCPListWidget->updateGCPList(); + // TODO -- would be cleaner to move responsibility for this to the model! + mGCPListWidget->setGCPList( &mPoints ); updateGeorefTransform(); } @@ -592,7 +593,7 @@ void QgsGeoreferencerMainWindow::movePoint( QPoint canvasPixels ) void QgsGeoreferencerMainWindow::releasePoint( QPoint p ) { Q_UNUSED( p ) - mGCPListWidget->updateGCPList(); + mGCPListWidget->updateResiduals(); // Get Map Sender if ( sender() == mToolMovePoint ) { @@ -706,7 +707,9 @@ void QgsGeoreferencerMainWindow::showGeorefConfigDialog() //update gcp model if ( mGCPListWidget ) { - mGCPListWidget->updateGCPList(); + const QgsCoordinateReferenceSystem targetCrs( QgsSettings().value( QStringLiteral( "/Plugin-GeoReferencer/targetsrs" ) ).toString() ); + mGCPListWidget->setTargetCrs( targetCrs, QgsProject::instance()->transformContext() ); + mGCPListWidget->updateResiduals(); } //and status bar updateTransformParamLabel(); @@ -1211,8 +1214,9 @@ bool QgsGeoreferencerMainWindow::loadGCPs( QString &error ) return false; QgsSettings().setValue( QStringLiteral( "/Plugin-GeoReferencer/targetsrs" ), actualDestinationCrs.authid() ); - clearGCPData(); + mGCPListWidget->setTargetCrs( actualDestinationCrs, QgsProject::instance()->transformContext() ); + for ( const QgsGcpPoint &point : points ) { addPoint( point.sourcePoint(), point.destinationPoint(), point.destinationPointCrs(), point.isEnabled(), false ); @@ -2087,7 +2091,7 @@ void QgsGeoreferencerMainWindow::clearGCPData() qDeleteAll( mPoints ); mPoints.clear(); - mGCPListWidget->updateGCPList(); + mGCPListWidget->setGCPList( &mPoints ); delete mNewlyAddedPointItem; mNewlyAddedPointItem = nullptr; diff --git a/tests/src/app/testqgsgeoreferencer.cpp b/tests/src/app/testqgsgeoreferencer.cpp index d42b76770696..262909958511 100644 --- a/tests/src/app/testqgsgeoreferencer.cpp +++ b/tests/src/app/testqgsgeoreferencer.cpp @@ -26,6 +26,7 @@ #include "georeferencer/qgsgeoreftransform.h" #include "georeferencer/qgsgeorefdatapoint.h" #include "georeferencer/qgsgcplist.h" +#include "georeferencer/qgsgcplistmodel.h" /** * \ingroup UnitTests @@ -50,6 +51,7 @@ class TestQgsGeoreferencer : public QObject void testTransformImageWithExistingGeoreference(); void testRasterChangeCoords(); void testUpdateResiduals(); + void testListModel(); private: QgisApp *mQgisApp = nullptr; @@ -544,5 +546,108 @@ void TestQgsGeoreferencer::testUpdateResiduals() QGSCOMPARENEAR( list.at( 2 )->residual().y(), 0, 0.00001 ); } +void TestQgsGeoreferencer::testListModel() +{ + // test the gcp list model + QgsGCPList list; + QgsMapCanvas c1; + QgsMapCanvas c2; + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 781662.375, 3350923.125 ), QgsPointXY( -30, 40 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), + true ) ); + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 787362.375, 3350923.125 ), QgsPointXY( 16697923, -3503549 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), + true ) ); + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 787362.375, 3362323.125 ), QgsPointXY( -35, 42 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), + true ) ); + QgsGeorefTransform transform( QgsGcpTransformerInterface::TransformMethod::Linear ); + transform.loadRaster( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/landsat.tif" ) ); + QVERIFY( transform.hasExistingGeoreference() ); + list.updateResiduals( &transform, QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), QgsProject::instance()->transformContext(), QgsUnitTypes::RenderPixels ); + + QgsGCPListModel model; + QCOMPARE( model.rowCount(), 0 ); + QCOMPARE( model.columnCount(), 9 ); + + model.setGCPList( &list ); + model.setTargetCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), QgsProject::instance()->transformContext() ); + + QCOMPARE( model.rowCount(), 3 ); + QCOMPARE( model.data( model.index( 0, 1 ) ).toString(), QStringLiteral( "0" ) ); + QCOMPARE( model.data( model.index( 0, 2 ) ).toString(), QStringLiteral( "781662.3750" ) ); + QCOMPARE( model.data( model.index( 0, 3 ) ).toString(), QStringLiteral( "3350923.1250" ) ); + QCOMPARE( model.data( model.index( 0, 4 ) ).toString(), QStringLiteral( "-30.0000" ) ); + QCOMPARE( model.data( model.index( 0, 5 ) ).toString(), QStringLiteral( "40.0000" ) ); + QCOMPARE( model.data( model.index( 0, 6 ) ).toString(), QStringLiteral( "n/a" ) ); + QCOMPARE( model.data( model.index( 0, 7 ) ).toString(), QStringLiteral( "n/a" ) ); + QCOMPARE( model.data( model.index( 0, 8 ) ).toString(), QStringLiteral( "n/a" ) ); + + QCOMPARE( model.data( model.index( 1, 1 ) ).toString(), QStringLiteral( "1" ) ); + QCOMPARE( model.data( model.index( 1, 2 ) ).toString(), QStringLiteral( "787362.3750" ) ); + QCOMPARE( model.data( model.index( 1, 3 ) ).toString(), QStringLiteral( "3350923.1250" ) ); + QCOMPARE( model.data( model.index( 1, 4 ) ).toString(), QStringLiteral( "150.0000" ) ); + QCOMPARE( model.data( model.index( 1, 5 ) ).toString(), QStringLiteral( "-30.0000" ) ); + QCOMPARE( model.data( model.index( 1, 6 ) ).toString(), QStringLiteral( "n/a" ) ); + QCOMPARE( model.data( model.index( 1, 7 ) ).toString(), QStringLiteral( "n/a" ) ); + QCOMPARE( model.data( model.index( 1, 8 ) ).toString(), QStringLiteral( "n/a" ) ); + + QCOMPARE( model.data( model.index( 2, 1 ) ).toString(), QStringLiteral( "2" ) ); + QCOMPARE( model.data( model.index( 2, 2 ) ).toString(), QStringLiteral( "787362.3750" ) ); + QCOMPARE( model.data( model.index( 2, 3 ) ).toString(), QStringLiteral( "3362323.1250" ) ); + QCOMPARE( model.data( model.index( 2, 4 ) ).toString(), QStringLiteral( "-35.0000" ) ); + QCOMPARE( model.data( model.index( 2, 5 ) ).toString(), QStringLiteral( "42.0000" ) ); + QCOMPARE( model.data( model.index( 2, 6 ) ).toString(), QStringLiteral( "n/a" ) ); + QCOMPARE( model.data( model.index( 2, 7 ) ).toString(), QStringLiteral( "n/a" ) ); + QCOMPARE( model.data( model.index( 2, 8 ) ).toString(), QStringLiteral( "n/a" ) ); + + // with transform set residuals should be visible + model.setGeorefTransform( &transform ); + QCOMPARE( model.data( model.index( 0, 6 ) ).toString(), QStringLiteral( "0.0000" ) ); + QCOMPARE( model.data( model.index( 0, 7 ) ).toString(), QStringLiteral( "-189.1892" ) ); + QCOMPARE( model.data( model.index( 0, 8 ) ).toString(), QStringLiteral( "189.1892" ) ); + + QCOMPARE( model.data( model.index( 1, 6 ) ).toString(), QStringLiteral( "105.7143" ) ); + QCOMPARE( model.data( model.index( 1, 7 ) ).toString(), QStringLiteral( "189.1892" ) ); + QCOMPARE( model.data( model.index( 1, 8 ) ).toString(), QStringLiteral( "216.7212" ) ); + + QCOMPARE( model.data( model.index( 2, 6 ) ).toString(), QStringLiteral( "-105.7143" ) ); + QCOMPARE( model.data( model.index( 2, 7 ) ).toString(), QStringLiteral( "0.0000" ) ); + QCOMPARE( model.data( model.index( 2, 8 ) ).toString(), QStringLiteral( "105.7143" ) ); + + // set data + // these columns are read-only + QVERIFY( !model.setData( model.index( 0, 0 ), 11 ) ); + QVERIFY( !model.setData( model.index( 0, 1 ), 11 ) ); + QVERIFY( !model.setData( model.index( 0, 6 ), 11 ) ); + QVERIFY( !model.setData( model.index( 0, 7 ), 11 ) ); + QVERIFY( !model.setData( model.index( 0, 8 ), 11 ) ); + + QVERIFY( model.setData( model.index( 0, 2 ), 777777.77 ) ); + QCOMPARE( model.data( model.index( 0, 2 ) ).toString(), QStringLiteral( "777777.7700" ) ); + QCOMPARE( list.at( 0 )->sourcePoint().x(), 777777.77 ); + + QVERIFY( model.setData( model.index( 0, 3 ), 3333333.33 ) ); + QCOMPARE( model.data( model.index( 0, 3 ) ).toString(), QStringLiteral( "3333333.3300" ) ); + QCOMPARE( list.at( 0 )->sourcePoint().y(), 3333333.33 ); + + QVERIFY( model.setData( model.index( 0, 4 ), 1660000.77 ) ); + QCOMPARE( model.data( model.index( 0, 4 ) ).toString(), QStringLiteral( "1660000.7700" ) ); + QCOMPARE( list.at( 0 )->destinationPoint().x(), 1660000.77 ); + + QVERIFY( model.setData( model.index( 0, 5 ), 4433333.33 ) ); + QCOMPARE( model.data( model.index( 0, 5 ) ).toString(), QStringLiteral( "4433333.3300" ) ); + QCOMPARE( list.at( 0 )->destinationPoint().y(), 4433333.33 ); + + // disable point + QVERIFY( model.setData( model.index( 0, 0 ), Qt::Unchecked, Qt::CheckStateRole ) ); + QCOMPARE( model.data( model.index( 0, 0 ), Qt::CheckStateRole ), Qt::Unchecked ); + QVERIFY( !list.at( 0 )->isEnabled() ); + // enable point + QVERIFY( model.setData( model.index( 0, 0 ), Qt::Checked, Qt::CheckStateRole ) ); + QCOMPARE( model.data( model.index( 0, 0 ), Qt::CheckStateRole ), Qt::Checked ); + QVERIFY( list.at( 0 )->isEnabled() ); +} + QGSTEST_MAIN( TestQgsGeoreferencer ) #include "testqgsgeoreferencer.moc" From 20a8b0759ec157d6ff2867555625d7b19ca552f4 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 10 Feb 2022 09:15:58 +1000 Subject: [PATCH 36/47] [georeferencer] Cleanup target CRS handling, and ensure destination points in table are always updated to reflect actual target CRS whenever it is changed (cherry picked from commit 15d7c8a301aa9d7cd129fdf64e34727c703b2577) --- src/app/georeferencer/qgsgeorefmainwindow.cpp | 124 ++++-------------- src/app/georeferencer/qgsgeorefmainwindow.h | 2 +- .../qgstransformsettingsdialog.cpp | 18 ++- .../qgstransformsettingsdialog.h | 12 +- 4 files changed, 51 insertions(+), 105 deletions(-) diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index 50dfa965d825..3d652ffbfcb5 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -87,6 +87,11 @@ QgsGeoreferencerMainWindow::QgsGeoreferencerMainWindow( QWidget *parent, Qt::Win centralWidget->setLayout( mCentralLayout ); mCentralLayout->setContentsMargins( 0, 0, 0, 0 ); + QgsSettings settings; + // default to last used target CRS + QString targetCRSString = settings.value( QStringLiteral( "/Plugin-GeoReferencer/targetsrs" ) ).toString(); + mTargetCrs = QgsCoordinateReferenceSystem( targetCRSString ); + createActions(); createActionGroups(); createMenus(); @@ -108,7 +113,6 @@ QgsGeoreferencerMainWindow::QgsGeoreferencerMainWindow( QWidget *parent, Qt::Win mCanvas->clearExtentHistory(); // reset zoomnext/zoomlast - QgsSettings settings; if ( settings.value( QStringLiteral( "/Plugin-GeoReferencer/Config/ShowDocked" ) ).toBool() ) { dockThisWindow( true ); @@ -151,7 +155,6 @@ QgsGeoreferencerMainWindow::~QgsGeoreferencerMainWindow() delete mToolMovePointQgis; } -// ----------------------------- protected --------------------------------- // void QgsGeoreferencerMainWindow::closeEvent( QCloseEvent *e ) { switch ( checkNeedGCPSave() ) @@ -196,11 +199,8 @@ void QgsGeoreferencerMainWindow::reset() } } -// -------------------------- private slots -------------------------------- // -// File slots void QgsGeoreferencerMainWindow::openRaster( const QString &fileName ) { - // clearLog(); switch ( checkNeedGCPSave() ) { case QgsGeoreferencerMainWindow::GCPSAVE: @@ -254,8 +254,6 @@ void QgsGeoreferencerMainWindow::openRaster( const QString &fileName ) statusBar()->showMessage( tr( "Raster loaded: %1" ).arg( mRasterFileName ) ); setWindowTitle( tr( "Georeferencer - %1" ).arg( fileInfo.fileName() ) ); - // showMessageInLog(tr("Input raster"), mRasterFileName); - //delete old points clearGCPData(); @@ -285,7 +283,7 @@ void QgsGeoreferencerMainWindow::openRaster( const QString &fileName ) mActionLinkQGisToGeoref->setChecked( false ); } - mCanvas->clearExtentHistory(); // reset zoomnext/zoomlast + mCanvas->clearExtentHistory(); mWorldFileName = guessWorldFileName( mRasterFileName ); } @@ -347,14 +345,6 @@ void QgsGeoreferencerMainWindow::doGeoreference() { QgisApp::instance()->addRasterLayer( mModifiedRasterFileName, QFileInfo( mModifiedRasterFileName ).completeBaseName(), QString() ); } - - // showMessageInLog(tr("Modified raster saved in"), mModifiedRasterFileName); - // saveGCPs(); - - // mTransformParam = QgsGeorefTransform::InvalidTransform; - // mGeorefTransform.selectTransformParametrisation(mTransformParam); - // mGCPListWidget->setGeorefTransform(&mGeorefTransform); - // mTransformParamLabel->setText(tr("Transform: ") + convertTransformEnumToString(mTransformParam)); } } } @@ -362,23 +352,21 @@ void QgsGeoreferencerMainWindow::doGeoreference() bool QgsGeoreferencerMainWindow::getTransformSettings() { QgsTransformSettingsDialog d( mRasterFileName, mModifiedRasterFileName ); + d.setTargetCrs( mTargetCrs ); if ( !d.exec() ) { return false; } d.getTransformSettings( mTransformParam, mResamplingMethod, mCompressionMethod, - mModifiedRasterFileName, mProjection, mPdfOutputMapFile, mPdfOutputFile, mSaveGcp, mUseZeroForTrans, mLoadInQgis, mUserResX, mUserResY ); + mModifiedRasterFileName, mPdfOutputMapFile, mPdfOutputFile, mSaveGcp, mUseZeroForTrans, mLoadInQgis, mUserResX, mUserResY ); + mTargetCrs = d.targetCrs(); + mTransformParamLabel->setText( tr( "Transform: " ) + QgsGcpTransformerInterface::methodToString( mTransformParam ) ); mGeorefTransform.selectTransformParametrisation( mTransformParam ); mGCPListWidget->setGeorefTransform( &mGeorefTransform ); mWorldFileName = guessWorldFileName( mRasterFileName ); - // showMessageInLog(tr("Output raster"), mModifiedRasterFileName.isEmpty() ? tr("Non set") : mModifiedRasterFileName); - // showMessageInLog(tr("Target projection"), mProjection.isEmpty() ? tr("Non set") : mProjection); - // logTransformOptions(); - // logRequaredGCPs(); - const bool hasReferencing = QgsGcpTransformerInterface::TransformMethod::InvalidTransform != mTransformParam || mLayer->crs().isValid(); mActionLinkGeorefToQgis->setEnabled( hasReferencing ); @@ -389,6 +377,13 @@ bool QgsGeoreferencerMainWindow::getTransformSettings() mActionLinkQGisToGeoref->setChecked( false ); } + //update gcp model + if ( mGCPListWidget ) + { + mGCPListWidget->setTargetCrs( mTargetCrs, QgsProject::instance()->transformContext() ); + mGCPListWidget->updateResiduals(); + } + updateTransformParamLabel(); return true; } @@ -429,7 +424,6 @@ void QgsGeoreferencerMainWindow::generateGDALScript() } } -// Edit slots void QgsGeoreferencerMainWindow::setAddPointTool() { mCanvas->setMapTool( mToolAddPoint ); @@ -456,7 +450,6 @@ void QgsGeoreferencerMainWindow::setMovePointTool() QgisApp::instance()->mapCanvas()->setMapTool( mToolMovePointQgis ); } -// View slots void QgsGeoreferencerMainWindow::setPanTool() { mCanvas->setMapTool( mToolPan ); @@ -509,7 +502,6 @@ void QgsGeoreferencerMainWindow::linkGeorefToQgis( bool link ) } } -// GCPs slots void QgsGeoreferencerMainWindow::addPoint( const QgsPointXY &sourceCoords, const QgsPointXY &destinationMapCoords, const QgsCoordinateReferenceSystem &destinationCrs, bool enable, bool finalize ) { @@ -615,7 +607,7 @@ void QgsGeoreferencerMainWindow::showCoordDialog( const QgsPointXY &sourceCoordi mNewlyAddedPointItem->setPointColor( QColor( 0, 200, 0 ) ); mNewlyAddedPointItem->setPos( mNewlyAddedPointItem->toCanvasCoordinates( sourceCoordinates ) ); - QgsCoordinateReferenceSystem lastProjection = mLastGCPProjection.isValid() ? mLastGCPProjection : mProjection; + QgsCoordinateReferenceSystem lastProjection = mLastGCPProjection.isValid() ? mLastGCPProjection : mTargetCrs; if ( mLayer && !mMapCoordsDialog ) { mMapCoordsDialog = new QgsMapCoordsDialog( QgisApp::instance()->mapCanvas(), sourceCoordinates, lastProjection, this ); @@ -673,7 +665,6 @@ void QgsGeoreferencerMainWindow::saveGCPsDialog() saveGCPs(); } -// Settings slots void QgsGeoreferencerMainWindow::showRasterPropertiesDialog() { if ( mLayer ) @@ -707,8 +698,6 @@ void QgsGeoreferencerMainWindow::showGeorefConfigDialog() //update gcp model if ( mGCPListWidget ) { - const QgsCoordinateReferenceSystem targetCrs( QgsSettings().value( QStringLiteral( "/Plugin-GeoReferencer/targetsrs" ) ).toString() ); - mGCPListWidget->setTargetCrs( targetCrs, QgsProject::instance()->transformContext() ); mGCPListWidget->updateResiduals(); } //and status bar @@ -716,7 +705,6 @@ void QgsGeoreferencerMainWindow::showGeorefConfigDialog() } } -// Histogram stretch slots void QgsGeoreferencerMainWindow::fullHistogramStretch() { mLayer->setContrastEnhancement( QgsContrastEnhancement::StretchToMinimumMaximum ); @@ -737,7 +725,6 @@ void QgsGeoreferencerMainWindow::recenterOnPoint( const QgsPointXY &point ) mCanvas->refresh(); } -// This slot is called whenever the georeference canvas changes the displayed extent void QgsGeoreferencerMainWindow::extentsChangedGeorefCanvas() { // Guard against endless recursion by ping-pong updates @@ -766,7 +753,6 @@ void QgsGeoreferencerMainWindow::extentsChangedGeorefCanvas() } } -// This slot is called whenever the qgis main canvas changes the displayed extent void QgsGeoreferencerMainWindow::extentsChangedQGisCanvas() { // Guard against endless recursion by ping-pong updates @@ -803,7 +789,6 @@ void QgsGeoreferencerMainWindow::updateCanvasRotation() mCanvas->refresh(); } -// Canvas info slots (copy/pasted from QGIS :) ) void QgsGeoreferencerMainWindow::showMouseCoords( const QgsPointXY &p ) { mCoordsLabel->setText( p.toString( mMousePrecisionDecimalPlaces ) ); @@ -843,8 +828,6 @@ void QgsGeoreferencerMainWindow::updateMouseCoordinatePrecision() mMousePrecisionDecimalPlaces = dp; } -// ------------------------------ private ---------------------------------- // -// Gui void QgsGeoreferencerMainWindow::createActions() { // File actions @@ -1019,7 +1002,6 @@ void QgsGeoreferencerMainWindow::createMenus() mPanelMenu = new QMenu( tr( "Panels" ) ); mPanelMenu->setObjectName( QStringLiteral( "mPanelMenu" ) ); mPanelMenu->addAction( dockWidgetGCPpoints->toggleViewAction() ); - // mPanelMenu->addAction(dockWidgetLogView->toggleViewAction()); mToolbarMenu = new QMenu( tr( "Toolbars" ) ); mToolbarMenu->setObjectName( QStringLiteral( "mToolbarMenu" ) ); @@ -1039,7 +1021,7 @@ void QgsGeoreferencerMainWindow::createMenus() menuView->addMenu( mPanelMenu ); menuView->addMenu( mToolbarMenu ); } - else // if ( layout == QDialogButtonBox::KdeLayout ) + else { menuSettings->addSeparator(); menuSettings->addMenu( mPanelMenu ); @@ -1049,20 +1031,11 @@ void QgsGeoreferencerMainWindow::createMenus() void QgsGeoreferencerMainWindow::createDockWidgets() { - // mLogViewer = new QPlainTextEdit; - // mLogViewer->setReadOnly(true); - // mLogViewer->setWordWrapMode(QTextOption::NoWrap); - // dockWidgetLogView->setWidget(mLogViewer); - mGCPListWidget = new QgsGCPListWidget( this ); mGCPListWidget->setGeorefTransform( &mGeorefTransform ); dockWidgetGCPpoints->setWidget( mGCPListWidget ); connect( mGCPListWidget, &QgsGCPListWidget::jumpToGCP, this, &QgsGeoreferencerMainWindow::recenterOnPoint ); -#if 0 - connect( mGCPListWidget, SIGNAL( replaceDataPoint( QgsGeorefDataPoint *, int ) ), - this, SLOT( replaceDataPoint( QgsGeorefDataPoint *, int ) ) ); -#endif connect( mGCPListWidget, static_cast( &QgsGCPListWidget::deleteDataPoint ), this, static_cast( &QgsGeoreferencerMainWindow::deleteDataPoint ) ); connect( mGCPListWidget, &QgsGCPListWidget::pointEnabled, this, &QgsGeoreferencerMainWindow::updateGeorefTransform ); @@ -1143,7 +1116,6 @@ void QgsGeoreferencerMainWindow::removeOldLayer() mCanvas->refresh(); } -// Mapcanvas Plugin void QgsGeoreferencerMainWindow::addRaster( const QString &file ) { QgsRasterLayer::LayerOptions options; @@ -1171,7 +1143,6 @@ void QgsGeoreferencerMainWindow::addRaster( const QString &file ) } } -// Settings void QgsGeoreferencerMainWindow::readSettings() { QgsSettings s; @@ -1202,7 +1173,6 @@ void QgsGeoreferencerMainWindow::writeSettings() s.setValue( QStringLiteral( "/Plugin-GeoReferencer/usezerofortrans" ), mUseZeroForTrans ); } -// GCP points bool QgsGeoreferencerMainWindow::loadGCPs( QString &error ) { QgsCoordinateReferenceSystem actualDestinationCrs; @@ -1213,7 +1183,7 @@ bool QgsGeoreferencerMainWindow::loadGCPs( QString &error ) if ( !error.isEmpty() ) return false; - QgsSettings().setValue( QStringLiteral( "/Plugin-GeoReferencer/targetsrs" ), actualDestinationCrs.authid() ); + mTargetCrs = actualDestinationCrs; clearGCPData(); mGCPListWidget->setTargetCrs( actualDestinationCrs, QgsProject::instance()->transformContext() ); @@ -1223,7 +1193,6 @@ bool QgsGeoreferencerMainWindow::loadGCPs( QString &error ) } mSavedPoints = mPoints.asPoints(); - // showMessageInLog(tr("GCP points loaded from"), mGCPpointsFileName); if ( mGCPsDirty ) { mGCPListWidget->setGCPList( &mPoints ); @@ -1238,7 +1207,7 @@ bool QgsGeoreferencerMainWindow::loadGCPs( QString &error ) void QgsGeoreferencerMainWindow::saveGCPs() { QString error; - if ( mPoints.saveGcps( mGCPpointsFileName, mProjection, QgsProject::instance()->transformContext(), error ) ) + if ( mPoints.saveGcps( mGCPpointsFileName, mTargetCrs, QgsProject::instance()->transformContext(), error ) ) { mSavedPoints = mPoints.asPoints(); } @@ -1271,7 +1240,6 @@ QgsGeoreferencerMainWindow::SaveGCPs QgsGeoreferencerMainWindow::checkNeedGCPSav return QgsGeoreferencerMainWindow::GCPDISCARD; } -// Georeference bool QgsGeoreferencerMainWindow::georeference() { if ( !checkReadyGeoref() ) @@ -1328,7 +1296,7 @@ bool QgsGeoreferencerMainWindow::georeference() { QgsImageWarper warper( this ); int res = warper.warpFile( mRasterFileName, mModifiedRasterFileName, mGeorefTransform, - mResamplingMethod, mUseZeroForTrans, mCompressionMethod, mProjection, mUserResX, mUserResY ); + mResamplingMethod, mUseZeroForTrans, mCompressionMethod, mTargetCrs, mUserResX, mUserResY ); if ( res == 0 ) // fault to compute GCP transform { //TODO: be more specific in the error message @@ -1700,7 +1668,7 @@ bool QgsGeoreferencerMainWindow::writePDFReportFile( const QString &fileName, co currentGCPStrings << tr( "no" ); } - const QgsPointXY transformedDestinationPoint = ( *gcpIt )->transformedDestinationPoint( mProjection, QgsProject::instance()->transformContext() ); + const QgsPointXY transformedDestinationPoint = ( *gcpIt )->transformedDestinationPoint( mTargetCrs, QgsProject::instance()->transformContext() ); currentGCPStrings << QString::number( ( *gcpIt )->sourcePoint().x(), 'f', 0 ) << QString::number( ( *gcpIt )->sourcePoint().y(), 'f', 0 ) << QString::number( transformedDestinationPoint.x(), 'f', 3 ) << QString::number( transformedDestinationPoint.y(), 'f', 3 ) << QString::number( residual.x() ) << QString::number( residual.y() ) << QString::number( residualTot ); @@ -1763,7 +1731,6 @@ void QgsGeoreferencerMainWindow::updateTransformParamLabel() mTransformParamLabel->setText( labelString ); } -// Gdal script void QgsGeoreferencerMainWindow::showGDALScript( const QStringList &commands ) { QString script = commands.join( QLatin1Char( '\n' ) ) + '\n'; @@ -1808,7 +1775,7 @@ QString QgsGeoreferencerMainWindow::generateGDALtranslateCommand( bool generateT for ( QgsGeorefDataPoint *pt : std::as_const( mPoints ) ) { - const QgsPointXY transformedDestinationPoint = pt->transformedDestinationPoint( mProjection, QgsProject::instance()->transformContext() ); + const QgsPointXY transformedDestinationPoint = pt->transformedDestinationPoint( mTargetCrs, QgsProject::instance()->transformContext() ); gdalCommand << QStringLiteral( "-gcp %1 %2 %3 %4" ).arg( pt->sourcePoint().x() ).arg( -pt->sourcePoint().y() ) .arg( transformedDestinationPoint.x() ).arg( transformedDestinationPoint.y() ); } @@ -1843,13 +1810,13 @@ QString QgsGeoreferencerMainWindow::generateGDALwarpCommand( const QString &resa gdalCommand << QStringLiteral( "-tr" ) << QString::number( targetResX, 'f' ) << QString::number( targetResY, 'f' ); } - if ( mProjection.authid().startsWith( QStringLiteral( "EPSG:" ), Qt::CaseInsensitive ) ) + if ( mTargetCrs.authid().startsWith( QStringLiteral( "EPSG:" ), Qt::CaseInsensitive ) ) { - gdalCommand << QStringLiteral( "-t_srs %1" ).arg( mProjection.authid() ); + gdalCommand << QStringLiteral( "-t_srs %1" ).arg( mTargetCrs.authid() ); } else { - gdalCommand << QStringLiteral( "-t_srs \"%1\"" ).arg( mProjection.toProj().simplified() ); + gdalCommand << QStringLiteral( "-t_srs \"%1\"" ).arg( mTargetCrs.toProj().simplified() ); } gdalCommand << QStringLiteral( "\"%1\"" ).arg( mTranslatedRasterFileName ) << QStringLiteral( "\"%1\"" ).arg( mModifiedRasterFileName ); @@ -1857,20 +1824,6 @@ QString QgsGeoreferencerMainWindow::generateGDALwarpCommand( const QString &resa return gdalCommand.join( QLatin1Char( ' ' ) ); } -// Log -//void QgsGeorefPluginGui::showMessageInLog(const QString &description, const QString &msg) -//{ -// QString logItem = QString("%1: %2").arg(description).arg(msg); -// -// mLogViewer->appendHtml(logItem); -//} -// -//void QgsGeorefPluginGui::clearLog() -//{ -// mLogViewer->clear(); -//} - -// Helpers bool QgsGeoreferencerMainWindow::checkReadyGeoref() { if ( mRasterFileName.isEmpty() ) @@ -1906,7 +1859,6 @@ bool QgsGeoreferencerMainWindow::checkReadyGeoref() if ( !updateGeorefTransform() ) { mMessageBar->pushMessage( tr( "Transform Failed" ), tr( "Failed to compute GCP transform: Transform is not solvable." ), Qgis::MessageLevel::Critical ); - // logRequaredGCPs(); return false; } @@ -1918,7 +1870,7 @@ bool QgsGeoreferencerMainWindow::updateGeorefTransform() QVector sourceCoordinates; QVector destinationCoords; if ( mGCPListWidget->gcpList() ) - mGCPListWidget->gcpList()->createGCPVectors( sourceCoordinates, destinationCoords, mProjection, QgsProject::instance()->transformContext() ); + mGCPListWidget->gcpList()->createGCPVectors( sourceCoordinates, destinationCoords, mTargetCrs, QgsProject::instance()->transformContext() ); else return false; @@ -2063,26 +2015,6 @@ bool QgsGeoreferencerMainWindow::equalGCPlists( const QList< QgsGcpPoint > &list return true; } -//void QgsGeorefPluginGui::logTransformOptions() -//{ -// showMessageInLog(tr("Interpolation"), convertResamplingEnumToString(mResamplingMethod)); -// showMessageInLog(tr("Compression method"), mCompressionMethod); -// showMessageInLog(tr("Zero for transparency"), mUseZeroForTrans ? "true" : "false"); -//} -// -//void QgsGeorefPluginGui::logRequaredGCPs() -//{ -// if (mGeorefTransform.minimumGcpCount() != 0) -// { -// if ((uint)mPoints.size() >= mGeorefTransform.minimumGcpCount()) -// showMessageInLog(tr("Info"), tr("For georeferencing requared at least %1 GCP points") -// .arg(mGeorefTransform.minimumGcpCount())); -// else -// showMessageInLog(tr("Critical"), tr("For georeferencing requared at least %1 GCP points") -// .arg(mGeorefTransform.minimumGcpCount())); -// } -//} - void QgsGeoreferencerMainWindow::clearGCPData() { //force all list widget editors to close before removing data points diff --git a/src/app/georeferencer/qgsgeorefmainwindow.h b/src/app/georeferencer/qgsgeorefmainwindow.h index 897ac636b570..5f6b325d1df0 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.h +++ b/src/app/georeferencer/qgsgeorefmainwindow.h @@ -232,7 +232,7 @@ class QgsGeoreferencerMainWindow : public QMainWindow, private Ui::QgsGeorefPlug QString mWorldFileName; QString mTranslatedRasterFileName; QString mGCPpointsFileName; - QgsCoordinateReferenceSystem mProjection; + QgsCoordinateReferenceSystem mTargetCrs; QgsCoordinateReferenceSystem mLastGCPProjection; QString mPdfOutputFile; QString mPdfOutputMapFile; diff --git a/src/app/georeferencer/qgstransformsettingsdialog.cpp b/src/app/georeferencer/qgstransformsettingsdialog.cpp index c9fa88d2e2fc..2d11bfc9c61c 100644 --- a/src/app/georeferencer/qgstransformsettingsdialog.cpp +++ b/src/app/georeferencer/qgstransformsettingsdialog.cpp @@ -104,10 +104,6 @@ QgsTransformSettingsDialog::QgsTransformSettingsDialog( const QString &raster, c cmbResampling->setCurrentIndex( settings.value( QStringLiteral( "/Plugin-GeoReferencer/lastresampling" ), 0 ).toInt() ); cmbCompressionComboBox->setCurrentIndex( settings.value( QStringLiteral( "/Plugin-GeoReferencer/lastcompression" ), 0 ).toInt() ); - QString targetCRSString = settings.value( QStringLiteral( "/Plugin-GeoReferencer/targetsrs" ) ).toString(); - QgsCoordinateReferenceSystem targetCRS = QgsCoordinateReferenceSystem::fromOgcWmsCrs( targetCRSString ); - mCrsSelector->setCrs( targetCRS ); - mWorldFileCheckBox->setChecked( settings.value( QStringLiteral( "/Plugin-Georeferencer/word_file_checkbox" ), false ).toBool() ); cbxUserResolution->setChecked( settings.value( QStringLiteral( "/Plugin-Georeferencer/user_specified_resolution" ), false ).toBool() ); @@ -126,10 +122,19 @@ QgsTransformSettingsDialog::QgsTransformSettingsDialog( const QString &raster, c connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsTransformSettingsDialog::showHelp ); } +void QgsTransformSettingsDialog::setTargetCrs( const QgsCoordinateReferenceSystem &crs ) +{ + mCrsSelector->setCrs( crs ); +} + +QgsCoordinateReferenceSystem QgsTransformSettingsDialog::targetCrs() const +{ + return mCrsSelector->crs(); +} + void QgsTransformSettingsDialog::getTransformSettings( QgsGeorefTransform::TransformMethod &tp, QgsImageWarper::ResamplingMethod &rm, - QString &comprMethod, QString &raster, - QgsCoordinateReferenceSystem &proj, QString &pdfMapFile, QString &pdfReportFile, bool &saveGcpPoints, bool &zt, bool &loadInQgis, + QString &comprMethod, QString &raster, QString &pdfMapFile, QString &pdfReportFile, bool &saveGcpPoints, bool &zt, bool &loadInQgis, double &resX, double &resY ) { if ( cmbTransformType->currentIndex() == -1 ) @@ -147,7 +152,6 @@ void QgsTransformSettingsDialog::getTransformSettings( QgsGeorefTransform::Trans { raster = mOutputRaster->filePath(); } - proj = mCrsSelector->crs(); pdfMapFile = mPdfMap->filePath(); pdfReportFile = mPdfReport->filePath(); zt = cbxZeroAsTrans->isChecked(); diff --git a/src/app/georeferencer/qgstransformsettingsdialog.h b/src/app/georeferencer/qgstransformsettingsdialog.h index 0d456db0236a..a2972075d0ae 100644 --- a/src/app/georeferencer/qgstransformsettingsdialog.h +++ b/src/app/georeferencer/qgstransformsettingsdialog.h @@ -29,9 +29,19 @@ class QgsTransformSettingsDialog : public QDialog, private Ui::QgsTransformSetti public: QgsTransformSettingsDialog( const QString &raster, const QString &output, QWidget *parent = nullptr ); + /** + * Sets the selected target \a crs. + */ + void setTargetCrs( const QgsCoordinateReferenceSystem &crs ); + + /** + * Returns the selected target CRS. + */ + QgsCoordinateReferenceSystem targetCrs() const; + void getTransformSettings( QgsGeorefTransform::TransformMethod &tp, QgsImageWarper::ResamplingMethod &rm, QString &comprMethod, - QString &raster, QgsCoordinateReferenceSystem &proj, QString &pdfMapFile, QString &pdfReportFile, bool &saveGcpPoints, bool &zt, bool &loadInQgis, + QString &raster, QString &pdfMapFile, QString &pdfReportFile, bool &saveGcpPoints, bool &zt, bool &loadInQgis, double &resX, double &resY ); static void resetSettings(); From e3399e50ab497fe44689a4465bd8e9b4ccfadbdf Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 10 Feb 2022 09:16:58 +1000 Subject: [PATCH 37/47] Remove unused code (cherry picked from commit 610a0a6f06e5f85c7756f8fe9650181d4b05f06c) --- .../qgstransformsettingsdialog.cpp | 27 ------------------- .../qgstransformsettingsdialog.h | 1 - 2 files changed, 28 deletions(-) diff --git a/src/app/georeferencer/qgstransformsettingsdialog.cpp b/src/app/georeferencer/qgstransformsettingsdialog.cpp index 2d11bfc9c61c..d1b14bd44f9b 100644 --- a/src/app/georeferencer/qgstransformsettingsdialog.cpp +++ b/src/app/georeferencer/qgstransformsettingsdialog.cpp @@ -277,33 +277,6 @@ QString QgsTransformSettingsDialog::generateModifiedRasterFileName( const QStrin return modifiedFileName; } -// Note this code is duplicated from qgisapp.cpp because -// I didn't want to make plugins on qgsapplication [TS] -QIcon QgsTransformSettingsDialog::getThemeIcon( const QString &name ) -{ - if ( QFile::exists( QgsApplication::activeThemePath() + name ) ) - { - return QIcon( QgsApplication::activeThemePath() + name ); - } - else if ( QFile::exists( QgsApplication::defaultThemePath() + name ) ) - { - return QIcon( QgsApplication::defaultThemePath() + name ); - } - else - { - QgsSettings settings; - QString themePath = ":/icons/" + settings.value( QStringLiteral( "Themes" ) ).toString() + name; - if ( QFile::exists( themePath ) ) - { - return QIcon( themePath ); - } - else - { - return QIcon( ":/icons/default" + name ); - } - } -} - void QgsTransformSettingsDialog::showHelp() { QgsHelp::openHelp( QStringLiteral( "working_with_raster/georeferencer.html#defining-the-transformation-settings" ) ); diff --git a/src/app/georeferencer/qgstransformsettingsdialog.h b/src/app/georeferencer/qgstransformsettingsdialog.h index a2972075d0ae..84e88652268b 100644 --- a/src/app/georeferencer/qgstransformsettingsdialog.h +++ b/src/app/georeferencer/qgstransformsettingsdialog.h @@ -52,7 +52,6 @@ class QgsTransformSettingsDialog : public QDialog, private Ui::QgsTransformSetti private slots: void cmbTransformType_currentIndexChanged( const QString &text ); void mWorldFileCheckBox_stateChanged( int state ); - QIcon getThemeIcon( const QString &name ); void showHelp(); private: From 5599bb9ef7353af9e8d80301e7f8456b95320c6b Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 10 Feb 2022 09:25:43 +1000 Subject: [PATCH 38/47] Cleanup dialog code (cherry picked from commit a2bb366c1d69ad2298671016adc22d58a59ef915) --- .../qgstransformsettingsdialog.cpp | 42 +++++++------------ .../qgstransformsettingsdialog.h | 3 -- .../qgstransformsettingsdialogbase.ui | 27 +----------- 3 files changed, 16 insertions(+), 56 deletions(-) diff --git a/src/app/georeferencer/qgstransformsettingsdialog.cpp b/src/app/georeferencer/qgstransformsettingsdialog.cpp index d1b14bd44f9b..d123c8e7c0d5 100644 --- a/src/app/georeferencer/qgstransformsettingsdialog.cpp +++ b/src/app/georeferencer/qgstransformsettingsdialog.cpp @@ -89,16 +89,16 @@ QgsTransformSettingsDialog::QgsTransformSettingsDialog( const QString &raster, c cmbTransformType->addItem( tr( "Projective" ), static_cast( QgsGcpTransformerInterface::TransformMethod::Projective ) ); // Populate CompressionComboBox - mListCompression.append( QStringLiteral( "None" ) ); - mListCompression.append( QStringLiteral( "LZW" ) ); - mListCompression.append( QStringLiteral( "PACKBITS" ) ); - mListCompression.append( QStringLiteral( "DEFLATE" ) ); - QStringList listCompressionTr; - for ( const QString &item : std::as_const( mListCompression ) ) - { - listCompressionTr.append( tr( item.toLatin1().data() ) ); - } - cmbCompressionComboBox->addItems( listCompressionTr ); + cmbCompressionComboBox->addItem( tr( "None" ), QStringLiteral( "None" ) ); + cmbCompressionComboBox->addItem( tr( "LZW" ), QStringLiteral( "LZW" ) ); + cmbCompressionComboBox->addItem( tr( "PACKBITS" ), QStringLiteral( "PACKBITS" ) ); + cmbCompressionComboBox->addItem( tr( "DEFLATE" ), QStringLiteral( "DEFLATE" ) ); + + cmbResampling->addItem( tr( "Nearest Neighbour" ), QgsImageWarper::ResamplingMethod::NearestNeighbour ); + cmbResampling->addItem( tr( "Linear" ), QgsImageWarper::ResamplingMethod::Bilinear ); + cmbResampling->addItem( tr( "Cubic" ), QgsImageWarper::ResamplingMethod::Cubic ); + cmbResampling->addItem( tr( "Cubic Spline" ), QgsImageWarper::ResamplingMethod::CubicSpline ); + cmbResampling->addItem( tr( "Lanczos" ), QgsImageWarper::ResamplingMethod::Lanczos ); cmbTransformType->setCurrentIndex( settings.value( QStringLiteral( "/Plugin-GeoReferencer/lasttransformation" ), -1 ).toInt() ); cmbResampling->setCurrentIndex( settings.value( QStringLiteral( "/Plugin-GeoReferencer/lastresampling" ), 0 ).toInt() ); @@ -142,8 +142,8 @@ void QgsTransformSettingsDialog::getTransformSettings( QgsGeorefTransform::Trans else tp = static_cast< QgsGcpTransformerInterface::TransformMethod >( cmbTransformType->currentData().toInt() ); - rm = ( QgsImageWarper::ResamplingMethod )cmbResampling->currentIndex(); - comprMethod = mListCompression.at( cmbCompressionComboBox->currentIndex() ).toUpper(); + rm = static_cast< QgsImageWarper::ResamplingMethod >( cmbResampling->currentData().toInt() ); + comprMethod = cmbCompressionComboBox->currentData().toString(); if ( mWorldFileCheckBox->isChecked() ) { raster.clear(); @@ -183,19 +183,6 @@ void QgsTransformSettingsDialog::resetSettings() s.setValue( QStringLiteral( "/Plugin-GeoReferencer/lastPDFReportDir" ), QDir::homePath() ); } -void QgsTransformSettingsDialog::changeEvent( QEvent *e ) -{ - QDialog::changeEvent( e ); - switch ( e->type() ) - { - case QEvent::LanguageChange: - retranslateUi( this ); - break; - default: - break; - } -} - void QgsTransformSettingsDialog::accept() { if ( !mOutputRaster->filePath().isEmpty() ) @@ -236,9 +223,10 @@ void QgsTransformSettingsDialog::accept() QDialog::accept(); } -void QgsTransformSettingsDialog::cmbTransformType_currentIndexChanged( const QString &text ) +void QgsTransformSettingsDialog::cmbTransformType_currentIndexChanged( const QString & ) { - if ( text == tr( "Linear" ) ) + if ( cmbTransformType->currentIndex() != -1 + && static_cast< QgsGcpTransformerInterface::TransformMethod >( cmbTransformType->currentData().toInt() ) == QgsGcpTransformerInterface::TransformMethod::Linear ) { mWorldFileCheckBox->setEnabled( true ); } diff --git a/src/app/georeferencer/qgstransformsettingsdialog.h b/src/app/georeferencer/qgstransformsettingsdialog.h index 84e88652268b..4407590d74c2 100644 --- a/src/app/georeferencer/qgstransformsettingsdialog.h +++ b/src/app/georeferencer/qgstransformsettingsdialog.h @@ -46,7 +46,6 @@ class QgsTransformSettingsDialog : public QDialog, private Ui::QgsTransformSetti static void resetSettings(); protected: - void changeEvent( QEvent *e ) override; void accept() override; private slots: @@ -58,8 +57,6 @@ class QgsTransformSettingsDialog : public QDialog, private Ui::QgsTransformSetti QString generateModifiedRasterFileName( const QString &raster ); QString mSourceRasterFile; - - QStringList mListCompression; }; #endif // QGSTRANSFORMSETTINGSDIALOG_H diff --git a/src/ui/georeferencer/qgstransformsettingsdialogbase.ui b/src/ui/georeferencer/qgstransformsettingsdialogbase.ui index fe2d9a833d06..bf4ab0cf1f65 100644 --- a/src/ui/georeferencer/qgstransformsettingsdialogbase.ui +++ b/src/ui/georeferencer/qgstransformsettingsdialogbase.ui @@ -240,33 +240,8 @@ - 0 + -1 - - - Nearest neighbour - - - - - Linear - - - - - Cubic - - - - - Cubic Spline - - - - - Lanczos - - From c822fdedf8b150f505329dca6fab0f83bf03172f Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 10 Feb 2022 09:42:41 +1000 Subject: [PATCH 39/47] When loading gcp points for a raster which is already georeferenced, assume the source points are in the raster's IF there's no explicit crs information in the .points file (cherry picked from commit 8a12a0be2287ff019e9178714722500420561e49) --- src/app/georeferencer/qgsgeorefmainwindow.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index 3d652ffbfcb5..d642a65d5e55 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -1123,6 +1123,19 @@ void QgsGeoreferencerMainWindow::addRaster( const QString &file ) options.skipCrsValidation = true; mLayer = std::make_unique< QgsRasterLayer >( file, QStringLiteral( "Raster" ), QStringLiteral( "gdal" ), options ); + // guess a reasonable target CRS to use by default + if ( mLayer->crs().isValid() ) + { + // if source raster already is already georeferenced, assume we'll be keeping the same CRS + mTargetCrs = mLayer->crs(); + } + // otherwise use the previous target crs, unless that's never been set + else if ( !mTargetCrs.isValid() ) + { + // in which case we'll use the current project CRS + mTargetCrs = QgsProject::instance()->crs(); + } + // add layer to map canvas mCanvas->setLayers( QList() << mLayer.get() ); @@ -1177,7 +1190,7 @@ bool QgsGeoreferencerMainWindow::loadGCPs( QString &error ) { QgsCoordinateReferenceSystem actualDestinationCrs; const QList< QgsGcpPoint > points = QgsGCPList::loadGcps( mGCPpointsFileName, - QgsProject::instance()->crs(), + mTargetCrs, actualDestinationCrs, error ); if ( !error.isEmpty() ) From 4e72461082787e8f876115655576730a37040469 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 10 Feb 2022 09:46:27 +1000 Subject: [PATCH 40/47] Fix reading and writing invalid crs information to gcp points files (cherry picked from commit feaa95402624cb01c3e4033fa73389b89276f823) --- src/app/georeferencer/qgsgcplist.cpp | 19 ++++++-- tests/src/app/testqgsgeoreferencer.cpp | 62 ++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/src/app/georeferencer/qgsgcplist.cpp b/src/app/georeferencer/qgsgcplist.cpp index 3498ad3b8f7f..9897e482e9fb 100644 --- a/src/app/georeferencer/qgsgcplist.cpp +++ b/src/app/georeferencer/qgsgcplist.cpp @@ -140,12 +140,15 @@ bool QgsGCPList::saveGcps( const QString &filePath, const QgsCoordinateReference if ( pointFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) { QTextStream points( &pointFile ); - points << QStringLiteral( "#CRS: %1" ).arg( targetCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) ); + if ( targetCrs.isValid() ) + { + points << QStringLiteral( "#CRS: %1" ).arg( targetCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) ); #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) - points << endl; + points << endl; #else - points << Qt::endl; + points << Qt::endl; #endif + } points << "mapX,mapY,sourceX,sourceY,enable,dX,dY,residual"; #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) @@ -199,7 +202,15 @@ QList QgsGCPList::loadGcps( const QString &filePath, const QgsCoord int i = 0; if ( line.contains( QLatin1String( "#CRS: " ) ) ) { - actualDestinationCrs = QgsCoordinateReferenceSystem( line.remove( QStringLiteral( "#CRS: " ) ) ); + const QString crsDef = line.remove( QStringLiteral( "#CRS: " ) ); + if ( !crsDef.trimmed().isEmpty() ) + { + actualDestinationCrs = QgsCoordinateReferenceSystem( crsDef ); + } + else + { + actualDestinationCrs = defaultDestinationCrs; + } line = points.readLine(); lineNumber++; } diff --git a/tests/src/app/testqgsgeoreferencer.cpp b/tests/src/app/testqgsgeoreferencer.cpp index 262909958511..cf5854f60e15 100644 --- a/tests/src/app/testqgsgeoreferencer.cpp +++ b/tests/src/app/testqgsgeoreferencer.cpp @@ -47,6 +47,7 @@ class TestQgsGeoreferencer : public QObject void testGeorefDataPoint(); void testGcpList(); void testSaveLoadGcps(); + void testSaveLoadGcpsNoCrs(); void testTransformImageNoGeoference(); void testTransformImageWithExistingGeoreference(); void testRasterChangeCoords(); @@ -291,6 +292,67 @@ void TestQgsGeoreferencer::testSaveLoadGcps() QCOMPARE( res.at( 2 ).destinationPointCrs().authid(), QStringLiteral( "EPSG:3857" ) ); } +void TestQgsGeoreferencer::testSaveLoadGcpsNoCrs() +{ + // test saving and loading GCPs when no destination CRS is set for the points + QgsGCPList list; + QgsMapCanvas c1; + QgsMapCanvas c2; + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 111, 222 ), QgsPointXY( -30, 40 ), QgsCoordinateReferenceSystem(), + true ) ); + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 11, 22 ), QgsPointXY( 34, -50 ), QgsCoordinateReferenceSystem(), + true ) ); + // disabled! + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 33, 44 ), QgsPointXY( 100, 200 ), QgsCoordinateReferenceSystem(), + false ) ); + + QTemporaryDir dir; + QVERIFY( dir.isValid() ); + const QString tempFilename = dir.filePath( QStringLiteral( "test2.points" ) ); + + QString error; + + QVERIFY( list.saveGcps( tempFilename, QgsCoordinateReferenceSystem(), + QgsProject::instance()->transformContext(), error ) ); + QVERIFY( error.isEmpty() ); + + + // load + QgsCoordinateReferenceSystem actualDestinationCrs; + QList res = QgsGCPList::loadGcps( tempFilename, + QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ), + actualDestinationCrs, + error ); + QVERIFY( error.isEmpty() ); + QCOMPARE( res.size(), 3 ); + // should fallback to default CRS + QCOMPARE( actualDestinationCrs.authid(), QStringLiteral( "EPSG:3111" ) ); + + QCOMPARE( res.at( 0 ).sourcePoint().x(), 111 ); + QCOMPARE( res.at( 0 ).sourcePoint().y(), 222 ); + QCOMPARE( res.at( 0 ).destinationPoint().x(), -30 ); + QCOMPARE( res.at( 0 ).destinationPoint().y(), 40 ); + QVERIFY( res.at( 0 ).isEnabled() ); + QCOMPARE( res.at( 0 ).destinationPointCrs().authid(), QStringLiteral( "EPSG:3111" ) ); + + QCOMPARE( res.at( 1 ).sourcePoint().x(), 11 ); + QCOMPARE( res.at( 1 ).sourcePoint().y(), 22 ); + QCOMPARE( res.at( 1 ).destinationPoint().x(), 34 ); + QCOMPARE( res.at( 1 ).destinationPoint().y(), -50 ); + QVERIFY( res.at( 1 ).isEnabled() ); + QCOMPARE( res.at( 1 ).destinationPointCrs().authid(), QStringLiteral( "EPSG:3111" ) ); + + QCOMPARE( res.at( 2 ).sourcePoint().x(), 33 ); + QCOMPARE( res.at( 2 ).sourcePoint().y(), 44 ); + QCOMPARE( res.at( 2 ).destinationPoint().x(), 100 ); + QCOMPARE( res.at( 2 ).destinationPoint().y(), 200 ); + QVERIFY( !res.at( 2 ).isEnabled() ); + QCOMPARE( res.at( 2 ).destinationPointCrs().authid(), QStringLiteral( "EPSG:3111" ) ); +} + void TestQgsGeoreferencer::testTransformImageNoGeoference() { QgsGeorefTransform transform( QgsGcpTransformerInterface::TransformMethod::Linear ); From df4f6ac2b0a4d55e680d934c9c51cc527b3a2a72 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 10 Feb 2022 09:50:25 +1000 Subject: [PATCH 41/47] More robust (cherry picked from commit a39ed7b4e44acb4a56ee72d2f5511af45e9c0888) --- src/app/georeferencer/qgsgcplistwidget.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/georeferencer/qgsgcplistwidget.cpp b/src/app/georeferencer/qgsgcplistwidget.cpp index 54c9e2e8e1f3..0dd0a6fd70d5 100644 --- a/src/app/georeferencer/qgsgcplistwidget.cpp +++ b/src/app/georeferencer/qgsgcplistwidget.cpp @@ -47,10 +47,10 @@ QgsGCPListWidget::QgsGCPListWidget( QWidget *parent ) setAlternatingRowColors( true ); // set delegates for items - setItemDelegateForColumn( 2, mCoordDelegate ); // srcX - setItemDelegateForColumn( 3, mCoordDelegate ); // srcY - setItemDelegateForColumn( 4, mDmsAndDdDelegate ); // dstX - setItemDelegateForColumn( 5, mDmsAndDdDelegate ); // dstY + setItemDelegateForColumn( static_cast< int >( QgsGCPListModel::Column::SourceX ), mCoordDelegate ); + setItemDelegateForColumn( static_cast< int >( QgsGCPListModel::Column::SourceY ), mCoordDelegate ); + setItemDelegateForColumn( static_cast< int >( QgsGCPListModel::Column::DestinationX ), mDmsAndDdDelegate ); + setItemDelegateForColumn( static_cast< int >( QgsGCPListModel::Column::DestinationY ), mDmsAndDdDelegate ); connect( this, &QAbstractItemView::doubleClicked, this, &QgsGCPListWidget::itemDoubleClicked ); From 18bd04bfe0bc079410810f1b99b3fff072e1f3d1 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 10 Feb 2022 11:18:56 +1000 Subject: [PATCH 42/47] When changing destination points coordinates in table, automatically set CRS of destination to match the georeference target CRS (cherry picked from commit 90c13252ef11243a6c907c4a2aacaf80a830eabf) --- src/app/georeferencer/qgsgcplistmodel.cpp | 6 +- src/app/georeferencer/qgsgeorefdatapoint.cpp | 6 ++ src/app/georeferencer/qgsgeorefdatapoint.h | 7 ++ tests/src/app/testqgsgeoreferencer.cpp | 75 ++++++++++++++++++++ 4 files changed, 93 insertions(+), 1 deletion(-) diff --git a/src/app/georeferencer/qgsgcplistmodel.cpp b/src/app/georeferencer/qgsgcplistmodel.cpp index 077e0ccbdc12..7eb378a94041 100644 --- a/src/app/georeferencer/qgsgcplistmodel.cpp +++ b/src/app/georeferencer/qgsgcplistmodel.cpp @@ -264,12 +264,16 @@ bool QgsGCPListModel::setData( const QModelIndex &index, const QVariant &value, case QgsGCPListModel::Column::DestinationX: case QgsGCPListModel::Column::DestinationY: { - QgsPointXY destinationPoint = point->destinationPoint(); + // when setting a destination point x/y, we need to use the transformed destination point + // as this is what we were showing to users + QgsPointXY destinationPoint = point->transformedDestinationPoint( mTargetCrs, mTransformContext ); if ( column == QgsGCPListModel::Column::DestinationX ) destinationPoint.setX( value.toDouble() ); else destinationPoint.setY( value.toDouble() ); point->setDestinationPoint( destinationPoint ); + // we also have to update the destination point crs to the target crs, as the point is now in a different CRS + point->setDestinationPointCrs( mTargetCrs ); emit dataChanged( index, index ); updateResiduals(); return true; diff --git a/src/app/georeferencer/qgsgeorefdatapoint.cpp b/src/app/georeferencer/qgsgeorefdatapoint.cpp index 43dcde1ebb70..9ef21ae87417 100644 --- a/src/app/georeferencer/qgsgeorefdatapoint.cpp +++ b/src/app/georeferencer/qgsgeorefdatapoint.cpp @@ -108,6 +108,12 @@ void QgsGeorefDataPoint::setDestinationPoint( const QgsPointXY &p ) updateCoords(); } +void QgsGeorefDataPoint::setDestinationPointCrs( const QgsCoordinateReferenceSystem &crs ) +{ + mGcpPoint.setDestinationPointCrs( crs ); + updateCoords(); +} + QgsPointXY QgsGeorefDataPoint::transformedDestinationPoint( const QgsCoordinateReferenceSystem &targetCrs, const QgsCoordinateTransformContext &context ) const { return mGcpPoint.transformedDestinationPoint( targetCrs, context ); diff --git a/src/app/georeferencer/qgsgeorefdatapoint.h b/src/app/georeferencer/qgsgeorefdatapoint.h index be3755dd3538..5bd57b2a616c 100644 --- a/src/app/georeferencer/qgsgeorefdatapoint.h +++ b/src/app/georeferencer/qgsgeorefdatapoint.h @@ -186,6 +186,13 @@ class APP_EXPORT QgsGeorefDataPoint : public QObject */ void setDestinationPoint( const QgsPointXY &p ); + /** + * Sets the \a crs of the destination point. + * + * \see destinationCrs() + */ + void setDestinationPointCrs( const QgsCoordinateReferenceSystem &crs ); + /** * Returns the destionationPoint() transformed to the given target CRS. */ diff --git a/tests/src/app/testqgsgeoreferencer.cpp b/tests/src/app/testqgsgeoreferencer.cpp index cf5854f60e15..f95d23593404 100644 --- a/tests/src/app/testqgsgeoreferencer.cpp +++ b/tests/src/app/testqgsgeoreferencer.cpp @@ -53,6 +53,7 @@ class TestQgsGeoreferencer : public QObject void testRasterChangeCoords(); void testUpdateResiduals(); void testListModel(); + void testListModelCrs(); private: QgisApp *mQgisApp = nullptr; @@ -711,5 +712,79 @@ void TestQgsGeoreferencer::testListModel() QVERIFY( list.at( 0 )->isEnabled() ); } +void TestQgsGeoreferencer::testListModelCrs() +{ + // test destination crs handling in list model + QgsGCPList list; + QgsMapCanvas c1; + QgsMapCanvas c2; + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 781662.375, 3350923.125 ), QgsPointXY( -30, 40 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), + true ) ); + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 787362.375, 3350923.125 ), QgsPointXY( 16697923, -3503549 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), + true ) ); + list.append( new QgsGeorefDataPoint( &c1, &c2, + QgsPointXY( 787362.375, 3362323.125 ), QgsPointXY( 17697923, -3403549 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), + true ) ); + + QgsGCPListModel model; + model.setGCPList( &list ); + model.setTargetCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), QgsProject::instance()->transformContext() ); + + // all destination points are shown in target crs + QCOMPARE( model.data( model.index( 0, 4 ) ).toString(), QStringLiteral( "-30.0000" ) ); + QCOMPARE( model.data( model.index( 0, 5 ) ).toString(), QStringLiteral( "40.0000" ) ); + QCOMPARE( model.data( model.index( 1, 4 ) ).toString(), QStringLiteral( "150.0000" ) ); + QCOMPARE( model.data( model.index( 1, 5 ) ).toString(), QStringLiteral( "-30.0000" ) ); + QCOMPARE( model.data( model.index( 2, 4 ) ).toString(), QStringLiteral( "158.9831" ) ); + QCOMPARE( model.data( model.index( 2, 5 ) ).toString(), QStringLiteral( "-29.2190" ) ); + + // setting a point's destination x or y will update that point to being stored in the target crs + QVERIFY( model.setData( model.index( 0, 4 ), -31.0 ) ); + QCOMPARE( model.data( model.index( 0, 4 ) ).toString(), QStringLiteral( "-31.0000" ) ); + QCOMPARE( model.data( model.index( 0, 5 ) ).toString(), QStringLiteral( "40.0000" ) ); + QCOMPARE( list.at( 0 )->destinationPoint().x(), -31.0 ); + QCOMPARE( list.at( 0 )->destinationPoint().y(), 40.0 ); + QCOMPARE( list.at( 0 )->destinationPointCrs().authid(), QStringLiteral( "EPSG:4326" ) ); + + QVERIFY( model.setData( model.index( 0, 5 ), 41.0 ) ); + QCOMPARE( model.data( model.index( 0, 4 ) ).toString(), QStringLiteral( "-31.0000" ) ); + QCOMPARE( model.data( model.index( 0, 5 ) ).toString(), QStringLiteral( "41.0000" ) ); + QCOMPARE( list.at( 0 )->destinationPoint().x(), -31.0 ); + QCOMPARE( list.at( 0 )->destinationPoint().y(), 41.0 ); + QCOMPARE( list.at( 0 )->destinationPointCrs().authid(), QStringLiteral( "EPSG:4326" ) ); + + // destination point was originally in EPSG:3857, should be changed to 4326 when destination x is set + QVERIFY( model.setData( model.index( 1, 4 ), 148 ) ); + QCOMPARE( model.data( model.index( 1, 4 ) ).toString(), QStringLiteral( "148.0000" ) ); + QCOMPARE( model.data( model.index( 1, 5 ) ).toString(), QStringLiteral( "-30.0000" ) ); + QCOMPARE( list.at( 1 )->destinationPoint().x(), 148 ); + QGSCOMPARENEAR( list.at( 1 )->destinationPoint().y(), -30, 0.001 ); + QCOMPARE( list.at( 1 )->destinationPointCrs().authid(), QStringLiteral( "EPSG:4326" ) ); + + QVERIFY( model.setData( model.index( 1, 5 ), -32.0 ) ); + QCOMPARE( model.data( model.index( 1, 4 ) ).toString(), QStringLiteral( "148.0000" ) ); + QCOMPARE( model.data( model.index( 1, 5 ) ).toString(), QStringLiteral( "-32.0000" ) ); + QCOMPARE( list.at( 1 )->destinationPoint().x(), 148 ); + QCOMPARE( list.at( 1 )->destinationPoint().y(), -32 ); + QCOMPARE( list.at( 1 )->destinationPointCrs().authid(), QStringLiteral( "EPSG:4326" ) ); + + // destination point was originally in EPSG:3857, should be changed to 4326 when destination y is set + QVERIFY( model.setData( model.index( 2, 5 ), -29.0 ) ); + QCOMPARE( model.data( model.index( 2, 4 ) ).toString(), QStringLiteral( "158.9831" ) ); + QCOMPARE( model.data( model.index( 2, 5 ) ).toString(), QStringLiteral( "-29.0000" ) ); + QGSCOMPARENEAR( list.at( 2 )->destinationPoint().x(), 158.9831, 0.001 ); + QCOMPARE( list.at( 2 )->destinationPoint().y(), -29 ); + QCOMPARE( list.at( 2 )->destinationPointCrs().authid(), QStringLiteral( "EPSG:4326" ) ); + + QVERIFY( model.setData( model.index( 2, 4 ), 159 ) ); + QCOMPARE( model.data( model.index( 2, 4 ) ).toString(), QStringLiteral( "159.0000" ) ); + QCOMPARE( model.data( model.index( 2, 5 ) ).toString(), QStringLiteral( "-29.0000" ) ); + QCOMPARE( list.at( 2 )->destinationPoint().x(), 159 ); + QCOMPARE( list.at( 2 )->destinationPoint().y(), -29 ); + QCOMPARE( list.at( 2 )->destinationPointCrs().authid(), QStringLiteral( "EPSG:4326" ) ); +} + QGSTEST_MAIN( TestQgsGeoreferencer ) #include "testqgsgeoreferencer.moc" From c8027f457df5b0a716cdfa20735df7f7f3bf5849 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 10 Feb 2022 11:29:48 +1000 Subject: [PATCH 43/47] Cleanup code, fix deleting points (cherry picked from commit 93564e06998babd08a7c4b298cd6cc8583274257) --- src/app/georeferencer/qgsgeorefdatapoint.cpp | 60 +++++++++++-------- src/app/georeferencer/qgsgeorefdatapoint.h | 11 +++- src/app/georeferencer/qgsgeorefmainwindow.cpp | 22 +++---- 3 files changed, 53 insertions(+), 40 deletions(-) diff --git a/src/app/georeferencer/qgsgeorefdatapoint.cpp b/src/app/georeferencer/qgsgeorefdatapoint.cpp index 9ef21ae87417..727a23f940dc 100644 --- a/src/app/georeferencer/qgsgeorefdatapoint.cpp +++ b/src/app/georeferencer/qgsgeorefdatapoint.cpp @@ -164,38 +164,48 @@ void QgsGeorefDataPoint::updateCoords() } } -bool QgsGeorefDataPoint::contains( QPoint p, bool isMapPlugin ) +bool QgsGeorefDataPoint::contains( QPoint p, QgsGcpPoint::PointType type ) { - if ( isMapPlugin ) + switch ( type ) { - const QPointF pnt = mGCPSourceItem->mapFromScene( p ); - return mGCPSourceItem->shape().contains( pnt ); - } - else - { - const QPointF pnt = mGCPDestinationItem->mapFromScene( p ); - return mGCPDestinationItem->shape().contains( pnt ); + case QgsGcpPoint::PointType::Source: + { + const QPointF pnt = mGCPSourceItem->mapFromScene( p ); + return mGCPSourceItem->shape().contains( pnt ); + } + + case QgsGcpPoint::PointType::Destination: + { + const QPointF pnt = mGCPDestinationItem->mapFromScene( p ); + return mGCPDestinationItem->shape().contains( pnt ); + } } + BUILTIN_UNREACHABLE } -void QgsGeorefDataPoint::moveTo( QPoint canvasPixels, bool isMapPlugin ) +void QgsGeorefDataPoint::moveTo( QPoint canvasPixels, QgsGcpPoint::PointType type ) { - if ( isMapPlugin ) + switch ( type ) { - const QgsPointXY pnt = mGCPSourceItem->toMapCoordinates( canvasPixels ); - mGcpPoint.setSourcePoint( pnt ); + case QgsGcpPoint::PointType::Source: + { + const QgsPointXY pnt = mGCPSourceItem->toMapCoordinates( canvasPixels ); + mGcpPoint.setSourcePoint( pnt ); + break; + } + case QgsGcpPoint::PointType::Destination: + { + mGcpPoint.setDestinationPoint( mGCPDestinationItem->toMapCoordinates( canvasPixels ) ); + if ( mSrcCanvas && mSrcCanvas->mapSettings().destinationCrs().isValid() ) + mGcpPoint.setDestinationPointCrs( mSrcCanvas->mapSettings().destinationCrs() ); + else + mGcpPoint.setDestinationPointCrs( mGCPDestinationItem->canvas()->mapSettings().destinationCrs() ); + + if ( !mGcpPoint.destinationPointCrs().isValid() ) + mGcpPoint.setDestinationPointCrs( QgsProject::instance()->crs() ); + break; + } } - else - { - mGcpPoint.setDestinationPoint( mGCPDestinationItem->toMapCoordinates( canvasPixels ) ); - if ( mSrcCanvas && mSrcCanvas->mapSettings().destinationCrs().isValid() ) - mGcpPoint.setDestinationPointCrs( mSrcCanvas->mapSettings().destinationCrs() ); - else - mGcpPoint.setDestinationPointCrs( mGCPDestinationItem->canvas()->mapSettings().destinationCrs() ); - } - if ( !mGcpPoint.destinationPointCrs().isValid() ) - mGcpPoint.setDestinationPointCrs( QgsProject::instance()->crs() ); - mGCPSourceItem->update(); - mGCPDestinationItem->update(); + updateCoords(); } diff --git a/src/app/georeferencer/qgsgeorefdatapoint.h b/src/app/georeferencer/qgsgeorefdatapoint.h index 5bd57b2a616c..b001f8572965 100644 --- a/src/app/georeferencer/qgsgeorefdatapoint.h +++ b/src/app/georeferencer/qgsgeorefdatapoint.h @@ -30,6 +30,13 @@ class APP_EXPORT QgsGcpPoint { public: + //! Coordinate point types + enum class PointType + { + Source, //!< Source point + Destination, //!< Destination point + }; + /** * Constructor for QgsGcpPoint. * @@ -215,7 +222,7 @@ class APP_EXPORT QgsGeorefDataPoint : public QObject int id() const { return mId; } void setId( int id ); - bool contains( QPoint p, bool isMapPlugin ); + bool contains( QPoint p, QgsGcpPoint::PointType type ); QgsMapCanvas *srcCanvas() const { return mSrcCanvas; } QgsMapCanvas *dstCanvas() const { return mDstCanvas; } @@ -233,7 +240,7 @@ class APP_EXPORT QgsGeorefDataPoint : public QObject QgsGcpPoint point() const { return mGcpPoint; } public slots: - void moveTo( QPoint canvasPixels, bool isMapPlugin ); + void moveTo( QPoint canvasPixels, QgsGcpPoint::PointType type ); void updateCoords(); private: diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index d642a65d5e55..de2024975ebd 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -531,17 +531,16 @@ void QgsGeoreferencerMainWindow::deleteDataPoint( QPoint coords ) for ( QgsGCPList::iterator it = mPoints.begin(); it != mPoints.end(); ++it ) { QgsGeorefDataPoint *pt = *it; - if ( /*pt->pixelCoords() == coords ||*/ pt->contains( coords, true ) ) // first operand for removing from GCP table + if ( pt->contains( coords, QgsGcpPoint::PointType::Source ) ) // first operand for removing from GCP table { delete *it; mPoints.erase( it ); - mGCPListWidget->updateResiduals(); - + mGCPListWidget->setGCPList( &mPoints ); mCanvas->refresh(); + updateGeorefTransform(); break; } } - updateGeorefTransform(); } void QgsGeoreferencerMainWindow::deleteDataPoint( int theGCPIndex ) @@ -555,13 +554,12 @@ void QgsGeoreferencerMainWindow::deleteDataPoint( int theGCPIndex ) void QgsGeoreferencerMainWindow::selectPoint( QPoint p ) { - // Get Map Sender - bool isMapPlugin = sender() == mToolMovePoint; - QgsGeorefDataPoint *&mvPoint = isMapPlugin ? mMovingPoint : mMovingPointQgis; + const QgsGcpPoint::PointType pointType = sender() == mToolMovePoint ? QgsGcpPoint::PointType::Source : QgsGcpPoint::PointType::Destination; + QgsGeorefDataPoint *&mvPoint = pointType == QgsGcpPoint::PointType::Source ? mMovingPoint : mMovingPointQgis; for ( QgsGCPList::const_iterator it = mPoints.constBegin(); it != mPoints.constEnd(); ++it ) { - if ( ( *it )->contains( p, isMapPlugin ) ) + if ( ( *it )->contains( p, pointType ) ) { mvPoint = *it; break; @@ -571,15 +569,13 @@ void QgsGeoreferencerMainWindow::selectPoint( QPoint p ) void QgsGeoreferencerMainWindow::movePoint( QPoint canvasPixels ) { - // Get Map Sender - bool isMapPlugin = sender() == mToolMovePoint; - QgsGeorefDataPoint *mvPoint = isMapPlugin ? mMovingPoint : mMovingPointQgis; + const QgsGcpPoint::PointType pointType = sender() == mToolMovePoint ? QgsGcpPoint::PointType::Source : QgsGcpPoint::PointType::Destination; + QgsGeorefDataPoint *&mvPoint = pointType == QgsGcpPoint::PointType::Source ? mMovingPoint : mMovingPointQgis; if ( mvPoint ) { - mvPoint->moveTo( canvasPixels, isMapPlugin ); + mvPoint->moveTo( canvasPixels, pointType ); } - } void QgsGeoreferencerMainWindow::releasePoint( QPoint p ) From b480c554c2e7c3735cbdb742695f5a81dd35d83b Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 10 Feb 2022 11:38:05 +1000 Subject: [PATCH 44/47] Show CRS details in tooltips for destination X/Y values Helps clarify for users exactly what CRS these destination coordinates are in (cherry picked from commit 622c12064993461a5b5562d16a2526e473a5dacf) --- src/app/georeferencer/qgsgcplistmodel.cpp | 27 +++++++++++++++++++++-- tests/src/app/testqgsgeoreferencer.cpp | 6 +++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/app/georeferencer/qgsgcplistmodel.cpp b/src/app/georeferencer/qgsgcplistmodel.cpp index 7eb378a94041..1c9c63744ef2 100644 --- a/src/app/georeferencer/qgsgcplistmodel.cpp +++ b/src/app/georeferencer/qgsgcplistmodel.cpp @@ -117,6 +117,12 @@ QVariant QgsGCPListModel::data( const QModelIndex &index, int role ) const switch ( role ) { case Qt::ToolTipRole: + { + const QString crsString = mTargetCrs.userFriendlyIdentifier(); + const double value = column == QgsGCPListModel::Column::DestinationX ? transformedDestinationPoint.x() : transformedDestinationPoint.y(); + return QStringLiteral( "%1
%2" ).arg( value ).arg( crsString ); + } + case Qt::EditRole: case Qt::UserRole: // use full precision @@ -329,6 +335,7 @@ QVariant QgsGCPListModel::headerData( int section, Qt::Orientation orientation, switch ( role ) { case Qt::DisplayRole: + case Qt::ToolTipRole: { QString residualUnitType; switch ( residualUnit() ) @@ -360,9 +367,25 @@ QVariant QgsGCPListModel::headerData( int section, Qt::Orientation orientation, case QgsGCPListModel::Column::SourceY: return tr( "Source Y" ); case QgsGCPListModel::Column::DestinationX: - return tr( "Dest. X" ); case QgsGCPListModel::Column::DestinationY: - return tr( "Dest. Y" ); + { + const QString heading = static_cast< Column >( section ) == QgsGCPListModel::Column::DestinationX ? tr( "Dest. X" ) : tr( "Dest. Y" ); + switch ( role ) + { + case Qt::DisplayRole: + return heading; + + case Qt::ToolTipRole: + { + const QString crsString = mTargetCrs.userFriendlyIdentifier(); + return QStringLiteral( "%1
%2" ).arg( heading, crsString ); + } + + default: + break; + } + break; + } case QgsGCPListModel::Column::ResidualDx: return tr( "dX (%1)" ).arg( residualUnitType ); case QgsGCPListModel::Column::ResidualDy: diff --git a/tests/src/app/testqgsgeoreferencer.cpp b/tests/src/app/testqgsgeoreferencer.cpp index f95d23593404..d89ec52773f0 100644 --- a/tests/src/app/testqgsgeoreferencer.cpp +++ b/tests/src/app/testqgsgeoreferencer.cpp @@ -642,6 +642,9 @@ void TestQgsGeoreferencer::testListModel() QCOMPARE( model.data( model.index( 0, 3 ) ).toString(), QStringLiteral( "3350923.1250" ) ); QCOMPARE( model.data( model.index( 0, 4 ) ).toString(), QStringLiteral( "-30.0000" ) ); QCOMPARE( model.data( model.index( 0, 5 ) ).toString(), QStringLiteral( "40.0000" ) ); + QCOMPARE( model.data( model.index( 0, 4 ), Qt::ToolTipRole ).toString(), QStringLiteral( "-30
EPSG:4326 - WGS 84" ) ); + QCOMPARE( model.data( model.index( 0, 5 ), Qt::ToolTipRole ).toString(), QStringLiteral( "40
EPSG:4326 - WGS 84" ) ); + QCOMPARE( model.data( model.index( 0, 6 ) ).toString(), QStringLiteral( "n/a" ) ); QCOMPARE( model.data( model.index( 0, 7 ) ).toString(), QStringLiteral( "n/a" ) ); QCOMPARE( model.data( model.index( 0, 8 ) ).toString(), QStringLiteral( "n/a" ) ); @@ -651,6 +654,9 @@ void TestQgsGeoreferencer::testListModel() QCOMPARE( model.data( model.index( 1, 3 ) ).toString(), QStringLiteral( "3350923.1250" ) ); QCOMPARE( model.data( model.index( 1, 4 ) ).toString(), QStringLiteral( "150.0000" ) ); QCOMPARE( model.data( model.index( 1, 5 ) ).toString(), QStringLiteral( "-30.0000" ) ); + // tooltip should show target CRS details for user clarification + QCOMPARE( model.data( model.index( 1, 4 ), Qt::ToolTipRole ).toString(), QStringLiteral( "150
EPSG:4326 - WGS 84" ) ); + QCOMPARE( model.data( model.index( 1, 5 ), Qt::ToolTipRole ).toString(), QStringLiteral( "-30
EPSG:4326 - WGS 84" ) ); QCOMPARE( model.data( model.index( 1, 6 ) ).toString(), QStringLiteral( "n/a" ) ); QCOMPARE( model.data( model.index( 1, 7 ) ).toString(), QStringLiteral( "n/a" ) ); QCOMPARE( model.data( model.index( 1, 8 ) ).toString(), QStringLiteral( "n/a" ) ); From c7423f2bca6db7fe864524288fbc1f3f66c8742a Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 10 Feb 2022 11:55:47 +1000 Subject: [PATCH 45/47] Use an appropriate number of decimal places for display (cherry picked from commit 27db7754a584541689b100bd316aa8472634c2e7) --- src/app/georeferencer/qgsgcplistmodel.cpp | 27 ++++--- src/app/georeferencer/qgsgcplistmodel.h | 5 ++ tests/src/app/testqgsgeoreferencer.cpp | 94 +++++++++++------------ 3 files changed, 69 insertions(+), 57 deletions(-) diff --git a/src/app/georeferencer/qgsgcplistmodel.cpp b/src/app/georeferencer/qgsgcplistmodel.cpp index 1c9c63744ef2..3b991742544f 100644 --- a/src/app/georeferencer/qgsgcplistmodel.cpp +++ b/src/app/georeferencer/qgsgcplistmodel.cpp @@ -92,17 +92,15 @@ QVariant QgsGCPListModel::data( const QModelIndex &index, int role ) const { switch ( role ) { - case Qt::ToolTipRole: case Qt::EditRole: case Qt::UserRole: // use full precision return column == QgsGCPListModel::Column::SourceX ? point->sourcePoint().x() : point->sourcePoint().y(); + case Qt::ToolTipRole: case Qt::DisplayRole: // truncate decimals for display - return QString::number( - column == QgsGCPListModel::Column::SourceX ? point->sourcePoint().x() : point->sourcePoint().y(), - 'f', 4 ); + return formatNumber( column == QgsGCPListModel::Column::SourceX ? point->sourcePoint().x() : point->sourcePoint().y() ); default: break; } @@ -120,7 +118,7 @@ QVariant QgsGCPListModel::data( const QModelIndex &index, int role ) const { const QString crsString = mTargetCrs.userFriendlyIdentifier(); const double value = column == QgsGCPListModel::Column::DestinationX ? transformedDestinationPoint.x() : transformedDestinationPoint.y(); - return QStringLiteral( "%1
%2" ).arg( value ).arg( crsString ); + return QStringLiteral( "%1
%2" ).arg( formatNumber( value ), crsString ); } case Qt::EditRole: @@ -130,9 +128,7 @@ QVariant QgsGCPListModel::data( const QModelIndex &index, int role ) const case Qt::DisplayRole: // truncate decimals for display - return QString::number( - column == QgsGCPListModel::Column::DestinationX ? transformedDestinationPoint.x() : transformedDestinationPoint.y(), - 'f', 4 ); + return formatNumber( column == QgsGCPListModel::Column::DestinationX ? transformedDestinationPoint.x() : transformedDestinationPoint.y() ); default: break; } @@ -166,15 +162,15 @@ QVariant QgsGCPListModel::data( const QModelIndex &index, int role ) const switch ( role ) { - case Qt::ToolTipRole: case Qt::EditRole: case Qt::UserRole: // use full precision return value; + case Qt::ToolTipRole: case Qt::DisplayRole: // truncate decimals for display - return QString::number( value, 'f', 4 ); + return formatNumber( value ); default: break; } @@ -436,4 +432,15 @@ void QgsGCPListModel::updateResiduals() index( rowCount() - 1, static_cast< int >( Column::TotalResidual ) ) ); } +QString QgsGCPListModel::formatNumber( double number ) +{ + int decimalPlaces = 4; + if ( std::fabs( number ) > 100000 ) + decimalPlaces = 2; + else if ( std::fabs( number ) < 1000 ) + decimalPlaces = 6; + + return QString::number( number, 'f', decimalPlaces ); +} + diff --git a/src/app/georeferencer/qgsgcplistmodel.h b/src/app/georeferencer/qgsgcplistmodel.h index 312ded4efa5c..7f55d746280d 100644 --- a/src/app/georeferencer/qgsgcplistmodel.h +++ b/src/app/georeferencer/qgsgcplistmodel.h @@ -72,6 +72,11 @@ class APP_EXPORT QgsGCPListModel : public QAbstractTableModel */ void updateResiduals(); + /** + * Formats a number for display with an appropriate number of decimal places. + */ + static QString formatNumber( double number ); + signals: void pointEnabled( QgsGeorefDataPoint *pnt, int i ); diff --git a/tests/src/app/testqgsgeoreferencer.cpp b/tests/src/app/testqgsgeoreferencer.cpp index d89ec52773f0..2c8d5d207c2b 100644 --- a/tests/src/app/testqgsgeoreferencer.cpp +++ b/tests/src/app/testqgsgeoreferencer.cpp @@ -638,51 +638,51 @@ void TestQgsGeoreferencer::testListModel() QCOMPARE( model.rowCount(), 3 ); QCOMPARE( model.data( model.index( 0, 1 ) ).toString(), QStringLiteral( "0" ) ); - QCOMPARE( model.data( model.index( 0, 2 ) ).toString(), QStringLiteral( "781662.3750" ) ); - QCOMPARE( model.data( model.index( 0, 3 ) ).toString(), QStringLiteral( "3350923.1250" ) ); - QCOMPARE( model.data( model.index( 0, 4 ) ).toString(), QStringLiteral( "-30.0000" ) ); - QCOMPARE( model.data( model.index( 0, 5 ) ).toString(), QStringLiteral( "40.0000" ) ); - QCOMPARE( model.data( model.index( 0, 4 ), Qt::ToolTipRole ).toString(), QStringLiteral( "-30
EPSG:4326 - WGS 84" ) ); - QCOMPARE( model.data( model.index( 0, 5 ), Qt::ToolTipRole ).toString(), QStringLiteral( "40
EPSG:4326 - WGS 84" ) ); + QCOMPARE( model.data( model.index( 0, 2 ) ).toString(), QStringLiteral( "781662.38" ) ); + QCOMPARE( model.data( model.index( 0, 3 ) ).toString(), QStringLiteral( "3350923.13" ) ); + QCOMPARE( model.data( model.index( 0, 4 ) ).toString(), QStringLiteral( "-30.000000" ) ); + QCOMPARE( model.data( model.index( 0, 5 ) ).toString(), QStringLiteral( "40.000000" ) ); + QCOMPARE( model.data( model.index( 0, 4 ), Qt::ToolTipRole ).toString(), QStringLiteral( "-30.000000
EPSG:4326 - WGS 84" ) ); + QCOMPARE( model.data( model.index( 0, 5 ), Qt::ToolTipRole ).toString(), QStringLiteral( "40.000000
EPSG:4326 - WGS 84" ) ); QCOMPARE( model.data( model.index( 0, 6 ) ).toString(), QStringLiteral( "n/a" ) ); QCOMPARE( model.data( model.index( 0, 7 ) ).toString(), QStringLiteral( "n/a" ) ); QCOMPARE( model.data( model.index( 0, 8 ) ).toString(), QStringLiteral( "n/a" ) ); QCOMPARE( model.data( model.index( 1, 1 ) ).toString(), QStringLiteral( "1" ) ); - QCOMPARE( model.data( model.index( 1, 2 ) ).toString(), QStringLiteral( "787362.3750" ) ); - QCOMPARE( model.data( model.index( 1, 3 ) ).toString(), QStringLiteral( "3350923.1250" ) ); - QCOMPARE( model.data( model.index( 1, 4 ) ).toString(), QStringLiteral( "150.0000" ) ); - QCOMPARE( model.data( model.index( 1, 5 ) ).toString(), QStringLiteral( "-30.0000" ) ); + QCOMPARE( model.data( model.index( 1, 2 ) ).toString(), QStringLiteral( "787362.38" ) ); + QCOMPARE( model.data( model.index( 1, 3 ) ).toString(), QStringLiteral( "3350923.13" ) ); + QCOMPARE( model.data( model.index( 1, 4 ) ).toString(), QStringLiteral( "149.999994" ) ); + QCOMPARE( model.data( model.index( 1, 5 ) ).toString(), QStringLiteral( "-29.999993" ) ); // tooltip should show target CRS details for user clarification - QCOMPARE( model.data( model.index( 1, 4 ), Qt::ToolTipRole ).toString(), QStringLiteral( "150
EPSG:4326 - WGS 84" ) ); - QCOMPARE( model.data( model.index( 1, 5 ), Qt::ToolTipRole ).toString(), QStringLiteral( "-30
EPSG:4326 - WGS 84" ) ); + QCOMPARE( model.data( model.index( 1, 4 ), Qt::ToolTipRole ).toString(), QStringLiteral( "149.999994
EPSG:4326 - WGS 84" ) ); + QCOMPARE( model.data( model.index( 1, 5 ), Qt::ToolTipRole ).toString(), QStringLiteral( "-29.999993
EPSG:4326 - WGS 84" ) ); QCOMPARE( model.data( model.index( 1, 6 ) ).toString(), QStringLiteral( "n/a" ) ); QCOMPARE( model.data( model.index( 1, 7 ) ).toString(), QStringLiteral( "n/a" ) ); QCOMPARE( model.data( model.index( 1, 8 ) ).toString(), QStringLiteral( "n/a" ) ); QCOMPARE( model.data( model.index( 2, 1 ) ).toString(), QStringLiteral( "2" ) ); - QCOMPARE( model.data( model.index( 2, 2 ) ).toString(), QStringLiteral( "787362.3750" ) ); - QCOMPARE( model.data( model.index( 2, 3 ) ).toString(), QStringLiteral( "3362323.1250" ) ); - QCOMPARE( model.data( model.index( 2, 4 ) ).toString(), QStringLiteral( "-35.0000" ) ); - QCOMPARE( model.data( model.index( 2, 5 ) ).toString(), QStringLiteral( "42.0000" ) ); + QCOMPARE( model.data( model.index( 2, 2 ) ).toString(), QStringLiteral( "787362.38" ) ); + QCOMPARE( model.data( model.index( 2, 3 ) ).toString(), QStringLiteral( "3362323.13" ) ); + QCOMPARE( model.data( model.index( 2, 4 ) ).toString(), QStringLiteral( "-35.000000" ) ); + QCOMPARE( model.data( model.index( 2, 5 ) ).toString(), QStringLiteral( "42.000000" ) ); QCOMPARE( model.data( model.index( 2, 6 ) ).toString(), QStringLiteral( "n/a" ) ); QCOMPARE( model.data( model.index( 2, 7 ) ).toString(), QStringLiteral( "n/a" ) ); QCOMPARE( model.data( model.index( 2, 8 ) ).toString(), QStringLiteral( "n/a" ) ); // with transform set residuals should be visible model.setGeorefTransform( &transform ); - QCOMPARE( model.data( model.index( 0, 6 ) ).toString(), QStringLiteral( "0.0000" ) ); - QCOMPARE( model.data( model.index( 0, 7 ) ).toString(), QStringLiteral( "-189.1892" ) ); - QCOMPARE( model.data( model.index( 0, 8 ) ).toString(), QStringLiteral( "189.1892" ) ); + QCOMPARE( model.data( model.index( 0, 6 ) ).toString(), QStringLiteral( "0.000000" ) ); + QCOMPARE( model.data( model.index( 0, 7 ) ).toString(), QStringLiteral( "-189.189188" ) ); + QCOMPARE( model.data( model.index( 0, 8 ) ).toString(), QStringLiteral( "189.189188" ) ); - QCOMPARE( model.data( model.index( 1, 6 ) ).toString(), QStringLiteral( "105.7143" ) ); - QCOMPARE( model.data( model.index( 1, 7 ) ).toString(), QStringLiteral( "189.1892" ) ); - QCOMPARE( model.data( model.index( 1, 8 ) ).toString(), QStringLiteral( "216.7212" ) ); + QCOMPARE( model.data( model.index( 1, 6 ) ).toString(), QStringLiteral( "105.714286" ) ); + QCOMPARE( model.data( model.index( 1, 7 ) ).toString(), QStringLiteral( "189.189188" ) ); + QCOMPARE( model.data( model.index( 1, 8 ) ).toString(), QStringLiteral( "216.721155" ) ); - QCOMPARE( model.data( model.index( 2, 6 ) ).toString(), QStringLiteral( "-105.7143" ) ); - QCOMPARE( model.data( model.index( 2, 7 ) ).toString(), QStringLiteral( "0.0000" ) ); - QCOMPARE( model.data( model.index( 2, 8 ) ).toString(), QStringLiteral( "105.7143" ) ); + QCOMPARE( model.data( model.index( 2, 6 ) ).toString(), QStringLiteral( "-105.714286" ) ); + QCOMPARE( model.data( model.index( 2, 7 ) ).toString(), QStringLiteral( "0.000000" ) ); + QCOMPARE( model.data( model.index( 2, 8 ) ).toString(), QStringLiteral( "105.714286" ) ); // set data // these columns are read-only @@ -693,19 +693,19 @@ void TestQgsGeoreferencer::testListModel() QVERIFY( !model.setData( model.index( 0, 8 ), 11 ) ); QVERIFY( model.setData( model.index( 0, 2 ), 777777.77 ) ); - QCOMPARE( model.data( model.index( 0, 2 ) ).toString(), QStringLiteral( "777777.7700" ) ); + QCOMPARE( model.data( model.index( 0, 2 ) ).toString(), QStringLiteral( "777777.77" ) ); QCOMPARE( list.at( 0 )->sourcePoint().x(), 777777.77 ); QVERIFY( model.setData( model.index( 0, 3 ), 3333333.33 ) ); - QCOMPARE( model.data( model.index( 0, 3 ) ).toString(), QStringLiteral( "3333333.3300" ) ); + QCOMPARE( model.data( model.index( 0, 3 ) ).toString(), QStringLiteral( "3333333.33" ) ); QCOMPARE( list.at( 0 )->sourcePoint().y(), 3333333.33 ); QVERIFY( model.setData( model.index( 0, 4 ), 1660000.77 ) ); - QCOMPARE( model.data( model.index( 0, 4 ) ).toString(), QStringLiteral( "1660000.7700" ) ); + QCOMPARE( model.data( model.index( 0, 4 ) ).toString(), QStringLiteral( "1660000.77" ) ); QCOMPARE( list.at( 0 )->destinationPoint().x(), 1660000.77 ); QVERIFY( model.setData( model.index( 0, 5 ), 4433333.33 ) ); - QCOMPARE( model.data( model.index( 0, 5 ) ).toString(), QStringLiteral( "4433333.3300" ) ); + QCOMPARE( model.data( model.index( 0, 5 ) ).toString(), QStringLiteral( "4433333.33" ) ); QCOMPARE( list.at( 0 )->destinationPoint().y(), 4433333.33 ); // disable point @@ -739,54 +739,54 @@ void TestQgsGeoreferencer::testListModelCrs() model.setTargetCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), QgsProject::instance()->transformContext() ); // all destination points are shown in target crs - QCOMPARE( model.data( model.index( 0, 4 ) ).toString(), QStringLiteral( "-30.0000" ) ); - QCOMPARE( model.data( model.index( 0, 5 ) ).toString(), QStringLiteral( "40.0000" ) ); - QCOMPARE( model.data( model.index( 1, 4 ) ).toString(), QStringLiteral( "150.0000" ) ); - QCOMPARE( model.data( model.index( 1, 5 ) ).toString(), QStringLiteral( "-30.0000" ) ); - QCOMPARE( model.data( model.index( 2, 4 ) ).toString(), QStringLiteral( "158.9831" ) ); - QCOMPARE( model.data( model.index( 2, 5 ) ).toString(), QStringLiteral( "-29.2190" ) ); + QCOMPARE( model.data( model.index( 0, 4 ) ).toString(), QStringLiteral( "-30.000000" ) ); + QCOMPARE( model.data( model.index( 0, 5 ) ).toString(), QStringLiteral( "40.000000" ) ); + QCOMPARE( model.data( model.index( 1, 4 ) ).toString(), QStringLiteral( "149.999994" ) ); + QCOMPARE( model.data( model.index( 1, 5 ) ).toString(), QStringLiteral( "-29.999993" ) ); + QCOMPARE( model.data( model.index( 2, 4 ) ).toString(), QStringLiteral( "158.983147" ) ); + QCOMPARE( model.data( model.index( 2, 5 ) ).toString(), QStringLiteral( "-29.218996" ) ); // setting a point's destination x or y will update that point to being stored in the target crs QVERIFY( model.setData( model.index( 0, 4 ), -31.0 ) ); - QCOMPARE( model.data( model.index( 0, 4 ) ).toString(), QStringLiteral( "-31.0000" ) ); - QCOMPARE( model.data( model.index( 0, 5 ) ).toString(), QStringLiteral( "40.0000" ) ); + QCOMPARE( model.data( model.index( 0, 4 ) ).toString(), QStringLiteral( "-31.000000" ) ); + QCOMPARE( model.data( model.index( 0, 5 ) ).toString(), QStringLiteral( "40.000000" ) ); QCOMPARE( list.at( 0 )->destinationPoint().x(), -31.0 ); QCOMPARE( list.at( 0 )->destinationPoint().y(), 40.0 ); QCOMPARE( list.at( 0 )->destinationPointCrs().authid(), QStringLiteral( "EPSG:4326" ) ); QVERIFY( model.setData( model.index( 0, 5 ), 41.0 ) ); - QCOMPARE( model.data( model.index( 0, 4 ) ).toString(), QStringLiteral( "-31.0000" ) ); - QCOMPARE( model.data( model.index( 0, 5 ) ).toString(), QStringLiteral( "41.0000" ) ); + QCOMPARE( model.data( model.index( 0, 4 ) ).toString(), QStringLiteral( "-31.000000" ) ); + QCOMPARE( model.data( model.index( 0, 5 ) ).toString(), QStringLiteral( "41.000000" ) ); QCOMPARE( list.at( 0 )->destinationPoint().x(), -31.0 ); QCOMPARE( list.at( 0 )->destinationPoint().y(), 41.0 ); QCOMPARE( list.at( 0 )->destinationPointCrs().authid(), QStringLiteral( "EPSG:4326" ) ); // destination point was originally in EPSG:3857, should be changed to 4326 when destination x is set QVERIFY( model.setData( model.index( 1, 4 ), 148 ) ); - QCOMPARE( model.data( model.index( 1, 4 ) ).toString(), QStringLiteral( "148.0000" ) ); - QCOMPARE( model.data( model.index( 1, 5 ) ).toString(), QStringLiteral( "-30.0000" ) ); + QCOMPARE( model.data( model.index( 1, 4 ) ).toString(), QStringLiteral( "148.000000" ) ); + QCOMPARE( model.data( model.index( 1, 5 ) ).toString(), QStringLiteral( "-29.999993" ) ); QCOMPARE( list.at( 1 )->destinationPoint().x(), 148 ); QGSCOMPARENEAR( list.at( 1 )->destinationPoint().y(), -30, 0.001 ); QCOMPARE( list.at( 1 )->destinationPointCrs().authid(), QStringLiteral( "EPSG:4326" ) ); QVERIFY( model.setData( model.index( 1, 5 ), -32.0 ) ); - QCOMPARE( model.data( model.index( 1, 4 ) ).toString(), QStringLiteral( "148.0000" ) ); - QCOMPARE( model.data( model.index( 1, 5 ) ).toString(), QStringLiteral( "-32.0000" ) ); + QCOMPARE( model.data( model.index( 1, 4 ) ).toString(), QStringLiteral( "148.000000" ) ); + QCOMPARE( model.data( model.index( 1, 5 ) ).toString(), QStringLiteral( "-32.000000" ) ); QCOMPARE( list.at( 1 )->destinationPoint().x(), 148 ); QCOMPARE( list.at( 1 )->destinationPoint().y(), -32 ); QCOMPARE( list.at( 1 )->destinationPointCrs().authid(), QStringLiteral( "EPSG:4326" ) ); // destination point was originally in EPSG:3857, should be changed to 4326 when destination y is set QVERIFY( model.setData( model.index( 2, 5 ), -29.0 ) ); - QCOMPARE( model.data( model.index( 2, 4 ) ).toString(), QStringLiteral( "158.9831" ) ); - QCOMPARE( model.data( model.index( 2, 5 ) ).toString(), QStringLiteral( "-29.0000" ) ); + QCOMPARE( model.data( model.index( 2, 4 ) ).toString(), QStringLiteral( "158.983147" ) ); + QCOMPARE( model.data( model.index( 2, 5 ) ).toString(), QStringLiteral( "-29.000000" ) ); QGSCOMPARENEAR( list.at( 2 )->destinationPoint().x(), 158.9831, 0.001 ); QCOMPARE( list.at( 2 )->destinationPoint().y(), -29 ); QCOMPARE( list.at( 2 )->destinationPointCrs().authid(), QStringLiteral( "EPSG:4326" ) ); QVERIFY( model.setData( model.index( 2, 4 ), 159 ) ); - QCOMPARE( model.data( model.index( 2, 4 ) ).toString(), QStringLiteral( "159.0000" ) ); - QCOMPARE( model.data( model.index( 2, 5 ) ).toString(), QStringLiteral( "-29.0000" ) ); + QCOMPARE( model.data( model.index( 2, 4 ) ).toString(), QStringLiteral( "159.000000" ) ); + QCOMPARE( model.data( model.index( 2, 5 ) ).toString(), QStringLiteral( "-29.000000" ) ); QCOMPARE( list.at( 2 )->destinationPoint().x(), 159 ); QCOMPARE( list.at( 2 )->destinationPoint().y(), -29 ); QCOMPARE( list.at( 2 )->destinationPointCrs().authid(), QStringLiteral( "EPSG:4326" ) ); From f188c0923838887310d175fd22a91a2ae681aeef Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 10 Feb 2022 14:45:46 +1000 Subject: [PATCH 46/47] Cleanup handling of 'Create world file only' option (cherry picked from commit 4877003f39bdd79db86d43811cce938e64bad983) --- src/app/georeferencer/qgsgeorefmainwindow.cpp | 16 ++++--- src/app/georeferencer/qgsgeorefmainwindow.h | 1 + .../qgstransformsettingsdialog.cpp | 42 ++++++------------- .../qgstransformsettingsdialog.h | 11 ++++- 4 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/app/georeferencer/qgsgeorefmainwindow.cpp b/src/app/georeferencer/qgsgeorefmainwindow.cpp index de2024975ebd..7057471aadff 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.cpp +++ b/src/app/georeferencer/qgsgeorefmainwindow.cpp @@ -189,6 +189,7 @@ void QgsGeoreferencerMainWindow::reset() { mRasterFileName.clear(); mModifiedRasterFileName.clear(); + mCreateWorldFileOnly = false; setWindowTitle( tr( "Georeferencer" ) ); //delete old points @@ -237,6 +238,7 @@ void QgsGeoreferencerMainWindow::openRaster( const QString &fileName ) mRasterFileName = fileName; } mModifiedRasterFileName.clear(); + mCreateWorldFileOnly = false; QString errMsg; if ( !QgsRasterLayer::isValidRasterFileName( mRasterFileName, errMsg ) ) @@ -337,7 +339,7 @@ void QgsGeoreferencerMainWindow::doGeoreference() mMessageBar->pushMessage( tr( "Georeference Successful" ), tr( "Raster was successfully georeferenced." ), Qgis::MessageLevel::Success ); if ( mLoadInQgis ) { - if ( mModifiedRasterFileName.isEmpty() ) + if ( mCreateWorldFileOnly ) { QgisApp::instance()->addRasterLayer( mRasterFileName, QFileInfo( mRasterFileName ).completeBaseName(), QString() ); } @@ -353,6 +355,7 @@ bool QgsGeoreferencerMainWindow::getTransformSettings() { QgsTransformSettingsDialog d( mRasterFileName, mModifiedRasterFileName ); d.setTargetCrs( mTargetCrs ); + d.setCreateWorldFileOnly( mCreateWorldFileOnly ); if ( !d.exec() ) { return false; @@ -360,6 +363,7 @@ bool QgsGeoreferencerMainWindow::getTransformSettings() d.getTransformSettings( mTransformParam, mResamplingMethod, mCompressionMethod, mModifiedRasterFileName, mPdfOutputMapFile, mPdfOutputFile, mSaveGcp, mUseZeroForTrans, mLoadInQgis, mUserResX, mUserResY ); + mCreateWorldFileOnly = d.createWorldFileOnly(); mTargetCrs = d.targetCrs(); mTransformParamLabel->setText( tr( "Transform: " ) + QgsGcpTransformerInterface::methodToString( mTransformParam ) ); @@ -1254,8 +1258,8 @@ bool QgsGeoreferencerMainWindow::georeference() if ( !checkReadyGeoref() ) return false; - if ( mModifiedRasterFileName.isEmpty() && ( QgsGcpTransformerInterface::TransformMethod::Linear == mGeorefTransform.transformParametrisation() || - QgsGcpTransformerInterface::TransformMethod::Helmert == mGeorefTransform.transformParametrisation() ) ) + if ( mCreateWorldFileOnly && ( QgsGcpTransformerInterface::TransformMethod::Linear == mGeorefTransform.transformParametrisation() || + QgsGcpTransformerInterface::TransformMethod::Helmert == mGeorefTransform.transformParametrisation() ) ) { QgsPointXY origin; double pixelXSize, pixelYSize, rotation; @@ -1301,7 +1305,7 @@ bool QgsGeoreferencerMainWindow::georeference() } return true; } - else // Helmert, Polinom 1, Polinom 2, Polinom 3 + else { QgsImageWarper warper( this ); int res = warper.warpFile( mRasterFileName, mModifiedRasterFileName, mGeorefTransform, @@ -1848,8 +1852,8 @@ bool QgsGeoreferencerMainWindow::checkReadyGeoref() return false; } - //MH: helmert transformation without warping disabled until qgis is able to read rotated rasters efficiently - if ( mModifiedRasterFileName.isEmpty() && QgsGcpTransformerInterface::TransformMethod::Linear != mTransformParam /*&& QgsGeorefTransform::Helmert != mTransformParam*/ ) + if ( mCreateWorldFileOnly + && ( QgsGcpTransformerInterface::TransformMethod::Linear != mTransformParam && QgsGcpTransformerInterface::TransformMethod::Helmert != mTransformParam ) ) { QMessageBox::information( this, tr( "Georeferencer" ), tr( "Please set output raster name." ) ); getTransformSettings(); diff --git a/src/app/georeferencer/qgsgeorefmainwindow.h b/src/app/georeferencer/qgsgeorefmainwindow.h index 5f6b325d1df0..dee1f37d3e45 100644 --- a/src/app/georeferencer/qgsgeorefmainwindow.h +++ b/src/app/georeferencer/qgsgeorefmainwindow.h @@ -243,6 +243,7 @@ class QgsGeoreferencerMainWindow : public QMainWindow, private Ui::QgsGeorefPlug QgsImageWarper::ResamplingMethod mResamplingMethod; QgsGeorefTransform mGeorefTransform; QString mCompressionMethod; + bool mCreateWorldFileOnly = false; QgsGCPList mPoints; QList< QgsGcpPoint > mSavedPoints; diff --git a/src/app/georeferencer/qgstransformsettingsdialog.cpp b/src/app/georeferencer/qgstransformsettingsdialog.cpp index d123c8e7c0d5..e9fea8a2cdb9 100644 --- a/src/app/georeferencer/qgstransformsettingsdialog.cpp +++ b/src/app/georeferencer/qgstransformsettingsdialog.cpp @@ -104,8 +104,6 @@ QgsTransformSettingsDialog::QgsTransformSettingsDialog( const QString &raster, c cmbResampling->setCurrentIndex( settings.value( QStringLiteral( "/Plugin-GeoReferencer/lastresampling" ), 0 ).toInt() ); cmbCompressionComboBox->setCurrentIndex( settings.value( QStringLiteral( "/Plugin-GeoReferencer/lastcompression" ), 0 ).toInt() ); - mWorldFileCheckBox->setChecked( settings.value( QStringLiteral( "/Plugin-Georeferencer/word_file_checkbox" ), false ).toBool() ); - cbxUserResolution->setChecked( settings.value( QStringLiteral( "/Plugin-Georeferencer/user_specified_resolution" ), false ).toBool() ); bool ok; dsbHorizRes->setValue( settings.value( QStringLiteral( "/Plugin-GeoReferencer/user_specified_resx" ), .0 ).toDouble( &ok ) ); @@ -132,6 +130,17 @@ QgsCoordinateReferenceSystem QgsTransformSettingsDialog::targetCrs() const return mCrsSelector->crs(); } +bool QgsTransformSettingsDialog::createWorldFileOnly() const +{ + return mWorldFileCheckBox->isChecked(); +} + +void QgsTransformSettingsDialog::setCreateWorldFileOnly( bool enabled ) +{ + mWorldFileCheckBox->setChecked( enabled ); + mWorldFileCheckBox_stateChanged( mWorldFileCheckBox->checkState() ); +} + void QgsTransformSettingsDialog::getTransformSettings( QgsGeorefTransform::TransformMethod &tp, QgsImageWarper::ResamplingMethod &rm, QString &comprMethod, QString &raster, QString &pdfMapFile, QString &pdfReportFile, bool &saveGcpPoints, bool &zt, bool &loadInQgis, @@ -144,14 +153,8 @@ void QgsTransformSettingsDialog::getTransformSettings( QgsGeorefTransform::Trans rm = static_cast< QgsImageWarper::ResamplingMethod >( cmbResampling->currentData().toInt() ); comprMethod = cmbCompressionComboBox->currentData().toString(); - if ( mWorldFileCheckBox->isChecked() ) - { - raster.clear(); - } - else - { - raster = mOutputRaster->filePath(); - } + raster = mOutputRaster->filePath(); + pdfMapFile = mPdfMap->filePath(); pdfReportFile = mPdfReport->filePath(); zt = cbxZeroAsTrans->isChecked(); @@ -166,23 +169,6 @@ void QgsTransformSettingsDialog::getTransformSettings( QgsGeorefTransform::Trans saveGcpPoints = saveGcpCheckBox->isChecked(); } -void QgsTransformSettingsDialog::resetSettings() -{ - QgsSettings s; - s.setValue( QStringLiteral( "/Plugin-GeoReferencer/lasttransformation" ), -1 ); - s.setValue( QStringLiteral( "/Plugin-GeoReferencer/lastresampling" ), 0 ); - s.setValue( QStringLiteral( "/Plugin-GeoReferencer/lastcompression" ), 0 ); - s.setValue( QStringLiteral( "/Plugin-GeoReferencer/targetsrs" ), QString() ); - s.setValue( QStringLiteral( "/Plugin-GeoReferencer/zeroastrans" ), false ); - s.setValue( QStringLiteral( "/Plugin-GeoReferencer/loadinqgis" ), false ); - s.setValue( QStringLiteral( "/Plugin-GeoReferencer/save_gcp_points" ), false ); - s.setValue( QStringLiteral( "/Plugin-GeoReferencer/user_specified_resolution" ), false ); - s.setValue( QStringLiteral( "/Plugin-GeoReferencer/user_specified_resx" ), 1.0 ); - s.setValue( QStringLiteral( "/Plugin-GeoReferencer/user_specified_resy" ), -1.0 ); - s.setValue( QStringLiteral( "/Plugin-GeoReferencer/word_file_checkbox" ), false ); - s.setValue( QStringLiteral( "/Plugin-GeoReferencer/lastPDFReportDir" ), QDir::homePath() ); -} - void QgsTransformSettingsDialog::accept() { if ( !mOutputRaster->filePath().isEmpty() ) @@ -216,10 +202,8 @@ void QgsTransformSettingsDialog::accept() settings.setValue( QStringLiteral( "/Plugin-GeoReferencer/user_specified_resolution" ), cbxUserResolution->isChecked() ); settings.setValue( QStringLiteral( "/Plugin-GeoReferencer/user_specified_resx" ), dsbHorizRes->value() ); settings.setValue( QStringLiteral( "/Plugin-GeoReferencer/user_specified_resy" ), dsbVerticalRes->value() ); - settings.setValue( QStringLiteral( "/Plugin-GeoReferencer/word_file_checkbox" ), mWorldFileCheckBox->isChecked() ); settings.setValue( QStringLiteral( "/Plugin-GeoReferencer/save_gcp_points" ), saveGcpCheckBox->isChecked() ); - QDialog::accept(); } diff --git a/src/app/georeferencer/qgstransformsettingsdialog.h b/src/app/georeferencer/qgstransformsettingsdialog.h index 4407590d74c2..13e0132006e4 100644 --- a/src/app/georeferencer/qgstransformsettingsdialog.h +++ b/src/app/georeferencer/qgstransformsettingsdialog.h @@ -39,11 +39,20 @@ class QgsTransformSettingsDialog : public QDialog, private Ui::QgsTransformSetti */ QgsCoordinateReferenceSystem targetCrs() const; + /** + * Returns TRUE if the create world file only option is set. + */ + bool createWorldFileOnly() const; + + /** + * Sets whether the create world file only option should be set. + */ + void setCreateWorldFileOnly( bool enabled ); + void getTransformSettings( QgsGeorefTransform::TransformMethod &tp, QgsImageWarper::ResamplingMethod &rm, QString &comprMethod, QString &raster, QString &pdfMapFile, QString &pdfReportFile, bool &saveGcpPoints, bool &zt, bool &loadInQgis, double &resX, double &resY ); - static void resetSettings(); protected: void accept() override; From 66d083507dd770e92910eca78a00d2afb2f6b97e Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 10 Feb 2022 14:48:47 +1000 Subject: [PATCH 47/47] Create world file only option is also compatible with Helmert transforms (cherry picked from commit 4e035e686bc33c64b183fe21648f2e1dac1a6d9d) --- src/app/georeferencer/qgstransformsettingsdialog.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/app/georeferencer/qgstransformsettingsdialog.cpp b/src/app/georeferencer/qgstransformsettingsdialog.cpp index e9fea8a2cdb9..bb6f5514410b 100644 --- a/src/app/georeferencer/qgstransformsettingsdialog.cpp +++ b/src/app/georeferencer/qgstransformsettingsdialog.cpp @@ -210,14 +210,15 @@ void QgsTransformSettingsDialog::accept() void QgsTransformSettingsDialog::cmbTransformType_currentIndexChanged( const QString & ) { if ( cmbTransformType->currentIndex() != -1 - && static_cast< QgsGcpTransformerInterface::TransformMethod >( cmbTransformType->currentData().toInt() ) == QgsGcpTransformerInterface::TransformMethod::Linear ) + && ( static_cast< QgsGcpTransformerInterface::TransformMethod >( cmbTransformType->currentData().toInt() ) == QgsGcpTransformerInterface::TransformMethod::Linear + || static_cast< QgsGcpTransformerInterface::TransformMethod >( cmbTransformType->currentData().toInt() ) == QgsGcpTransformerInterface::TransformMethod::Helmert ) ) { mWorldFileCheckBox->setEnabled( true ); } else { + // world file only option is only compatible with helmert/linear transforms mWorldFileCheckBox->setEnabled( false ); - // reset world file checkbox when transformation differ from Linear mWorldFileCheckBox->setChecked( false ); } }