31#include <QRegularExpression> 
   51#include <proj_experimental.h> 
   54#include <ogr_srs_api.h> 
   66bool QgsCoordinateReferenceSystem::sDisableSrIdCache = 
false;
 
   70bool QgsCoordinateReferenceSystem::sDisableOgcCache = 
false;
 
   74bool QgsCoordinateReferenceSystem::sDisableProjCache = 
false;
 
   78bool QgsCoordinateReferenceSystem::sDisableWktCache = 
false;
 
   82bool QgsCoordinateReferenceSystem::sDisableSrsIdCache = 
false;
 
   86bool QgsCoordinateReferenceSystem::sDisableStringCache = 
false;
 
   95    if ( 
const char *proj4src = proj_as_proj_string( 
QgsProjContext::get(), boundCrs.get(), PJ_PROJ_4, 
nullptr ) )
 
   97      return QString( proj4src );
 
 
  114  d = 
new QgsCoordinateReferenceSystemPrivate();
 
 
  120  d = 
new QgsCoordinateReferenceSystemPrivate();
 
 
  128  , mValidationHint( srs.mValidationHint )
 
  129  , mNativeFormat( srs.mNativeFormat )
 
 
  136  mValidationHint = srs.mValidationHint;
 
  137  mNativeFormat = srs.mNativeFormat;
 
 
  147  const auto constDbs = dbs;
 
  148  for ( 
const QString &db : constDbs )
 
  150    QFileInfo myInfo( db );
 
  151    if ( !myInfo.exists() )
 
  161    int result = openDatabase( db, database );
 
  162    if ( result != SQLITE_OK )
 
  168    QString sql = QStringLiteral( 
"select srs_id from tbl_srs" );
 
  170    statement = database.
prepare( sql, rc );
 
  174      int ret = statement.
step();
 
  176      if ( ret == SQLITE_DONE )
 
  182      if ( ret == SQLITE_ROW )
 
  188        QgsMessageLog::logMessage( QObject::tr( 
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr( 
"SpatiaLite" ) );
 
  193  std::sort( results.begin(), results.end() );
 
 
  249  if ( horizontalObj && verticalObj )
 
  256    QStringList formattedErrorList;
 
  257    for ( 
const QString &rawError : std::as_const( errors ) )
 
  259      QString formattedError = rawError;
 
  260      formattedError.replace( QLatin1String( 
"proj_create_compound_crs: " ), QString() );
 
  261      formattedErrorList.append( formattedError );
 
  263    error = formattedErrorList.join( 
'\n' );
 
 
  290      QgsDebugError( QStringLiteral( 
"Unexpected case reached!" ) );
 
 
  297  if ( definition.isEmpty() )
 
  301  if ( !sDisableStringCache )
 
  303    QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sStringCache()->constFind( definition );
 
  304    if ( crsIt != sStringCache()->constEnd() )
 
  307      *
this = crsIt.value();
 
  314  const thread_local QRegularExpression reCrsId( QStringLiteral( 
"^(epsg|esri|osgeo|ignf|ogc|nkg|zangi|iau_2015|iau2000|postgis|internal|user)\\:(\\w+)$" ), QRegularExpression::CaseInsensitiveOption );
 
  315  QRegularExpressionMatch match = reCrsId.match( definition );
 
  316  if ( match.capturedStart() == 0 )
 
  318    QString authName = match.captured( 1 ).toLower();
 
  319    if ( authName == QLatin1String( 
"epsg" ) )
 
  323    else if ( authName == QLatin1String( 
"postgis" ) )
 
  325      const long id = match.captured( 2 ).toLong();
 
  330    else if ( authName == QLatin1String( 
"esri" )
 
  331              || authName == QLatin1String( 
"osgeo" )
 
  332              || authName == QLatin1String( 
"ignf" )
 
  333              || authName == QLatin1String( 
"zangi" )
 
  334              || authName == QLatin1String( 
"iau2000" )
 
  335              || authName == QLatin1String( 
"ogc" )
 
  336              || authName == QLatin1String( 
"nkg" )
 
  337              || authName == QLatin1String( 
"iau_2015" )
 
  344      const long id = match.captured( 2 ).toLong();
 
  352    const thread_local QRegularExpression reCrsStr( QStringLiteral( 
"^(?:(wkt|proj4|proj)\\:)?(.+)$" ), QRegularExpression::CaseInsensitiveOption );
 
  353    match = reCrsStr.match( definition );
 
  354    if ( match.capturedStart() == 0 )
 
  356      if ( match.captured( 1 ).startsWith( QLatin1String( 
"proj" ), Qt::CaseInsensitive ) )
 
  368  if ( !sDisableStringCache )
 
  369    sStringCache()->insert( definition, *
this );
 
 
  375  if ( definition.isEmpty() )
 
  381  if ( OSRSetFromUserInput( 
crs, definition.toLocal8Bit().constData() ) == OGRERR_NONE )
 
  384    OSRDestroySpatialReference( 
crs );
 
 
  394  const char *configOld = CPLGetConfigOption( 
"GDAL_FIX_ESRI_WKT", 
"" );
 
  395  const char *configNew = 
"GEOGCS";
 
  397  if ( strcmp( configOld, 
"" ) == 0 )
 
  399    CPLSetConfigOption( 
"GDAL_FIX_ESRI_WKT", configNew );
 
  400    if ( strcmp( configNew, CPLGetConfigOption( 
"GDAL_FIX_ESRI_WKT", 
"" ) ) != 0 )
 
  402                          .arg( configNew, CPLGetConfigOption( 
"GDAL_FIX_ESRI_WKT", 
"" ) ) );
 
  403    QgsDebugMsgLevel( QStringLiteral( 
"set GDAL_FIX_ESRI_WKT : %1" ).arg( configNew ), 4 );
 
  407    QgsDebugMsgLevel( QStringLiteral( 
"GDAL_FIX_ESRI_WKT was already set : %1" ).arg( configNew ), 4 );
 
 
  417  if ( !sDisableOgcCache )
 
  419    QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sOgcCache()->constFind( 
crs );
 
  420    if ( crsIt != sOgcCache()->constEnd() )
 
  423      *
this = crsIt.value();
 
  429  QString wmsCrs = 
crs;
 
  434  const QString authorityLower = authority.toLower();
 
  436       ( authorityLower == QLatin1String( 
"user" ) ||
 
  437         authorityLower == QLatin1String( 
"custom" ) ||
 
  438         authorityLower == QLatin1String( 
"qgis" ) ) )
 
  443      if ( !sDisableOgcCache )
 
  444        sOgcCache()->insert( 
crs, *
this );
 
  450    wmsCrs = authority + 
':' + code;
 
  454  const QString legacyKey = wmsCrs.toLower();
 
  457    if ( it.key().compare( legacyKey, Qt::CaseInsensitive ) == 0 )
 
  459      const QStringList parts = it.key().split( 
':' );
 
  460      const QString auth = parts.at( 0 );
 
  461      const QString code = parts.at( 1 );
 
  462      if ( loadFromAuthCode( auth, code ) )
 
  465        if ( !sDisableOgcCache )
 
  466          sOgcCache()->insert( 
crs, *
this );
 
  475    if ( !sDisableOgcCache )
 
  476      sOgcCache()->insert( 
crs, *
this );
 
  481  if ( wmsCrs.compare( QLatin1String( 
"CRS:27" ), Qt::CaseInsensitive ) == 0 ||
 
  482       wmsCrs.compare( QLatin1String( 
"OGC:CRS27" ), Qt::CaseInsensitive ) == 0 )
 
  489  if ( wmsCrs.compare( QLatin1String( 
"CRS:83" ), Qt::CaseInsensitive ) == 0 ||
 
  490       wmsCrs.compare( QLatin1String( 
"OGC:CRS83" ), Qt::CaseInsensitive ) == 0 )
 
  497  if ( wmsCrs.compare( QLatin1String( 
"CRS:84" ), Qt::CaseInsensitive ) == 0 ||
 
  498       wmsCrs.compare( QLatin1String( 
"OGC:CRS84" ), Qt::CaseInsensitive ) == 0 )
 
  502      d->mAxisInverted = 
false;
 
  503      d->mAxisInvertedDirty = 
false;
 
  507    if ( !sDisableOgcCache )
 
  508      sOgcCache()->insert( 
crs, *
this );
 
  515  if ( !authority.isEmpty() && !code.isEmpty() && loadFromAuthCode( authority, code ) )
 
  518    if ( !sDisableOgcCache )
 
  519      sOgcCache()->insert( 
crs, *
this );
 
  524  if ( !sDisableOgcCache )
 
 
  534  if ( d->mIsValid || !sCustomSrsValidation )
 
  538  if ( sCustomSrsValidation )
 
  539    sCustomSrsValidation( *
this );
 
 
  545  if ( !sDisableSrIdCache )
 
  547    QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrIdCache()->constFind( 
id );
 
  548    if ( crsIt != sSrIdCache()->constEnd() )
 
  551      *
this = crsIt.value();
 
  560    if ( it.value().endsWith( QStringLiteral( 
",%1" ).arg( 
id ) ) )
 
  562      const QStringList parts = it.key().split( 
':' );
 
  563      const QString auth = parts.at( 0 );
 
  564      const QString code = parts.at( 1 );
 
  565      if ( loadFromAuthCode( auth, code ) )
 
  568        if ( !sDisableSrIdCache )
 
  569          sSrIdCache()->insert( 
id, *
this );
 
  579  if ( !sDisableSrIdCache )
 
  580    sSrIdCache()->insert( 
id, *
this );
 
 
  588  if ( !sDisableSrsIdCache )
 
  590    QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrsIdCache()->constFind( 
id );
 
  591    if ( crsIt != sSrsIdCache()->constEnd() )
 
  594      *
this = crsIt.value();
 
  603    if ( it.value().startsWith( QString::number( 
id ) + 
',' ) )
 
  605      const QStringList parts = it.key().split( 
':' );
 
  606      const QString auth = parts.at( 0 );
 
  607      const QString code = parts.at( 1 );
 
  608      if ( loadFromAuthCode( auth, code ) )
 
  611        if ( !sDisableSrsIdCache )
 
  612          sSrsIdCache()->insert( 
id, *
this );
 
  620                                  QStringLiteral( 
"srs_id" ), QString::number( 
id ) );
 
  623  if ( !sDisableSrsIdCache )
 
  624    sSrsIdCache()->insert( 
id, *
this );
 
 
  628bool QgsCoordinateReferenceSystem::loadFromDatabase( 
const QString &db, 
const QString &expression, 
const QString &value )
 
  632  QgsDebugMsgLevel( 
"load CRS from " + db + 
" where " + expression + 
" is " + value, 3 );
 
  634  d->mWktPreferred.clear();
 
  636  QFileInfo myInfo( db );
 
  637  if ( !myInfo.exists() )
 
  647  myResult = openDatabase( db, database );
 
  648  if ( myResult != SQLITE_OK )
 
  665  QString mySql = 
