QGIS API Documentation  3.13.0-Master (5a3b1ced84)
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 : [email protected]
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"
24 
26  : QWidget( parent )
27 {
28  QHBoxLayout *layout = new QHBoxLayout();
29  layout->setContentsMargins( 0, 0, 0, 0 );
30  layout->setSpacing( 6 );
31  setLayout( layout );
32 
33  mCrsComboBox = new QgsHighlightableComboBox( this );
34  mCrsComboBox->addItem( tr( "invalid projection" ), QgsProjectionSelectionWidget::CurrentCrs );
35  mCrsComboBox->setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Preferred );
36 
37  mProjectCrs = QgsProject::instance()->crs();
38  addProjectCrsOption();
39 
40  QgsSettings settings;
41  mDefaultCrs = QgsCoordinateReferenceSystem( settings.value( QStringLiteral( "/projections/defaultProjectCrs" ), geoEpsgCrsAuthId(), QgsSettings::App ).toString() );
42  if ( mDefaultCrs.authid() != mProjectCrs.authid() )
43  {
44  //only show default CRS option if it's different to the project CRS, avoids
45  //needlessly cluttering the widget
46  addDefaultCrsOption();
47  }
48 
49  addRecentCrs();
50 
51  layout->addWidget( mCrsComboBox );
52 
53  mButton = new QToolButton( this );
54  mButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionSetProjection.svg" ) ) );
55  mButton->setToolTip( tr( "Select CRS" ) );
56  layout->addWidget( mButton );
57 
58  setFocusPolicy( Qt::StrongFocus );
59  setFocusProxy( mButton );
60  setAcceptDrops( true );
61 
62  connect( mButton, &QToolButton::clicked, this, &QgsProjectionSelectionWidget::selectCrs );
63  connect( mCrsComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsProjectionSelectionWidget::comboIndexChanged );
64 }
65 
67 {
68  switch ( static_cast< CrsOption >( mCrsComboBox->currentData().toInt() ) )
69  {
71  return mLayerCrs;
73  return mProjectCrs;
75  return mDefaultCrs;
77  return mCrs;
79  {
80  long srsid = mCrsComboBox->currentData( Qt::UserRole + 1 ).toLongLong();
82  return crs;
83  }
86  }
87  return mCrs;
88 }
89 
91 {
92  int optionIndex = mCrsComboBox->findData( option );
93 
94  if ( visible && optionIndex < 0 )
95  {
96  //add missing CRS option
97  switch ( option )
98  {
100  {
101  setLayerCrs( mLayerCrs );
102  return;
103  }
105  {
106  addProjectCrsOption();
107  return;
108  }
110  {
111  addDefaultCrsOption();
112  return;
113  }
115  {
116  addCurrentCrsOption();
117  return;
118  }
120  //recently used CRS option cannot be readded
121  return;
123  {
124  addNotSetOption();
125 
126  if ( optionVisible( CurrentCrs ) && !mCrs.isValid() )
127  {
128  // hide invalid option if not set option is shown
129  setOptionVisible( CurrentCrs, false );
130  }
131 
132  return;
133  }
134  }
135  }
136  else if ( !visible && optionIndex >= 0 )
137  {
138  //remove CRS option
139  mCrsComboBox->removeItem( optionIndex );
140 
141  if ( option == CrsNotSet )
142  {
143  setOptionVisible( CurrentCrs, true );
144  }
145  }
146 }
147 
149 {
150  mNotSetText = text;
151  int optionIndex = mCrsComboBox->findData( CrsNotSet );
152  if ( optionIndex >= 0 )
153  {
154  mCrsComboBox->setItemText( optionIndex, mNotSetText );
155  }
156 }
157 
158 void QgsProjectionSelectionWidget::setMessage( const QString &text )
159 {
160  mMessage = text;
161 }
162 
164 {
165  int optionIndex = mCrsComboBox->findData( option );
166  return optionIndex >= 0;
167 }
168 
170 {
171  //find out crs id of current proj4 string
172  QgsProjectionSelectionDialog dlg( this );
173  if ( !mMessage.isEmpty() )
174  dlg.setMessage( mMessage );
175  dlg.setCrs( mCrs );
176 
177  if ( optionVisible( QgsProjectionSelectionWidget::CrsOption::CrsNotSet ) )
178  {
179  dlg.setShowNoProjection( true );
180  }
181 
182  if ( dlg.exec() )
183  {
184  mCrsComboBox->blockSignals( true );
185  mCrsComboBox->setCurrentIndex( mCrsComboBox->findData( QgsProjectionSelectionWidget::CurrentCrs ) );
186  mCrsComboBox->blockSignals( false );
188  setCrs( crs );
189  emit crsChanged( crs );
190  }
191  else
192  {
193  QApplication::restoreOverrideCursor();
194  }
195 }
196 
197 void QgsProjectionSelectionWidget::dragEnterEvent( QDragEnterEvent *event )
198 {
199  if ( !( event->possibleActions() & Qt::CopyAction ) )
200  {
201  event->ignore();
202  return;
203  }
204 
205  if ( mapLayerFromMimeData( event->mimeData() ) )
206  {
207  // dragged an acceptable layer, phew
208  event->setDropAction( Qt::CopyAction );
209  event->accept();
210  mCrsComboBox->setHighlighted( true );
211  update();
212  }
213  else
214  {
215  event->ignore();
216  }
217 }
218 
219 void QgsProjectionSelectionWidget::dragLeaveEvent( QDragLeaveEvent *event )
220 {
221  if ( mCrsComboBox->isHighlighted() )
222  {
223  event->accept();
224  mCrsComboBox->setHighlighted( false );
225  update();
226  }
227  else
228  {
229  event->ignore();
230  }
231 }
232 
234 {
235  if ( !( event->possibleActions() & Qt::CopyAction ) )
236  {
237  event->ignore();
238  return;
239  }
240 
241  if ( QgsMapLayer *layer = mapLayerFromMimeData( event->mimeData() ) )
242  {
243  // dropped a map layer
244  setFocus( Qt::MouseFocusReason );
245  event->setDropAction( Qt::CopyAction );
246  event->accept();
247 
248  if ( layer->crs().isValid() )
249  setCrs( layer->crs() );
250  }
251  else
252  {
253  event->ignore();
254  }
255  mCrsComboBox->setHighlighted( false );
256  update();
257 }
258 
259 void QgsProjectionSelectionWidget::addNotSetOption()
260 {
261  mCrsComboBox->insertItem( 0, mNotSetText, QgsProjectionSelectionWidget::CrsNotSet );
262  if ( !mCrs.isValid() )
263  whileBlocking( mCrsComboBox )->setCurrentIndex( 0 );
264 }
265 
266 void QgsProjectionSelectionWidget::comboIndexChanged( int idx )
267 {
268  switch ( static_cast< CrsOption >( mCrsComboBox->itemData( idx ).toInt() ) )
269  {
271  emit crsChanged( mLayerCrs );
272  break;
274  emit crsChanged( mProjectCrs );
275  break;
277  emit crsChanged( mCrs );
278  break;
280  emit crsChanged( mDefaultCrs );
281  break;
283  {
284  long srsid = mCrsComboBox->itemData( idx, Qt::UserRole + 1 ).toLongLong();
286  emit crsChanged( crs );
287  break;
288  }
290  emit cleared();
292  break;
293  }
294  updateTooltip();
295 }
296 
298 {
299  if ( crs.isValid() )
300  {
303  mCrsComboBox->setItemText( mCrsComboBox->findData( QgsProjectionSelectionWidget::CurrentCrs ),
304  crsOptionText( crs ) );
305  mCrsComboBox->blockSignals( true );
306  mCrsComboBox->setCurrentIndex( mCrsComboBox->findData( QgsProjectionSelectionWidget::CurrentCrs ) );
307  mCrsComboBox->blockSignals( false );
308  }
309  else
310  {
311  int crsNotSetIndex = mCrsComboBox->findData( QgsProjectionSelectionWidget::CrsNotSet );
312  if ( crsNotSetIndex >= 0 )
313  {
314  mCrsComboBox->blockSignals( true );
315  mCrsComboBox->setCurrentIndex( crsNotSetIndex );
316  mCrsComboBox->blockSignals( false );
317  }
318  else
319  {
320  mCrsComboBox->setItemText( mCrsComboBox->findData( QgsProjectionSelectionWidget::CurrentCrs ),
321  crsOptionText( crs ) );
322  }
323  }
324  if ( mCrs != crs )
325  {
326  mCrs = crs;
327  emit crsChanged( crs );
328  }
329  updateTooltip();
330 }
331 
333 {
334  int layerItemIndex = mCrsComboBox->findData( QgsProjectionSelectionWidget::LayerCrs );
335  if ( crs.isValid() )
336  {
337  if ( layerItemIndex > -1 )
338  {
339  mCrsComboBox->setItemText( layerItemIndex, tr( "Layer CRS: %1" ).arg( crs.userFriendlyIdentifier() ) );
340  }
341  else
342  {
343  mCrsComboBox->insertItem( firstRecentCrsIndex(), tr( "Layer CRS: %1" ).arg( crs.userFriendlyIdentifier() ), QgsProjectionSelectionWidget::LayerCrs );
344  }
345  }
346  else
347  {
348  if ( layerItemIndex > -1 )
349  {
350  mCrsComboBox->removeItem( layerItemIndex );
351  }
352  }
353  mLayerCrs = crs;
354 }
355 
356 void QgsProjectionSelectionWidget::addProjectCrsOption()
357 {
358  if ( mProjectCrs.isValid() )
359  {
360  mCrsComboBox->addItem( tr( "Project CRS: %1" ).arg( mProjectCrs.userFriendlyIdentifier() ), QgsProjectionSelectionWidget::ProjectCrs );
361  }
362 }
363 
364 void QgsProjectionSelectionWidget::addDefaultCrsOption()
365 {
366  mCrsComboBox->addItem( tr( "Default CRS: %1" ).arg( mDefaultCrs.userFriendlyIdentifier() ), QgsProjectionSelectionWidget::DefaultCrs );
367 }
368 
369 void QgsProjectionSelectionWidget::addCurrentCrsOption()
370 {
371  int index = optionVisible( CrsNotSet ) ? 1 : 0;
372  mCrsComboBox->insertItem( index, crsOptionText( mCrs ), QgsProjectionSelectionWidget::CurrentCrs );
373 
374 }
375 
377 {
378  if ( crs.isValid() )
379  return crs.userFriendlyIdentifier();
380  else
381  return tr( "invalid projection" );
382 }
383 
384 void QgsProjectionSelectionWidget::addRecentCrs()
385 {
386  const QList< QgsCoordinateReferenceSystem> recentProjections = QgsCoordinateReferenceSystem::recentCoordinateReferenceSystems();
387  for ( const QgsCoordinateReferenceSystem &crs : recentProjections )
388  {
389  long srsid = crs.srsid();
390 
391  //check if already shown
392  if ( crsIsShown( srsid ) )
393  {
394  continue;
395  }
396 
397  if ( crs.isValid() )
398  {
400  mCrsComboBox->setItemData( mCrsComboBox->count() - 1, QVariant( ( long long )srsid ), Qt::UserRole + 1 );
401  }
402  }
403 }
404 
405 bool QgsProjectionSelectionWidget::crsIsShown( const long srsid ) const
406 {
407  return srsid == mLayerCrs.srsid() || srsid == mDefaultCrs.srsid() || srsid == mProjectCrs.srsid();
408 }
409 
410 int QgsProjectionSelectionWidget::firstRecentCrsIndex() const
411 {
412  for ( int i = 0; i < mCrsComboBox->count(); ++i )
413  {
414  if ( static_cast< CrsOption >( mCrsComboBox->itemData( i ).toInt() ) == RecentCrs )
415  {
416  return i;
417  }
418  }
419  return -1;
420 }
421 
422 void QgsProjectionSelectionWidget::updateTooltip()
423 {
425  if ( c.isValid() )
426  setToolTip( c.toWkt( QgsCoordinateReferenceSystem::WKT2_2018, true ) );
427  else
428  setToolTip( QString() );
429 }
430 
431 QgsMapLayer *QgsProjectionSelectionWidget::mapLayerFromMimeData( const QMimeData *data ) const
432 {
434  for ( const QgsMimeDataUtils::Uri &u : uriList )
435  {
436  // is this uri from the current project?
437  if ( QgsMapLayer *layer = u.mapLayer() )
438  {
439  return layer;
440  }
441  }
442  return nullptr;
443 }
Base class for all map layer types.
Definition: qgsmaplayer.h:80
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the current CRS for the widget.
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.
Current project CRS (if OTF reprojection enabled)
static UriList decodeUriList(const QMimeData *data)
void setMessage(const QString &message)
Sets a message to show in the dialog.
void setNotSetText(const QString &text)
Sets the text to show for the not set option.
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
void dragLeaveEvent(QDragLeaveEvent *event) override
void setShowNoProjection(bool show)
Sets whether a "no/invalid" projection option should be shown.
QgsProjectionSelectionWidget(QWidget *parent=nullptr)
Constructor for QgsProjectionSelectionWidget.
QgsCoordinateReferenceSystem crs() const
Returns the CRS currently selected in the widget.
A generic dialog to prompt the user for a Coordinate Reference System.
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
QgsCoordinateReferenceSystem crs() const
Returns the currently selected CRS for the widget.
CrsOption
Predefined CRS options shown in widget.
static QString crsOptionText(const QgsCoordinateReferenceSystem &crs)
Returns display text for the specified crs.
void selectCrs()
Opens the dialog for selecting a new CRS.
static QList< QgsCoordinateReferenceSystem > recentCoordinateReferenceSystems()
Returns a list of recently used CRS.
void setMessage(const QString &text)
Sets a message to show in the dialog.
QgsCoordinateReferenceSystem crs
Definition: qgsproject.h:98
void cleared()
Emitted when the not set option is selected.
QString userFriendlyIdentifier(IdentifierType type=MediumString) const
Returns a user friendly identifier for the CRS.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the initial crs to show within the dialog.
Full WKT2 string, conforming to ISO 19162:2018 / OGC 18-010, with all possible nodes and new keyword ...
void dragEnterEvent(QDragEnterEvent *event) override
bool isHighlighted() const
Returns true if the combo box is currently highlighted.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:262
void crsChanged(const QgsCoordinateReferenceSystem &)
Emitted when the selected CRS is changed.
void dropEvent(QDropEvent *event) override
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:457
This class represents a coordinate reference system (CRS).
QList< QgsMimeDataUtils::Uri > UriList
A QComboBox subclass with the ability to "highlight" the edges of the widget.
long srsid() const
Returns the internal CRS ID, if available.
bool optionVisible(CrsOption option) const
Returns whether the specified CRS option is visible in the widget.
CONSTLATIN1STRING geoEpsgCrsAuthId()
Geographic coord sys from EPSG authority.
Definition: qgis.h:646
void setOptionVisible(CrsOption option, bool visible)
Sets whether a predefined CRS option should be shown in the widget.
QString authid() const
Returns the authority identifier for the CRS.
static QgsCoordinateReferenceSystem fromSrsId(long srsId)
Creates a CRS from a specified QGIS SRS ID.
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
void setHighlighted(bool highlighted)
Sets whether the combo box is currently highlighted.
void setLayerCrs(const QgsCoordinateReferenceSystem &crs)
Sets the layer CRS for the widget.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.