36#include <QResizeEvent> 
   38#include <QRegularExpression> 
   45  QFont f = teProjection->font();
 
   46  f.setPointSize( f.pointSize() - 2 );
 
   47  teProjection->setFont( f );
 
   49  leSearch->setShowSearchIcon( 
true );
 
   51  connect( lstCoordinateSystems, &QTreeWidget::itemDoubleClicked, 
this, &QgsProjectionSelectionTreeWidget::lstCoordinateSystems_itemDoubleClicked );
 
   52  connect( lstRecent, &QTreeWidget::itemDoubleClicked, 
this, &QgsProjectionSelectionTreeWidget::lstRecent_itemDoubleClicked );
 
   53  connect( lstCoordinateSystems, &QTreeWidget::currentItemChanged, 
this, &QgsProjectionSelectionTreeWidget::lstCoordinateSystems_currentItemChanged );
 
   54  connect( lstRecent, &QTreeWidget::currentItemChanged, 
this, &QgsProjectionSelectionTreeWidget::lstRecent_currentItemChanged );
 
   55  connect( cbxHideDeprecated, &QCheckBox::stateChanged, 
this, &QgsProjectionSelectionTreeWidget::updateFilter );
 
   56  connect( leSearch, &QgsFilterLineEdit::textChanged, 
this, &QgsProjectionSelectionTreeWidget::updateFilter );
 
   58  mAreaCanvas->setVisible( mShowMap );
 
   63  lstCoordinateSystems->header()->setSectionResizeMode( AuthidColumn, QHeaderView::Stretch );
 
   64  lstCoordinateSystems->header()->resizeSection( QgisCrsIdColumn, 0 );
 
   65  lstCoordinateSystems->header()->setSectionResizeMode( QgisCrsIdColumn, QHeaderView::Fixed );
 
   68  lstCoordinateSystems->setColumnHidden( QgisCrsIdColumn, 
true );
 
   69  lstRecent->header()->setSectionResizeMode( AuthidColumn, QHeaderView::Stretch );
 
   70  lstRecent->header()->resizeSection( QgisCrsIdColumn, 0 );
 
   71  lstRecent->header()->setSectionResizeMode( QgisCrsIdColumn, QHeaderView::Fixed );
 
   72  lstRecent->setColumnHidden( QgisCrsIdColumn, 
true );
 
   75  lstRecent->header()->setMinimumSectionSize( 10 );
 
   76  lstRecent->header()->setStretchLastSection( 
false );
 
   77  lstRecent->header()->resizeSection( ClearColumn, 20 );
 
   80  lstRecent->setContextMenuPolicy( Qt::CustomContextMenu );
 
   81  connect( lstRecent, &QTreeWidget::customContextMenuRequested, 
this, [
this]( 
const QPoint & pos )
 
   84    if ( lstRecent->topLevelItemCount() == 0 )
 
   88    QTreeWidgetItem *currentItem = lstRecent->itemAt( pos );
 
   91      QAction *clearSelected = menu.addAction( 
QgsApplication::getThemeIcon( 
"/mIconClearItem.svg" ),  tr( 
"Remove selected CRS from recently used CRS" ) );
 
   92      connect( clearSelected, &QAction::triggered, 
this, [
this, currentItem ] { removeRecentCrsItem( currentItem ); } );
 
   96    QAction *clearAll = menu.addAction( 
QgsApplication::getThemeIcon( 
"/console/iconClearConsole.svg" ), tr( 
"Clear all recently used CRS" ) );
 
   98    menu.exec( lstRecent->viewport()->mapToGlobal( pos ) );
 
  102  lstRecent->installEventFilter( 
this );
 
  106  mCheckBoxNoProjection->setHidden( 
true );
 
  107  mCheckBoxNoProjection->setEnabled( 
false );
 
  108  connect( mCheckBoxNoProjection, &QCheckBox::toggled, 
this, [ = ]
 
  110    if ( !mBlockSignals )
 
  116  connect( mCheckBoxNoProjection, &QCheckBox::toggled, 
this, [ = ]( 
bool checked )
 
  118    if ( mCheckBoxNoProjection->isEnabled() )
 
  120      mFrameProjections->setDisabled( checked );
 
  125  mSplitter->restoreState( settings.value( QStringLiteral( 
"Windows/ProjectionSelector/splitterState" ) ).toByteArray() );
 
  131  settings.
setValue( QStringLiteral( 
"Windows/ProjectionSelector/splitterState" ), mSplitter->saveState() );
 
  141  lstCoordinateSystems->header()->resizeSection( NameColumn, event->size().width() - 240 );
 
  142  lstCoordinateSystems->header()->resizeSection( AuthidColumn, 240 );
 
  143  lstCoordinateSystems->header()->resizeSection( QgisCrsIdColumn, 0 );
 
  145  lstRecent->header()->resizeSection( NameColumn, event->size().width() - 260 );
 
  146  lstRecent->header()->resizeSection( AuthidColumn, 240 );
 
  147  lstRecent->header()->resizeSection( QgisCrsIdColumn, 0 );
 
  148  lstRecent->header()->resizeSection( ClearColumn, 20 );
 
  158  loadCrsList( &mCrsFilter );
 
  159  loadUserCrsList( &mCrsFilter );
 
  161  if ( !mRecentProjListDone )
 
  165    mRecentProjListDone = 
true;
 
  169  mBlockSignals = 
true; 
 
  171  mBlockSignals = 
false;
 
  176  QWidget::showEvent( event );
 
  183  if ( obj != lstRecent )
 
  186  if ( ev->type() != QEvent::KeyPress )
 
  189  QKeyEvent *keyEvent = 
static_cast<QKeyEvent *
>( ev );
 
  190  if ( keyEvent->matches( QKeySequence::Delete ) )
 
  192    removeRecentCrsItem( lstRecent->currentItem() );
 
  199QString QgsProjectionSelectionTreeWidget::ogcWmsCrsFilterAsSqlExpression( QSet<QString> *crsFilter )
 
  201  QString sqlExpression = QStringLiteral( 
"1" );           
 
  202  QMap<QString, QStringList> authParts;
 
  205    return sqlExpression;
 
  225  const auto authIds { *crsFilter };
 
  226  for ( 
const QString &auth_id : authIds )
 
  228    QStringList parts = auth_id.split( 
':' );
 
  230    if ( parts.size() < 2 )
 
  233    authParts[ parts.at( 0 ).toUpper()].append( parts.at( 1 ).toUpper() );
 
  236  if ( authParts.isEmpty() )
 
  237    return sqlExpression;
 
  239  if ( !authParts.isEmpty() )
 
  241    QString prefix = QStringLiteral( 
" AND (" );
 
  242    for ( 
auto it = authParts.constBegin(); it != authParts.constEnd(); ++it )
 
  244      sqlExpression += QStringLiteral( 
"%1(upper(auth_name)='%2' AND upper(auth_id) IN ('%3'))" )
 
  247                             it.value().join( QLatin1String( 
"','" ) ) );
 
  248      prefix = QStringLiteral( 
" OR " );
 
  250    sqlExpression += 
')';
 
  255  return sqlExpression;
 
  258void QgsProjectionSelectionTreeWidget::applySelection( 
int column, QString value )
 
  260  if ( !mProjListDone || !mUserProjListDone )
 
  263    mSearchColumn = column;
 
  264    mSearchValue  = value;
 
  268  if ( column == QgsProjectionSelectionTreeWidget::None )
 
  271    column = mSearchColumn;
 
  272    value  = mSearchValue;
 
  274    mSearchColumn = QgsProjectionSelectionTreeWidget::None;
 
  275    mSearchValue.clear();
 
  278  if ( column == QgsProjectionSelectionTreeWidget::None )
 
  281  QList<QTreeWidgetItem *> nodes = lstCoordinateSystems->findItems( value, Qt::MatchExactly | Qt::MatchRecursive, column );
 
  282  if ( !nodes.isEmpty() )
 
  284    QgsDebugMsgLevel( QStringLiteral( 
"found %1,%2" ).arg( column ).arg( value ), 4 );
 
  285    lstCoordinateSystems->setCurrentItem( nodes.first() );
 
  289    QgsDebugMsgLevel( QStringLiteral( 
"nothing found for %1,%2" ).arg( column ).arg( value ), 4 );
 
  291    lstCoordinateSystems->clearSelection();
 
  292    lstRecent->clearSelection();
 
  293    teProjection->clear();
 
  299  if ( !mProjListDone || !mUserProjListDone )
 
  302  QList<QTreeWidgetItem *> nodes = lstCoordinateSystems->findItems( QString::number( 
crs.
srsid() ), Qt::MatchExactly | Qt::MatchRecursive, QgisCrsIdColumn );
 
  303  if ( nodes.isEmpty() )
 
  306  QTreeWidgetItem *item = 
new QTreeWidgetItem( lstRecent, QStringList()
 
  307      << nodes.first()->text( NameColumn )
 
  308      << nodes.first()->text( AuthidColumn )
 
  309      << nodes.first()->text( QgisCrsIdColumn ) );
 
  312  QToolButton *clearButton = 
new QToolButton();
 
  314  clearButton->setAutoRaise( 
true );
 
  315  clearButton->setToolTip( tr( 
"Remove from recently used CRS" ) );
 
  316  connect( clearButton, &QToolButton::clicked, 
this, [
this, item] { removeRecentCrsItem( item ); } );
 
  317  lstRecent->setItemWidget( item, ClearColumn, clearButton );
 
  320  lstRecent->insertTopLevelItem( 0,  item );
 
  325QString QgsProjectionSelectionTreeWidget::selectedName()
 
  328  QTreeWidgetItem *lvi = lstCoordinateSystems->currentItem();
 
  329  return lvi ? lvi->text( NameColumn ) : QString();
 
  336    mCheckBoxNoProjection->setChecked( 
true );
 
  340    bool changed = 
false;
 
  343      changed = mDeferredLoadCrs != 
crs;
 
  344      mDeferredLoadCrs = 
crs;
 
  346    mBlockSignals = 
true;
 
  347    mCheckBoxNoProjection->setChecked( 
false );
 
  348    mBlockSignals = 
false;
 
  351      applySelection( AuthidColumn, 
crs.
authid() );
 
  353      loadUnknownCrs( 
crs );
 
  364  mAreaCanvas->setCanvasRect( rect );
 
  369  return mAreaCanvas->canvasRect();
 
  372QString QgsProjectionSelectionTreeWidget::expressionForItem( QTreeWidgetItem *item, 
const QString &expression )
 const 
  377  if ( !item || item->text( QgisCrsIdColumn ).isEmpty() )
 
  383  QString databaseFileName;
 
  387    if ( !QFileInfo::exists( databaseFileName ) )
 
  394    databaseFileName = mSrsDatabaseFileName;
 
  402  int rc = sqlite3_open_v2( databaseFileName.toUtf8().constData(), &database, SQLITE_OPEN_READONLY, 
nullptr );
 
  406                               "Because of this the projection selector will not work…" ).arg( databaseFileName ),
 
  407                               Qgis::MessageLevel::Critical );
 
  412  const char *tail = 
