QGIS API Documentation  3.20.0-Odense (decaadbb31)
qgscoordinateoperationwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscoordinateoperationwidget.cpp
3  ---------------------------
4  begin : December 2019
5  copyright : (C) 2019 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
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"
28 
29 #include <QDir>
30 #include <QPushButton>
31 #include <QRegularExpression>
32 
33 #include "qgsprojutils.h"
34 #include <proj.h>
35 
37  : QWidget( parent )
38 {
39  setupUi( this );
40 
41  mLabelSrcDescription->setTextInteractionFlags( Qt::TextBrowserInteraction );
42  mLabelSrcDescription->setOpenExternalLinks( true );
43  mInstallGridButton->hide();
44 
45  connect( mInstallGridButton, &QPushButton::clicked, this, &QgsCoordinateOperationWidget::installGrid );
46  connect( mAllowFallbackCheckBox, &QCheckBox::toggled, this, [ = ]
47  {
48  if ( !mBlockSignals )
49  emit operationChanged();
50  } );
51  mCoordinateOperationTableWidget->setColumnCount( 3 );
52 
53  QStringList headers;
54  headers << tr( "Transformation" ) << tr( "Accuracy (meters)" ) << tr( "Area of Use" );
55  mCoordinateOperationTableWidget->setHorizontalHeaderLabels( headers );
56 
57  mHideDeprecatedCheckBox->setVisible( false );
58  mShowSupersededCheckBox->setVisible( true );
59  mLabelDstDescription->hide();
60 
61  connect( mHideDeprecatedCheckBox, &QCheckBox::stateChanged, this, [ = ] { loadAvailableOperations(); } );
62  connect( mShowSupersededCheckBox, &QCheckBox::toggled, this, &QgsCoordinateOperationWidget::showSupersededToggled );
63  connect( mCoordinateOperationTableWidget, &QTableWidget::currentItemChanged, this, &QgsCoordinateOperationWidget::tableCurrentItemChanged );
64  connect( mCoordinateOperationTableWidget, &QTableWidget::itemDoubleClicked, this, &QgsCoordinateOperationWidget::operationDoubleClicked );
65 
66  mLabelSrcDescription->clear();
67  mLabelDstDescription->clear();
68 }
69 
71 {
72  if ( canvas )
73  {
74  // show canvas extent in preview widget
75  QPolygonF mainCanvasPoly = canvas->mapSettings().visiblePolygon();
76  QgsGeometry g = QgsGeometry::fromQPolygonF( mainCanvasPoly );
77  // close polygon
78  mainCanvasPoly << mainCanvasPoly.at( 0 );
79  if ( QgsProject::instance()->crs() !=
81  {
82  // reproject extent
86  g = g.densifyByCount( 5 );
87  try
88  {
89  g.transform( ct );
90  }
91  catch ( QgsCsException & )
92  {
93  }
94  }
95  mAreaCanvas->setCanvasRect( g.boundingBox() );
96  }
97 }
98 
100 {
101  mMakeDefaultCheckBox->setVisible( show );
102 }
103 
105 {
106  return mMakeDefaultCheckBox->isChecked();
107 }
108 
110 {
111  return !mCoordinateOperationTableWidget->selectedItems().isEmpty();
112 }
113 
114 QList<QgsCoordinateOperationWidget::OperationDetails> QgsCoordinateOperationWidget::availableOperations() const
115 {
116  QList<QgsCoordinateOperationWidget::OperationDetails> res;
117  res.reserve( mDatumTransforms.size() );
118  for ( const QgsDatumTransform::TransformDetails &details : mDatumTransforms )
119  {
120  OperationDetails op;
121  op.proj = details.proj;
122  op.sourceTransformId = -1;
123  op.destinationTransformId = -1;
124  op.isAvailable = details.isAvailable;
125  res << op;
126  }
127  return res;
128 }
129 
130 void QgsCoordinateOperationWidget::loadAvailableOperations()
131 {
132  mCoordinateOperationTableWidget->setRowCount( 0 );
133 
134  int row = 0;
135  int preferredInitialRow = -1;
136 
137  for ( const QgsDatumTransform::TransformDetails &transform : std::as_const( mDatumTransforms ) )
138  {
139  std::unique_ptr< QTableWidgetItem > item = std::make_unique< QTableWidgetItem >();
140  item->setData( ProjRole, transform.proj );
141  item->setData( AvailableRole, transform.isAvailable );
142  item->setFlags( item->flags() & ~Qt::ItemIsEditable );
143 
144  QString name = transform.name;
145  if ( !transform.authority.isEmpty() && !transform.code.isEmpty() )
146  name += QStringLiteral( " %1 %2:%3" ).arg( QString( QChar( 0x2013 ) ), transform.authority, transform.code );
147  item->setText( name );
148 
149  if ( row == 0 ) // highlight first (preferred) operation
150  {
151  QFont f = item->font();
152  f.setBold( true );
153  item->setFont( f );
154  item->setForeground( QBrush( QColor( 0, 120, 0 ) ) );
155  }
156 
157  if ( !transform.isAvailable )
158  {
159  item->setForeground( QBrush( palette().color( QPalette::Disabled, QPalette::Text ) ) );
160  }
161 
162  if ( preferredInitialRow < 0 && transform.isAvailable )
163  {
164  // try to select a "preferred" entry by default
165  preferredInitialRow = row;
166  }
167 
168  QString missingMessage;
169  if ( !transform.isAvailable )
170  {
171  QStringList gridMessages;
172  QStringList missingGrids;
173  QStringList missingGridPackages;
174  QStringList missingGridUrls;
175 
176  for ( const QgsDatumTransform::GridDetails &grid : transform.grids )
177  {
178  if ( !grid.isAvailable )
179  {
180  missingGrids << grid.shortName;
181  missingGridPackages << grid.packageName;
182  missingGridUrls << grid.url;
183  QString m = tr( "This transformation requires the grid file “%1”, which is not available for use on the system." ).arg( grid.shortName );
184  if ( !grid.url.isEmpty() )
185  {
186  if ( !grid.packageName.isEmpty() )
187  {
188  m += ' ' + tr( "This grid is part of the <i>%1</i> package, available for download from <a href=\"%2\">%2</a>." ).arg( grid.packageName, grid.url );
189  }
190  else
191  {
192  m += ' ' + tr( "This grid is available for download from <a href=\"%1\">%1</a>." ).arg( grid.url );
193  }
194  }
195  gridMessages << m;
196  }
197  }
198 
199  item->setData( MissingGridsRole, missingGrids );
200  item->setData( MissingGridPackageNamesRole, missingGridPackages );
201  item->setData( MissingGridUrlsRole, missingGridUrls );
202 
203  if ( gridMessages.count() > 1 )
204  {
205  for ( int k = 0; k < gridMessages.count(); ++k )
206  gridMessages[k] = QStringLiteral( "<li>%1</li>" ).arg( gridMessages.at( k ) );
207 
208  missingMessage = QStringLiteral( "<ul>%1</ul" ).arg( gridMessages.join( QString() ) );
209  }
210  else if ( !gridMessages.empty() )
211  {
212  missingMessage = gridMessages.constFirst();
213  }
214  }
215 
216  QStringList areasOfUse;
217  QStringList authorityCodes;
218 
219  QStringList opText;
220  QString lastSingleOpScope;
221  QString lastSingleOpRemarks;
222  for ( const QgsDatumTransform::SingleOperationDetails &singleOpDetails : transform.operationDetails )
223  {
224  QString text;
225  if ( !singleOpDetails.scope.isEmpty() )
226  {
227  text += QStringLiteral( "<b>%1</b>: %2" ).arg( tr( "Scope" ), formatScope( singleOpDetails.scope ) );
228  lastSingleOpScope = singleOpDetails.scope;
229  }
230  if ( !singleOpDetails.remarks.isEmpty() )
231  {
232  if ( !text.isEmpty() )
233  text += QLatin1String( "<br>" );
234  text += QStringLiteral( "<b>%1</b>: %2" ).arg( tr( "Remarks" ), singleOpDetails.remarks );
235  lastSingleOpRemarks = singleOpDetails.remarks;
236  }
237  if ( !singleOpDetails.areaOfUse.isEmpty() )
238  {
239  if ( !areasOfUse.contains( singleOpDetails.areaOfUse ) )
240  areasOfUse << singleOpDetails.areaOfUse;
241  }
242  if ( !singleOpDetails.authority.isEmpty() && !singleOpDetails.code.isEmpty() )
243  {
244  const QString identifier = QStringLiteral( "%1:%2" ).arg( singleOpDetails.authority, singleOpDetails.code );
245  if ( !authorityCodes.contains( identifier ) )
246  authorityCodes << identifier;
247  }
248 
249  if ( !text.isEmpty() )
250  {
251  opText.append( text );
252  }
253  }
254 
255  QString text;
256  if ( !transform.scope.isEmpty() && transform.scope != lastSingleOpScope )
257  {
258  text += QStringLiteral( "<b>%1</b>: %2" ).arg( tr( "Scope" ), transform.scope );
259  }
260  if ( !transform.remarks.isEmpty() && transform.remarks != lastSingleOpRemarks )
261  {
262  if ( !text.isEmpty() )
263  text += QLatin1String( "<br>" );
264  text += QStringLiteral( "<b>%1</b>: %2" ).arg( tr( "Remarks" ), transform.remarks );
265  }
266  if ( !text.isEmpty() )
267  {
268  opText.append( text );
269  }
270 
271  if ( opText.count() > 1 )
272  {
273  for ( int k = 0; k < opText.count(); ++k )
274  opText[k] = QStringLiteral( "<li>%1</li>" ).arg( opText.at( k ) );
275  }
276 
277  if ( !transform.areaOfUse.isEmpty() && !areasOfUse.contains( transform.areaOfUse ) )
278  areasOfUse << transform.areaOfUse;
279  item->setData( BoundsRole, transform.bounds );
280 
281  const QString id = !transform.authority.isEmpty() && !transform.code.isEmpty() ? QStringLiteral( "%1:%2" ).arg( transform.authority, transform.code ) : QString();
282  if ( !id.isEmpty() && !authorityCodes.contains( id ) )
283  authorityCodes << id;
284 
285  const QColor disabled = palette().color( QPalette::Disabled, QPalette::Text );
286  const QColor active = palette().color( QPalette::Active, QPalette::Text );
287 
288  const QColor codeColor( static_cast< int >( active.red() * 0.6 + disabled.red() * 0.4 ),
289  static_cast< int >( active.green() * 0.6 + disabled.green() * 0.4 ),
290  static_cast< int >( active.blue() * 0.6 + disabled.blue() * 0.4 ) );
291  const QString toolTipString = QStringLiteral( "<b>%1</b>" ).arg( transform.name )
292  + ( !opText.empty() ? ( opText.count() == 1 ? QStringLiteral( "<p>%1</p>" ).arg( opText.at( 0 ) ) : QStringLiteral( "<ul>%1</ul>" ).arg( opText.join( QString() ) ) ) : QString() )
293  + ( !areasOfUse.empty() ? QStringLiteral( "<p><b>%1</b>: %2</p>" ).arg( tr( "Area of use" ), areasOfUse.join( QLatin1String( ", " ) ) ) : QString() )
294  + ( !authorityCodes.empty() ? QStringLiteral( "<p><b>%1</b>: %2</p>" ).arg( tr( "Identifiers" ), authorityCodes.join( QLatin1String( ", " ) ) ) : QString() )
295  + ( !missingMessage.isEmpty() ? QStringLiteral( "<p><b style=\"color: red\">%1</b></p>" ).arg( missingMessage ) : QString() )
296  + QStringLiteral( "<p><code style=\"color: %1\">%2</code></p>" ).arg( codeColor.name(), transform.proj );
297 
298  item->setToolTip( toolTipString );
299  mCoordinateOperationTableWidget->setRowCount( row + 1 );
300  mCoordinateOperationTableWidget->setItem( row, 0, item.release() );
301 
302  item = std::make_unique< QTableWidgetItem >();
303  item->setFlags( item->flags() & ~Qt::ItemIsEditable );
304  item->setText( transform.accuracy >= 0 ? QString::number( transform.accuracy ) : tr( "Unknown" ) );
305  item->setToolTip( toolTipString );
306  if ( !transform.isAvailable )
307  {
308  item->setForeground( QBrush( palette().color( QPalette::Disabled, QPalette::Text ) ) );
309  }
310  mCoordinateOperationTableWidget->setItem( row, 1, item.release() );
311 
312  // area of use column
313  item = std::make_unique< QTableWidgetItem >();
314  item->setFlags( item->flags() & ~Qt::ItemIsEditable );
315  item->setText( areasOfUse.join( QLatin1String( ", " ) ) );
316  item->setToolTip( toolTipString );
317  if ( !transform.isAvailable )
318  {
319  item->setForeground( QBrush( palette().color( QPalette::Disabled, QPalette::Text ) ) );
320  }
321  mCoordinateOperationTableWidget->setItem( row, 2, item.release() );
322 
323  row++;
324  }
325 
326  if ( mCoordinateOperationTableWidget->currentRow() < 0 )
327  mCoordinateOperationTableWidget->selectRow( preferredInitialRow >= 0 ? preferredInitialRow : 0 );
328 
329  mCoordinateOperationTableWidget->resizeColumnsToContents();
330 
331  tableCurrentItemChanged( nullptr, nullptr );
332 }
333 
335 {
336  QgsSettings settings;
337  settings.setValue( QStringLiteral( "Windows/DatumTransformDialog/hideDeprecated" ), mHideDeprecatedCheckBox->isChecked() );
338 
339  for ( int i = 0; i < 2; i++ )
340  {
341  settings.setValue( QStringLiteral( "Windows/DatumTransformDialog/columnWidths/%1" ).arg( i ), mCoordinateOperationTableWidget->columnWidth( i ) );
342  }
343 }
344 
346 {
347  OperationDetails preferred;
348 
349  // for proj 6, return the first available transform -- they are sorted by preference by proj already
350  for ( const QgsDatumTransform::TransformDetails &transform : std::as_const( mDatumTransforms ) )
351  {
352  if ( transform.isAvailable )
353  {
354  preferred.proj = transform.proj;
355  preferred.isAvailable = transform.isAvailable;
356  break;
357  }
358  }
359  return preferred;
360 }
361 
362 QString QgsCoordinateOperationWidget::formatScope( const QString &s )
363 {
364  QString scope = s;
365 
366  QRegularExpression reGNSS( QStringLiteral( "\\bGNSS\\b" ) );
367  scope.replace( reGNSS, QObject::tr( "GNSS (Global Navigation Satellite System)" ) );
368 
369  QRegularExpression reCORS( QStringLiteral( "\\bCORS\\b" ) );
370  scope.replace( reCORS, QObject::tr( "CORS (Continually Operating Reference Station)" ) );
371 
372  return scope;
373 }
374 
376 {
377  int row = mCoordinateOperationTableWidget->currentRow();
378  OperationDetails op;
379 
380  if ( row >= 0 )
381  {
382  QTableWidgetItem *srcItem = mCoordinateOperationTableWidget->item( row, 0 );
383  op.sourceTransformId = srcItem ? srcItem->data( TransformIdRole ).toInt() : -1;
384  QTableWidgetItem *destItem = mCoordinateOperationTableWidget->item( row, 1 );
385  op.destinationTransformId = destItem ? destItem->data( TransformIdRole ).toInt() : -1;
386  op.proj = srcItem ? srcItem->data( ProjRole ).toString() : QString();
387  op.isAvailable = srcItem ? srcItem->data( AvailableRole ).toBool() : true;
388  op.allowFallback = mAllowFallbackCheckBox->isChecked();
389  }
390  else
391  {
392  op.sourceTransformId = -1;
393  op.destinationTransformId = -1;
394  op.proj = QString();
395  }
396  return op;
397 }
398 
400 {
401  int prevRow = mCoordinateOperationTableWidget->currentRow();
402  mBlockSignals++;
403  for ( int row = 0; row < mCoordinateOperationTableWidget->rowCount(); ++row )
404  {
405  QTableWidgetItem *srcItem = mCoordinateOperationTableWidget->item( row, 0 );
406  if ( srcItem && srcItem->data( ProjRole ).toString() == operation.proj )
407  {
408  mCoordinateOperationTableWidget->selectRow( row );
409  break;
410  }
411  }
412 
413  bool fallbackChanged = mAllowFallbackCheckBox->isChecked() != operation.allowFallback;
414  mAllowFallbackCheckBox->setChecked( operation.allowFallback );
415  mBlockSignals--;
416 
417  if ( mCoordinateOperationTableWidget->currentRow() != prevRow || fallbackChanged )
418  emit operationChanged();
419 }
420 
422 {
423  const QString op = context.calculateCoordinateOperation( mSourceCrs, mDestinationCrs );
424  if ( !op.isEmpty() )
425  {
426  OperationDetails deets;
427  deets.proj = op;
428  deets.allowFallback = context.allowFallbackTransform( mSourceCrs, mDestinationCrs );
429  setSelectedOperation( deets );
430  }
431  else
432  {
434  }
435 }
436 
438 {
439  mAllowFallbackCheckBox->setVisible( visible );
440 }
441 
442 bool QgsCoordinateOperationWidget::gridShiftTransformation( const QString &itemText ) const
443 {
444  return !itemText.isEmpty() && !itemText.contains( QLatin1String( "towgs84" ), Qt::CaseInsensitive );
445 }
446 
447 bool QgsCoordinateOperationWidget::testGridShiftFileAvailability( QTableWidgetItem *item ) const
448 {
449  if ( !item )
450  {
451  return true;
452  }
453 
454  QString itemText = item->text();
455  if ( itemText.isEmpty() )
456  {
457  return true;
458  }
459 
460  char *projLib = getenv( "PROJ_LIB" );
461  if ( !projLib ) //no information about installation directory
462  {
463  return true;
464  }
465 
466  QStringList itemEqualSplit = itemText.split( '=' );
467  QString filename;
468  for ( int i = 1; i < itemEqualSplit.size(); ++i )
469  {
470  if ( i > 1 )
471  {
472  filename.append( '=' );
473  }
474  filename.append( itemEqualSplit.at( i ) );
475  }
476 
477  QDir projDir( projLib );
478  if ( projDir.exists() )
479  {
480  //look if filename in directory
481  QStringList fileList = projDir.entryList();
482  QStringList::const_iterator fileIt = fileList.constBegin();
483  for ( ; fileIt != fileList.constEnd(); ++fileIt )
484  {
485 #if defined(Q_OS_WIN)
486  if ( fileIt->compare( filename, Qt::CaseInsensitive ) == 0 )
487 #else
488  if ( fileIt->compare( filename ) == 0 )
489 #endif //Q_OS_WIN
490  {
491  return true;
492  }
493  }
494  item->setToolTip( tr( "File '%1' not found in directory '%2'" ).arg( filename, projDir.absolutePath() ) );
495  return false; //not found in PROJ_LIB directory
496  }
497  return true;
498 }
499 
500 void QgsCoordinateOperationWidget::tableCurrentItemChanged( QTableWidgetItem *, QTableWidgetItem * )
501 {
502  int row = mCoordinateOperationTableWidget->currentRow();
503  if ( row < 0 )
504  {
505  mLabelSrcDescription->clear();
506  mLabelDstDescription->clear();
507  mAreaCanvas->hide();
508  mInstallGridButton->hide();
509  }
510  else
511  {
512  QTableWidgetItem *srcItem = mCoordinateOperationTableWidget->item( row, 0 );
513  mLabelSrcDescription->setText( srcItem ? srcItem->toolTip() : QString() );
514  if ( srcItem )
515  {
516  // find area of intersection of operation, source and dest bounding boxes
517  // see https://github.com/OSGeo/PROJ/issues/1549 for justification
518  const QgsRectangle operationRect = srcItem->data( BoundsRole ).value< QgsRectangle >();
519  const QgsRectangle sourceRect = mSourceCrs.bounds();
520  const QgsRectangle destRect = mDestinationCrs.bounds();
521  QgsRectangle rect = operationRect.intersect( sourceRect );
522  rect = rect.intersect( destRect );
523 
524  mAreaCanvas->setPreviewRect( rect );
525  mAreaCanvas->show();
526 
527  const QStringList missingGrids = srcItem->data( MissingGridsRole ).toStringList();
528  mInstallGridButton->setVisible( !missingGrids.empty() );
529  if ( !missingGrids.empty() )
530  {
531  mInstallGridButton->setText( tr( "Install “%1” Grid…" ).arg( missingGrids.at( 0 ) ) );
532  }
533  }
534  else
535  {
536  mAreaCanvas->setPreviewRect( QgsRectangle() );
537  mAreaCanvas->hide();
538  mInstallGridButton->hide();
539  }
540  QTableWidgetItem *destItem = mCoordinateOperationTableWidget->item( row, 1 );
541  mLabelDstDescription->setText( destItem ? destItem->toolTip() : QString() );
542  }
543  OperationDetails newOp = selectedOperation();
544  if ( newOp.proj != mPreviousOp.proj && !mBlockSignals )
545  emit operationChanged();
546  mPreviousOp = newOp;
547 }
548 
550 {
551  mSourceCrs = sourceCrs;
552  mDatumTransforms = QgsDatumTransform::operations( mSourceCrs, mDestinationCrs, mShowSupersededCheckBox->isChecked() );
553  loadAvailableOperations();
554 }
555 
557 {
558  mDestinationCrs = destinationCrs;
559  mDatumTransforms = QgsDatumTransform::operations( mSourceCrs, mDestinationCrs, mShowSupersededCheckBox->isChecked() );
560  loadAvailableOperations();
561 }
562 
563 void QgsCoordinateOperationWidget::showSupersededToggled( bool )
564 {
565  mDatumTransforms = QgsDatumTransform::operations( mSourceCrs, mDestinationCrs, mShowSupersededCheckBox->isChecked() );
566  loadAvailableOperations();
567 }
568 
569 void QgsCoordinateOperationWidget::installGrid()
570 {
571  int row = mCoordinateOperationTableWidget->currentRow();
572  QTableWidgetItem *srcItem = mCoordinateOperationTableWidget->item( row, 0 );
573  if ( !srcItem )
574  return;
575 
576  const QStringList missingGrids = srcItem->data( MissingGridsRole ).toStringList();
577  if ( missingGrids.empty() )
578  return;
579 
580  const QStringList missingGridPackagesNames = srcItem->data( MissingGridPackageNamesRole ).toStringList();
581  const QString packageName = missingGridPackagesNames.value( 0 );
582  const QStringList missingGridUrls = srcItem->data( MissingGridUrlsRole ).toStringList();
583  const QString gridUrl = missingGridUrls.value( 0 );
584 
585  QString downloadMessage;
586  if ( !packageName.isEmpty() )
587  {
588  downloadMessage = tr( "This grid is part of the “<i>%1</i>” package, available for download from <a href=\"%2\">%2</a>." ).arg( packageName, gridUrl );
589  }
590  else if ( !gridUrl.isEmpty() )
591  {
592  downloadMessage = tr( "This grid is available for download from <a href=\"%1\">%1</a>." ).arg( gridUrl );
593  }
594 
595  const QString longMessage = tr( "<p>This transformation requires the grid file “%1”, which is not available for use on the system.</p>" ).arg( missingGrids.at( 0 ) );
596 
597  QgsInstallGridShiftFileDialog *dlg = new QgsInstallGridShiftFileDialog( missingGrids.at( 0 ), this );
598  dlg->setAttribute( Qt::WA_DeleteOnClose );
599  dlg->setWindowTitle( tr( "Install Grid File" ) );
600  dlg->setDescription( longMessage );
601  dlg->setDownloadMessage( downloadMessage );
602  dlg->exec();
603 }
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination crs for the operations shown in the widget.
QgsCoordinateOperationWidget::OperationDetails selectedOperation() const
Returns the details of the operation currently selected within the widget.
bool makeDefaultSelected() const
Returns true if the "make default" option is selected.
QgsCoordinateReferenceSystem sourceCrs() const
Returns the source CRS for the operations shown in the widget.
void setShowMakeDefault(bool show)
Sets whether the "make default" checkbox should be shown.
void operationChanged()
Emitted when the operation selected in the dialog is changed.
void operationDoubleClicked()
Emitted when an operation is double-clicked in the widget.
bool hasSelection() const
Returns true if there is a valid selection in the widget.
void setShowFallbackOption(bool visible)
Sets whether the "allow fallback" operations option is visible.
QgsCoordinateOperationWidget::OperationDetails defaultOperation() const
Returns the details of the default operation suggested by the widget.
QgsCoordinateOperationWidget(QWidget *parent=nullptr)
Constructor for QgsCoordinateOperationWidget.
void setMapCanvas(QgsMapCanvas *canvas)
Sets a map canvas to link to the widget, which allows the widget's choices to reflect the current can...
void setSelectedOperationUsingContext(const QgsCoordinateTransformContext &context)
Automatically sets the selected operation using the settings encapsulated in a transform context.
QList< QgsCoordinateOperationWidget::OperationDetails > availableOperations() const
Returns a list of the available operations shown in the widget.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs)
Sets the source crs for the operations shown in the widget.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination CRS for the operations shown in the widget.
void setSelectedOperation(const QgsCoordinateOperationWidget::OperationDetails &operation)
Sets the details of the operation currently selected within the widget.
This class represents a coordinate reference system (CRS).
QgsRectangle bounds() const
Returns the approximate bounds for the region the CRS is usable within.
static Q_INVOKABLE QgsCoordinateReferenceSystem fromEpsgId(long epsg)
Creates a CRS from a given EPSG ID.
Contains information about the context in which a coordinate transform is executed.
bool allowFallbackTransform(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination) const
Returns true if approximate "ballpark" transforms may be used when transforming between a source and ...
QString calculateCoordinateOperation(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination) const
Returns the Proj coordinate operation string to use when transforming from the specified source CRS t...
Class for doing transforms between two map coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
static QList< QgsDatumTransform::TransformDetails > operations(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination, bool includeSuperseded=false)
Returns a list of coordinate operations available for transforming coordinates from the source to des...
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
QgsGeometry densifyByCount(int extraNodesPerSegment) const
Returns a copy of the geometry which has been densified by adding the specified number of extra nodes...
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
OperationResult transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection direction=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:86
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QPolygonF visiblePolygon() const
Returns the visible area as a polygon (may be rotated)
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:467
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
Definition: qgsrectangle.h:333
const QgsCoordinateReferenceSystem & crs
bool allowFallback
true if fallback transforms can be used
QString proj
Proj coordinate operation description, for Proj >= 6.0 builds only.
Contains information about a projection transformation grid file.
QString shortName
Short name of transform grid.
bool isAvailable
true if grid is currently available for use
QString packageName
Name of package the grid is included within.
QString url
Url to download grid from.
Contains information about a single coordinate operation.
QString authority
Authority name, e.g. EPSG.
QString code
Authority code, e.g. "8447" (for EPSG:8447).
QString remarks
Remarks for operation, from EPSG registry database.
QString areaOfUse
Area of use, from EPSG registry database.
QString scope
Scope of operation, from EPSG registry database.
Contains information about a coordinate transformation operation.