QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
qgsprojectionselectionwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsprojectionselectionwidget.cpp
3  --------------------------------------
4  Date : 05.01.2015
5  Copyright : (C) 2015 Denis Rouzaud
6  Email : denis.rouzaud@gmail.com
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 <QHBoxLayout>
17 
19 #include "qgsapplication.h"
21 #include "qgsproject.h"
22 #include "qgssettings.h"
25 
27  : QWidget( parent )
28 {
29  QHBoxLayout *layout = new QHBoxLayout();
30  layout->setContentsMargins( 0, 0, 0, 0 );
31  layout->setSpacing( 6 );
32  setLayout( layout );
33 
34  mCrsComboBox = new QgsHighlightableComboBox( this );
35  mCrsComboBox->addItem( tr( "invalid projection" ), QgsProjectionSelectionWidget::CurrentCrs );
36  mCrsComboBox->setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Preferred );
37 
38  mProjectCrs = QgsProject::instance()->crs();
39  addProjectCrsOption();
40 
41  QgsSettings settings;
42  mDefaultCrs = QgsCoordinateReferenceSystem( settings.value( QStringLiteral( "/projections/defaultProjectCrs" ), geoEpsgCrsAuthId(), QgsSettings::App ).toString() );
43  if ( mDefaultCrs.authid() != mProjectCrs.authid() )
44  {
45  //only show default CRS option if it's different to the project CRS, avoids
46  //needlessly cluttering the widget
47  addDefaultCrsOption();
48  }
49 
50  addRecentCrs();
51 
52  layout->addWidget( mCrsComboBox );
53 
54  mButton = new QToolButton( this );
55  mButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionSetProjection.svg" ) ) );
56  mButton->setToolTip( tr( "Select CRS" ) );
57  layout->addWidget( mButton );
58 
59  setFocusPolicy( Qt::StrongFocus );
60  setFocusProxy( mButton );
61  setAcceptDrops( true );
62 
63  connect( mButton, &QToolButton::clicked, this, &QgsProjectionSelectionWidget::selectCrs );
64  connect( mCrsComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsProjectionSelectionWidget::comboIndexChanged );
65 
67  {
68  mCrs.updateDefinition();
69  mLayerCrs.updateDefinition();
70  mProjectCrs.updateDefinition();
71  mDefaultCrs.updateDefinition();
72  } );
73 }
74 
76 {
77  switch ( static_cast< CrsOption >( mCrsComboBox->currentData().toInt() ) )
78  {
80  return mLayerCrs;
82  return mProjectCrs;
84  return mDefaultCrs;
86  return mCrs;
88  {
89  long srsid = mCrsComboBox->currentData( Qt::UserRole + 1 ).toLongLong();
91  return crs;
92  }
95  }
96  return mCrs;
97 }
98 
100 {
101  int optionIndex = mCrsComboBox->findData( option );
102 
103  if ( visible && optionIndex < 0 )
104  {
105  //add missing CRS option
106  switch ( option )
107  {
109  {
110  setLayerCrs( mLayerCrs );
111  return;
112  }
114  {
115  addProjectCrsOption();
116  return;
117  }
119  {
120  addDefaultCrsOption();
121  return;
122  }
124  {
125  addCurrentCrsOption();
126  return;
127  }
129  //recently used CRS option cannot be readded
130  return;
132  {
133  addNotSetOption();
134 
135  if ( optionVisible( CurrentCrs ) && !mCrs.isValid() )
136  {
137  // hide invalid option if not set option is shown
138  setOptionVisible( CurrentCrs, false );
139  }
140 
141  return;
142  }
143  }
144  }
145  else if ( !visible && optionIndex >= 0 )
146  {
147  //remove CRS option
148  mCrsComboBox->removeItem( optionIndex );
149 
150  if ( option == CrsNotSet )
151  {
152  setOptionVisible( CurrentCrs, true );
153  }
154  }
155 }
156 
158 {
159  mNotSetText = text;
160  int optionIndex = mCrsComboBox->findData( CrsNotSet );
161  if ( optionIndex >= 0 )
162  {
163  mCrsComboBox->setItemText( optionIndex, mNotSetText );
164  }
165 }
166 
167 void QgsProjectionSelectionWidget::setMessage( const QString &text )
168 {
169  mMessage = text;
170 }
171 
173 {
174  int optionIndex = mCrsComboBox->findData( option );
175  return optionIndex >= 0;
176 }
177 
179 {
180  //find out crs id of current proj4 string
181  QgsProjectionSelectionDialog dlg( this );
182  if ( !mMessage.isEmpty() )
183  dlg.setMessage( mMessage );
184  dlg.setCrs( mCrs );
185 
186  if ( !mNotSetText.isEmpty() )
187  dlg.setNotSetText( mNotSetText );
188 
189  if ( optionVisible( QgsProjectionSelectionWidget::CrsOption::CrsNotSet ) )
190  {
191  dlg.setShowNoProjection( true );
192  }
194 
195  if ( dlg.exec() )
196  {
197  mCrsComboBox->blockSignals( true );
198  mCrsComboBox->setCurrentIndex( mCrsComboBox->findData( QgsProjectionSelectionWidget::CurrentCrs ) );
199  mCrsComboBox->blockSignals( false );
201  setCrs( crs );
202  emit crsChanged( crs );
203  }
204  else
205  {
206  QApplication::restoreOverrideCursor();
207  }
208 }
209 
210 void QgsProjectionSelectionWidget::dragEnterEvent( QDragEnterEvent *event )
211 {
212  if ( !( event->possibleActions() & Qt::CopyAction ) )
213  {
214  event->ignore();
215  return;
216  }
217 
218  if ( mapLayerFromMimeData( event->mimeData() ) )
219  {
220  // dragged an acceptable layer, phew
221  event->setDropAction( Qt::CopyAction );
222  event->accept();
223  mCrsComboBox->setHighlighted( true );
224  update();
225  }
226  else
227  {
228  event->ignore();
229  }
230 }
231 
232 void QgsProjectionSelectionWidget::dragLeaveEvent( QDragLeaveEvent *event )
233 {
234  if ( mCrsComboBox->isHighlighted() )
235  {
236  event->accept();
237  mCrsComboBox->setHighlighted( false );
238  update();
239  }
240  else
241  {
242  event->ignore();
243  }
244 }
245 
247 {
248  if ( !( event->possibleActions() & Qt::CopyAction ) )
249  {
250  event->ignore();
251  return;
252  }
253 
254  if ( QgsMapLayer *layer = mapLayerFromMimeData( event->mimeData() ) )
255  {
256  // dropped a map layer
257  setFocus( Qt::MouseFocusReason );
258  event->setDropAction( Qt::CopyAction );
259  event->accept();
260 
261  if ( layer->crs().isValid() )
262  setCrs( layer->crs() );
263  }
264  else
265  {
266  event->ignore();
267  }
268  mCrsComboBox->setHighlighted( false );
269  update();
270 }
271 
272 void QgsProjectionSelectionWidget::addNotSetOption()
273 {
274  mCrsComboBox->insertItem( 0, mNotSetText, QgsProjectionSelectionWidget::CrsNotSet );
275  if ( !mCrs.isValid() )
276  whileBlocking( mCrsComboBox )->setCurrentIndex( 0 );
277 }
278 
279 void QgsProjectionSelectionWidget::comboIndexChanged( int idx )
280 {
281  switch ( static_cast< CrsOption >( mCrsComboBox->itemData( idx ).toInt() ) )
282  {
284  emit crsChanged( mLayerCrs );
285  break;
287  emit crsChanged( mProjectCrs );
288  break;
290  emit crsChanged( mCrs );
291  break;
293  emit crsChanged( mDefaultCrs );
294  break;
296  {
297  long srsid = mCrsComboBox->itemData( idx, Qt::UserRole + 1 ).toLongLong();
299  emit crsChanged( crs );
300  break;
301  }
303  emit cleared();
305  break;
306  }
307  updateTooltip();
308 }
309 
311 {
312  if ( crs.isValid() )
313  {
316  mCrsComboBox->setItemText( mCrsComboBox->findData( QgsProjectionSelectionWidget::CurrentCrs ),
317  crsOptionText( crs ) );
318  mCrsComboBox->blockSignals( true );
319  mCrsComboBox->setCurrentIndex( mCrsComboBox->findData( QgsProjectionSelectionWidget::CurrentCrs ) );
320  mCrsComboBox->blockSignals( false );
321  }
322  else
323  {
324  int crsNotSetIndex = mCrsComboBox->findData( QgsProjectionSelectionWidget::CrsNotSet );
325  if ( crsNotSetIndex >= 0 )
326  {
327  mCrsComboBox->blockSignals( true );
328  mCrsComboBox->setCurrentIndex( crsNotSetIndex );
329  mCrsComboBox->blockSignals( false );
330  }
331  else
332  {
333  mCrsComboBox->setItemText( mCrsComboBox->findData( QgsProjectionSelectionWidget::CurrentCrs ),
334  crsOptionText( crs ) );
335  }
336  }
337  if ( mCrs != crs )
338  {
339  mCrs = crs;
340  emit crsChanged( crs );
341  }
342  updateTooltip();
343 }
344 
346 {
347  int layerItemIndex = mCrsComboBox->findData( QgsProjectionSelectionWidget::LayerCrs );
348  if ( crs.isValid() )
349  {
350  if ( layerItemIndex > -1 )
351  {
352  mCrsComboBox->setItemText( layerItemIndex, tr( "Layer CRS: %1" ).arg( crs.userFriendlyIdentifier() ) );
353  }
354  else
355  {
356  mCrsComboBox->insertItem( firstRecentCrsIndex(), tr( "Layer CRS: %1" ).arg( crs.userFriendlyIdentifier() ), QgsProjectionSelectionWidget::LayerCrs );
357  }
358  }
359  else
360  {
361  if ( layerItemIndex > -1 )
362  {
363  mCrsComboBox->removeItem( layerItemIndex );
364  }
365  }
366  mLayerCrs = crs;
367 }
368 
369 void QgsProjectionSelectionWidget::addProjectCrsOption()
370 {
371  if ( mProjectCrs.isValid() )
372  {
373  mCrsComboBox->addItem( tr( "Project CRS: %1" ).arg( mProjectCrs.userFriendlyIdentifier() ), QgsProjectionSelectionWidget::ProjectCrs );
374  }
375 }
376 
377 void QgsProjectionSelectionWidget::addDefaultCrsOption()
378 {
379  mCrsComboBox->addItem( tr( "Default CRS: %1" ).arg( mDefaultCrs.userFriendlyIdentifier() ), QgsProjectionSelectionWidget::DefaultCrs );
380 }
381 
382 void QgsProjectionSelectionWidget::addCurrentCrsOption()
383 {
384  int index = optionVisible( CrsNotSet ) ? 1 : 0;
385  mCrsComboBox->insertItem( index, crsOptionText( mCrs ), QgsProjectionSelectionWidget::CurrentCrs );
386 
387 }
388 
390 {
391  if ( crs.isValid() )
392  return crs.userFriendlyIdentifier();
393  else
394  return tr( "invalid projection" );
395 }
396 
397 void QgsProjectionSelectionWidget::addRecentCrs()
398 {
399  const QList< QgsCoordinateReferenceSystem> recentProjections = QgsCoordinateReferenceSystem::recentCoordinateReferenceSystems();
400  for ( const QgsCoordinateReferenceSystem &crs : recentProjections )
401  {
402  long srsid = crs.srsid();
403 
404  //check if already shown
405  if ( crsIsShown( srsid ) )
406  {
407  continue;
408  }
409 
410  if ( crs.isValid() )
411  {
413  mCrsComboBox->setItemData( mCrsComboBox->count() - 1, QVariant( ( long long )srsid ), Qt::UserRole + 1 );
414  }
415  }
416 }
417 
418 bool QgsProjectionSelectionWidget::crsIsShown( const long srsid ) const
419 {
420  return srsid == mLayerCrs.srsid() || srsid == mDefaultCrs.srsid() || srsid == mProjectCrs.srsid();
421 }
422 
423 int QgsProjectionSelectionWidget::firstRecentCrsIndex() const
424 {
425  for ( int i = 0; i < mCrsComboBox->count(); ++i )
426  {
427  if ( static_cast< CrsOption >( mCrsComboBox->itemData( i ).toInt() ) == RecentCrs )
428  {
429  return i;
430  }
431  }
432  return -1;
433 }
434 
435 void QgsProjectionSelectionWidget::updateTooltip()
436 {
438  if ( c.isValid() )
439  setToolTip( c.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED, true ) );
440  else
441  setToolTip( QString() );
442 }
443 
444 QgsMapLayer *QgsProjectionSelectionWidget::mapLayerFromMimeData( const QMimeData *data ) const
445 {
447  for ( const QgsMimeDataUtils::Uri &u : uriList )
448  {
449  // is this uri from the current project?
450  if ( QgsMapLayer *layer = u.mapLayer() )
451  {
452  return layer;
453  }
454  }
455  return nullptr;
456 }
static QgsCoordinateReferenceSystemRegistry * coordinateReferenceSystemRegistry()
Returns the application's coordinate reference system (CRS) registry, which handles known CRS definit...
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
void userCrsChanged(const QString &id)
Emitted whenever an existing user CRS definition is changed.
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
void updateDefinition()
Updates the definition and parameters of the coordinate reference system to their latest values.
static QList< QgsCoordinateReferenceSystem > recentCoordinateReferenceSystems()
Returns a list of recently used CRS.
QString userFriendlyIdentifier(IdentifierType type=MediumString) const
Returns a user friendly identifier for the CRS.
QString authid() const
Returns the authority identifier for the CRS.
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
static QgsCoordinateReferenceSystem fromSrsId(long srsId)
Creates a CRS from a specified QGIS SRS ID.
long srsid() const
Returns the internal CRS ID, if available.
A QComboBox subclass with the ability to "highlight" the edges of the widget.
void setHighlighted(bool highlighted)
Sets whether the combo box is currently highlighted.
bool isHighlighted() const
Returns true if the combo box is currently highlighted.
Base class for all map layer types.
Definition: qgsmaplayer.h:85
QList< QgsMimeDataUtils::Uri > UriList
static UriList decodeUriList(const QMimeData *data)
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:501
QgsCoordinateReferenceSystem crs
Definition: qgsproject.h:104
A generic dialog to prompt the user for a Coordinate Reference System.
void setNotSetText(const QString &text)
Sets the text to show for the not set option.
void setShowNoProjection(bool show)
Sets whether a "no/invalid" projection option should be shown.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the initial crs to show within the dialog.
void setMessage(const QString &message)
Sets a message to show in the dialog.
QgsCoordinateReferenceSystem crs() const
Returns the CRS currently selected in the widget.
void setRequireValidSelection()
Sets the dialog to require a valid selection only, preventing users from accepting the dialog if no s...
void cleared()
Emitted when the not set option is selected.
void selectCrs()
Opens the dialog for selecting a new CRS.
void crsChanged(const QgsCoordinateReferenceSystem &)
Emitted when the selected CRS is changed.
CrsOption
Predefined CRS options shown in widget.
@ CrsNotSet
Not set (hidden by default)
@ ProjectCrs
Current project CRS (if OTF reprojection enabled)
@ CurrentCrs
Current user selected CRS.
QgsProjectionSelectionWidget(QWidget *parent=nullptr)
Constructor for QgsProjectionSelectionWidget.
bool optionVisible(CrsOption option) const
Returns whether the specified CRS option is visible in the widget.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the current CRS for the widget.
void setNotSetText(const QString &text)
Sets the text to show for the not set option.
void setLayerCrs(const QgsCoordinateReferenceSystem &crs)
Sets the layer CRS for the widget.
void setOptionVisible(CrsOption option, bool visible)
Sets whether a predefined CRS option should be shown in the widget.
QgsCoordinateReferenceSystem crs() const
Returns the currently selected CRS for the widget.
void dragEnterEvent(QDragEnterEvent *event) override
void setMessage(const QString &text)
Sets a message to show in the dialog.
static QString crsOptionText(const QgsCoordinateReferenceSystem &crs)
Returns display text for the specified crs.
void dragLeaveEvent(QDragLeaveEvent *event) override
void dropEvent(QDropEvent *event) override
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.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:263
CONSTLATIN1STRING geoEpsgCrsAuthId()
Geographic coord sys from EPSG authority.
Definition: qgis.h:710
const QgsCoordinateReferenceSystem & crs