nullptr;
 
  413  sqlite3_stmt *stmt = 
nullptr;
 
  414  QString sql = QStringLiteral( 
"select %1 from tbl_srs where srs_id=%2" )
 
  416                      item->text( QgisCrsIdColumn ) );
 
  418  QgsDebugMsgLevel( QStringLiteral( 
"Finding selected attribute using : %1" ).arg( sql ), 4 );
 
  419  rc = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
 
  421  QString attributeValue;
 
  422  if ( rc == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW )
 
  425    attributeValue = QString::fromUtf8( ( 
char * )sqlite3_column_text( stmt, 0 ) );
 
  429  sqlite3_finalize( stmt );
 
  431  sqlite3_close( database );
 
  434  return attributeValue;
 
  441  if ( mCheckBoxNoProjection->isEnabled() && mCheckBoxNoProjection->isChecked() )
 
  444  if ( !mInitialized && mDeferredLoadCrs.
isValid() )
 
  445    return mDeferredLoadCrs;
 
  447  const QString srsIdString = expressionForItem( item, QStringLiteral( 
"srs_id" ) );
 
  448  if ( !srsIdString.isEmpty() )
 
  450    int srid = srsIdString.toLong();
 
  459    if ( item && item->data( 0, RoleWkt ).isValid() )
 
  461    else if ( item && item->data( 0, RoleProj ).isValid() )
 
  470  return crsForItem( lstCoordinateSystems->currentItem() );
 
  475  mCheckBoxNoProjection->setVisible( show );
 
  476  mCheckBoxNoProjection->setEnabled( show );
 
  479    mFrameProjections->setDisabled( mCheckBoxNoProjection->isChecked() );
 
  486  mAreaCanvas->setVisible( show );
 
  491  return !mCheckBoxNoProjection->isHidden();
 
  496  mCheckBoxNoProjection->setText( text );
 
  506  QTreeWidgetItem *item = lstCoordinateSystems->currentItem();
 
  507  if ( mCheckBoxNoProjection->isChecked() )
 
  509  else if ( !mInitialized && mDeferredLoadCrs.
isValid() )
 
  512    return item && ( !item->text( QgisCrsIdColumn ).isEmpty() || item->data( 0, RoleWkt ).isValid() );
 
  515long QgsProjectionSelectionTreeWidget::selectedCrsId()
 
  517  QTreeWidgetItem *item = lstCoordinateSystems->currentItem();
 
  519  if ( item && !item->text( QgisCrsIdColumn ).isEmpty() )
 
  520    return lstCoordinateSystems->currentItem()->text( QgisCrsIdColumn ).toLong();
 
  528  mCrsFilter = crsFilter;
 
  529  mProjListDone = 
