QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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.
QString authid() const
Returns the authority identifier for the CRS.
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:168
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:89
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:467
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:107
QgsCoordinateReferenceSystem crs
Definition: qgsproject.h:106
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the project's coordinate transform context, which stores various information regarding which dat...
Definition: qgsproject.cpp:791
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:1742
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:1741
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