1 /***************************************************************************
2     qgsgcplistmodel.cpp - Model implementation of GCPList Model/View
3      --------------------------------------
4     Date                 : 27-Feb-2009
5     Copyright            : (c) 2009 by Manuel Massing
6     Email                : m.massing at warped-space.de
7  ***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 
16 #include "qgsgcplist.h"
17 #include "qgsgcplistmodel.h"
18 #include "qgis.h"
19 #include "qgsgeorefdatapoint.h"
20 #include "qgsgeoreftransform.h"
21 #include "qgssettings.h"
22 
23 #include <cmath>
24 
25 class QgsStandardItem : public QStandardItem
26 {
27   public:
QgsStandardItem(const QString & text)28     explicit QgsStandardItem( const QString &text ) : QStandardItem( text )
29     {
30       // In addition to the DisplayRole, also set the user role, which is used for sorting.
31       // This is needed for numerical sorting to work correctly (otherwise sorting is lexicographic).
32       setData( QVariant( text ), Qt::UserRole );
33       setTextAlignment( Qt::AlignRight );
34     }
35 
QgsStandardItem(int value)36     explicit QgsStandardItem( int value ) : QStandardItem( QString::number( value ) )
37     {
38       setData( QVariant( value ), Qt::UserRole );
39       setTextAlignment( Qt::AlignCenter );
40     }
41 
QgsStandardItem(double value)42     explicit QgsStandardItem( double value ) : QStandardItem( QString::number( value, 'f', 4 ) )
43     {
44       setData( QVariant( value ), Qt::UserRole );
45       //show the full precision when editing points
46       setData( QVariant( value ), Qt::EditRole );
47       setData( QVariant( value ), Qt::ToolTipRole );
48       setTextAlignment( Qt::AlignRight );
49     }
50 };
51 
QgsGCPListModel(QObject * parent)52 QgsGCPListModel::QgsGCPListModel( QObject *parent )
53   : QStandardItemModel( parent )
54 {
55   // Use data provided by Qt::UserRole as sorting key (needed for numerical sorting).
56   setSortRole( Qt::UserRole );
57 }
58 
setGCPList(QgsGCPList * theGCPList)59 void QgsGCPListModel::setGCPList( QgsGCPList *theGCPList )
60 {
61   mGCPList = theGCPList;
62   updateModel();
63 }
64 
65 // ------------------------------- public ---------------------------------- //
setGeorefTransform(QgsGeorefTransform * georefTransform)66 void QgsGCPListModel::setGeorefTransform( QgsGeorefTransform *georefTransform )
67 {
68   mGeorefTransform = georefTransform;
69   updateModel();
70 }
71 
updateModel()72 void QgsGCPListModel::updateModel()
73 {
74   //clear();
75   if ( !mGCPList )
76     return;
77 
78   bool bTransformUpdated = false;
79 
80   QVector<QgsPointXY> mapCoords, pixelCoords;
81   mGCPList->createGCPVectors( mapCoords, pixelCoords );
82 
83   //  // Setup table header
84   QStringList itemLabels;
85   QString unitType;
86   QgsSettings s;
87   bool mapUnitsPossible = false;
88 
89   if ( mGeorefTransform )
90   {
91     bTransformUpdated = mGeorefTransform->updateParametersFromGCPs( mapCoords, pixelCoords );
92     mapUnitsPossible = mGeorefTransform->providesAccurateInverseTransformation();
93   }
94 
95 
96   if ( s.value( QStringLiteral( "/Plugin-GeoReferencer/Config/ResidualUnits" ) ) == "mapUnits" && mapUnitsPossible )
97   {
98     unitType = tr( "map units" );
99   }
100   else
101   {
102     unitType = tr( "pixels" );
103   }
104 
105   itemLabels << tr( "Visible" )
106              << tr( "ID" )
107              << tr( "Source X" )
108              << tr( "Source Y" )
109              << tr( "Dest. X" )
110              << tr( "Dest. Y" )
111              << tr( "dX (%1)" ).arg( unitType )
112              << tr( "dY (%1)" ).arg( unitType )
113              << tr( "Residual (%1)" ).arg( unitType );
114 
115   setHorizontalHeaderLabels( itemLabels );
116   setRowCount( mGCPList->size() );
117 
118   for ( int i = 0; i < mGCPList->sizeAll(); ++i )
119   {
120     int j = 0;
121     QgsGeorefDataPoint *p = mGCPList->at( i );
122 
123     if ( !p )
124       continue;
125 
126     p->setId( i );
127 
128     QStandardItem *si = new QStandardItem();
129     si->setTextAlignment( Qt::AlignCenter );
130     si->setCheckable( true );
131     if ( p->isEnabled() )
132       si->setCheckState( Qt::Checked );
133     else
134       si->setCheckState( Qt::Unchecked );
135 
136     setItem( i, j++, si );
137     setItem( i, j++, new QgsStandardItem( i ) );
138     setItem( i, j++, new QgsStandardItem( p->pixelCoords().x() ) );
139     setItem( i, j++, new QgsStandardItem( p->pixelCoords().y() ) );
140     setItem( i, j++, new QgsStandardItem( p->mapCoords().x() ) );
141     setItem( i, j++, new QgsStandardItem( p->mapCoords().y() ) );
142 
143     double residual;
144     double dX = 0;
145     double dY = 0;
146     // Calculate residual if transform is available and up-to-date
147     if ( mGeorefTransform && bTransformUpdated && mGeorefTransform->parametersInitialized() )
148     {
149       QgsPointXY dst;
150       QgsPointXY pixel = mGeorefTransform->hasCrs() ? mGeorefTransform->toColumnLine( p->pixelCoords() ) : p->pixelCoords();
151       if ( unitType == tr( "pixels" ) )
152       {
153         // Transform from world to raster coordinate:
154         // This is the transform direction used by the warp operation.
155         // As transforms of order >=2 are not invertible, we are only
156         // interested in the residual in this direction
157         if ( mGeorefTransform->transformWorldToRaster( p->mapCoords(), dst ) )
158         {
159           dX = ( dst.x() - pixel.x() );
160           dY = -( dst.y() - pixel.y() );
161         }
162       }
163       else if ( unitType == tr( "map units" ) )
164       {
165         if ( mGeorefTransform->transformRasterToWorld( pixel, dst ) )
166         {
167           dX = ( dst.x() - p->mapCoords().x() );
168           dY = ( dst.y() - p->mapCoords().y() );
169         }
170       }
171     }
172     residual = std::sqrt( dX * dX + dY * dY );
173 
174     p->setResidual( QPointF( dX, dY ) );
175 
176     if ( residual >= 0.f )
177     {
178       setItem( i, j++, new QgsStandardItem( dX ) );
179       setItem( i, j++, new QgsStandardItem( dY ) );
180       setItem( i, j++, new QgsStandardItem( residual ) );
181     }
182     else
183     {
184       setItem( i, j++, new QgsStandardItem( QStringLiteral( "n/a" ) ) );
185       setItem( i, j++, new QgsStandardItem( QStringLiteral( "n/a" ) ) );
186       setItem( i, j++, new QgsStandardItem( QStringLiteral( "n/a" ) ) );
187     }
188   }
189 }
190 
191 // --------------------------- public slots -------------------------------- //
replaceDataPoint(QgsGeorefDataPoint * newDataPoint,int i)192 void QgsGCPListModel::replaceDataPoint( QgsGeorefDataPoint *newDataPoint, int i )
193 {
194   mGCPList->replace( i, newDataPoint );
195 }
196 
onGCPListModified()197 void QgsGCPListModel::onGCPListModified()
198 {
199 }
200 
onTransformationModified()201 void QgsGCPListModel::onTransformationModified()
202 {
203 }
204