false;
 
  530  mUserProjListDone = 
false;
 
  531  lstCoordinateSystems->clear();
 
  534void QgsProjectionSelectionTreeWidget::loadUserCrsList( QSet<QString> *crsFilter )
 
  536  if ( mUserProjListDone )
 
  539  QgsDebugMsgLevel( QStringLiteral( 
"Fetching user projection list..." ), 4 );
 
  543  mUserProjList = 
new QTreeWidgetItem( lstCoordinateSystems, QStringList( tr( 
"User Defined Coordinate Systems" ) ) );
 
  544  mUserProjList->setFlags( mUserProjList->flags() & ~Qt::ItemIsSelectable );
 
  546  QFont fontTemp = mUserProjList->font( 0 );
 
  547  fontTemp.setItalic( 
true );
 
  548  fontTemp.setBold( 
true );
 
  549  mUserProjList->setFont( 0, fontTemp );
 
  555    const QString authid = QStringLiteral( 
"USER:%1" ).arg( details.id );
 
  556    if ( crsFilter && !crsFilter->isEmpty() && !crsFilter->contains( authid ) && !crsFilter->contains( authid.toLower() ) )
 
  559    QTreeWidgetItem *newItem = 
new QTreeWidgetItem( mUserProjList, QStringList() << details.name );
 
  560    newItem->setText( QgisCrsIdColumn, QString::number( details.id ) );
 
  561    newItem->setText( AuthidColumn, authid );
 
  564  mUserProjListDone = 
