QGIS API Documentation  3.13.0-Master (740be229cb)
qgsdatumtransformdialog.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsdatumtransformdialog.cpp
3  ---------------------------
4  begin : November 2013
5  copyright : (C) 2013 by Marco Hugentobler
6  email : marco.hugentobler at sourcepole dot ch
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
19 #include "qgscoordinatetransform.h"
21 #include "qgslogger.h"
22 #include "qgssettings.h"
23 #include "qgsproject.h"
24 #include "qgsguiutils.h"
25 #include "qgsgui.h"
26 #include "qgshelp.h"
27 
28 #include <QDir>
29 #include <QPushButton>
30 
31 #if PROJ_VERSION_MAJOR>=6
32 #include "qgsprojutils.h"
33 #include <proj.h>
34 #endif
35 
36 bool QgsDatumTransformDialog::run( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, QWidget *parent, QgsMapCanvas *mapCanvas, const QString &windowTitle )
37 {
38  if ( sourceCrs == destinationCrs )
39  return true;
40 
42  if ( context.hasTransform( sourceCrs, destinationCrs ) )
43  {
44  return true;
45  }
46 
47  QgsDatumTransformDialog dlg( sourceCrs, destinationCrs, false, true, false, qMakePair( -1, -1 ), parent, nullptr, QString(), mapCanvas );
48  if ( !windowTitle.isEmpty() )
49  dlg.setWindowTitle( windowTitle );
50 
51  if ( dlg.shouldAskUserForSelection() )
52  {
53  if ( dlg.exec() )
54  {
55  const TransformInfo dt = dlg.selectedDatumTransform();
62  return true;
63  }
64  else
65  {
66  return false;
67  }
68  }
69  else
70  {
71  dlg.applyDefaultTransform();
72  return true;
73  }
74 }
75 
77  const QgsCoordinateReferenceSystem &dCrs, const bool allowCrsChanges, const bool showMakeDefault, const bool forceChoice,
78  QPair<int, int> selectedDatumTransforms,
79  QWidget *parent,
80  Qt::WindowFlags f, const QString &selectedProj, QgsMapCanvas *mapCanvas, bool allowFallback )
81  : QDialog( parent, f )
82  , mPreviousCursorOverride( qgis::make_unique< QgsTemporaryCursorRestoreOverride >() ) // this dialog is often shown while cursor overrides are in place, so temporarily remove them
83 {
84  setupUi( this );
85 
86  QgsCoordinateReferenceSystem sourceCrs = sCrs;
87  QgsCoordinateReferenceSystem destinationCrs = dCrs;
88 
90 
91  if ( !showMakeDefault )
92  mCoordinateOperationsWidget->setShowMakeDefault( false );
93 
94  if ( forceChoice )
95  {
96  mButtonBox->removeButton( mButtonBox->button( QDialogButtonBox::Cancel ) );
97  setWindowFlags( windowFlags() | Qt::CustomizeWindowHint );
98  setWindowFlags( windowFlags() & ~Qt::WindowCloseButtonHint );
99  }
100 
101 #if PROJ_VERSION_MAJOR>=6
102  if ( !sourceCrs.isValid() )
103  sourceCrs = QgsProject::instance()->crs();
104  if ( !sourceCrs.isValid() )
105  sourceCrs = QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) );
106  if ( !destinationCrs.isValid() )
107  destinationCrs = QgsProject::instance()->crs();
108  if ( !destinationCrs.isValid() )
109  destinationCrs = QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) );
110 
111  mSourceProjectionSelectionWidget->setOptionVisible( QgsProjectionSelectionWidget::CrsNotSet, false );
112  mDestinationProjectionSelectionWidget->setOptionVisible( QgsProjectionSelectionWidget::CrsNotSet, false );
113 #endif
114 
115  mSourceProjectionSelectionWidget->setCrs( sourceCrs );
116  mDestinationProjectionSelectionWidget->setCrs( destinationCrs );
117  if ( !allowCrsChanges )
118  {
119  mCrsStackedWidget->setCurrentIndex( 1 );
120  mSourceProjectionSelectionWidget->setEnabled( false );
121  mDestinationProjectionSelectionWidget->setEnabled( false );
122  mSourceCrsLabel->setText( QgsProjectionSelectionWidget::crsOptionText( sourceCrs ) );
123  mDestCrsLabel->setText( QgsProjectionSelectionWidget::crsOptionText( destinationCrs ) );
124  }
125 
126  mCoordinateOperationsWidget->setMapCanvas( mapCanvas );
127 
128  connect( mSourceProjectionSelectionWidget, &QgsProjectionSelectionWidget::crsChanged, this, &QgsDatumTransformDialog::setSourceCrs );
129  connect( mDestinationProjectionSelectionWidget, &QgsProjectionSelectionWidget::crsChanged, this, &QgsDatumTransformDialog::setDestinationCrs );
130 
131  mCoordinateOperationsWidget->setSourceCrs( sourceCrs );
132  mCoordinateOperationsWidget->setDestinationCrs( destinationCrs );
133 
134  connect( mButtonBox, &QDialogButtonBox::helpRequested, this, [ = ]
135  {
136  QgsHelp::openHelp( QStringLiteral( "working_with_projections/working_with_projections.html" ) );
137  } );
138 
139  connect( mCoordinateOperationsWidget, &QgsCoordinateOperationWidget::operationChanged, this, &QgsDatumTransformDialog::operationChanged );
141  deets.proj = selectedProj;
142  deets.sourceTransformId = selectedDatumTransforms.first;
143  deets.destinationTransformId = selectedDatumTransforms.second;
144  deets.allowFallback = allowFallback;
145  mCoordinateOperationsWidget->setSelectedOperation( deets );
146 
147  connect( mCoordinateOperationsWidget, &QgsCoordinateOperationWidget::operationDoubleClicked, this, [ = ]
148  {
149 
150 #if PROJ_VERSION_MAJOR>=6
151  if ( mCoordinateOperationsWidget->sourceCrs().isValid() && mCoordinateOperationsWidget->destinationCrs().isValid()
152  && mCoordinateOperationsWidget->selectedOperation().isAvailable )
153  accept();
154 #else
155  if ( mCoordinateOperationsWidget->sourceCrs().isValid() && mCoordinateOperationsWidget->destinationCrs().isValid() && mCoordinateOperationsWidget->hasSelection() )
156  accept();
157 #endif
158  } );
159 }
160 
161 void QgsDatumTransformDialog::setOKButtonEnabled()
162 {
163 #if PROJ_VERSION_MAJOR>=6
164  mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( mCoordinateOperationsWidget->sourceCrs().isValid() && mCoordinateOperationsWidget->destinationCrs().isValid()
165  && mCoordinateOperationsWidget->selectedOperation().isAvailable );
166 #else
167  mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( mCoordinateOperationsWidget->sourceCrs().isValid() && mCoordinateOperationsWidget->destinationCrs().isValid() && mCoordinateOperationsWidget->hasSelection() );
168 #endif
169 }
170 
172 {
173  if ( mCoordinateOperationsWidget->makeDefaultSelected() && mCoordinateOperationsWidget->hasSelection() )
174  {
175  QgsSettings settings;
176  settings.beginGroup( QStringLiteral( "/Projections" ) );
177 
179 
180  QString srcAuthId = dt.sourceCrs.authid();
181  QString destAuthId = dt.destinationCrs.authid();
182  int sourceDatumTransform = dt.sourceTransformId;
183  QString sourceDatumProj;
185  if ( sourceDatumTransform >= 0 )
186  sourceDatumProj = QgsDatumTransform::datumTransformToProj( sourceDatumTransform );
187  int destinationDatumTransform = dt.destinationTransformId;
188  QString destinationDatumProj;
189  if ( destinationDatumTransform >= 0 )
190  destinationDatumProj = QgsDatumTransform::datumTransformToProj( destinationDatumTransform );
192  settings.setValue( srcAuthId + QStringLiteral( "//" ) + destAuthId + QStringLiteral( "_srcTransform" ), sourceDatumProj );
193  settings.setValue( srcAuthId + QStringLiteral( "//" ) + destAuthId + QStringLiteral( "_destTransform" ), destinationDatumProj );
194  settings.setValue( srcAuthId + QStringLiteral( "//" ) + destAuthId + QStringLiteral( "_coordinateOp" ), dt.proj );
195  settings.setValue( srcAuthId + QStringLiteral( "//" ) + destAuthId + QStringLiteral( "_allowFallback" ), dt.allowFallback );
196  }
197  QDialog::accept();
198 }
199 
201 {
202  if ( !mButtonBox->button( QDialogButtonBox::Cancel ) )
203  return; // users HAVE to make a choice, no click on the dialog "x" to avoid this!
204 
205  QDialog::reject();
206 }
207 
208 bool QgsDatumTransformDialog::shouldAskUserForSelection() const
209 {
210  if ( mCoordinateOperationsWidget->availableOperations().count() > 1 )
211  {
212  return QgsSettings().value( QStringLiteral( "/projections/promptWhenMultipleTransformsExist" ), false, QgsSettings::App ).toBool();
213  }
214  // TODO: show if transform grids are required, but missing
215  return false;
216 }
217 
218 QgsDatumTransformDialog::TransformInfo QgsDatumTransformDialog::defaultDatumTransform() const
219 {
220  TransformInfo preferred;
221  preferred.sourceCrs = mCoordinateOperationsWidget->sourceCrs();
222  preferred.destinationCrs = mCoordinateOperationsWidget->destinationCrs();
223  QgsCoordinateOperationWidget::OperationDetails defaultOp = mCoordinateOperationsWidget->defaultOperation();
224  preferred.sourceTransformId = defaultOp.sourceTransformId;
225  preferred.destinationTransformId = defaultOp.destinationTransformId;
226  preferred.proj = defaultOp.proj;
227  return preferred;
228 }
229 
230 void QgsDatumTransformDialog::applyDefaultTransform()
231 {
232  if ( mCoordinateOperationsWidget->availableOperations().count() > 0 )
233  {
235  const TransformInfo dt = defaultDatumTransform();
239 
240 #if PROJ_VERSION_MAJOR>=6
241  // on proj 6 builds, removing a coordinate operation falls back to default
243 #else
244  context.addCoordinateOperation( dt.sourceCrs, dt.destinationCrs, dt.proj );
245 #endif
247  }
248 }
249 
251 {
252  QgsCoordinateOperationWidget::OperationDetails selected = mCoordinateOperationsWidget->selectedOperation();
253  TransformInfo sdt;
254  sdt.sourceCrs = mCoordinateOperationsWidget->sourceCrs();
255  sdt.destinationCrs = mCoordinateOperationsWidget->destinationCrs();
256  sdt.sourceTransformId = selected.sourceTransformId;
258  sdt.proj = selected.proj;
259  sdt.allowFallback = selected.allowFallback;
260  return sdt;
261 }
262 
263 bool QgsDatumTransformDialog::gridShiftTransformation( const QString &itemText ) const
264 {
265  return !itemText.isEmpty() && !itemText.contains( QLatin1String( "towgs84" ), Qt::CaseInsensitive );
266 }
267 
268 void QgsDatumTransformDialog::operationChanged()
269 {
270  setOKButtonEnabled();
271 }
272 
273 void QgsDatumTransformDialog::setSourceCrs( const QgsCoordinateReferenceSystem &sourceCrs )
274 {
275  mCoordinateOperationsWidget->setSourceCrs( sourceCrs );
276  setOKButtonEnabled();
277 }
278 
279 void QgsDatumTransformDialog::setDestinationCrs( const QgsCoordinateReferenceSystem &destinationCrs )
280 {
281  mCoordinateOperationsWidget->setDestinationCrs( destinationCrs );
282  setOKButtonEnabled();
283 }
Dialog transformation entry info.
void removeCoordinateOperation(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs)
Removes the coordinate operation for the specified sourceCrs and destinationCrs.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
QgsCoordinateReferenceSystem destinationCrs
Destination coordinate reference system.
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:731
TransformInfo selectedDatumTransform()
Returns the source and destination transforms, each being a pair of QgsCoordinateReferenceSystems and...
QString proj
Proj coordinate operation description, for Proj >= 6.0 builds only.
bool addCoordinateOperation(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QString &coordinateOperationProjString, bool allowFallback=true)
Adds a new coordinateOperationProjString to use when projecting coordinates from the specified source...
void operationDoubleClicked()
Emitted when an operation is double-clicked in the widget.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:78
void operationChanged()
Emitted when the operation selected in the dialog is changed.
int destinationTransformId
Destination transform ID.
QgsCoordinateReferenceSystem sourceCrs
Source coordinate reference system.
static QString crsOptionText(const QgsCoordinateReferenceSystem &crs)
Returns display text for the specified crs.
bool allowFallback
true if fallback transforms can be used
QgsCoordinateReferenceSystem crs
Definition: qgsproject.h:98
QString proj
Proj coordinate operation description, for Proj >= 6.0 builds only.
static bool run(const QgsCoordinateReferenceSystem &sourceCrs=QgsCoordinateReferenceSystem(), const QgsCoordinateReferenceSystem &destinationCrs=QgsCoordinateReferenceSystem(), QWidget *parent=nullptr, QgsMapCanvas *mapCanvas=nullptr, const QString &windowTitle=QString())
Runs the dialog (if required) prompting for the desired transform to use from sourceCrs to destinatio...
Contains information about the context in which a coordinate transform is executed.
Q_DECL_DEPRECATED bool addSourceDestinationDatumTransform(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, int sourceTransformId, int destinationTransformId)
Adds a new sourceTransform and destinationTransform to use when projecting coordinates from the speci...
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:99
void beginGroup(const QString &prefix, QgsSettings::Section section=QgsSettings::NoSection)
Appends prefix to the current group.
Definition: qgssettings.cpp:87
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:732
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
void crsChanged(const QgsCoordinateReferenceSystem &)
Emitted when the selected CRS is changed.
bool allowFallback
true if fallback transforms can be used
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:457
This class represents a coordinate reference system (CRS).
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the project&#39;s coordinate transform context, which stores various information regarding which dat...
Definition: qgsproject.cpp:723
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition: qgsgui.cpp:133
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:36
static Q_DECL_DEPRECATED QString datumTransformToProj(int datumTransformId)
Returns a proj string representing the specified datumTransformId datum transform ID...
QgsDatumTransformDialog(const QgsCoordinateReferenceSystem &sourceCrs=QgsCoordinateReferenceSystem(), const QgsCoordinateReferenceSystem &destinationCrs=QgsCoordinateReferenceSystem(), bool allowCrsChanges=false, bool showMakeDefault=true, bool forceChoice=true, QPair< int, int > selectedDatumTransforms=qMakePair(-1, -1), QWidget *parent=nullptr, Qt::WindowFlags f=nullptr, const QString &selectedProj=QString(), QgsMapCanvas *mapCanvas=nullptr, bool allowFallback=true)
Constructor for QgsDatumTransformDialog.
Temporarily removes all cursor overrides for the QApplication for the lifetime of the object...
Definition: qgsguiutils.h:235
bool hasTransform(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination) const
Returns true if the context has a valid coordinate operation to use when transforming from the specif...
QString authid() const
Returns the authority identifier for the CRS.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.