"select srs_id,description,projection_acronym," 
  666                  "ellipsoid_acronym,parameters,srid,auth_name||':'||auth_id,is_geo,wkt " 
  668  statement = database.
prepare( mySql, myResult );
 
  671  if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
 
  676    d->mEllipsoidAcronym.clear();
 
  678    d->mWktPreferred.clear();
 
  681    d->mIsGeographic = statement.
columnAsText( 7 ).toInt() != 0;
 
  683    d->mAxisInvertedDirty = 
true;
 
  685    if ( d->mSrsId >= 
USER_CRS_START_ID && ( d->mAuthId.isEmpty() || d->mAuthId == QChar( 
':' ) ) )
 
  687      d->mAuthId = QStringLiteral( 
"USER:%1" ).arg( d->mSrsId );
 
  689    else if ( !d->mAuthId.startsWith( QLatin1String( 
"USER:" ), Qt::CaseInsensitive ) )
 
  691      QStringList parts = d->mAuthId.split( 
':' );
 
  692      QString auth = parts.at( 0 );
 
  693      QString code = parts.at( 1 );
 
  700      d->mIsValid = d->hasPj();
 
  706      if ( !wkt.isEmpty() )
 
  714        setProjString( d->mProj4 );
 
  724void QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread( 
PJ_CONTEXT *pj_context )
 
  731  if ( !sDisableSrIdCache )
 
  734    if ( !sDisableSrIdCache )
 
  736      for ( 
auto it = sSrIdCache()->begin(); it != sSrIdCache()->end(); )
 
  738        auto &v = it.value();
 
  739        if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
 
  740          it = sSrIdCache()->erase( it );
 
  746  if ( !sDisableOgcCache )
 
  749    if ( !sDisableOgcCache )
 
  751      for ( 
auto it = sOgcCache()->begin(); it != sOgcCache()->end(); )
 
  753        auto &v = it.value();
 
  754        if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
 
  755          it = sOgcCache()->erase( it );
 
  761  if ( !sDisableProjCache )
 
  764    if ( !sDisableProjCache )
 
  766      for ( 
auto it = sProj4Cache()->begin(); it != sProj4Cache()->end(); )
 
  768        auto &v = it.value();
 
  769        if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
 
  770          it = sProj4Cache()->erase( it );
 
  776  if ( !sDisableWktCache )
 
  779    if ( !sDisableWktCache )
 
  781      for ( 
auto it = sWktCache()->begin(); it != sWktCache()->end(); )
 
  783        auto &v = it.value();
 
  784        if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
 
  785          it = sWktCache()->erase( it );
 
  791  if ( !sDisableSrsIdCache )
 
  794    if ( !sDisableSrsIdCache )
 
  796      for ( 
auto it = sSrsIdCache()->begin(); it != sSrsIdCache()->end(); )
 
  798        auto &v = it.value();
 
  799        if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
 
  800          it = sSrsIdCache()->erase( it );
 
  806  if ( !sDisableStringCache )
 
  809    if ( !sDisableStringCache )
 
  811      for ( 
auto it = sStringCache()->begin(); it != sStringCache()->end(); )
 
  813        auto &v = it.value();
 
  814        if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
 
  815          it = sStringCache()->erase( it );
 
  825  if ( d->mAxisInvertedDirty )
 
  828    d->mAxisInvertedDirty = 
false;
 
  831  return d->mAxisInverted;
 
 
  845  const thread_local QMap< Qgis::CrsAxisDirection, QString > mapping =
 
  888  QList< Qgis::CrsAxisDirection > res;
 
  889  const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
 
  892    res.reserve( axisCount );
 
  894    for ( 
int i = 0; i < axisCount; ++i )
 
  896      const char *outDirection = 