true;
 
  567void QgsProjectionSelectionTreeWidget::loadCrsList( QSet<QString> *crsFilter )
 
  573  QString sqlFilter = ogcWmsCrsFilterAsSqlExpression( crsFilter );
 
  579  mGeoList = 
new QTreeWidgetItem( lstCoordinateSystems, QStringList( tr( 
"Geographic Coordinate Systems" ) ) );
 
  580  mGeoList->setFlags( mGeoList->flags() & ~Qt::ItemIsSelectable );
 
  582  QFont fontTemp = mGeoList->font( 0 );
 
  583  fontTemp.setItalic( 
true );
 
  584  fontTemp.setBold( 
true );
 
  585  mGeoList->setFont( 0, fontTemp );
 
  589  mProjList = 
new QTreeWidgetItem( lstCoordinateSystems, QStringList( tr( 
"Projected Coordinate Systems" ) ) );
 
  590  mProjList->setFlags( mProjList->flags() & ~Qt::ItemIsSelectable );
 
  592  fontTemp = mProjList->font( 0 );
 
  593  fontTemp.setItalic( 
true );
 
  594  fontTemp.setBold( 
true );
 
  595  mProjList->setFont( 0, fontTemp );
 
  603  if ( !QFileInfo::exists( mSrsDatabaseFileName ) )
 
  605    mProjListDone = 
true;
 
  611  int rc = sqlite3_open_v2( mSrsDatabaseFileName.toUtf8().constData(), &database, SQLITE_OPEN_READONLY, 
nullptr );
 
  616    showDBMissingWarning( mSrsDatabaseFileName );
 
  620  const char *tail = 
nullptr;
 
  621  sqlite3_stmt *stmt = 
nullptr;
 
  625  QString sql = QStringLiteral( 
"select description, srs_id, upper(auth_name||':'||auth_id), is_geo, name, parameters, deprecated from vw_srs where %1 order by name,description" )
 
  628  rc = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
 
  630  if ( rc == SQLITE_OK )
 
  632    QTreeWidgetItem *newItem = 
nullptr;
 
  635    QString previousSrsType;
 
  636    QTreeWidgetItem *previousSrsTypeNode = 
nullptr;
 
  638    while ( sqlite3_step( stmt ) == SQLITE_ROW )
 
  641      int isGeo = sqlite3_column_int( stmt, 3 );
 
  646        newItem = 
new QTreeWidgetItem( mGeoList, QStringList( QString::fromUtf8( ( 
char * )sqlite3_column_text( stmt, 0 ) ) ) );
 
  649        newItem->setText( AuthidColumn, QString::fromUtf8( ( 
char * )sqlite3_column_text( stmt, 2 ) ) );
 
  652        newItem->setText( QgisCrsIdColumn, QString::fromUtf8( ( 
char * )sqlite3_column_text( stmt, 1 ) ) );
 
  657        QTreeWidgetItem *node = 
