QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
qgssourceselectdialog.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgssourceselectdialog.cpp
3  -------------------------
4  begin : Nov 26, 2015
5  copyright : (C) 2015 by Sandro Mani
6  email : [email protected]
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 
18 #include "qgssourceselectdialog.h"
19 #include "qgsowsconnection.h"
20 #include "qgsnewhttpconnection.h"
23 #include "qgscontexthelp.h"
24 #include "qgsproject.h"
26 #include "qgscoordinatetransform.h"
27 #include "qgslogger.h"
28 #include "qgsmapcanvas.h"
30 #include "qgscrscache.h"
31 
32 #include <QItemDelegate>
33 #include <QListWidgetItem>
34 #include <QMessageBox>
35 #include <QSettings>
36 #include <QFileDialog>
37 #include <QRadioButton>
38 #include <QImageReader>
39 
44 {
45  public:
48  QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const override;
49 };
50 
51 
53  : QDialog( parent, fl ), mServiceName( serviceName ), mServiceType( serviceType ), mBuildQueryButton( 0 ), mImageEncodingGroup( 0 )
54 {
55  setupUi( this );
56  setWindowTitle( QString( "Add %1 Layer from a Server" ).arg( mServiceName ) );
57 
58  mAddButton = buttonBox->addButton( tr( "&Add" ), QDialogButtonBox::ActionRole );
59  mAddButton->setEnabled( false );
60  connect( mAddButton, SIGNAL( clicked() ), this, SLOT( addButtonClicked() ) );
61 
63  {
64  mBuildQueryButton = buttonBox->addButton( tr( "&Build query" ), QDialogButtonBox::ActionRole );
66  connect( mBuildQueryButton, SIGNAL( clicked() ), this, SLOT( buildQueryButtonClicked() ) );
67  }
68 
69  connect( buttonBox, SIGNAL( rejected() ), this, SLOT( reject() ) );
70  connect( btnNew, SIGNAL( clicked() ), this, SLOT( addEntryToServerList() ) );
71  connect( btnEdit, SIGNAL( clicked() ), this, SLOT( modifyEntryOfServerList() ) );
72  connect( btnDelete, SIGNAL( clicked() ), this, SLOT( deleteEntryOfServerList() ) );
73  connect( btnConnect, SIGNAL( clicked() ), this, SLOT( connectToServer() ) );
74  connect( btnChangeSpatialRefSys, SIGNAL( clicked() ), this, SLOT( changeCRS() ) );
75  connect( lineFilter, SIGNAL( textChanged( QString ) ), this, SLOT( filterChanged( QString ) ) );
76  populateConnectionList();
79 
80  treeView->setItemDelegate( new QgsSourceSelectItemDelegate( treeView ) );
81 
82  QSettings settings;
83  restoreGeometry( settings.value( "/Windows/SourceSelectDialog/geometry" ).toByteArray() );
84  cbxUseTitleLayerName->setChecked( settings.value( "/Windows/SourceSelectDialog/UseTitleLayerName", false ).toBool() );
85 
86  mModel = new QStandardItemModel();
87  mModel->setHorizontalHeaderItem( 0, new QStandardItem( "Title" ) );
88  mModel->setHorizontalHeaderItem( 1, new QStandardItem( "Name" ) );
89  mModel->setHorizontalHeaderItem( 2, new QStandardItem( "Abstract" ) );
90  if ( serviceType == FeatureService )
91  {
92  mModel->setHorizontalHeaderItem( 3, new QStandardItem( "Cache Feature" ) );
93  mModel->setHorizontalHeaderItem( 4, new QStandardItem( "Filter" ) );
94  gbImageEncoding->hide();
95  }
96  else
97  {
98  cbxFeatureCurrentViewExtent->hide();
99  mImageEncodingGroup = new QButtonGroup( this );
100  }
101 
102  mModelProxy = new QSortFilterProxyModel( this );
104  mModelProxy->setSortCaseSensitivity( Qt::CaseInsensitive );
105  treeView->setModel( mModelProxy );
106 
107  connect( treeView, SIGNAL( doubleClicked( const QModelIndex& ) ), this, SLOT( treeWidgetItemDoubleClicked( const QModelIndex& ) ) );
108  connect( treeView->selectionModel(), SIGNAL( currentRowChanged( QModelIndex, QModelIndex ) ), this, SLOT( treeWidgetCurrentRowChanged( const QModelIndex&, const QModelIndex& ) ) );
109 }
110 
112 {
113  QSettings settings;
114  settings.setValue( "/Windows/SourceSelectDialog/geometry", saveGeometry() );
115  settings.setValue( "/Windows/SourceSelectDialog/UseTitleLayerName", cbxUseTitleLayerName->isChecked() );
116 
117  delete mProjectionSelector;
118  delete mModel;
119  delete mModelProxy;
120 }
121 
123 {
124  mCanvasExtent = canvasExtent;
125  mCanvasCrs = canvasCrs;
126 }
127 
129 {
130  QLayoutItem* item;
131  while (( item = gbImageEncoding->layout()->takeAt( 0 ) ) != nullptr )
132  {
133  delete item->widget();
134  delete item;
135  }
136  bool first = true;
138  foreach ( const QString& encoding, availableEncodings )
139  {
140  bool supported = false;
141  foreach ( const QByteArray& fmt, supportedFormats )
142  {
143  if ( encoding.startsWith( fmt, Qt::CaseInsensitive ) )
144  {
145  supported = true;
146  }
147  }
148  if ( !supported )
149  {
150  continue;
151  }
152 
153  QRadioButton* button = new QRadioButton( encoding, this );
154  button->setChecked( first );
155  gbImageEncoding->layout()->addWidget( button );
156  mImageEncodingGroup->addButton( button );
157  first = false;
158  }
159 }
160 
162 {
164 }
165 
166 void QgsSourceSelectDialog::populateConnectionList()
167 {
169  cmbConnections->clear();
170  foreach ( const QString& item, conns )
171  {
172  cmbConnections->addItem( item );
173  }
174  bool connectionsAvailable = !conns.isEmpty();
175  btnConnect->setEnabled( connectionsAvailable );
176  btnEdit->setEnabled( connectionsAvailable );
177  btnDelete->setEnabled( connectionsAvailable );
178  btnSave->setEnabled( connectionsAvailable );
179 
180  //set last used connection
182  int index = cmbConnections->findText( selectedConnection );
183  if ( index != -1 )
184  {
185  cmbConnections->setCurrentIndex( index );
186  }
187 }
188 
189 QString QgsSourceSelectDialog::getPreferredCrs( const QSet<QString>& crsSet ) const
190 {
191  if ( crsSet.size() < 1 )
192  {
193  return "";
194  }
195 
196  //first: project CRS
197  long ProjectCRSID = QgsProject::instance()->readNumEntry( "SpatialRefSys", "/ProjectCRSID", -1 );
198  //convert to EPSG
199  QgsCoordinateReferenceSystem projectRefSys = QgsCRSCache::instance()->crsBySrsId( ProjectCRSID );
200  QString ProjectCRS;
201  if ( projectRefSys.isValid() )
202  {
203  ProjectCRS = projectRefSys.authid();
204  }
205 
206  if ( !ProjectCRS.isEmpty() && crsSet.contains( ProjectCRS ) )
207  {
208  return ProjectCRS;
209  }
210 
211  //second: WGS84
212  if ( crsSet.contains( GEO_EPSG_CRS_AUTHID ) )
213  {
214  return GEO_EPSG_CRS_AUTHID;
215  }
216 
217  //third: first entry in set
218  return *( crsSet.constBegin() );
219 }
220 
221 void QgsSourceSelectDialog::addEntryToServerList()
222 {
223 
224  QgsNewHttpConnection nc( 0, QString( "/Qgis/connections-%1/" ).arg( mServiceName.toLower() ) );
225  nc.setWindowTitle( tr( "Create a new %1 connection" ).arg( mServiceName ) );
226 
227  if ( nc.exec() )
228  {
229  populateConnectionList();
230  emit connectionsChanged();
231  }
232 }
233 
234 void QgsSourceSelectDialog::modifyEntryOfServerList()
235 {
236  QgsNewHttpConnection nc( 0, QString( "/Qgis/connections-%1/" ).arg( mServiceName.toLower() ), cmbConnections->currentText() );
237  nc.setWindowTitle( tr( "Modify %1 connection" ).arg( mServiceName ) );
238 
239  if ( nc.exec() )
240  {
241  populateConnectionList();
242  emit connectionsChanged();
243  }
244 }
245 
246 void QgsSourceSelectDialog::deleteEntryOfServerList()
247 {
248  QString msg = tr( "Are you sure you want to remove the %1 connection and all associated settings?" )
249  .arg( cmbConnections->currentText() );
250  QMessageBox::StandardButton result = QMessageBox::information( this, tr( "Confirm Delete" ), msg, QMessageBox::Ok | QMessageBox::Cancel );
251  if ( result == QMessageBox::Ok )
252  {
253  QgsOWSConnection::deleteConnection( mServiceName, cmbConnections->currentText() );
254  cmbConnections->removeItem( cmbConnections->currentIndex() );
255  emit connectionsChanged();
256  bool connectionsAvailable = cmbConnections->count() > 0;
257  btnConnect->setEnabled( connectionsAvailable );
258  btnEdit->setEnabled( connectionsAvailable );
259  btnDelete->setEnabled( connectionsAvailable );
260  btnSave->setEnabled( connectionsAvailable );
261  }
262 }
263 
264 void QgsSourceSelectDialog::connectToServer()
265 {
266  bool haveLayers = false;
267  btnConnect->setEnabled( false );
268  mModel->setRowCount( 0 );
270 
271  QgsOWSConnection connection( mServiceName, cmbConnections->currentText() );
272 
273  setCursor( Qt::WaitCursor );
274  bool success = connectToService( connection );
275  unsetCursor();
276  if ( success )
277  {
278  haveLayers = mModel->rowCount() > 0;
279 
280  if ( haveLayers )
281  {
282  for ( int i = 0; i < treeView->header()->count(); ++i )
283  {
284  treeView->resizeColumnToContents( i );
285  if ( i < 2 && treeView->columnWidth( i ) > 300 )
286  {
287  treeView->setColumnWidth( i, 300 );
288  }
289  }
290  treeView->selectionModel()->select( mModel->index( 0, 0 ), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows );
291  treeView->setFocus();
292  }
293  else
294  {
295  QMessageBox::information( 0, tr( "No Layers" ), tr( "The query returned no layers." ) );
296  }
297  }
298 
299  btnConnect->setEnabled( true );
300  mAddButton->setEnabled( haveLayers );
301  if ( mServiceType == FeatureService )
302  {
303  mBuildQueryButton->setEnabled( haveLayers );
304  }
305  btnChangeSpatialRefSys->setEnabled( haveLayers );
306 }
307 
308 void QgsSourceSelectDialog::addButtonClicked()
309 {
310  if ( treeView->selectionModel()->selectedRows().isEmpty() )
311  {
312  return;
313  }
314 
315  QgsOWSConnection connection( mServiceName, cmbConnections->currentText() );
316 
317  QString pCrsString( labelCoordRefSys->text() );
318  QgsCoordinateReferenceSystem pCrs( pCrsString );
319  //prepare canvas extent info for layers with "cache features" option not set
320  QgsRectangle extent = mCanvasExtent;
321  //does canvas have "on the fly" reprojection set?
322  if ( pCrs.isValid() && mCanvasCrs.isValid() )
323  {
324  try
325  {
326  extent = QgsCoordinateTransform( mCanvasCrs, pCrs ).transform( extent );
327  QgsDebugMsg( QString( "canvas transform: Canvas CRS=%1, Provider CRS=%2, BBOX=%3" )
328  .arg( mCanvasCrs.authid(), pCrs.authid(), extent.asWktCoordinates() ) );
329  }
330  catch ( const QgsCsException& )
331  {
332  // Extent is not in range for specified CRS, leave extent empty.
333  }
334  }
335 
336  //create layers that user selected from this feature source
337  QModelIndexList list = treeView->selectionModel()->selectedRows();
338  for ( int i = 0; i < list.size(); i++ )
339  { //add a wfs layer to the map
340  QModelIndex idx = mModelProxy->mapToSource( list[i] );
341  if ( !idx.isValid() )
342  {
343  continue;
344  }
345  int row = idx.row();
346  QString layerTitle = mModel->item( row, 0 )->text(); //layer title/id
347  QString layerName = mModel->item( row, 1 )->text(); //layer name
348  bool cacheFeatures = mServiceType == FeatureService ? mModel->item( row, 3 )->checkState() == Qt::Checked : false;
349  QString filter = mServiceType == FeatureService ? mModel->item( row, 4 )->text() : ""; //optional filter specified by user
350  if ( cbxUseTitleLayerName->isChecked() && !layerTitle.isEmpty() )
351  {
352  layerName = layerTitle;
353  }
354  QgsRectangle layerExtent;
355  if ( mServiceType == FeatureService && ( cbxFeatureCurrentViewExtent->isChecked() || !cacheFeatures ) )
356  {
357  layerExtent = extent;
358  }
359  QString uri = getLayerURI( connection, layerTitle, layerName, pCrsString, filter, layerExtent );
360 
361  QgsDebugMsg( "Layer " + layerName + ", uri: " + uri );
362  emit addLayer( uri, layerName );
363  }
364  accept();
365 }
366 
367 void QgsSourceSelectDialog::changeCRS()
368 {
369  if ( mProjectionSelector->exec() )
370  {
372  labelCoordRefSys->setText( crsString );
373  }
374 }
375 
376 void QgsSourceSelectDialog::changeCRSFilter()
377 {
378  QgsDebugMsg( "changeCRSFilter called" );
379  //evaluate currently selected typename and set the CRS filter in mProjectionSelector
380  QModelIndex currentIndex = treeView->selectionModel()->currentIndex();
381  if ( currentIndex.isValid() )
382  {
383  QString currentTypename = currentIndex.sibling( currentIndex.row(), 1 ).data().toString();
384  QgsDebugMsg( QString( "the current typename is: %1" ).arg( currentTypename ) );
385 
386  QMap<QString, QStringList>::const_iterator crsIterator = mAvailableCRS.find( currentTypename );
387  if ( crsIterator != mAvailableCRS.end() )
388  {
389  QSet<QString> crsNames;
390  foreach ( const QString& crsName, crsIterator.value() )
391  {
392  crsNames.insert( crsName );
393  }
394  if ( mProjectionSelector )
395  {
397  QString preferredCRS = getPreferredCrs( crsNames ); //get preferred EPSG system
398  if ( !preferredCRS.isEmpty() )
399  {
402 
403  labelCoordRefSys->setText( preferredCRS );
404  }
405  }
406  }
407  }
408 }
409 
410 void QgsSourceSelectDialog::on_cmbConnections_activated( int index )
411 {
412  Q_UNUSED( index );
413  QgsOWSConnection::setSelectedConnection( mServiceName, cmbConnections->currentText() );
414 }
415 
416 void QgsSourceSelectDialog::treeWidgetItemDoubleClicked( const QModelIndex& index )
417 {
418  QgsDebugMsg( "double click called" );
419  QgsOWSConnection connection( mServiceName, cmbConnections->currentText() );
420  buildQuery( connection, index );
421 }
422 
423 void QgsSourceSelectDialog::treeWidgetCurrentRowChanged( const QModelIndex & current, const QModelIndex & previous )
424 {
425  Q_UNUSED( previous )
426  QgsDebugMsg( "treeWidget_currentRowChanged called" );
427  changeCRSFilter();
428  if ( mServiceType == FeatureService )
429  {
430  mBuildQueryButton->setEnabled( current.isValid() );
431  }
432  mAddButton->setEnabled( current.isValid() );
433 }
434 
435 void QgsSourceSelectDialog::buildQueryButtonClicked()
436 {
437  QgsDebugMsg( "mBuildQueryButton click called" );
438  QgsOWSConnection connection( mServiceName, cmbConnections->currentText() );
439  buildQuery( connection, treeView->selectionModel()->currentIndex() );
440 }
441 
442 void QgsSourceSelectDialog::filterChanged( QString text )
443 {
444  QgsDebugMsg( "FeatureType filter changed to :" + text );
445  QRegExp::PatternSyntax mySyntax = QRegExp::PatternSyntax( QRegExp::RegExp );
446  Qt::CaseSensitivity myCaseSensitivity = Qt::CaseInsensitive;
447  QRegExp myRegExp( text, myCaseSensitivity, mySyntax );
448  mModelProxy->setFilterRegExp( myRegExp );
450 }
451 
453 {
454  QVariant indexData = index.data( Qt::DisplayRole );
455  if ( indexData.isNull() )
456  {
457  return QSize();
458  }
459  QSize size = option.fontMetrics.boundingRect( indexData.toString() ).size();
460  size.setHeight( size.height() + 2 );
461  return size;
462 }
463 
464 void QgsSourceSelectDialog::on_buttonBox_helpRequested() const
465 {
467 }
const char * className() const
QgsPoint transform(const QgsPoint &p, TransformDirection direction=ForwardTransform) const
Transform the point from Source Coordinate System to Destination Coordinate System If the direction i...
Connections management.
QgsSourceSelectItemDelegate(QObject *parent=0)
Constructor.
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const
QByteArray toByteArray() const
static unsigned index
A rectangle specified with double values.
Definition: qgsrectangle.h:35
void setHeight(int height)
QgsSourceSelectDialog(const QString &serviceName, ServiceType serviceType, QWidget *parent, Qt::WindowFlags fl)
Constructor.
void setupUi(QWidget *widget)
virtual void reject()
QgsCoordinateReferenceSystem crsByOgcWmsCrs(const QString &ogcCrs) const
Returns the CRS from a given OGC WMS-format Coordinate Reference System string.
void setCursor(const QCursor &)
virtual void sort(int column, Qt::SortOrder order)
void addLayer(QString uri, QString typeName)
Emitted when a layer is added from the dialog.
void addButton(QAbstractButton *button)
virtual void setSourceModel(QAbstractItemModel *sourceModel)
static QString selectedConnection(const QString &theService)
Retreives the selected connection for the specified service.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
int size() const
void rejected()
virtual bool connectToService(const QgsOWSConnection &connection)=0
To be implemented in the child class.
virtual QWidget * widget()
QStandardItemModel * mModel
A generic dialog to prompt the user for a Coordinate Reference System.
static QStringList connectionList(const QString &theService)
Returns the list of connections for the specified service.
virtual QLayout * layout()
virtual const QMetaObject * metaObject() const
int exec()
void setOgcWmsCrsFilter(const QSet< QString > &crsFilter)
filters this dialog by the given CRSs
const_iterator insert(const T &value)
void clear()
QgsCoordinateReferenceSystem mCanvasCrs
QgsGenericProjectionSelector * mProjectionSelector
QString tr(const char *sourceText, const char *disambiguation, int n)
QString text() const
StandardButton information(QWidget *parent, const QString &title, const QString &text, QFlags< QMessageBox::StandardButton > buttons, StandardButton defaultButton)
Qt::CheckState checkState() const
int readNumEntry(const QString &scope, const QString &key, int def=0, bool *ok=nullptr) const
QgsCoordinateReferenceSystem crsBySrsId(long srsId) const
Returns the CRS from a specified QGIS SRS ID.
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
static void setSelectedConnection(const QString &theService, const QString &name)
Marks the specified connection for the specified service as selected.
void populateImageEncodings(const QStringList &availableEncodings)
Updates the UI for the list of available image encodings from the specified list. ...
static void run(const QString &context)
void setValue(const QString &key, const QVariant &value)
QSize size() const
bool isValid() const
void setEnabled(bool)
bool isNull() const
static void deleteConnection(const QString &theService, const QString &name)
Deletes the connection for the specified service with the specified name.
bool restoreGeometry(const QByteArray &geometry)
void connectionsChanged()
Emitted when the connections for the service were changed.
bool isEmpty() const
bool isEmpty() const
void setMessage(QString theMessage="")
If no parameter is passed, the message will be a generic &#39;define the CRS for this layer&#39;...
int row() const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
const QString GEO_EPSG_CRS_AUTHID
Geographic coord sys from EPSG authority.
Definition: qgis.cpp:74
int result() const
iterator end()
QSortFilterProxyModel * mModelProxy
QButtonGroup * mImageEncodingGroup
virtual void accept()
ServiceType
Whether the dialog is for a map service or a feature service.
QString asWktCoordinates() const
returns string representation in Wkt form
QString toLower() const
QMap< QString, QStringList > mAvailableCRS
QStandardItem * item(int row, int column) const
const_iterator constBegin() const
bool contains(const T &value) const
void setChecked(bool)
QVariant value(const QString &key, const QVariant &defaultValue) const
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const
virtual QString getLayerURI(const QgsOWSConnection &connection, const QString &layerTitle, const QString &layerName, const QString &crs=QString(), const QString &filter=QString(), const QgsRectangle &bBox=QgsRectangle()) const =0
To be implemented in the child class.
void setSortCaseSensitivity(Qt::CaseSensitivity cs)
Qt::SortOrder sortOrder() const
QList< QByteArray > supportedImageFormats()
QVariant data(int role) const
QByteArray saveGeometry() const
QModelIndex sibling(int row, int column) const
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:382
void setWindowTitle(const QString &)
Class for storing a coordinate reference system (CRS)
int height() const
virtual int rowCount(const QModelIndex &parent) const
Class for doing transforms between two map coordinate systems.
bool toBool() const
typedef WindowFlags
Custom exception class for Coordinate Reference System related exceptions.
void setRowCount(int rows)
void setDisabled(bool disable)
long srsid() const
Returns the SrsId, if available.
Dialog to allow the user to configure and save connection information for an HTTP Server for WMS...
virtual void buildQuery(const QgsOWSConnection &, const QModelIndex &)
May be implemented in child classes for services which support customized queries.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const
int sortColumn() const
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QString toString() const
QAbstractButton * checkedButton() const
static QgsCRSCache * instance()
Returns a pointer to the QgsCRSCache singleton.
Definition: qgscrscache.cpp:91
QString getSelectedImageEncoding() const
Returns the selected image encoding.
iterator find(const Key &key)
void setCurrentExtentAndCrs(const QgsRectangle &canvasExtent, const QgsCoordinateReferenceSystem &canvasCrs)
Sets the current extent and CRS.
QString authid() const
Returns the authority identifier for the CRS, which includes both the authority (eg EPSG) and the CRS...
virtual QLayoutItem * takeAt(int index)=0
void setFilterRegExp(const QRegExp &regExp)
const T value(const Key &key) const
Item delegate with tweaked sizeHint.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
void setHorizontalHeaderItem(int column, QStandardItem *item)