nullptr;
 
  897      proj_cs_get_axis_info( context, pjCs.get(), i,
 
  907      const thread_local QRegularExpression rx( QStringLiteral( 
"([^\\s]+).*" ) );
 
  908      const QRegularExpressionMatch match = rx.match( QString( outDirection ) );
 
  909      if ( !match.hasMatch() )
 
  912      const QString direction = match.captured( 1 );
 
  914      for ( 
auto it = mapping.constBegin(); it != mapping.constEnd(); ++it )
 
  916        if ( it.value().compare( direction, Qt::CaseInsensitive ) == 0 )
 
 
  931  return createFromWktInternal( wkt, QString() );
 
 
  934bool QgsCoordinateReferenceSystem::createFromWktInternal( 
const QString &wkt, 
const QString &description )
 
  942  if ( !sDisableWktCache )
 
  944    QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sWktCache()->constFind( wkt );
 
  945    if ( crsIt != sWktCache()->constEnd() )
 
  948      *
this = crsIt.value();
 
  950      if ( !
description.isEmpty() && d->mDescription.isEmpty() )
 
  955        sWktCache()->insert( wkt, *
this );
 
  964  d->mWktPreferred.clear();
 
  967    QgsDebugMsgLevel( QStringLiteral( 
"theWkt is uninitialized, operation failed" ), 4 );
 
  972  QgsCoordinateReferenceSystem::RecordMap record = getRecord( 
"select * from tbl_srs where wkt=" + 
QgsSqliteUtils::quotedString( wkt ) + 
" order by deprecated" );
 
  973  if ( !record.empty() )
 
  975    long srsId = record[QStringLiteral( 
"srs_id" )].toLong();
 
  988    if ( d->mSrsId == 0 )
 
  991      long id = matchToUserCrs();
 
 1000  if ( !sDisableWktCache )
 
 1001    sWktCache()->insert( wkt, *
this );
 
 1019  if ( projString.isEmpty() )
 
 1024  if ( projString.trimmed().isEmpty() )
 
 1026    d->mIsValid = 
false;
 
 1028    d->mWktPreferred.clear();
 
 1033  if ( !sDisableProjCache )
 
 1035    QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sProj4Cache()->constFind( projString );
 
 1036    if ( crsIt != sProj4Cache()->constEnd() )
 
 1039      *
this = crsIt.value();
 
 1053  QString myProj4String = projString.trimmed();
 
 1054  myProj4String.remove( QStringLiteral( 
"+type=crs" ) );
 
 1055  myProj4String = myProj4String.trimmed();
 
 1057  d->mIsValid = 
false;
 
 1058  d->mWktPreferred.clear();
 
 1063    const QString projCrsString = myProj4String + ( myProj4String.contains( QStringLiteral( 
"+type=crs" ) ) ? QString() : QStringLiteral( 
" +type=crs" ) );
 
 1071        const QString 
authid = QStringLiteral( 
"%1:%2" ).arg( authName, authCode );
 
 1075          if ( !sDisableProjCache )
 
 1076            sProj4Cache()->insert( projString, *
this );
 
 1083    QgsCoordinateReferenceSystem::RecordMap myRecord = getRecord( 
"select * from tbl_srs where parameters=" + 
QgsSqliteUtils::quotedString( myProj4String ) + 
" order by deprecated" );
 
 1085    if ( !myRecord.empty() )
 
 1087      id = myRecord[QStringLiteral( 
"srs_id" )].toLong();
 
 1096      setProjString( myProj4String );
 
 1099      id = matchToUserCrs();
 
 1108    setProjString( myProj4String );
 
 1112  if ( !sDisableProjCache )
 
 1113    sProj4Cache()->insert( projString, *
this );
 
 
 1119QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord( 
const QString &sql )
 
 1121  QString myDatabaseFileName;
 
 1122  QgsCoordinateReferenceSystem::RecordMap myMap;
 
 1123  QString myFieldName;
 
 1124  QString myFieldValue;
 
 1131  QFileInfo myInfo( myDatabaseFileName );
 
 1132  if ( !myInfo.exists() )
 
 1134    QgsDebugError( 
"failed : " + myDatabaseFileName + 
" does not exist!" );
 
 1139  myResult = openDatabase( myDatabaseFileName, database );
 
 1140  if ( myResult != SQLITE_OK )
 
 1145  statement = database.
prepare( sql, myResult );
 
 1147  if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
 
 1151    for ( 
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
 
 1153      myFieldName = statement.
columnName( myColNo );
 
 1155      myMap[myFieldName] = myFieldValue;
 
 1157    if ( statement.
step() != SQLITE_DONE )
 
 1159      QgsDebugMsgLevel( QStringLiteral( 
"Multiple records found in srs.db" ), 4 );
 
 1168  if ( myMap.empty() )
 
 1171    QFileInfo myFileInfo;
 
 1172    myFileInfo.setFile( myDatabaseFileName );
 
 1173    if ( !myFileInfo.exists() )
 
 1175      QgsDebugError( QStringLiteral( 
"user qgis.db not found" ) );
 
 1180    myResult = openDatabase( myDatabaseFileName, database );
 
 1181    if ( myResult != SQLITE_OK )
 
 1186    statement = database.
prepare( sql, myResult );
 
 1188    if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
 
 1192      for ( 
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
 
 1194        myFieldName = statement.
columnName( myColNo );
 
 1196        myMap[myFieldName] = myFieldValue;
 
 1199      if ( statement.
step() != SQLITE_DONE )
 
 1201        QgsDebugMsgLevel( QStringLiteral( 
"Multiple records found in srs.db" ), 4 );
 
 1232  if ( d->mDescription.isNull() )
 
 1238    return d->mDescription;
 
 
 1245  if ( !
authid().isEmpty() )
 
 1255    id = 
isValid() ? QObject::tr( 
"Custom CRS" ) : QObject::tr( 
"Unknown CRS" );
 
 1257    id = QObject::tr( 
"Custom CRS: %1" ).arg(
 
 1260  else if ( !
toProj().isEmpty() )
 
 1263  if ( !
id.isEmpty() && !std::isnan( d->mCoordinateEpoch ) )
 
 1271  if ( d->mProjectionAcronym.isNull() )
 
 1277    return d->mProjectionAcronym;
 
 
 1283  if ( d->mEllipsoidAcronym.isNull() )
 
 1285    if ( 
PJ *obj = d->threadLocalProjObject() )
 
 1290        const QString ellipsoidAuthName( proj_get_id_auth_name( ellipsoid.get(), 0 ) );
 
 1291        const QString ellipsoidAuthCode( proj_get_id_code( ellipsoid.get(), 0 ) );
 
 1292        if ( !ellipsoidAuthName.isEmpty() && !ellipsoidAuthCode.isEmpty() )
 
 1293          d->mEllipsoidAcronym = QStringLiteral( 
"%1:%2" ).arg( ellipsoidAuthName, ellipsoidAuthCode );
 
 1296          double semiMajor, semiMinor, invFlattening;
 
 1297          int semiMinorComputed = 0;
 
 1298          if ( proj_ellipsoid_get_parameters( 
QgsProjContext::get(), ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
 
 1300            d->mEllipsoidAcronym = QStringLiteral( 
"PARAMETER:%1:%2" ).arg( 
qgsDoubleToString( semiMajor ),
 
 1305            d->mEllipsoidAcronym.clear();
 
 1310    return d->mEllipsoidAcronym;
 
 1314    return d->mEllipsoidAcronym;
 
 
 1328  if ( d->mProj4.isEmpty() )
 
 1330    if ( 
PJ *obj = d->threadLocalProjObject() )
 
 1336  return d->mProj4.trimmed();
 
 
 1342  switch ( d->mProjType )
 
 1344    case PJ_TYPE_UNKNOWN:
 
 1347    case PJ_TYPE_ELLIPSOID:
 
 1348    case PJ_TYPE_PRIME_MERIDIAN:
 
 1349    case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
 
 1350    case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
 
 1351    case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
 
 1352    case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
 
 1353    case PJ_TYPE_DATUM_ENSEMBLE:
 
 1354    case PJ_TYPE_CONVERSION:
 
 1355    case PJ_TYPE_TRANSFORMATION:
 
 1356    case PJ_TYPE_CONCATENATED_OPERATION:
 
 1357    case PJ_TYPE_OTHER_COORDINATE_OPERATION:
 
 1358    case PJ_TYPE_TEMPORAL_DATUM:
 
 1359    case PJ_TYPE_ENGINEERING_DATUM:
 
 1360    case PJ_TYPE_PARAMETRIC_DATUM:
 
 1364    case PJ_TYPE_GEOGRAPHIC_CRS:
 
 1368    case PJ_TYPE_GEODETIC_CRS:
 
 1370    case PJ_TYPE_GEOCENTRIC_CRS:
 
 1372    case PJ_TYPE_GEOGRAPHIC_2D_CRS:
 
 1374    case PJ_TYPE_GEOGRAPHIC_3D_CRS:
 
 1376    case PJ_TYPE_VERTICAL_CRS:
 
 1378    case PJ_TYPE_PROJECTED_CRS:
 
 1380    case PJ_TYPE_COMPOUND_CRS:
 
 1382    case PJ_TYPE_TEMPORAL_CRS:
 
 1384    case PJ_TYPE_ENGINEERING_CRS:
 
 1386    case PJ_TYPE_BOUND_CRS:
 
 1388    case PJ_TYPE_OTHER_CRS:
 
 1390#if PROJ_VERSION_MAJOR>9 || (PROJ_VERSION_MAJOR==9 && PROJ_VERSION_MINOR>=2) 
 1391    case PJ_TYPE_DERIVED_PROJECTED_CRS:
 
 1393    case PJ_TYPE_COORDINATE_METADATA:
 
 
 1407  return proj_is_deprecated( pj );
 
 
 1412  return d->mIsGeographic;
 
 
 1430#if PROJ_VERSION_MAJOR>8 || (PROJ_VERSION_MAJOR==8 && PROJ_VERSION_MINOR>=1) 
 1433  return QString( proj_get_celestial_body_name( context, pj ) );
 
 1435  throw QgsNotSupportedException( QObject::tr( 
"Retrieving celestial body requires a QGIS build based on PROJ 8.1 or later" ) );
 
 
 1441  if ( d->mCoordinateEpoch == epoch )
 
 1447  d->mCoordinateEpoch = epoch;
 
 1448  d->setPj( std::move( clone ) );
 
 
 1453  return d->mCoordinateEpoch;
 
 
 1465#if PROJ_VERSION_MAJOR>=8 
 1473  res.mName = QString( proj_get_name( ensemble.get() ) );
 
 1474  res.mAuthority = QString( proj_get_id_auth_name( ensemble.get(), 0 ) );
 
 1475  res.mCode = QString( proj_get_id_code( ensemble.get(), 0 ) );
 
 1476  res.mRemarks = QString( proj_get_remarks( ensemble.get() ) );
 
 1477  res.mScope = QString( proj_get_scope( ensemble.get() ) );
 
 1478  res.mAccuracy = proj_datum_ensemble_get_accuracy( context, ensemble.get() );
 
 1480  const int memberCount = proj_datum_ensemble_get_member_count( context, ensemble.get() );
 
 1481  for ( 
int i = 0; i < memberCount; ++i )
 
 1488    details.mName = QString( proj_get_name( member.get() ) );
 
 1489    details.mAuthority = QString( proj_get_id_auth_name( member.get(), 0 ) );
 
 1490    details.mCode = QString( proj_get_id_code( member.get(), 0 ) );
 
 1491    details.mRemarks = QString( proj_get_remarks( member.get() ) );
 
 1492    details.mScope = QString( proj_get_scope( member.get() ) );
 
 1494    res.mMembers << details;
 
 1498  throw QgsNotSupportedException( QObject::tr( 
"Calculating datum ensembles requires a QGIS build based on PROJ 8.0 or later" ) );
 
 
 1507  QString projString = 
toProj();
 
 1508  projString.replace( QLatin1String( 
"+type=crs" ), QString() );
 
 1511  if ( !transformation )
 
 1514  PJ_COORD coord = proj_coord( 0, 0, 0, HUGE_VAL );
 
 1515  coord.uv.u = point.
x() * M_PI / 180.0;
 
 1516  coord.uv.v = point.
y() * M_PI / 180.0;
 
 1518  proj_errno_reset( transformation.get() );
 
 1519  const PJ_FACTORS pjFactors = proj_factors( transformation.get(), coord );
 
 1520  if ( proj_errno( transformation.get() ) )
 
 1525  res.mIsValid = 
true;
 
 1526  res.mMeridionalScale = pjFactors.meridional_scale;
 
 1527  res.mParallelScale = pjFactors.parallel_scale;
 
 1528  res.mArealScale = pjFactors.areal_scale;
 
 1529  res.mAngularDistortion = pjFactors.angular_distortion;
 
 1530  res.mMeridianParallelAngle = pjFactors.meridian_parallel_angle * 180 / M_PI;
 
 1531  res.mMeridianConvergence = pjFactors.meridian_convergence * 180 / M_PI;
 
 1532  res.mTissotSemimajor = pjFactors.tissot_semimajor;
 
 1533  res.mTissotSemiminor = pjFactors.tissot_semiminor;
 
 1534  res.mDxDlam = pjFactors.dx_dlam;
 
 1535  res.mDxDphi = pjFactors.dx_dphi;
 
 1536  res.mDyDlam = pjFactors.dy_dlam;
 
 1537  res.mDyDphi = pjFactors.dy_dphi;
 
 
 1549  QString projString = 
toProj();
 
 1550  projString.replace( QLatin1String( 
"+type=crs" ), QString() );
 
 1551  if ( projString.isEmpty() )
 
 1555  if ( !transformation )
 
 1558  PJ_PROJ_INFO info = proj_pj_info( transformation.get() );
 
 
 1573  return d->mMapUnits;
 
 
 1581  PJ *obj = d->threadLocalProjObject();
 
 1586  double southLat = 0;
 
 1588  double northLat = 0;
 
 1591                              &westLon, &southLat, &eastLon, &northLat, 
nullptr ) )
 
 
 1606  const auto parts { 
authid().split( 
':' ) };
 
 1607  if ( parts.length() == 2 )
 
 1609    if ( parts[0] == QLatin1String( 
"EPSG" ) )
 
 1610      return  QStringLiteral( 
"http://www.opengis.net/def/crs/EPSG/0/%1" ).arg( parts[1] ) ;
 
 1611    else if ( parts[0] == QLatin1String( 
"OGC" ) )
 
 1613      return  QStringLiteral( 
"http://www.opengis.net/def/crs/OGC/1.3/%1" ).arg( parts[1] ) ;
 
 
 1629  const auto parts { 
authid().split( 
':' ) };
 
 1630  if ( parts.length() == 2 )
 
 1632    if ( parts[0] == QLatin1String( 
"EPSG" ) )
 
 1633      return  QStringLiteral( 
"urn:ogc:def:crs:EPSG:0:%1" ).arg( parts[1] );
 
 1634    else if ( parts[0] == QLatin1String( 
"OGC" ) )
 
 1636      return  QStringLiteral( 
"urn:ogc:def:crs:OGC:1.3:%1" ).arg( parts[1] );
 
 
 1667void QgsCoordinateReferenceSystem::setProjString( 
const QString &proj4String )
 
 1670  d->mProj4 = proj4String;
 
 1671  d->mWktPreferred.clear();
 
 1674  QString trimmed = proj4String.trimmed();
 
 1676  trimmed += QLatin1String( 
" +type=crs" );
 
 1686    const int errNo = proj_context_errno( ctx );
 
 1687    QgsDebugError( QStringLiteral( 
"proj string rejected: %1" ).arg( proj_errno_string( errNo ) ) );
 
 1689    d->mIsValid = 
false;
 
 1693    d->mEllipsoidAcronym.clear();
 
 1700bool QgsCoordinateReferenceSystem::setWktString( 
const QString &wkt )
 
 1703  d->mIsValid = 
false;
 
 1704  d->mWktPreferred.clear();
 
 1706  PROJ_STRING_LIST warnings = 
nullptr;
 
 1707  PROJ_STRING_LIST grammarErrors = 
nullptr;
 
 1715    QgsDebugMsgLevel( QStringLiteral( 
"\n---------------------------------------------------------------" ), 2 );
 
 1716    QgsDebugMsgLevel( QStringLiteral( 
"This CRS could *** NOT *** be set from the supplied Wkt " ), 2 );
 
 1718    for ( 
auto iter = warnings; iter && *iter; ++iter )
 
 1720    for ( 
auto iter = grammarErrors; iter && *iter; ++iter )
 
 1722    QgsDebugMsgLevel( QStringLiteral( 
"---------------------------------------------------------------\n" ), 2 );
 
 1724  proj_string_list_destroy( warnings );
 
 1725  proj_string_list_destroy( grammarErrors );
 
 1731    if ( !sDisableWktCache )
 
 1732      sWktCache()->insert( wkt, *
this );
 
 1739    QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
 
 1740    QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
 
 1742    if ( authName.isEmpty() || authCode.isEmpty() )
 
 1748    if ( !authName.isEmpty() && !authCode.isEmpty() )
 
 1750      if ( loadFromAuthCode( authName, authCode ) )
 
 1753        if ( !sDisableWktCache )
 
 1754          sWktCache()->insert( wkt, *
this );
 
 1762      d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
 
 1770void QgsCoordinateReferenceSystem::setMapUnits()
 
 1797  if ( !coordinateSystem )
 
 1803  const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
 
 1804  if ( axisCount > 0 )
 
 1806    const char *outUnitName = 
nullptr;
 
 1808    proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
 
 1817    const QString unitName( outUnitName );
 
 1821    if ( unitName.compare( QLatin1String( 
"degree" ), Qt::CaseInsensitive ) == 0 ||
 
 1822         unitName.compare( QLatin1String( 
"degree minute second" ), Qt::CaseInsensitive ) == 0 ||
 
 1823         unitName.compare( QLatin1String( 
"degree minute second hemisphere" ), Qt::CaseInsensitive ) == 0 ||
 
 1824         unitName.compare( QLatin1String( 
"degree minute" ), Qt::CaseInsensitive ) == 0 ||
 
 1825         unitName.compare( QLatin1String( 
"degree hemisphere" ), Qt::CaseInsensitive ) == 0 ||
 
 1826         unitName.compare( QLatin1String( 
"degree minute hemisphere" ), Qt::CaseInsensitive ) == 0 ||
 
 1827         unitName.compare( QLatin1String( 
"hemisphere degree" ), Qt::CaseInsensitive ) == 0 ||
 
 1828         unitName.compare( QLatin1String( 
"hemisphere degree minute" ), Qt::CaseInsensitive ) == 0 ||
 
 1829         unitName.compare( QLatin1String( 
"hemisphere degree minute second" ), Qt::CaseInsensitive ) == 0 ||
 
 1830         unitName.compare( QLatin1String( 
"degree (supplier to define representation)" ), Qt::CaseInsensitive ) == 0 )
 
 1832    else if ( unitName.compare( QLatin1String( 
"metre" ), Qt::CaseInsensitive ) == 0
 
 1833              || unitName.compare( QLatin1String( 
"m" ), Qt::CaseInsensitive ) == 0
 
 1834              || unitName.compare( QLatin1String( 
"meter" ), Qt::CaseInsensitive ) == 0 )
 
 1837    else if ( unitName.compare( QLatin1String( 
"US survey foot" ), Qt::CaseInsensitive ) == 0 ||
 
 1838              unitName.compare( QLatin1String( 
"foot" ), Qt::CaseInsensitive ) == 0 )
 
 1840    else if ( unitName.compare( QLatin1String( 
"kilometre" ), Qt::CaseInsensitive ) == 0 )  
 
 1842    else if ( unitName.compare( QLatin1String( 
"centimetre" ), Qt::CaseInsensitive ) == 0 )  
 
 1844    else if ( unitName.compare( QLatin1String( 
"millimetre" ), Qt::CaseInsensitive ) == 0 )  
 
 1846    else if ( unitName.compare( QLatin1String( 
"Statute mile" ), Qt::CaseInsensitive ) == 0 )
 
 1848    else if ( unitName.compare( QLatin1String( 
"nautical mile" ), Qt::CaseInsensitive ) == 0 )
 
 1850    else if ( unitName.compare( QLatin1String( 
"yard" ), Qt::CaseInsensitive ) == 0 )
 
 1867  if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull()
 
 1870    QgsDebugMsgLevel( 
"QgsCoordinateReferenceSystem::findMatchingProj will only " 
 1871                      "work if prj acr ellipsoid acr and proj4string are set" 
 1872                      " and the current projection is valid!", 4 );
 
 1882  QString mySql = QString( 
"select srs_id,parameters from tbl_srs where " 
 1883                           "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated" )
 
 1890  myResult = openDatabase( myDatabaseFileName, database );
 
 1891  if ( myResult != SQLITE_OK )
 
 1896  statement = database.
prepare( mySql, myResult );
 
 1897  if ( myResult == SQLITE_OK )
 
 1900    while ( statement.
step() == SQLITE_ROW )
 
 1904      if ( 
toProj() == myProj4String.trimmed() )
 
 1906        return mySrsId.toLong();
 
 1917  myResult = openDatabase( myDatabaseFileName, database );
 
 1918  if ( myResult != SQLITE_OK )
 
 1923  statement = database.
prepare( mySql, myResult );
 
 1925  if ( myResult == SQLITE_OK )
 
 1927    while ( statement.
step() == SQLITE_ROW )
 
 1931      if ( 
toProj() == myProj4String.trimmed() )
 
 1933        return mySrsId.toLong();
 
 
 1947  if ( !d->mIsValid && !srs.d->mIsValid )
 
 1950  if ( !d->mIsValid || !srs.d->mIsValid )
 
 1958  if ( isUser != otherIsUser )
 
 1962  if ( !isUser && ( !d->mAuthId.isEmpty() || !srs.d->mAuthId.isEmpty() ) )
 
 1963    return d->mAuthId == srs.d->mAuthId;
 
 
 1970  return  !( *
this == srs );
 
 
 1975  if ( 
PJ *obj = d->threadLocalProjObject() )
 
 1978    if ( isDefaultPreferredFormat && !d->mWktPreferred.isEmpty() )
 
 1981      return d->mWktPreferred;
 
 1984    PJ_WKT_TYPE 
type = PJ_WKT1_GDAL;
 
 1988        type = PJ_WKT1_GDAL;
 
 1991        type = PJ_WKT1_ESRI;
 
 1994        type = PJ_WKT2_2015;
 
 1997        type = PJ_WKT2_2015_SIMPLIFIED;
 
 2000        type = PJ_WKT2_2019;
 
 2003        type = PJ_WKT2_2019_SIMPLIFIED;
 
 2007    const QByteArray multiLineOption = QStringLiteral( 
"MULTILINE=%1" ).arg( multiline ? QStringLiteral( 
"YES" ) : QStringLiteral( 
"NO" ) ).toLocal8Bit();
 
 2008    const QByteArray indentatationWidthOption = QStringLiteral( 
"INDENTATION_WIDTH=%1" ).arg( multiline ? QString::number( indentationWidth ) : QStringLiteral( 
"0" ) ).toLocal8Bit();
 
 2009    const char *
const options[] = {multiLineOption.constData(), indentatationWidthOption.constData(), 
nullptr};
 
 2012    if ( isDefaultPreferredFormat )
 
 2015      d->mWktPreferred = res;
 
 
 2027  QDomNode srsNode = node.namedItem( QStringLiteral( 
"spatialrefsys" ) );
 
 2029  if ( ! srsNode.isNull() )
 
 2031    bool initialized = 
false;
 
 2034    long srsid = srsNode.namedItem( QStringLiteral( 
"srsid" ) ).toElement().text().toLong( &ok );
 
 2040      node = srsNode.namedItem( QStringLiteral( 
"authid" ) );
 
 2041      if ( !node.isNull() )
 
 2052        node = srsNode.namedItem( QStringLiteral( 
"epsg" ) );
 
 2053        if ( !node.isNull() )
 
 2071      const QString 
description = srsNode.namedItem( QStringLiteral( 
"description" ) ).toElement().text();
 
 2073      const QString wkt = srsNode.namedItem( QStringLiteral( 
"wkt" ) ).toElement().text();
 
 2074      initialized = createFromWktInternal( wkt, 
description );
 
 2079      node = srsNode.namedItem( QStringLiteral( 
"proj4" ) );
 
 2080      const QString proj4 = node.toElement().text();
 
 2087      node = srsNode.namedItem( QStringLiteral( 
"proj4" ) );
 
 2088      const QString proj4 = node.toElement().text();
 
 2089      if ( !proj4.trimmed().isEmpty() )
 
 2090        setProjString( node.toElement().text() );
 
 2092      node = srsNode.namedItem( QStringLiteral( 
"srsid" ) );
 
 2093      d->mSrsId = node.toElement().text().toLong();
 
 2095      node = srsNode.namedItem( QStringLiteral( 
"srid" ) );
 
 2096      d->mSRID = node.toElement().text().toLong();
 
 2098      node = srsNode.namedItem( QStringLiteral( 
"authid" ) );
 
 2099      d->mAuthId = node.toElement().text();
 
 2101      node = srsNode.namedItem( QStringLiteral( 
"description" ) );
 
 2102      d->mDescription = node.toElement().text();
 
 2104      node = srsNode.namedItem( QStringLiteral( 
"projectionacronym" ) );
 
 2105      d->mProjectionAcronym = node.toElement().text();
 
 2107      node = srsNode.namedItem( QStringLiteral( 
"ellipsoidacronym" ) );
 
 2108      d->mEllipsoidAcronym = node.toElement().text();
 
 2110      node = srsNode.namedItem( QStringLiteral( 
"geographicflag" ) );
 
 2111      d->mIsGeographic = node.toElement().text() == QLatin1String( 
"true" );
 
 2113      d->mWktPreferred.clear();
 
 2119    const QString epoch = srsNode.toElement().attribute( QStringLiteral( 
"coordinateEpoch" ) );
 
 2120    if ( !epoch.isEmpty() )
 
 2122      bool epochOk = 
false;
 
 2123      d->mCoordinateEpoch = epoch.toDouble( &epochOk );
 
 2125        d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
 
 2129      d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
 
 2132    mNativeFormat = qgsEnumKeyToValue<Qgis::CrsDefinitionFormat>( srsNode.toElement().attribute( QStringLiteral( 
"nativeFormat" ) ), 
Qgis::CrsDefinitionFormat::Wkt );
 
 2137    d = 
new QgsCoordinateReferenceSystemPrivate();
 
 
 2145  QDomElement layerNode = node.toElement();
 
 2146  QDomElement srsElement = doc.createElement( QStringLiteral( 
"spatialrefsys" ) );
 
 2148  srsElement.setAttribute( QStringLiteral( 
"nativeFormat" ), qgsEnumValueToKey<Qgis::CrsDefinitionFormat>( mNativeFormat ) );
 
 2150  if ( std::isfinite( d->mCoordinateEpoch ) )
 
 2152    srsElement.setAttribute( QStringLiteral( 
"coordinateEpoch" ), d->mCoordinateEpoch );
 
 2155  QDomElement wktElement = doc.createElement( QStringLiteral( 
"wkt" ) );
 
 2157  srsElement.appendChild( wktElement );
 
 2159  QDomElement proj4Element = doc.createElement( QStringLiteral( 
"proj4" ) );
 
 2160  proj4Element.appendChild( doc.createTextNode( 
toProj() ) );
 
 2161  srsElement.appendChild( proj4Element );
 
 2163  QDomElement srsIdElement = doc.createElement( QStringLiteral( 
"srsid" ) );
 
 2164  srsIdElement.appendChild( doc.createTextNode( QString::number( 
srsid() ) ) );
 
 2165  srsElement.appendChild( srsIdElement );
 
 2167  QDomElement sridElement = doc.createElement( QStringLiteral( 
"srid" ) );
 
 2168  sridElement.appendChild( doc.createTextNode( QString::number( 
postgisSrid() ) ) );
 
 2169  srsElement.appendChild( sridElement );
 
 2171  QDomElement authidElement = doc.createElement( QStringLiteral( 
"authid" ) );
 
 2172  authidElement.appendChild( doc.createTextNode( 
authid() ) );
 
 2173  srsElement.appendChild( authidElement );
 
 2175  QDomElement descriptionElement = doc.createElement( QStringLiteral( 
"description" ) );
 
 2176  descriptionElement.appendChild( doc.createTextNode( 
description() ) );
 
 2177  srsElement.appendChild( descriptionElement );
 
 2179  QDomElement projectionAcronymElement = doc.createElement( QStringLiteral( 
"projectionacronym" ) );
 
 2180  projectionAcronymElement.appendChild( doc.createTextNode( 
projectionAcronym() ) );
 
 2181  srsElement.appendChild( projectionAcronymElement );
 
 2183  QDomElement ellipsoidAcronymElement = doc.createElement( QStringLiteral( 
"ellipsoidacronym" ) );
 
 2184  ellipsoidAcronymElement.appendChild( doc.createTextNode( 
ellipsoidAcronym() ) );
 
 2185  srsElement.appendChild( ellipsoidAcronymElement );
 
 2187  QDomElement geographicFlagElement = doc.createElement( QStringLiteral( 
"geographicflag" ) );
 
 2188  QString geoFlagText = QStringLiteral( 
"false" );
 
 2191    geoFlagText = QStringLiteral( 
"true" );
 
 2194  geographicFlagElement.appendChild( doc.createTextNode( geoFlagText ) );
 
 2195  srsElement.appendChild( geographicFlagElement );
 
 2197  layerNode.appendChild( srsElement );
 
 
 2209QString QgsCoordinateReferenceSystem::projFromSrsId( 
const int srsId )
 
 2211  QString myDatabaseFileName;
 
 2212  QString myProjString;
 
 2213  QString mySql = QStringLiteral( 
"select parameters from tbl_srs where srs_id = %1 order by deprecated" ).arg( srsId );
 
 2222    QFileInfo myFileInfo;
 
 2223    myFileInfo.setFile( myDatabaseFileName );
 
 2224    if ( !myFileInfo.exists() ) 
 
 2226      QgsDebugError( QStringLiteral( 
"users qgis.db not found" ) );
 
 2239  rc = openDatabase( myDatabaseFileName, database );
 
 2245  statement = database.
prepare( mySql, rc );
 
 2247  if ( rc == SQLITE_OK )
 
 2249    if ( statement.
step() == SQLITE_ROW )
 
 2255  return myProjString;
 
 2262    myResult = database.
open_v2( path, SQLITE_OPEN_READONLY, 
nullptr );
 
 2264    myResult = database.
open( path );
 
 2266  if ( myResult != SQLITE_OK )
 
 2275                               .arg( database.
errorMessage() ), QObject::tr( 
"CRS" ) );
 
 2282  sCustomSrsValidation = f;
 
 
 2287  return sCustomSrsValidation;
 
 
 2290void QgsCoordinateReferenceSystem::debugPrint()
 
 2293  QgsDebugMsgLevel( 
"* Valid : " + ( d->mIsValid ? QString( 
"true" ) : QString( 
"false" ) ), 1 );
 
 2314  mValidationHint = html;
 
 
 2319  return mValidationHint;
 
 
 2329  mNativeFormat = format;
 
 
 2334  return mNativeFormat;
 
 
 2337long QgsCoordinateReferenceSystem::getRecordCount()
 
 2342  long          myRecordCount = 0;
 
 2345  if ( myResult != SQLITE_OK )
 
 2351  QString mySql = QStringLiteral( 
"select count(*) from tbl_srs" );
 
 2352  statement = database.
prepare( mySql, myResult );
 
 2353  if ( myResult == SQLITE_OK )
 
 2355    if ( statement.
step() == SQLITE_ROW )
 
 2357      QString myRecordCountString = statement.
columnAsText( 0 );
 
 2358      myRecordCount = myRecordCountString.toLong();
 
 2361  return myRecordCount;
 
 2367  bool isGeographic = 
false;
 
 2371  if ( !horizontalCrs )
 
 2375  if ( coordinateSystem )
 
 2377    const int axisCount = proj_cs_get_axis_count( pjContext, coordinateSystem.get() );
 
 2378    if ( axisCount > 0 )
 
 2380      const char *outUnitAuthName = 
nullptr;
 
 2381      const char *outUnitAuthCode = 
nullptr;
 
 2383      proj_cs_get_axis_info( pjContext, coordinateSystem.get(), 0,
 
 2392      if ( outUnitAuthName && outUnitAuthCode )
 
 2394        const char *unitCategory = 
nullptr;
 
 2395        if ( proj_uom_get_info_from_database( pjContext, outUnitAuthName, outUnitAuthCode, 
nullptr, 
nullptr, &unitCategory ) )
 
 2397          isGeographic = QString( unitCategory ).compare( QLatin1String( 
"angular" ), Qt::CaseInsensitive ) == 0;
 
 2402  return isGeographic;
 
 
 2407  thread_local const QRegularExpression projRegExp( QStringLiteral( 
"\\+proj=(\\S+)" ) );
 
 2408  const QRegularExpressionMatch projMatch = projRegExp.match( proj );
 
 2409  if ( !projMatch.hasMatch() )
 
 2411    QgsDebugMsgLevel( QStringLiteral( 
"no +proj argument found [%2]" ).arg( proj ), 2 );
 
 2414  operation = projMatch.captured( 1 );
 
 2416  const QRegularExpressionMatch ellipseMatch = projRegExp.match( proj );
 
 2417  if ( ellipseMatch.hasMatch() )
 
 2419    ellipsoid = ellipseMatch.captured( 1 );
 
 
 2433bool QgsCoordinateReferenceSystem::loadFromAuthCode( 
const QString &auth, 
const QString &code )
 
 2439  d->mIsValid = 
false;
 
 2440  d->mWktPreferred.clear();
 
 2452  proj4.replace( QLatin1String( 
"+type=crs" ), QString() );
 
 2453  proj4 = proj4.trimmed();
 
 2457  d->mWktPreferred.clear();
 
 2458  d->mDescription = QString( proj_get_name( 
crs.get() ) );
 
 2459  d->mAuthId = QStringLiteral( 
"%1:%2" ).arg( auth, code );
 
 2461  d->mAxisInvertedDirty = 
true;
 
 2466  d->mEllipsoidAcronym.clear();
 
 2467  d->setPj( std::move( 
crs ) );
 
 2469  const QString dbVals = 
sAuthIdToQgisSrsIdMap.value( QStringLiteral( 
"%1:%2" ).arg( auth, code ).toUpper() );
 
 2470  if ( !dbVals.isEmpty() )
 
 2472    const QStringList parts = dbVals.split( 
',' );
 
 2473    d->mSrsId = parts.at( 0 ).toInt();
 
 2474    d->mSRID = parts.at( 1 ).toInt();
 
 2482QList<long> QgsCoordinateReferenceSystem::userSrsIds()
 
 2484  QList<long> results;
 
 2488  QFileInfo myInfo( db );
 
 2489  if ( !myInfo.exists() )
 
 2499  int result = openDatabase( db, database );
 
 2500  if ( result != SQLITE_OK )
 
 2502    QgsDebugError( 
"failed : " + db + 
" could not be opened!" );
 
 2506  QString sql = QStringLiteral( 
"select srs_id from tbl_srs where srs_id >= %1" ).arg( 
USER_CRS_START_ID );
 
 2508  statement = database.
prepare( sql, rc );
 
 2511    int ret = statement.
step();
 
 2513    if ( ret == SQLITE_DONE )
 
 2519    if ( ret == SQLITE_ROW )
 
 2525      QgsMessageLog::logMessage( QObject::tr( 
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr( 
"SpatiaLite" ) );
 
 2533long QgsCoordinateReferenceSystem::matchToUserCrs()
 const 
 2535  PJ *obj = d->threadLocalProjObject();
 
 2539  const QList< long > ids = userSrsIds();
 
 2540  for ( 
long id : ids )
 
 2543    if ( candidate.
projObject() && proj_is_equivalent_to( obj, candidate.
projObject(), PJ_COMP_EQUIVALENT ) )
 
 2551static void sync_db_proj_logger( 
void * , 
int level, 
const char *message )
 
 2556  if ( level == PJ_LOG_ERROR )
 
 2560  else if ( level == PJ_LOG_DEBUG )
 
 2568  setlocale( LC_ALL, 
"C" );
 
 2571  int inserted = 0, updated = 0, deleted = 0, errors = 0;
 
 2576  if ( database.
open( dbFilePath ) != SQLITE_OK )
 
 2582  if ( sqlite3_exec( database.get(), 
"BEGIN TRANSACTION", 
nullptr, 
nullptr, 
nullptr ) != SQLITE_OK )
 
 2590  char *errMsg = 
nullptr;
 
 2592  bool createdTypeColumn = 
false;
 
 2593  if ( sqlite3_exec( database.get(), 
"ALTER TABLE tbl_srs ADD COLUMN srs_type text", 
nullptr, 
nullptr, 
nullptr ) == SQLITE_OK )
 
 2595    createdTypeColumn = 
true;
 
 2596    if ( sqlite3_exec( database.get(), 
"CREATE INDEX srs_type ON tbl_srs(srs_type)", 
nullptr, 
nullptr, 
nullptr ) != SQLITE_OK )
 
 2598      QgsDebugError( QStringLiteral( 
"Could not create index for srs_type" ) );
 
 2603  if ( sqlite3_exec( database.get(), 
"create table tbl_info (proj_major INT, proj_minor INT, proj_patch INT)", 
nullptr, 
nullptr, 
nullptr ) == SQLITE_OK )
 
 2605    QString sql = QStringLiteral( 
"INSERT INTO tbl_info(proj_major, proj_minor, proj_patch) VALUES (%1, %2,%3)" )
 
 2606                  .arg( QString::number( PROJ_VERSION_MAJOR ),
 
 2607                        QString::number( PROJ_VERSION_MINOR ),
 
 2608                        QString::number( PROJ_VERSION_PATCH ) );
 
 2609    if ( sqlite3_exec( database.get(), sql.toUtf8(), 
nullptr, 
nullptr, &errMsg ) != SQLITE_OK )
 
 2611      QgsDebugError( QStringLiteral( 
"Could not execute: %1 [%2/%3]\n" ).arg(
 
 2614                       errMsg ? errMsg : 
"(unknown error)" ) );
 
 2616        sqlite3_free( errMsg );
 
 2623    QString sql = QStringLiteral( 
"SELECT proj_major, proj_minor, proj_patch FROM tbl_info" );
 
 2624    statement = database.
prepare( sql, result );
 
 2625    if ( result != SQLITE_OK )
 
 2630    if ( statement.
step() == SQLITE_ROW )
 
 2635      if ( !createdTypeColumn && major == PROJ_VERSION_MAJOR && minor == PROJ_VERSION_MINOR && patch == PROJ_VERSION_PATCH )
 
 2641      QgsDebugError( QStringLiteral( 
"Could not retrieve previous CRS sync PROJ version number" ) );
 
 2648  proj_log_func( pjContext, 
nullptr, sync_db_proj_logger );
 
 2650  PROJ_STRING_LIST authorities = proj_get_authorities_from_database( pjContext );
 
 2652  int nextSrsId = 67218;
 
 2653  int nextSrId = 520007218;
 
 2654  for ( 
auto authIter = authorities; authIter && *authIter; ++authIter )
 
 2656    const QString authority( *authIter );
 
 2657    QgsDebugMsgLevel( QStringLiteral( 
"Loading authority '%1'" ).arg( authority ), 2 );
 
 2658    PROJ_STRING_LIST codes = proj_get_codes_from_database( pjContext, *authIter, PJ_TYPE_CRS, 
true );
 
 2660    QStringList allCodes;
 
 2662    for ( 
auto codesIter = codes; codesIter && *codesIter; ++codesIter )
 
 2664      const QString code( *codesIter );
 
 2670        QgsDebugError( QStringLiteral( 
"Could not load '%1:%2'" ).arg( authority, code ) );
 
 2674      const PJ_TYPE pjType = proj_get_type( 
crs.get( ) );
 
 2676      QString srsTypeString;
 
 2681        case PJ_TYPE_ELLIPSOID:
 
 2682        case PJ_TYPE_PRIME_MERIDIAN:
 
 2683        case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
 
 2684        case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
 
 2685        case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
 
 2686        case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
 
 2687        case PJ_TYPE_DATUM_ENSEMBLE:
 
 2688        case PJ_TYPE_CONVERSION:
 
 2689        case PJ_TYPE_TRANSFORMATION:
 
 2690        case PJ_TYPE_CONCATENATED_OPERATION:
 
 2691        case PJ_TYPE_OTHER_COORDINATE_OPERATION:
 
 2692        case PJ_TYPE_TEMPORAL_DATUM:
 
 2693        case PJ_TYPE_ENGINEERING_DATUM:
 
 2694        case PJ_TYPE_PARAMETRIC_DATUM:
 
 2695        case PJ_TYPE_UNKNOWN:
 
 2699        case PJ_TYPE_GEOGRAPHIC_CRS:
 
 2702        case PJ_TYPE_GEODETIC_CRS:
 
 2706        case PJ_TYPE_GEOCENTRIC_CRS:
 
 2710        case PJ_TYPE_GEOGRAPHIC_2D_CRS:
 
 2714        case PJ_TYPE_GEOGRAPHIC_3D_CRS:
 
 2718        case PJ_TYPE_PROJECTED_CRS:
 
 2722        case PJ_TYPE_COMPOUND_CRS:
 
 2726        case PJ_TYPE_TEMPORAL_CRS:
 
 2730        case PJ_TYPE_ENGINEERING_CRS:
 
 2734        case PJ_TYPE_BOUND_CRS:
 
 2738        case PJ_TYPE_VERTICAL_CRS:
 
 2742#if PROJ_VERSION_MAJOR>9 || (PROJ_VERSION_MAJOR==9 && PROJ_VERSION_MINOR>=2) 
 2743        case PJ_TYPE_DERIVED_PROJECTED_CRS:
 
 2746        case PJ_TYPE_COORDINATE_METADATA:
 
 2749        case PJ_TYPE_OTHER_CRS:
 
 2758      proj4.replace( QLatin1String( 
"+type=crs" ), QString() );
 
 2759      proj4 = proj4.trimmed();
 
 2761      if ( proj4.isEmpty() )
 
 2763        QgsDebugMsgLevel( QStringLiteral( 
"No proj4 for '%1:%2'" ).arg( authority, code ), 2 );
 
 2774      if ( translatedOperation.isEmpty() && !
operation.isEmpty() )
 
 2776        std::cout << QStringLiteral( 
"Operation needs translation in QgsCoordinateReferenceSystemUtils::translateProjection: %1" ).arg( 
operation ).toLocal8Bit().constData() << std::endl;
 
 2777        qFatal( 
"aborted" );
 
 2780      const bool deprecated = proj_is_deprecated( 
crs.get() );
 
 2781      const QString name( proj_get_name( 
crs.get() ) );
 
 2783      QString sql = QStringLiteral( 
"SELECT parameters,description,deprecated,srs_type FROM tbl_srs WHERE auth_name='%1' AND auth_id='%2'" ).arg( authority, code );
 
 2784      statement = database.
prepare( sql, result );
 
 2785      if ( result != SQLITE_OK )
 
 2794      bool dbSrsDeprecated = deprecated;
 
 2795      if ( statement.
step() == SQLITE_ROW )
 
 2799        dbSrsDeprecated = statement.
columnAsText( 2 ).toInt() != 0;
 
 2803      if ( !dbSrsProj4.isEmpty() || !dbSrsDesc.isEmpty() )
 
 2805        if ( proj4 != dbSrsProj4 || name != dbSrsDesc || deprecated != dbSrsDeprecated || dbSrsType != srsTypeString )
 
 2808          sql = QStringLiteral( 
"UPDATE tbl_srs SET parameters=%1,description=%2,deprecated=%3, srs_type=%4 WHERE auth_name=%5 AND auth_id=%6" )
 
 2811                .arg( deprecated ? 1 : 0 )
 
 2815          if ( sqlite3_exec( database.get(), sql.toUtf8(), 
nullptr, 
nullptr, &errMsg ) != SQLITE_OK )
 
 2817            QgsDebugError( QStringLiteral( 
"Could not execute: %1 [%2/%3]\n" ).arg(
 
 2820                             errMsg ? errMsg : 
"(unknown error)" ) );
 
 2822              sqlite3_free( errMsg );
 
 2836        const QString dbVals = 
sAuthIdToQgisSrsIdMap.value( QStringLiteral( 
"%1:%2" ).arg( authority, code ) );
 
 2839        if ( !dbVals.isEmpty() )
 
 2841          const QStringList parts = dbVals.split( 
',' );
 
 2842          srsId = parts.at( 0 );
 
 2843          srId = parts.at( 1 );
 
 2845        if ( srId.isEmpty() )
 
 2847          srId = QString::number( nextSrId );
 
 2850        if ( srsId.isEmpty() )
 
 2852          srsId = QString::number( nextSrsId );
 
 2856        if ( !srsId.isEmpty() )
 
 2858          sql = QStringLiteral( 
"INSERT INTO tbl_srs(srs_id, description,projection_acronym,ellipsoid_acronym,parameters,srid,auth_name,auth_id,is_geo,deprecated,srs_type) VALUES (%1, %2,%3,%4,%5,%6,%7,%8,%9,%10,%11)" )
 
 2868                .arg( deprecated ? 1 : 0 )
 
 2873          sql = QStringLiteral( 
"INSERT INTO tbl_srs(description,projection_acronym,ellipsoid_acronym,parameters,srid,auth_name,auth_id,is_geo,deprecated,srs_type) VALUES (%1,%2,%3,%4,%5,%6,%7,%8,%9,%10)" )
 
 2882                .arg( deprecated ? 1 : 0 )
 
 2887        if ( sqlite3_exec( database.get(), sql.toUtf8(), 
nullptr, 
nullptr, &errMsg ) == SQLITE_OK )
 
 2893          qCritical( 
"Could not execute: %s [%s/%s]\n",
 
 2894                     sql.toLocal8Bit().constData(),
 
 2895                     sqlite3_errmsg( database.get() ),
 
 2896                     errMsg ? errMsg : 
"(unknown error)" );
 
 2900            sqlite3_free( errMsg );
 
 2905    proj_string_list_destroy( codes );
 
 2907    const QString sql = QStringLiteral( 
"DELETE FROM tbl_srs WHERE auth_name='%1' AND NOT auth_id IN (%2)" ).arg( authority, allCodes.join( 
',' ) );
 
 2908    if ( sqlite3_exec( database.get(), sql.toUtf8(), 
nullptr, 
nullptr, 
nullptr ) == SQLITE_OK )
 
 2910      deleted = sqlite3_changes( database.get() );
 
 2915      qCritical( 
"Could not execute: %s [%s]\n",
 
 2916                 sql.toLocal8Bit().constData(),
 
 2917                 sqlite3_errmsg( database.get() ) );
 
 2921  proj_string_list_destroy( authorities );
 
 2923  QString sql = QStringLiteral( 
"UPDATE tbl_info set proj_major=%1,proj_minor=%2,proj_patch=%3" )
 
 2924                .arg( QString::number( PROJ_VERSION_MAJOR ),
 
 2925                      QString::number( PROJ_VERSION_MINOR ),
 
 2926                      QString::number( PROJ_VERSION_PATCH ) );
 
 2927  if ( sqlite3_exec( database.get(), sql.toUtf8(), 
nullptr, 
nullptr, &errMsg ) != SQLITE_OK )
 
 2929    QgsDebugError( QStringLiteral( 
"Could not execute: %1 [%2/%3]\n" ).arg(
 
 2932                     errMsg ? errMsg : 
"(unknown error)" ) );
 
 2934      sqlite3_free( errMsg );
 
 2938  if ( sqlite3_exec( database.get(), 
"COMMIT", 
nullptr, 
nullptr, 
nullptr ) != SQLITE_OK )
 
 2940    QgsDebugError( QStringLiteral( 
"Could not commit transaction: %1 [%2]\n" ).arg(
 
 2942                     sqlite3_errmsg( database.get() ) )
 
 2948  QgsDebugMsgLevel( QStringLiteral( 
"CRS update (inserted:%1 updated:%2 deleted:%3 errors:%4)" ).arg( inserted ).arg( updated ).arg( deleted ).arg( errors ), 4 );
 
 2956    return updated + inserted;
 
 
 2959const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::stringCache()
 
 2961  return *sStringCache();
 
 2964const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::projCache()
 
 2966  return *sProj4Cache();
 
 2969const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::ogcCache()
 
 2971  return *sOgcCache();
 
 2974const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::wktCache()
 
 2976  return *sWktCache();
 
 2979const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srIdCache()
 
 2981  return *sSrIdCache();
 
 2984const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srsIdCache()
 
 2986  return *sSrsIdCache();
 
 2996  if ( 
PJ *obj = d->threadLocalProjObject() )
 
 
 3042  if ( 
PJ *obj = d->threadLocalProjObject() )
 
 
 3075  if ( 
PJ *obj = d->threadLocalProjObject() )
 
 
 3086  if ( 
PJ *obj = d->threadLocalProjObject() )
 
 
 3099  else if ( 
PJ *obj = d->threadLocalProjObject() )
 
 3102    return geoCrs ? QStringLiteral( 
"%1:%2" ).arg( proj_get_id_auth_name( geoCrs.get(), 0 ), proj_get_id_code( geoCrs.get(), 0 ) ) : QString();
 
 
 3112  return d->threadLocalProjObject();
 
 
 3125  d->mIsValid = 
false;
 
 3127  d->mWktPreferred.clear();
 
 3134  switch ( proj_get_type( 
object ) )
 
 3136    case PJ_TYPE_GEODETIC_CRS:
 
 3137    case PJ_TYPE_GEOCENTRIC_CRS:
 
 3138    case PJ_TYPE_GEOGRAPHIC_CRS:
 
 3139    case PJ_TYPE_GEOGRAPHIC_2D_CRS:
 
 3140    case PJ_TYPE_GEOGRAPHIC_3D_CRS:
 
 3141    case PJ_TYPE_VERTICAL_CRS:
 
 3142    case PJ_TYPE_PROJECTED_CRS:
 
 3143    case PJ_TYPE_COMPOUND_CRS:
 
 3144    case PJ_TYPE_TEMPORAL_CRS:
 
 3145    case PJ_TYPE_ENGINEERING_CRS:
 
 3146    case PJ_TYPE_BOUND_CRS:
 
 3147    case PJ_TYPE_OTHER_CRS:
 
 3163    const QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
 
 3164    const QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
 
 3165    if ( !authName.isEmpty() && !authCode.isEmpty() && loadFromAuthCode( authName, authCode ) )
 
 3173      d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
 
 
 3184  QStringList projections;
 
 3186  projections.reserve( res.size() );
 
 3189    projections << QString::number( 
crs.
srsid() );
 
 
 3216  sSrIdCacheLock()->lockForWrite();
 
 3217  if ( !sDisableSrIdCache )
 
 3220      sDisableSrIdCache = 
true;
 
 3221    sSrIdCache()->clear();
 
 3223  sSrIdCacheLock()->unlock();
 
 3225  sOgcLock()->lockForWrite();
 
 3226  if ( !sDisableOgcCache )
 
 3229      sDisableOgcCache = 
true;
 
 3230    sOgcCache()->clear();
 
 3232  sOgcLock()->unlock();
 
 3234  sProj4CacheLock()->lockForWrite();
 
 3235  if ( !sDisableProjCache )
 
 3238      sDisableProjCache = 
true;
 
 3239    sProj4Cache()->clear();
 
 3241  sProj4CacheLock()->unlock();
 
 3243  sCRSWktLock()->lockForWrite();
 
 3244  if ( !sDisableWktCache )
 
 3247      sDisableWktCache = 
true;
 
 3248    sWktCache()->clear();
 
 3250  sCRSWktLock()->unlock();
 
 3252  sCRSSrsIdLock()->lockForWrite();
 
 3253  if ( !sDisableSrsIdCache )
 
 3256      sDisableSrsIdCache = 
true;
 
 3257    sSrsIdCache()->clear();
 
 3259  sCRSSrsIdLock()->unlock();
 
 3261  sCrsStringLock()->lockForWrite();
 
 3262  if ( !sDisableStringCache )
 
 3265      sDisableStringCache = 
true;
 
 3266    sStringCache()->clear();
 
 3268  sCrsStringLock()->unlock();
 
 
 3277  if ( !c1.d->mIsValid && !c2.d->mIsValid )
 
 3280  if ( !c1.d->mIsValid && c2.d->mIsValid )
 
 3283  if ( c1.d->mIsValid && !c2.d->mIsValid )
 
 3289  if ( c1IsUser && !c2IsUser )
 
 3292  if ( !c1IsUser && c2IsUser )
 
 3295  if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
 
 3297    if ( c1.d->mAuthId != c2.d->mAuthId )
 
 3298      return c1.d->mAuthId > c2.d->mAuthId;
 
 3306  if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
 
 3309  if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
 
 3312  if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
 
 3315  if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
 
 3318  return c1.d->mCoordinateEpoch > c2.d->mCoordinateEpoch;
 
 
 3326  if ( !c1.d->mIsValid && !c2.d->mIsValid )
 
 3329  if ( c1.d->mIsValid && !c2.d->mIsValid )
 
 3332  if ( !c1.d->mIsValid && c2.d->mIsValid )
 
 3338  if ( !c1IsUser && c2IsUser )
 
 3341  if ( c1IsUser && !c2IsUser )
 
 3344  if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
 
 3346    if ( c1.d->mAuthId != c2.d->mAuthId )
 
 3347      return c1.d->mAuthId < c2.d->mAuthId;
 
 3355  if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
 
 3358  if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
 
 3361  if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
 
 3364  if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
 
 3367  return c1.d->mCoordinateEpoch < c2.d->mCoordinateEpoch;
 
 
 3372  return !( c1 < c2 );
 
 
 3376  return !( c1 > c2 );
 
 
 
 
 
 
 
CrsIdentifierType
Available identifier string types for representing coordinate reference systems.
 
@ ShortString
A heavily abbreviated string, for use when a compact representation is required.
 
@ MediumString
A medium-length string, recommended for general purpose use.
 
DistanceUnit
Units of distance.
 
@ Centimeters
Centimeters.
 
@ Millimeters
Millimeters.
 
@ Miles
Terrestrial miles.
 
@ Unknown
Unknown distance unit.
 
@ Degrees
Degrees, for planar geographic CRS distance measurements.
 
@ NauticalMiles
Nautical miles.
 
@ Critical
Critical/error message.
 
CrsType
Coordinate reference system types.
 
@ Compound
Compound (horizontal + vertical) CRS.
 
@ Projected
Projected CRS.
 
@ DerivedProjected
Derived projected CRS.
 
@ Engineering
Engineering CRS.
 
@ Geographic3d
3D geopraphic CRS
 
@ Geographic2d
2D geographic CRS
 
@ Geocentric
Geocentric CRS.
 
CrsDefinitionFormat
CRS definition formats.
 
@ Wkt
WKT format (always recommended over proj string format)
 
CrsAxisDirection
Coordinate reference system axis directions.
 
@ ColumnPositive
Column positive.
 
@ SouthSouthEast
South South East.
 
@ ColumnNegative
Column negative.
 
@ RowPositive
Row positive.
 
@ DisplayDown
Display down.
 
@ GeocentricZ
Geocentric (Z)
 
@ DisplayRight
Display right.
 
@ WestSouthWest
West South West.
 
@ RowNegative
Row negative.
 
@ NorthNorthEast
North North East.
 
@ EastNorthEast
East North East.
 
@ Unspecified
Unspecified.
 
@ NorthNorthWest
North North West.
 
@ GeocentricY
Geocentric (Y)
 
@ CounterClockwise
Counter clockwise.
 
@ SouthSouthWest
South South West.
 
@ DisplayLeft
Display left.
 
@ WestNorthWest
West North West.
 
@ EastSouthEast
East South East.
 
@ GeocentricX
Geocentric (X)
 
CrsWktVariant
Coordinate reference system WKT formatting variants.
 
@ Wkt2_2019Simplified
WKT2_2019 with the simplification rule of WKT2_SIMPLIFIED.
 
@ Wkt2_2015Simplified
Same as WKT2_2015 with the following exceptions: UNIT keyword used. ID node only on top element....
 
@ Wkt1Esri
WKT1 as traditionally output by ESRI software, deriving from OGC 99-049.
 
@ Preferred
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
 
@ Wkt2_2019
Full WKT2 string, conforming to ISO 19162:2019 / OGC 18-010, with all possible nodes and new keyword ...
 
@ Wkt2_2015
Full WKT2 string, conforming to ISO 19162:2015(E) / OGC 12-063r5 with all possible nodes and new keyw...
 
@ Wkt1Gdal
WKT1 as traditionally output by GDAL, deriving from OGC 01-009. A notable departure from WKT1_GDAL wi...
 
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.
 
void removeRecent(const QgsCoordinateReferenceSystem &crs)
Removes a CRS from the list of recently used CRS.
 
long addUserCrs(const QgsCoordinateReferenceSystem &crs, const QString &name, Qgis::CrsDefinitionFormat nativeFormat=Qgis::CrsDefinitionFormat::Wkt)
Adds a new crs definition as a custom ("USER") CRS.
 
void clearRecent()
Cleans the list of recently used CRS.
 
QList< QgsCoordinateReferenceSystem > recentCrs()
Returns a list of recently used CRS.
 
void pushRecent(const QgsCoordinateReferenceSystem &crs)
Pushes a recently used CRS to the top of the recent CRS list.
 
QMap< QString, QgsProjOperation > projOperations() const
Returns a map of all valid PROJ operations.
 
static QString translateProjection(const QString &projection)
Returns a translated string for a projection method.
 
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 createFromOgcWmsCrs(const QString &crs)
Sets this CRS to the given OGC WMS-format Coordinate Reference Systems.
 
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
 
bool createFromWkt(const QString &wkt)
Sets this CRS using a WKT definition.
 
bool createFromString(const QString &definition)
Set up this CRS from a string definition.
 
bool hasVerticalAxis() const
Returns true if the CRS has a vertical axis.
 
void validate()
Perform some validation on this CRS.
 
~QgsCoordinateReferenceSystem()
 
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 CUSTOM_CRS_VALIDATION customCrsValidation()
Gets custom function.
 
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
 
static QgsCoordinateReferenceSystem createCompoundCrs(const QgsCoordinateReferenceSystem &horizontalCrs, const QgsCoordinateReferenceSystem &verticalCrs, QString &error)
Given a horizontal and vertical CRS, attempts to create a compound CRS from them.
 
static Q_INVOKABLE QgsCoordinateReferenceSystem fromEpsgId(long epsg)
Creates a CRS from a given EPSG ID.
 
Q_DECL_DEPRECATED bool createFromProj4(const QString &projString)
Sets this CRS by passing it a PROJ style formatted string.
 
static Q_DECL_DEPRECATED QStringList recentProjections()
Returns a list of recently used projections.
 
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
 
QString toOgcUri() const
Returns the crs as OGC URI (format: http://www.opengis.net/def/crs/OGC/1.3/CRS84) Returns an empty st...
 
QString toOgcUrn() const
Returns the crs as OGC URN (format: urn:ogc:def:crs:OGC:1.3:CRS84) Returns an empty string on failure...
 
static void setCustomCrsValidation(CUSTOM_CRS_VALIDATION f)
Sets custom function to force valid CRS.
 
static Q_DECL_DEPRECATED void pushRecentCoordinateReferenceSystem(const QgsCoordinateReferenceSystem &crs)
Pushes a recently used CRS to the top of the recent CRS list.
 
long postgisSrid() const
Returns PostGIS SRID for the CRS.
 
QgsCoordinateReferenceSystem horizontalCrs() const
Returns the horizontal CRS associated with this CRS object.
 
Q_DECL_DEPRECATED long findMatchingProj()
Walks the CRS databases (both system and user database) trying to match stored PROJ string to a datab...
 
static QList< long > validSrsIds()
Returns a list of all valid SRS IDs present in the CRS database.
 
QgsProjectionFactors factors(const QgsPoint &point) const
Calculate various cartographic properties, such as scale factors, angular distortion and meridian con...
 
void setValidationHint(const QString &html)
Set user hint for validation.
 
Q_DECL_DEPRECATED QString toProj4() const
Returns a Proj string representation of this CRS.
 
bool operator==(const QgsCoordinateReferenceSystem &srs) const
Overloaded == operator used to compare to CRS's.
 
QString projectionAcronym() const
Returns the projection acronym for the projection used by the CRS.
 
QString userFriendlyIdentifier(Qgis::CrsIdentifierType type=Qgis::CrsIdentifierType::MediumString) const
Returns a user friendly identifier for the CRS.
 
Qgis::CrsDefinitionFormat nativeFormat() const
Returns the native format for the CRS definition.
 
CrsType
Enumeration of types of IDs accepted in createFromId() method.
 
@ InternalCrsId
Internal ID used by QGIS in the local SQLite database.
 
@ PostgisCrsId
SRID used in PostGIS. DEPRECATED – DO NOT USE.
 
bool createFromUserInput(const QString &definition)
Set up this CRS from various text formats.
 
QgsCoordinateReferenceSystem()
Constructs an invalid CRS object.
 
static int syncDatabase()
Update proj.4 parameters in our database from proj.4.
 
bool operator!=(const QgsCoordinateReferenceSystem &srs) const
Overloaded != operator used to compare to CRS's.
 
void setNativeFormat(Qgis::CrsDefinitionFormat format)
Sets the native format for the CRS definition.
 
bool createFromProj(const QString &projString, bool identify=true)
Sets this CRS by passing it a PROJ style formatted string.
 
QgsCoordinateReferenceSystem verticalCrs() const
Returns the vertical CRS associated with this CRS object.
 
static Q_DECL_DEPRECATED void removeRecentCoordinateReferenceSystem(const QgsCoordinateReferenceSystem &crs)
Removes a CRS from the list of recently used CRS.
 
QgsDatumEnsemble datumEnsemble() const
Attempts to retrieve datum ensemble details from the CRS.
 
QgsCoordinateReferenceSystem toGeographicCrs() const
Returns the geographic CRS associated with this CRS object.
 
bool isDynamic() const
Returns true if the CRS is a dynamic CRS.
 
bool createFromSrsId(long srsId)
Sets this CRS by lookup of internal QGIS CRS ID in the CRS database.
 
Q_DECL_DEPRECATED bool createFromId(long id, CrsType type=PostgisCrsId)
Sets this CRS by lookup of the given ID in the CRS database.
 
static QgsCoordinateReferenceSystem fromProjObject(PJ *object)
Constructs a QgsCoordinateReferenceSystem from a PROJ PJ object.
 
static void invalidateCache(bool disableCache=false)
Clears the internal cache used to initialize QgsCoordinateReferenceSystem objects.
 
QgsCoordinateReferenceSystem & operator=(const QgsCoordinateReferenceSystem &srs)
Assignment operator.
 
void updateDefinition()
Updates the definition and parameters of the coordinate reference system to their latest values.
 
static QgsCoordinateReferenceSystem fromProj(const QString &proj)
Creates a CRS from a proj style formatted string.
 
static Q_DECL_DEPRECATED void setupESRIWktFix()
Make sure that ESRI WKT import is done properly.
 
static Q_DECL_DEPRECATED QList< QgsCoordinateReferenceSystem > recentCoordinateReferenceSystems()
Returns a list of recently used CRS.
 
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
 
PJ * projObject() const
Returns the underlying PROJ PJ object corresponding to the CRS, or nullptr if the CRS is invalid.
 
void setCoordinateEpoch(double epoch)
Sets the coordinate epoch, as a decimal year.
 
Q_DECL_DEPRECATED bool createFromSrid(long srid)
Sets this CRS by lookup of the given PostGIS SRID in the CRS database.
 
long saveAsUserCrs(const QString &name, Qgis::CrsDefinitionFormat nativeFormat=Qgis::CrsDefinitionFormat::Wkt)
Saves the CRS as a new custom ("USER") CRS.
 
static Q_DECL_DEPRECATED void clearRecentCoordinateReferenceSystems()
Cleans the list of recently used CRS.
 
QString celestialBodyName() const
Attempts to retrieve the name of the celestial body associated with the CRS (e.g.
 
static QgsCoordinateReferenceSystem fromWkt(const QString &wkt)
Creates a CRS from a WKT spatial ref sys definition string.
 
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
 
static QgsCoordinateReferenceSystem fromSrsId(long srsId)
Creates a CRS from a specified QGIS SRS ID.
 
double coordinateEpoch() const
Returns the coordinate epoch, as a decimal year.
 
QString geographicCrsAuthId() const
Returns auth id of related geographic CRS.
 
QString validationHint() const
Gets user hint for validation.
 
QgsProjOperation operation() const
Returns information about the PROJ operation associated with the coordinate reference system,...
 
QList< Qgis::CrsAxisDirection > axisOrdering() const
Returns an ordered list of the axis directions reflecting the native axis order for the CRS.
 
long srsid() const
Returns the internal CRS ID, if available.
 
Qgis::CrsType type() const
Returns the type of the CRS.
 
Qgis::DistanceUnit mapUnits
 
bool hasAxisInverted() const
Returns whether the axis order is inverted for the CRS compared to the order east/north (longitude/la...
 
bool createFromProjObject(PJ *object)
Sets this CRS by passing it a PROJ PJ object, corresponding to a PROJ CRS object.
 
bool isDeprecated() const
Returns true if the CRS is considered deprecated.
 
static Q_DECL_DEPRECATED QgsCoordinateReferenceSystem fromProj4(const QString &proj4)
Creates a CRS from a proj style formatted string.
 
Contains information about a member of a datum ensemble.
 
Contains information about a datum ensemble.
 
static void warning(const QString &msg)
Goes to qWarning.
 
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.
 
@ AUTH_CODE
unknown/unhandled flavor
 
static CRSFlavor parseCrsName(const QString &crsName, QString &authority, QString &code)
Parse a CRS name in one of the flavors of OGC services, and decompose it as authority and code.
 
static QString OGRSpatialReferenceToWkt(OGRSpatialReferenceH srs)
Returns a WKT string corresponding to the specified OGR srs object.
 
Point geometry type, with support for z-dimension and m-values.
 
static PJ_CONTEXT * get()
Returns a thread local instance of a proj context, safe for use in the current thread.
 
Contains information about a PROJ operation.
 
static proj_pj_unique_ptr crsToHorizontalCrs(const PJ *crs)
Given a PROJ crs (which may be a compound or bound crs, or some other type), extract the horizontal c...
 
@ FlagMatchBoundCrsToUnderlyingSourceCrs
Allow matching a BoundCRS object to its underlying SourceCRS.
 
static proj_pj_unique_ptr createCompoundCrs(const PJ *horizontalCrs, const PJ *verticalCrs, QStringList *errors=nullptr)
Given a PROJ horizontal and vertical CRS, attempt to create a compound CRS from them.
 
static bool isDynamic(const PJ *crs)
Returns true if the given proj coordinate system is a dynamic CRS.
 
static proj_pj_unique_ptr unboundCrs(const PJ *crs)
Given a PROJ crs (which may be a compound or bound crs, or some other type), ensure that it is not a ...
 
static bool identifyCrs(const PJ *crs, QString &authName, QString &authCode, IdentifyFlags flags=IdentifyFlags())
Attempts to identify a crs, matching it to a known authority and code within an acceptable level of t...
 
static bool hasVerticalAxis(const PJ *crs)
Returns true if a PROJ crs has a vertical axis.
 
static proj_pj_unique_ptr crsToVerticalCrs(const PJ *crs)
Given a PROJ crs (which may be a compound crs, or some other type), extract the vertical crs from it.
 
static proj_pj_unique_ptr crsToDatumEnsemble(const PJ *crs)
Given a PROJ crs, attempt to retrieve the datum ensemble from it.
 
std::unique_ptr< PJ, ProjPJDeleter > proj_pj_unique_ptr
Scoped Proj PJ object.
 
static bool axisOrderIsSwapped(const PJ *crs)
Returns true if the given proj coordinate system uses requires y/x coordinate order instead of x/y.
 
contains various cartographic properties, such as scale factors, angular distortion and meridian conv...
 
The QgsReadWriteLocker class is a convenience class that simplifies locking and unlocking QReadWriteL...
 
void unlock()
Unlocks the lock.
 
void changeMode(Mode mode)
Change the mode of the lock to mode.
 
A rectangle specified with double values.
 
void setYMinimum(double y)
Set the minimum y value.
 
void setXMinimum(double x)
Set the minimum x value.
 
void setYMaximum(double y)
Set the maximum y value.
 
void setXMaximum(double x)
Set the maximum x value.
 
static QString quotedString(const QString &value)
Returns a quoted string value, surround by ' characters and with special characters correctly escaped...
 
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
 
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
 
int open(const QString &path)
Opens the database at the specified file path.
 
QString errorMessage() const
Returns the most recent error message encountered by the database.
 
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
 
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
 
QString columnAsText(int column) const
Returns the column value from the current statement row as a string.
 
QString columnName(int column) const
Returns the name of column.
 
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
 
qlonglong columnAsInt64(int column) const
Gets column value from the current statement row as a long long integer (64 bits).
 
int columnCount() const
Gets the number of columns that this statement returns.
 
#define Q_NOWARN_DEPRECATED_POP
 
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
 
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
 
#define Q_NOWARN_DEPRECATED_PUSH
 
bool qgsNanCompatibleEquals(double a, double b)
Compare two doubles, treating nan values as equal.
 
const int USER_CRS_START_ID
Magick number that determines whether a projection crsid is a system (srs.db) or user (~/....
 
QString getFullProjString(PJ *obj)
 
bool operator>=(const QgsCoordinateReferenceSystem &c1, const QgsCoordinateReferenceSystem &c2)
 
bool operator<(const QgsCoordinateReferenceSystem &c1, const QgsCoordinateReferenceSystem &c2)
 
bool testIsGeographic(PJ *crs)
 
void getOperationAndEllipsoidFromProjString(const QString &proj, QString &operation, QString &ellipsoid)
 
QHash< QString, QgsCoordinateReferenceSystem > StringCrsCacheHash
 
bool operator<=(const QgsCoordinateReferenceSystem &c1, const QgsCoordinateReferenceSystem &c2)
 
QHash< long, QgsCoordinateReferenceSystem > SrIdCrsCacheHash
 
bool operator>(const QgsCoordinateReferenceSystem &c1, const QgsCoordinateReferenceSystem &c2)
 
void * OGRSpatialReferenceH
 
struct projCtx_t PJ_CONTEXT
 
void(* CUSTOM_CRS_VALIDATION)(QgsCoordinateReferenceSystem &)
 
const QMap< QString, QString > sAuthIdToQgisSrsIdMap
 
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
 
#define QgsDebugMsgLevel(str, level)
 
#define QgsDebugError(str)
 
const QgsCoordinateReferenceSystem & crs