nullptr;
 
  658        QString srsType = QString::fromUtf8( ( 
char * )sqlite3_column_text( stmt, 4 ) );
 
  659        if ( srsType.isEmpty() )
 
  660          srsType = tr( 
"Other" );
 
  664        if ( srsType == previousSrsType )
 
  666          node = previousSrsTypeNode;
 
  671          QList<QTreeWidgetItem *> nodes = lstCoordinateSystems->findItems( srsType, Qt::MatchExactly | Qt::MatchRecursive, NameColumn );
 
  672          if ( nodes.isEmpty() )
 
  676            node = 
new QTreeWidgetItem( mProjList, QStringList( srsType ) );
 
  677            node->setFlags( node->flags() & ~Qt::ItemIsSelectable );
 
  679            QFont fontTemp = node->font( 0 );
 
  680            fontTemp.setItalic( 
true );
 
  681            node->setFont( 0, fontTemp );
 
  685            node = nodes.first();
 
  688          previousSrsType = srsType;
 
  689          previousSrsTypeNode = node;
 
  692        newItem = 
new QTreeWidgetItem( node, QStringList( QString::fromUtf8( ( 
char * )sqlite3_column_text( stmt, 0 ) ) ) );
 
  694        newItem->setText( AuthidColumn, QString::fromUtf8( ( 
char * )sqlite3_column_text( stmt, 2 ) ) );
 
  696        newItem->setText( QgisCrsIdColumn, QString::fromUtf8( ( 
char * )sqlite3_column_text( stmt, 1 ) ) );
 
  698        newItem->parent()->setExpanded( 
true );
 
  702      newItem->setData( 0, RoleDeprecated, QString::fromUtf8( ( 
char * )sqlite3_column_text( stmt, 6 ) ) );
 
  703      newItem->setHidden( cbxHideDeprecated->isChecked() );
 
  705    mProjList->setExpanded( 
true );
 
  709  sqlite3_finalize( stmt );
 
  711  sqlite3_close( database );
 
  713  mProjListDone = 
true;
 
  720    mUnknownList = 
new QTreeWidgetItem( lstCoordinateSystems, QStringList( tr( 
"Custom Coordinate Systems" ) ) );
 
  721    mUnknownList->setFlags( mUnknownList->flags() & ~Qt::ItemIsSelectable );
 
  723    QFont fontTemp = mUnknownList->font( 0 );
 
  724    fontTemp.setItalic( 
true );
 
  725    fontTemp.setBold( 
true );
 
  726    mUnknownList->setFont( 0, fontTemp );
 
  730  QTreeWidgetItem *newItem = 
