QGIS API Documentation  3.25.0-Master (6b426f5f8a)
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 #include "qgsprojutils.h"
32 #include <proj.h>
33 
34 bool QgsDatumTransformDialog::run( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, QWidget *parent, QgsMapCanvas *mapCanvas, const QString &windowTitle )
35 {
36  if ( sourceCrs == destinationCrs )
37  return true;
38 
40  if ( context.hasTransform( sourceCrs, destinationCrs ) )
41  {
42  return true;
43  }
44 
45  QgsDatumTransformDialog dlg( sourceCrs, destinationCrs, false, true, false, qMakePair( -1, -1 ), parent, Qt::WindowFlags(), QString(), mapCanvas );
46  if ( !windowTitle.isEmpty() )
47  dlg.setWindowTitle( windowTitle );
48 
49  if ( dlg.shouldAskUserForSelection() )
50  {
51  if ( dlg.exec() )
52  {
53  const TransformInfo dt = dlg.selectedDatumTransform();
60  return true;
61  }
62  else
63  {
64  return false;
65  }
66  }
67  else
68  {
69  dlg.applyDefaultTransform();
70  return true;
71  }
72 }
73 
75  const QgsCoordinateReferenceSystem &dCrs, const bool allowCrsChanges, const bool showMakeDefault, const bool forceChoice,
76  QPair<int, int> selectedDatumTransforms,
77  QWidget *parent,
78  Qt::WindowFlags f, const QString &selectedProj, QgsMapCanvas *mapCanvas, bool allowFallback )
79  : QDialog( parent, f )
80  , mPreviousCursorOverride( std::make_unique< QgsTemporaryCursorRestoreOverride >() ) // this dialog is often shown while cursor overrides are in place, so temporarily remove them
81 {
82  setupUi( this );
83 
84  QgsCoordinateReferenceSystem sourceCrs = sCrs;
85  QgsCoordinateReferenceSystem destinationCrs = dCrs;
86 
88 
89  if ( !showMakeDefault )
90  mCoordinateOperationsWidget->setShowMakeDefault( false );
91 
92  if ( forceChoice )
93  {
94  mButtonBox->removeButton( mButtonBox->button( QDialogButtonBox::Cancel ) );
95  setWindowFlags( windowFlags() | Qt::CustomizeWindowHint );
96  setWindowFlags( windowFlags() & ~Qt::WindowCloseButtonHint );
97  }
98 
99  if ( !sourceCrs.isValid() )
100  sourceCrs = QgsProject::instance()->crs();
101  if ( !sourceCrs.isValid() )
102  sourceCrs = QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) );
103  if ( !destinationCrs.isValid() )
104  destinationCrs = QgsProject::instance()->crs();
105  if ( !destinationCrs.isValid() )
106  destinationCrs = QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) );
107 
108  mSourceProjectionSelectionWidget->setOptionVisible( QgsProjectionSelectionWidget::CrsNotSet, false );
109  mDestinationProjectionSelectionWidget->setOptionVisible( QgsProjectionSelectionWidget::CrsNotSet, false );
110 
111  mSourceProjectionSelectionWidget->setCrs( sourceCrs );
112  mDestinationProjectionSelectionWidget->setCrs( destinationCrs );
113  if ( !allowCrsChanges )
114  {
115  mCrsStackedWidget->setCurrentIndex( 1 );
116  mSourceProjectionSelectionWidget->setEnabled( false );
117  mDestinationProjectionSelectionWidget->setEnabled( false );
118  mSourceCrsLabel->setText( QgsProjectionSelectionWidget::crsOptionText( sourceCrs ) );
119  mDestCrsLabel->setText( QgsProjectionSelectionWidget::crsOptionText( destinationCrs ) );
120  }
121 
122  mCoordinateOperationsWidget->setMapCanvas( mapCanvas );
123 
124  connect( mSourceProjectionSelectionWidget, &QgsProjectionSelectionWidget::crsChanged, this, &QgsDatumTransformDialog::setSourceCrs );
125  connect( mDestinationProjectionSelectionWidget, &QgsProjectionSelectionWidget::crsChanged, this, &QgsDatumTransformDialog::setDestinationCrs );
126 
127  mCoordinateOperationsWidget->setSourceCrs( sourceCrs );
128  mCoordinateOperationsWidget->setDestinationCrs( destinationCrs );
129 
130  connect( mButtonBox, &QDialogButtonBox::helpRequested, this, [ = ]
131  {
132  QgsHelp::openHelp( QStringLiteral( "working_with_projections/working_with_projections.html" ) );
133  } );
134 
135  connect( mCoordinateOperationsWidget, &QgsCoordinateOperationWidget::operationChanged, this, &QgsDatumTransformDialog::operationChanged );
137  deets.proj = selectedProj;
138  deets.sourceTransformId = selectedDatumTransforms.first;
139  deets.destinationTransformId = selectedDatumTransforms.second;
140  deets.allowFallback = allowFallback;
141  mCoordinateOperationsWidget->setSelectedOperation( deets );
142 
143  connect( mCoordinateOperationsWidget, &QgsCoordinateOperationWidget::operationDoubleClicked, this, [ = ]
144  {
145  if ( mCoordinateOperationsWidget->sourceCrs().isValid() && mCoordinateOperationsWidget->destinationCrs().isValid()
146  && mCoordinateOperationsWidget->selectedOperation().isAvailable )
147  accept();
148  } );
149 }
150 
151 void QgsDatumTransformDialog::setOKButtonEnabled()
152 {
153  mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( mCoordinateOperationsWidget->sourceCrs().isValid() && mCoordinateOperationsWidget->destinationCrs().isValid()
154  && mCoordinateOperationsWidget->selectedOperation().isAvailable );
155 }
156 
158 {
159  if ( mCoordinateOperationsWidget->makeDefaultSelected() && mCoordinateOperationsWidget->hasSelection() )
160  {
161  QgsSettings settings;
162  settings.beginGroup( QStringLiteral( "/Projections" ) );
163 
165 
166  const QString srcAuthId = dt.sourceCrs.authid();
167  const QString destAuthId = dt.destinationCrs.authid();
168  const int sourceDatumTransform = dt.sourceTransformId;
169  QString sourceDatumProj;
171  if ( sourceDatumTransform >= 0 )
172  sourceDatumProj = QgsDatumTransform::datumTransformToProj( sourceDatumTransform );
173  const int destinationDatumTransform = dt.destinationTransformId;
174  QString destinationDatumProj;
175  if ( destinationDatumTransform >= 0 )
176  destinationDatumProj = QgsDatumTransform::datumTransformToProj( destinationDatumTransform );
178  settings.setValue( srcAuthId + QStringLiteral( "//" ) + destAuthId + QStringLiteral( "_srcTransform" ), sourceDatumProj );
179  settings.setValue( srcAuthId + QStringLiteral( "//" ) + destAuthId + QStringLiteral( "_destTransform" ), destinationDatumProj );
180  settings.setValue( srcAuthId + QStringLiteral( "//" ) + destAuthId + QStringLiteral( "_coordinateOp" ), dt.proj );
181  settings.setValue( srcAuthId + QStringLiteral( "//" ) + destAuthId + QStringLiteral( "_allowFallback" ), dt.allowFallback );
182  }
183  QDialog::accept();
184 }
185 
187 {
188  if ( !mButtonBox->button( QDialogButtonBox::Cancel ) )
189  return; // users HAVE to make a choice, no click on the dialog "x" to avoid this!
190 
191  QDialog::reject();
192 }
193 
194 bool QgsDatumTransformDialog::shouldAskUserForSelection() const
195 {
196  if ( mCoordinateOperationsWidget->availableOperations().count() > 1 )
197  {
198  return QgsSettings().value( QStringLiteral( "/projections/promptWhenMultipleTransformsExist" ), false, QgsSettings::App ).toBool();
199  }
200  // TODO: show if transform grids are required, but missing
201  return false;
202 }
203 
204 QgsDatumTransformDialog::TransformInfo QgsDatumTransformDialog::defaultDatumTransform() const
205 {
206  TransformInfo preferred;
207  preferred.sourceCrs = mCoordinateOperationsWidget->sourceCrs();
208  preferred.destinationCrs = mCoordinateOperationsWidget->destinationCrs();
209  const QgsCoordinateOperationWidget::OperationDetails defaultOp = mCoordinateOperationsWidget->defaultOperation();
210  preferred.sourceTransformId = defaultOp.sourceTransformId;
211  preferred.destinationTransformId = defaultOp.destinationTransformId;
212  preferred.proj = defaultOp.proj;
213  return preferred;
214 }
215 
216 void QgsDatumTransformDialog::applyDefaultTransform()
217 {
218  if ( mCoordinateOperationsWidget->availableOperations().count() > 0 )
219  {
221  const TransformInfo dt = defaultDatumTransform();
223  context.addSourceDestinationDatumTransform( dt.sourceCrs, dt.destinationCrs, dt.sourceTransformId, dt.destinationTransformId );
225 
226  // on proj 6 builds, removing a coordinate operation falls back to default
227  context.removeCoordinateOperation( dt.sourceCrs, dt.destinationCrs );
229  }
230 }
231 
233 {
234  const QgsCoordinateOperationWidget::OperationDetails selected = mCoordinateOperationsWidget->selectedOperation();
235  TransformInfo sdt;
236  sdt.sourceCrs = mCoordinateOperationsWidget->sourceCrs();
237  sdt.destinationCrs = mCoordinateOperationsWidget->destinationCrs();
238  sdt.sourceTransformId = selected.sourceTransformId;
240  sdt.proj = selected.proj;
241  sdt.allowFallback = selected.allowFallback;
242  return sdt;
243 }
244 
245 bool QgsDatumTransformDialog::gridShiftTransformation( const QString &itemText ) const
246 {
247  return !itemText.isEmpty() && !itemText.contains( QLatin1String( "towgs84" ), Qt::CaseInsensitive );
248 }
249 
250 void QgsDatumTransformDialog::operationChanged()
251 {
252  setOKButtonEnabled();
253 }
254 
255 void QgsDatumTransformDialog::setSourceCrs( const QgsCoordinateReferenceSystem &sourceCrs )
256 {
257  mCoordinateOperationsWidget->setSourceCrs( sourceCrs );
258  setOKButtonEnabled();
259 }
260 
261 void QgsDatumTransformDialog::setDestinationCrs( const QgsCoordinateReferenceSystem &destinationCrs )
262 {
263  mCoordinateOperationsWidget->setDestinationCrs( destinationCrs );
264  setOKButtonEnabled();
265 }
void operationChanged()
Emitted when the operation selected in the dialog is changed.
void operationDoubleClicked()
Emitted when an operation is double-clicked in the widget.
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
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...
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 removeCoordinateOperation(const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs)
Removes the coordinate operation for the specified sourceCrs and destinationCrs.
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...
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=Qt::WindowFlags(), const QString &selectedProj=QString(), QgsMapCanvas *mapCanvas=nullptr, bool allowFallback=true)
Constructor for QgsDatumTransformDialog.
TransformInfo selectedDatumTransform()
Returns the source and destination transforms, each being a pair of QgsCoordinateReferenceSystems and...
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...
static Q_DECL_DEPRECATED QString datumTransformToProj(int datumTransformId)
Returns a proj string representing the specified datumTransformId datum transform ID.
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:180
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:36
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:90
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:472
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:109
QgsCoordinateReferenceSystem crs
Definition: qgsproject.h:108
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the project's coordinate transform context, which stores various information regarding which dat...
Definition: qgsproject.cpp:906
void crsChanged(const QgsCoordinateReferenceSystem &)
Emitted when the selected CRS is changed.
@ CrsNotSet
Not set (hidden by default)
static QString crsOptionText(const QgsCoordinateReferenceSystem &crs)
Returns display text for the specified crs.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void beginGroup(const QString &prefix, QgsSettings::Section section=QgsSettings::NoSection)
Appends prefix to the current group.
Definition: qgssettings.cpp:89
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Temporarily removes all cursor overrides for the QApplication for the lifetime of the object.
Definition: qgsguiutils.h:253
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:2533
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:2532
bool allowFallback
true if fallback transforms can be used
QString proj
Proj coordinate operation description, for Proj >= 6.0 builds only.
Dialog transformation entry info.
int destinationTransformId
Destination transform ID.
QString proj
Proj coordinate operation description, for Proj >= 6.0 builds only.
QgsCoordinateReferenceSystem sourceCrs
Source coordinate reference system.
QgsCoordinateReferenceSystem destinationCrs
Destination coordinate reference system.
bool allowFallback
true if fallback transforms can be used