new QTreeWidgetItem( mUnknownList, QStringList( 
crs.
description().isEmpty() ? QObject::tr( 
"Custom CRS" ) : 
crs.
description() ) );
 
  732  newItem->setData( 0, RoleProj, 
crs.
toProj() );
 
  734  lstCoordinateSystems->setCurrentItem( newItem );
 
  738void QgsProjectionSelectionTreeWidget::lstCoordinateSystems_currentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem * )
 
  748  lstCoordinateSystems->scrollToItem( current );
 
  752  if ( current->childCount() == 0 )
 
  755    if ( !mBlockSignals )
 
  761    updateBoundsPreview();
 
  763    const QString crsId = current->text( QgisCrsIdColumn );
 
  764    if ( !crsId.isEmpty() )
 
  766      QList<QTreeWidgetItem *> nodes = lstRecent->findItems( current->text( QgisCrsIdColumn ), Qt::MatchExactly, QgisCrsIdColumn );
 
  767      if ( !nodes.isEmpty() )
 
  769        QgsDebugMsgLevel( QStringLiteral( 
"found srs %1 in recent" ).arg( current->text( QgisCrsIdColumn ) ), 4 );
 
  770        lstRecent->setCurrentItem( nodes.first() );
 
  774        QgsDebugMsgLevel( QStringLiteral( 
"srs %1 not recent" ).arg( current->text( QgisCrsIdColumn ) ), 4 );
 
  775        lstRecent->clearSelection();
 
  776        lstCoordinateSystems->setFocus( Qt::OtherFocusReason );
 
  781      lstRecent->clearSelection();
 
  782      lstCoordinateSystems->setFocus( Qt::OtherFocusReason );
 
  788    current->setSelected( 
false );
 
  789    teProjection->clear();
 
  790    lstRecent->clearSelection();
 
  795void QgsProjectionSelectionTreeWidget::lstCoordinateSystems_itemDoubleClicked( QTreeWidgetItem *current, 
int column )
 
  809  if ( current->childCount() == 0 )
 
  813void QgsProjectionSelectionTreeWidget::lstRecent_currentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem * )
 
  823  lstRecent->scrollToItem( current );
 
  825  QList<QTreeWidgetItem *> nodes = lstCoordinateSystems->findItems( current->text( QgisCrsIdColumn ), Qt::MatchExactly | Qt::MatchRecursive, QgisCrsIdColumn );
 
  826  if ( !nodes.isEmpty() )
 
  827    lstCoordinateSystems->setCurrentItem( nodes.first() );
 
  830void QgsProjectionSelectionTreeWidget::lstRecent_itemDoubleClicked( QTreeWidgetItem *current, 
int column )
 
  842  QList<QTreeWidgetItem *> nodes = lstCoordinateSystems->findItems( current->text( QgisCrsIdColumn ), Qt::MatchExactly | Qt::MatchRecursive, QgisCrsIdColumn );
 
  843  if ( !nodes.isEmpty() )
 
  847void QgsProjectionSelectionTreeWidget::updateFilter()
 
  850  const thread_local QRegularExpression filterRx( QStringLiteral( 
"\\s+" ) );
 
  851  filterTxtCopy.replace( filterRx, QStringLiteral( 
".*" ) );
 
  852  const QRegularExpression re( filterTxtCopy, QRegularExpression::PatternOption::CaseInsensitiveOption );
 
  854  const bool hideDeprecated = cbxHideDeprecated->isChecked();
 
  856  auto filterTreeWidget = [ = ]( QTreeWidget * tree )
 
  858    QTreeWidgetItemIterator itr( tree );
 
  861      if ( ( *itr )->childCount() == 0 ) 
 
  863        if ( hideDeprecated && ( *itr )->data( 0, RoleDeprecated ).toBool() )
 
  865          ( *itr )->setHidden( 
true );
 
  866          if ( ( *itr )->isSelected() )
 
  868            ( *itr )->setSelected( 
false );
 
  869            teProjection->clear();
 
  872        else if ( ( *itr )->text( NameColumn ).contains( re )
 
  873                  || ( *itr )->text( AuthidColumn ).contains( re )
 
  876          ( *itr )->setHidden( 
false );
 
  877          QTreeWidgetItem *parent = ( *itr )->parent();
 
  880            parent->setExpanded( 
true );
 
  881            parent->setHidden( 
false );
 
  882            parent = parent->parent();
 
  887          ( *itr )->setHidden( 
true );
 
  892        ( *itr )->setHidden( 
true );
 
  899  filterTreeWidget( lstRecent );
 
  902  filterTreeWidget( lstCoordinateSystems );
 
  909long QgsProjectionSelectionTreeWidget::getLargestCrsIdMatch( 
const QString &sql )
 
  918  const char   *tail = 
nullptr;
 
  919  sqlite3_stmt *stmt = 
nullptr;
 
  926  if ( QFileInfo::exists( databaseFileName ) ) 
 
  928    result = sqlite3_open_v2( databaseFileName.toUtf8().constData(), &database, SQLITE_OPEN_READONLY, 
nullptr );
 
  935      showDBMissingWarning( databaseFileName );
 
  939    result = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
 
  941    if ( result == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW )
 
  943      QString srsIdString = QString::fromUtf8( ( 
char * )sqlite3_column_text( stmt, 0 ) );
 
  944      srsId = srsIdString.toLong();
 
  946      sqlite3_finalize( stmt );
 
  947      sqlite3_close( database );
 
  954    result = sqlite3_open_v2( mSrsDatabaseFileName.toUtf8().constData(), &database, SQLITE_OPEN_READONLY, 
nullptr );
 
  957      QgsDebugError( QStringLiteral( 
"Can't open * user * database: %1" ).arg( sqlite3_errmsg( database ) ) );
 
  963  result = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
 
  965  if ( result == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW )
 
  967    QString srsIdString = QString::fromUtf8( ( 
char * )sqlite3_column_text( stmt, 0 ) );
 
  968    srsId = srsIdString.toLong();
 
  972  sqlite3_finalize( stmt );
 
  973  sqlite3_close( database );
 
  978void QgsProjectionSelectionTreeWidget::updateBoundsPreview()
 
  980  QTreeWidgetItem *lvi = lstCoordinateSystems->currentItem();
 
  981  if ( !lvi || ( lvi->text( QgisCrsIdColumn ).isEmpty() && !lvi->data( 0, RoleWkt ).isValid() ) )
 
  989  QString extentString = tr( 
"Extent not known" );
 
  990  mAreaCanvas->setPreviewRect( rect );
 
  993    extentString = QStringLiteral( 
"%1, %2, %3, %4" )
 
 1000  QStringList properties;
 
 1002    properties << tr( 
"Geographic (uses latitude and longitude for coordinates)" );
 
 1007  properties << ( currentCrs.
isDynamic() ? tr( 
"Dynamic (relies on a datum which is not plate-fixed)" ) : tr( 
"Static (relies on a datum which is plate-fixed)" ) );
 
 1012    if ( !celestialBody.isEmpty() )
 
 1014      properties << tr( 
"Celestial body: %1" ).arg( celestialBody );
 
 1028      if ( !ensemble.
code().isEmpty() )
 
 1029        id = QStringLiteral( 
"<i>%1</i> (%2:%3)" ).arg( ensemble.
name(), ensemble.
authority(), ensemble.
code() );
 
 1031        id = QStringLiteral( 
"<i>%</i>”" ).arg( ensemble.
name() );
 
 1034        properties << tr( 
"Based on %1, which has a limited accuracy of <b>at best %2 meters</b>." ).arg( 
id ).arg( ensemble.
accuracy() );
 
 1038        properties << tr( 
"Based on %1, which has a limited accuracy." ).arg( 
id );
 
 1048  properties << tr( 
"Method: %1" ).arg( operation.
description() );
 
 1050  const QString propertiesString = QStringLiteral( 
"<dt><b>%1</b></dt><dd><ul><li>%2</li></ul></dd>" ).arg( tr( 
"Properties" ),
 
 1051                                   properties.join( QLatin1String( 
"</li><li>" ) ) );
 
 1053  const QString extentHtml = QStringLiteral( 
"<dt><b>%1</b></dt><dd>%2</dd>" ).arg( tr( 
"Extent" ), extentString );
 
 1054  const QString wktString = QStringLiteral( 
"<dt><b>%1</b></dt><dd><code>%2</code></dd>" ).arg( tr( 
"WKT" ), currentCrs.
toWkt( 
QgsCoordinateReferenceSystem::WKT_PREFERRED, 
true ).replace( 
'\n', QLatin1String( 
"<br>" ) ).replace( 
' ', QLatin1String( 
" " ) ) );
 
 1055  const QString proj4String = QStringLiteral( 
"<dt><b>%1</b></dt><dd><code>%2</code></dd>" ).arg( tr( 
"Proj4" ), currentCrs.
toProj() );
 
 1058  const int smallerPointSize = std::max( font().pointSize() - 1, 8 ); 
 
 1060  const int smallerPointSize = std::max( font().pointSize() - 2, 6 );
 
 1063  teProjection->setText( QStringLiteral( 
"<div style=\"font-size: %1pt\"><h3>%2</h3><dl>" ).arg( smallerPointSize ).arg( selectedName() ) + propertiesString + wktString + proj4String + extentHtml + QStringLiteral( 
"</dl></div>" ) );
 
 1066QStringList QgsProjectionSelectionTreeWidget::authorities()
 
 1069  const char   *tail = 
nullptr;
 
 1070  sqlite3_stmt *stmt = 
nullptr;
 
 1072  int result = sqlite3_open_v2( mSrsDatabaseFileName.toUtf8().constData(), &database, SQLITE_OPEN_READONLY, 
nullptr );
 
 1075    QgsDebugError( QStringLiteral( 
"Can't open * user * database: %1" ).arg( sqlite3_errmsg( database ) ) );
 
 1077    return QStringList();
 
 1080  QString sql = QStringLiteral( 
"select distinct auth_name from tbl_srs" );
 
 1081  result = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
 
 1083  QStringList authorities;
 
 1084  if ( result == SQLITE_OK )
 
 1086    while ( sqlite3_step( stmt ) == SQLITE_ROW )
 
 1088      authorities << QString::fromUtf8( ( 
char * )sqlite3_column_text( stmt, 0 ) );
 
 1094  sqlite3_finalize( stmt );
 
 1095  sqlite3_close( database );
 
 1100QString QgsProjectionSelectionTreeWidget::sqlSafeString( 
const QString &theSQL )
 const 
 1102  QString retval = theSQL;
 
 1103  retval.replace( 
'\\', QLatin1String( 
"\\\\" ) );
 
 1104  retval.replace( 
'\"', QLatin1String( 
"\\\"" ) );
 
 1105  retval.replace( 
'\'', QLatin1String( 
"\\'" ) );
 
 1106  retval.replace( 
'%', QLatin1String( 
"\\%" ) );
 
 1110void QgsProjectionSelectionTreeWidget::showDBMissingWarning( 
const QString &fileName )
 
 1113  QMessageBox::critical( 
this, tr( 
"Resource Location Error" ),
 
 1114                         tr( 
"Error reading database file from: \n %1\n" 
 1115                             "Because of this the projection selector will not work…" )
 
 1122  if ( lstRecent->topLevelItemCount() == 0 )
 
 1128  if ( QMessageBox::question( 
this, tr( 
"Clear Recent CRS" ),
 
 1129                              tr( 
"Are you sure you want to clear the list of recently used coordinate reference system?" ),
 
 1130                              QMessageBox::Yes | QMessageBox::No ) != QMessageBox::Yes )
 
 1139void QgsProjectionSelectionTreeWidget::removeRecentCrsItem( QTreeWidgetItem *item )
 
 1141  int index = lstRecent->indexOfTopLevelItem( item );
 
 1146  lstRecent->takeTopLevelItem( index );
 
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
 
static QgsCoordinateReferenceSystemRegistry * coordinateReferenceSystemRegistry()
Returns the application's coordinate reference system (CRS) registry, which handles known CRS definit...
 
static QString qgisUserDatabaseFilePath()
Returns the path to the user qgis.db file.
 
static QString srsDatabaseFilePath()
Returns the path to the srs.db file.
 
Contains details of a custom (user defined) CRS.
 
QList< QgsCoordinateReferenceSystemRegistry::UserCrsDetails > userCrsList() const
Returns a list containing the details of all registered custom (user-defined) CRSes.
 
This class represents a coordinate reference system (CRS).
 
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
 
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
 
Q_GADGET Qgis::DistanceUnit mapUnits
 
QgsRectangle bounds() const
Returns the approximate bounds for the region the CRS is usable within.
 
QString toProj() const
Returns a Proj string representation of this CRS.
 
static void pushRecentCoordinateReferenceSystem(const QgsCoordinateReferenceSystem &crs)
Pushes a recently used CRS to the top of the recent CRS list.
 
QgsDatumEnsemble datumEnsemble() const SIP_THROW(QgsNotSupportedException)
Attempts to retrieve datum ensemble details from the CRS.
 
static void removeRecentCoordinateReferenceSystem(const QgsCoordinateReferenceSystem &crs)
Removes a CRS from the list of recently used CRS.
 
bool isDynamic() const
Returns true if the CRS is a dynamic CRS.
 
static QgsCoordinateReferenceSystem fromProj(const QString &proj)
Creates a CRS from a proj style formatted string.
 
static QList< QgsCoordinateReferenceSystem > recentCoordinateReferenceSystems()
Returns a list of recently used CRS.
 
static void clearRecentCoordinateReferenceSystems()
Cleans the list of recently used CRS.
 
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
 
static QgsCoordinateReferenceSystem fromWkt(const QString &wkt)
Creates a CRS from a WKT spatial ref sys definition string.
 
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
 
QgsProjOperation operation() const
Returns information about the PROJ operation associated with the coordinate reference system,...
 
long srsid() const
Returns the internal CRS ID, if available.
 
QString celestialBodyName() const SIP_THROW(QgsNotSupportedException)
Attempts to retrieve the name of the celestial body associated with the CRS (e.g.
 
Contains information about a datum ensemble.
 
QString code() const
Identification code, e.g.
 
QString authority() const
Authority name, e.g.
 
bool isValid() const
Returns true if the datum ensemble is a valid object, or false if it is a null/invalid object.
 
QString name() const
Display name of datum ensemble.
 
double accuracy() const
Positional accuracy (in meters).
 
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
 
Custom exception class which is raised when an operation is not supported.
 
Contains information about a PROJ operation.
 
QString description() const
Description.
 
A rectangle specified with double values.
 
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
 
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
 
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
 
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
 
double area() const SIP_HOLDGIL
Returns the area of the rectangle.
 
This class is a composition of two QSettings instances:
 
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
 
static QString qRegExpEscape(const QString &string)
Returns an escaped string matching the behavior of QRegExp::escape.
 
static Q_INVOKABLE QString toString(Qgis::DistanceUnit unit)
Returns a translated string representing a distance unit.
 
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
 
const int USER_CRS_START_ID
Magick number that determines whether a projection crsid is a system (srs.db) or user (~/....
 
#define QgsDebugMsgLevel(str, level)
 
#define QgsDebugError(str)
 
const QgsCoordinateReferenceSystem & crs