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;
848 const thread_local QMap< Qgis::CrsAxisDirection, QString > mapping =
891 QList< Qgis::CrsAxisDirection > res;
892 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
895 res.reserve( axisCount );
897 for (
int i = 0; i < axisCount; ++i )
899 const char *outDirection =
nullptr;
900 proj_cs_get_axis_info( context, pjCs.get(), i,
910 const thread_local QRegularExpression rx( QStringLiteral(
"([^\\s]+).*" ) );
911 const QRegularExpressionMatch match = rx.match( QString( outDirection ) );
912 if ( !match.hasMatch() )
915 const QString direction = match.captured( 1 );
917 for (
auto it = mapping.constBegin(); it != mapping.constEnd(); ++it )
919 if ( it.value().compare( direction, Qt::CaseInsensitive ) == 0 )
934 return createFromWktInternal( wkt, QString() );
937bool QgsCoordinateReferenceSystem::createFromWktInternal(
const QString &wkt,
const QString &description )
945 if ( !sDisableWktCache )
947 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sWktCache()->constFind( wkt );
948 if ( crsIt != sWktCache()->constEnd() )
951 *
this = crsIt.value();
953 if ( !
description.isEmpty() && d->mDescription.isEmpty() )
958 sWktCache()->insert( wkt, *
this );
967 d->mWktPreferred.clear();
970 QgsDebugMsgLevel( QStringLiteral(
"theWkt is uninitialized, operation failed" ), 4 );
975 QgsCoordinateReferenceSystem::RecordMap record = getRecord(
"select * from tbl_srs where wkt=" +
QgsSqliteUtils::quotedString( wkt ) +
" order by deprecated" );
976 if ( !record.empty() )
978 long srsId = record[QStringLiteral(
"srs_id" )].toLong();
991 if ( d->mSrsId == 0 )
994 long id = matchToUserCrs();
1003 if ( !sDisableWktCache )
1004 sWktCache()->insert( wkt, *
this );
1022 if ( projString.isEmpty() )
1027 if ( projString.trimmed().isEmpty() )
1029 d->mIsValid =
false;
1031 d->mWktPreferred.clear();
1036 if ( !sDisableProjCache )
1038 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sProj4Cache()->constFind( projString );
1039 if ( crsIt != sProj4Cache()->constEnd() )
1042 *
this = crsIt.value();
1056 QString myProj4String = projString.trimmed();
1057 myProj4String.remove( QStringLiteral(
"+type=crs" ) );
1058 myProj4String = myProj4String.trimmed();
1060 d->mIsValid =
false;
1061 d->mWktPreferred.clear();
1066 const QString projCrsString = myProj4String + ( myProj4String.contains( QStringLiteral(
"+type=crs" ) ) ? QString() : QStringLiteral(
" +type=crs" ) );
1074 const QString
authid = QStringLiteral(
"%1:%2" ).arg( authName, authCode );
1078 if ( !sDisableProjCache )
1079 sProj4Cache()->insert( projString, *
this );
1086 QgsCoordinateReferenceSystem::RecordMap myRecord = getRecord(
"select * from tbl_srs where parameters=" +
QgsSqliteUtils::quotedString( myProj4String ) +
" order by deprecated" );
1088 if ( !myRecord.empty() )
1090 id = myRecord[QStringLiteral(
"srs_id" )].toLong();
1099 setProjString( myProj4String );
1102 id = matchToUserCrs();
1111 setProjString( myProj4String );
1115 if ( !sDisableProjCache )
1116 sProj4Cache()->insert( projString, *
this );
1122QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord(
const QString &sql )
1124 QString myDatabaseFileName;
1125 QgsCoordinateReferenceSystem::RecordMap myMap;
1126 QString myFieldName;
1127 QString myFieldValue;
1134 QFileInfo myInfo( myDatabaseFileName );
1135 if ( !myInfo.exists() )
1137 QgsDebugError(
"failed : " + myDatabaseFileName +
" does not exist!" );
1142 myResult = openDatabase( myDatabaseFileName, database );
1143 if ( myResult != SQLITE_OK )
1148 statement = database.
prepare( sql, myResult );
1150 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1154 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1156 myFieldName = statement.
columnName( myColNo );
1158 myMap[myFieldName] = myFieldValue;
1160 if ( statement.
step() != SQLITE_DONE )
1162 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1171 if ( myMap.empty() )
1174 QFileInfo myFileInfo;
1175 myFileInfo.setFile( myDatabaseFileName );
1176 if ( !myFileInfo.exists() )
1178 QgsDebugError( QStringLiteral(
"user qgis.db not found" ) );
1183 myResult = openDatabase( myDatabaseFileName, database );
1184 if ( myResult != SQLITE_OK )
1189 statement = database.
prepare( sql, myResult );
1191 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1195 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1197 myFieldName = statement.
columnName( myColNo );
1199 myMap[myFieldName] = myFieldValue;
1202 if ( statement.
step() != SQLITE_DONE )
1204 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1235 if ( d->mDescription.isNull() )
1241 return d->mDescription;
1258 id =
isValid() ? QObject::tr(
"Custom CRS" ) : QObject::tr(
"Unknown CRS" );
1260 id = QObject::tr(
"Custom CRS: %1" ).arg(
1262 :
toWkt(
Qgis::CrsWktVariant::Preferred ) );
1263 else if ( !
toProj().isEmpty() )
1266 if ( !
id.isEmpty() && !std::isnan( d->mCoordinateEpoch ) )
1267 id += QStringLiteral(
" @ %1" ).arg(
qgsDoubleToString( d->mCoordinateEpoch, 3 ) );
1274 if ( d->mProjectionAcronym.isNull() )
1280 return d->mProjectionAcronym;
1286 if ( d->mEllipsoidAcronym.isNull() )
1288 if (
PJ *obj = d->threadLocalProjObject() )
1293 const QString ellipsoidAuthName( proj_get_id_auth_name( ellipsoid.get(), 0 ) );
1294 const QString ellipsoidAuthCode( proj_get_id_code( ellipsoid.get(), 0 ) );
1295 if ( !ellipsoidAuthName.isEmpty() && !ellipsoidAuthCode.isEmpty() )
1296 d->mEllipsoidAcronym = QStringLiteral(
"%1:%2" ).arg( ellipsoidAuthName, ellipsoidAuthCode );
1299 double semiMajor, semiMinor, invFlattening;
1300 int semiMinorComputed = 0;
1301 if ( proj_ellipsoid_get_parameters(
QgsProjContext::get(), ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
1303 d->mEllipsoidAcronym = QStringLiteral(
"PARAMETER:%1:%2" ).arg(
qgsDoubleToString( semiMajor ),
1308 d->mEllipsoidAcronym.clear();
1313 return d->mEllipsoidAcronym;
1317 return d->mEllipsoidAcronym;
1331 if ( d->mProj4.isEmpty() )
1333 if (
PJ *obj = d->threadLocalProjObject() )
1339 return d->mProj4.trimmed();
1345 switch ( d->mProjType )
1347 case PJ_TYPE_UNKNOWN:
1350 case PJ_TYPE_ELLIPSOID:
1351 case PJ_TYPE_PRIME_MERIDIAN:
1352 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
1353 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
1354 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
1355 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
1356 case PJ_TYPE_DATUM_ENSEMBLE:
1357 case PJ_TYPE_CONVERSION:
1358 case PJ_TYPE_TRANSFORMATION:
1359 case PJ_TYPE_CONCATENATED_OPERATION:
1360 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
1361 case PJ_TYPE_TEMPORAL_DATUM:
1362 case PJ_TYPE_ENGINEERING_DATUM:
1363 case PJ_TYPE_PARAMETRIC_DATUM:
1367 case PJ_TYPE_GEOGRAPHIC_CRS:
1371 case PJ_TYPE_GEODETIC_CRS:
1373 case PJ_TYPE_GEOCENTRIC_CRS:
1375 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
1377 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
1379 case PJ_TYPE_VERTICAL_CRS:
1381 case PJ_TYPE_PROJECTED_CRS:
1383 case PJ_TYPE_COMPOUND_CRS:
1385 case PJ_TYPE_TEMPORAL_CRS:
1387 case PJ_TYPE_ENGINEERING_CRS:
1389 case PJ_TYPE_BOUND_CRS:
1391 case PJ_TYPE_OTHER_CRS:
1393#if PROJ_VERSION_MAJOR>9 || (PROJ_VERSION_MAJOR==9 && PROJ_VERSION_MINOR>=2)
1394 case PJ_TYPE_DERIVED_PROJECTED_CRS:
1396 case PJ_TYPE_COORDINATE_METADATA:
1410 return proj_is_deprecated( pj );
1415 return d->mIsGeographic;
1433#if PROJ_VERSION_MAJOR>8 || (PROJ_VERSION_MAJOR==8 && PROJ_VERSION_MINOR>=1)
1436 return QString( proj_get_celestial_body_name( context, pj ) );
1438 throw QgsNotSupportedException( QObject::tr(
"Retrieving celestial body requires a QGIS build based on PROJ 8.1 or later" ) );
1444 if ( d->mCoordinateEpoch == epoch )
1450 d->mCoordinateEpoch = epoch;
1451 d->setPj( std::move( clone ) );
1456 return d->mCoordinateEpoch;
1468#if PROJ_VERSION_MAJOR>=8
1476 res.mName = QString( proj_get_name( ensemble.get() ) );
1477 res.mAuthority = QString( proj_get_id_auth_name( ensemble.get(), 0 ) );
1478 res.mCode = QString( proj_get_id_code( ensemble.get(), 0 ) );
1479 res.mRemarks = QString( proj_get_remarks( ensemble.get() ) );
1480 res.mScope = QString( proj_get_scope( ensemble.get() ) );
1481 res.mAccuracy = proj_datum_ensemble_get_accuracy( context, ensemble.get() );
1483 const int memberCount = proj_datum_ensemble_get_member_count( context, ensemble.get() );
1484 for (
int i = 0; i < memberCount; ++i )
1491 details.mName = QString( proj_get_name( member.get() ) );
1492 details.mAuthority = QString( proj_get_id_auth_name( member.get(), 0 ) );
1493 details.mCode = QString( proj_get_id_code( member.get(), 0 ) );
1494 details.mRemarks = QString( proj_get_remarks( member.get() ) );
1495 details.mScope = QString( proj_get_scope( member.get() ) );
1497 res.mMembers << details;
1501 throw QgsNotSupportedException( QObject::tr(
"Calculating datum ensembles requires a QGIS build based on PROJ 8.0 or later" ) );
1510 QString projString =
toProj();
1511 projString.replace( QLatin1String(
"+type=crs" ), QString() );
1514 if ( !transformation )
1517 PJ_COORD coord = proj_coord( 0, 0, 0, HUGE_VAL );
1518 coord.uv.u = point.
x() * M_PI / 180.0;
1519 coord.uv.v = point.
y() * M_PI / 180.0;
1521 proj_errno_reset( transformation.get() );
1522 const PJ_FACTORS pjFactors = proj_factors( transformation.get(), coord );
1523 if ( proj_errno( transformation.get() ) )
1528 res.mIsValid =
true;
1529 res.mMeridionalScale = pjFactors.meridional_scale;
1530 res.mParallelScale = pjFactors.parallel_scale;
1531 res.mArealScale = pjFactors.areal_scale;
1532 res.mAngularDistortion = pjFactors.angular_distortion;
1533 res.mMeridianParallelAngle = pjFactors.meridian_parallel_angle * 180 / M_PI;
1534 res.mMeridianConvergence = pjFactors.meridian_convergence * 180 / M_PI;
1535 res.mTissotSemimajor = pjFactors.tissot_semimajor;
1536 res.mTissotSemiminor = pjFactors.tissot_semiminor;
1537 res.mDxDlam = pjFactors.dx_dlam;
1538 res.mDxDphi = pjFactors.dx_dphi;
1539 res.mDyDlam = pjFactors.dy_dlam;
1540 res.mDyDphi = pjFactors.dy_dphi;
1552 QString projString =
toProj();
1553 projString.replace( QLatin1String(
"+type=crs" ), QString() );
1554 if ( projString.isEmpty() )
1558 if ( !transformation )
1561 PJ_PROJ_INFO info = proj_pj_info( transformation.get() );
1576 return d->mMapUnits;
1584 PJ *obj = d->threadLocalProjObject();
1589 double southLat = 0;
1591 double northLat = 0;
1594 &westLon, &southLat, &eastLon, &northLat,
nullptr ) )
1609 const auto parts {
authid().split(
':' ) };
1610 if ( parts.length() == 2 )
1612 if ( parts[0] == QLatin1String(
"EPSG" ) )
1613 return QStringLiteral(
"http://www.opengis.net/def/crs/EPSG/0/%1" ).arg( parts[1] ) ;
1614 else if ( parts[0] == QLatin1String(
"OGC" ) )
1616 return QStringLiteral(
"http://www.opengis.net/def/crs/OGC/1.3/%1" ).arg( parts[1] ) ;
1632 const auto parts {
authid().split(
':' ) };
1633 if ( parts.length() == 2 )
1635 if ( parts[0] == QLatin1String(
"EPSG" ) )
1636 return QStringLiteral(
"urn:ogc:def:crs:EPSG::%1" ).arg( parts[1] );
1637 else if ( parts[0] == QLatin1String(
"OGC" ) )
1639 return QStringLiteral(
"urn:ogc:def:crs:OGC:1.3:%1" ).arg( parts[1] );
1670void QgsCoordinateReferenceSystem::setProjString(
const QString &proj4String )
1673 d->mProj4 = proj4String;
1674 d->mWktPreferred.clear();
1677 QString trimmed = proj4String.trimmed();
1679 trimmed += QLatin1String(
" +type=crs" );
1689 const int errNo = proj_context_errno( ctx );
1690 QgsDebugError( QStringLiteral(
"proj string rejected: %1" ).arg( proj_errno_string( errNo ) ) );
1692 d->mIsValid =
false;
1696 d->mEllipsoidAcronym.clear();
1703bool QgsCoordinateReferenceSystem::setWktString(
const QString &wkt )
1706 d->mIsValid =
false;
1707 d->mWktPreferred.clear();
1709 PROJ_STRING_LIST warnings =
nullptr;
1710 PROJ_STRING_LIST grammarErrors =
nullptr;
1718 QgsDebugMsgLevel( QStringLiteral(
"\n---------------------------------------------------------------" ), 2 );
1719 QgsDebugMsgLevel( QStringLiteral(
"This CRS could *** NOT *** be set from the supplied Wkt " ), 2 );
1721 for (
auto iter = warnings; iter && *iter; ++iter )
1723 for (
auto iter = grammarErrors; iter && *iter; ++iter )
1725 QgsDebugMsgLevel( QStringLiteral(
"---------------------------------------------------------------\n" ), 2 );
1727 proj_string_list_destroy( warnings );
1728 proj_string_list_destroy( grammarErrors );
1734 if ( !sDisableWktCache )
1735 sWktCache()->insert( wkt, *
this );
1742 QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
1743 QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
1745 if ( authName.isEmpty() || authCode.isEmpty() )
1751 if ( !authName.isEmpty() && !authCode.isEmpty() )
1753 if ( loadFromAuthCode( authName, authCode ) )
1756 if ( !sDisableWktCache )
1757 sWktCache()->insert( wkt, *
this );
1765 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
1773void QgsCoordinateReferenceSystem::setMapUnits()
1800 if ( !coordinateSystem )
1806 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
1807 if ( axisCount > 0 )
1809 const char *outUnitName =
nullptr;
1811 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
1820 const QString unitName( outUnitName );
1824 if ( unitName.compare( QLatin1String(
"degree" ), Qt::CaseInsensitive ) == 0 ||
1825 unitName.compare( QLatin1String(
"degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1826 unitName.compare( QLatin1String(
"degree minute second hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1827 unitName.compare( QLatin1String(
"degree minute" ), Qt::CaseInsensitive ) == 0 ||
1828 unitName.compare( QLatin1String(
"degree hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1829 unitName.compare( QLatin1String(
"degree minute hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1830 unitName.compare( QLatin1String(
"hemisphere degree" ), Qt::CaseInsensitive ) == 0 ||
1831 unitName.compare( QLatin1String(
"hemisphere degree minute" ), Qt::CaseInsensitive ) == 0 ||
1832 unitName.compare( QLatin1String(
"hemisphere degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1833 unitName.compare( QLatin1String(
"degree (supplier to define representation)" ), Qt::CaseInsensitive ) == 0 )
1835 else if ( unitName.compare( QLatin1String(
"metre" ), Qt::CaseInsensitive ) == 0
1836 || unitName.compare( QLatin1String(
"m" ), Qt::CaseInsensitive ) == 0
1837 || unitName.compare( QLatin1String(
"meter" ), Qt::CaseInsensitive ) == 0 )
1839 else if ( unitName.compare( QLatin1String(
"US survey foot" ), Qt::CaseInsensitive ) == 0 )
1841 else if ( unitName.compare( QLatin1String(
"foot" ), Qt::CaseInsensitive ) == 0 )
1843 else if ( unitName.compare( QLatin1String(
"British yard (Sears 1922)" ), Qt::CaseInsensitive ) == 0 )
1845 else if ( unitName.compare( QLatin1String(
"British yard (Sears 1922 truncated)" ), Qt::CaseInsensitive ) == 0 )
1847 else if ( unitName.compare( QLatin1String(
"British foot (Sears 1922)" ), Qt::CaseInsensitive ) == 0 )
1849 else if ( unitName.compare( QLatin1String(
"British foot (Sears 1922 truncated)" ), Qt::CaseInsensitive ) == 0 )
1851 else if ( unitName.compare( QLatin1String(
"British chain (Sears 1922)" ), Qt::CaseInsensitive ) == 0 )
1853 else if ( unitName.compare( QLatin1String(
"British chain (Sears 1922 truncated)" ), Qt::CaseInsensitive ) == 0 )
1855 else if ( unitName.compare( QLatin1String(
"British link (Sears 1922)" ), Qt::CaseInsensitive ) == 0 )
1857 else if ( unitName.compare( QLatin1String(
"British link (Sears 1922 truncated)" ), Qt::CaseInsensitive ) == 0 )
1859 else if ( unitName.compare( QLatin1String(
"British yard (Benoit 1895 A)" ), Qt::CaseInsensitive ) == 0 )
1861 else if ( unitName.compare( QLatin1String(
"British foot (Benoit 1895 A)" ), Qt::CaseInsensitive ) == 0 )
1863 else if ( unitName.compare( QLatin1String(
"British chain (Benoit 1895 A)" ), Qt::CaseInsensitive ) == 0 )
1865 else if ( unitName.compare( QLatin1String(
"British link (Benoit 1895 A)" ), Qt::CaseInsensitive ) == 0 )
1867 else if ( unitName.compare( QLatin1String(
"British yard (Benoit 1895 B)" ), Qt::CaseInsensitive ) == 0 )
1869 else if ( unitName.compare( QLatin1String(
"British foot (Benoit 1895 B)" ), Qt::CaseInsensitive ) == 0 )
1871 else if ( unitName.compare( QLatin1String(
"British chain (Benoit 1895 B)" ), Qt::CaseInsensitive ) == 0 )
1873 else if ( unitName.compare( QLatin1String(
"British link (Benoit 1895 B)" ), Qt::CaseInsensitive ) == 0 )
1875 else if ( unitName.compare( QLatin1String(
"British foot (1865)" ), Qt::CaseInsensitive ) == 0 )
1877 else if ( unitName.compare( QLatin1String(
"British foot (1936)" ), Qt::CaseInsensitive ) == 0 )
1879 else if ( unitName.compare( QLatin1String(
"Indian foot" ), Qt::CaseInsensitive ) == 0 )
1881 else if ( unitName.compare( QLatin1String(
"Indian foot (1937)" ), Qt::CaseInsensitive ) == 0 )
1883 else if ( unitName.compare( QLatin1String(
"Indian foot (1962)" ), Qt::CaseInsensitive ) == 0 )
1885 else if ( unitName.compare( QLatin1String(
"Indian foot (1975)" ), Qt::CaseInsensitive ) == 0 )
1887 else if ( unitName.compare( QLatin1String(
"Indian yard" ), Qt::CaseInsensitive ) == 0 )
1889 else if ( unitName.compare( QLatin1String(
"Indian yard (1937)" ), Qt::CaseInsensitive ) == 0 )
1891 else if ( unitName.compare( QLatin1String(
"Indian yard (1962)" ), Qt::CaseInsensitive ) == 0 )
1893 else if ( unitName.compare( QLatin1String(
"Indian yard (1975)" ), Qt::CaseInsensitive ) == 0 )
1895 else if ( unitName.compare( QLatin1String(
"Gold Coast foot" ), Qt::CaseInsensitive ) == 0 )
1897 else if ( unitName.compare( QLatin1String(
"Clarke's foot" ), Qt::CaseInsensitive ) == 0 )
1899 else if ( unitName.compare( QLatin1String(
"Clarke's yard" ), Qt::CaseInsensitive ) == 0 )
1901 else if ( unitName.compare( QLatin1String(
"Clarke's chain" ), Qt::CaseInsensitive ) == 0 )
1903 else if ( unitName.compare( QLatin1String(
"Clarke's link" ), Qt::CaseInsensitive ) == 0 )
1905 else if ( unitName.compare( QLatin1String(
"kilometre" ), Qt::CaseInsensitive ) == 0 )
1907 else if ( unitName.compare( QLatin1String(
"centimetre" ), Qt::CaseInsensitive ) == 0 )
1909 else if ( unitName.compare( QLatin1String(
"millimetre" ), Qt::CaseInsensitive ) == 0 )
1911 else if ( unitName.compare( QLatin1String(
"Statute mile" ), Qt::CaseInsensitive ) == 0 )
1913 else if ( unitName.compare( QLatin1String(
"nautical mile" ), Qt::CaseInsensitive ) == 0 )
1915 else if ( unitName.compare( QLatin1String(
"yard" ), Qt::CaseInsensitive ) == 0 )
1917 else if ( unitName.compare( QLatin1String(
"fathom" ), Qt::CaseInsensitive ) == 0 )
1919 else if ( unitName.compare( QLatin1String(
"US survey chain" ), Qt::CaseInsensitive ) == 0 )
1921 else if ( unitName.compare( QLatin1String(
"chain" ), Qt::CaseInsensitive ) == 0 )
1923 else if ( unitName.compare( QLatin1String(
"link" ), Qt::CaseInsensitive ) == 0 )
1925 else if ( unitName.compare( QLatin1String(
"US survey link" ), Qt::CaseInsensitive ) == 0 )
1927 else if ( unitName.compare( QLatin1String(
"US survey mile" ), Qt::CaseInsensitive ) == 0 )
1929 else if ( unitName.compare( QLatin1String(
"German legal metre" ), Qt::CaseInsensitive ) == 0 )
1946 if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull()
1949 QgsDebugMsgLevel(
"QgsCoordinateReferenceSystem::findMatchingProj will only "
1950 "work if prj acr ellipsoid acr and proj4string are set"
1951 " and the current projection is valid!", 4 );
1961 QString mySql = QString(
"select srs_id,parameters from tbl_srs where "
1962 "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated" )
1969 myResult = openDatabase( myDatabaseFileName, database );
1970 if ( myResult != SQLITE_OK )
1975 statement = database.
prepare( mySql, myResult );
1976 if ( myResult == SQLITE_OK )
1979 while ( statement.
step() == SQLITE_ROW )
1983 if (
toProj() == myProj4String.trimmed() )
1985 return mySrsId.toLong();
1996 myResult = openDatabase( myDatabaseFileName, database );
1997 if ( myResult != SQLITE_OK )
2002 statement = database.
prepare( mySql, myResult );
2004 if ( myResult == SQLITE_OK )
2006 while ( statement.
step() == SQLITE_ROW )
2010 if (
toProj() == myProj4String.trimmed() )
2012 return mySrsId.toLong();
2026 if ( !d->mIsValid && !srs.d->mIsValid )
2029 if ( !d->mIsValid || !srs.d->mIsValid )
2037 if ( isUser != otherIsUser )
2041 if ( !isUser && ( !d->mAuthId.isEmpty() || !srs.d->mAuthId.isEmpty() ) )
2042 return d->mAuthId == srs.d->mAuthId;
2049 return !( *
this == srs );
2054 if (
PJ *obj = d->threadLocalProjObject() )
2057 if ( isDefaultPreferredFormat && !d->mWktPreferred.isEmpty() )
2060 return d->mWktPreferred;
2063 PJ_WKT_TYPE
type = PJ_WKT1_GDAL;
2067 type = PJ_WKT1_GDAL;
2070 type = PJ_WKT1_ESRI;
2073 type = PJ_WKT2_2015;
2076 type = PJ_WKT2_2015_SIMPLIFIED;
2079 type = PJ_WKT2_2019;
2082 type = PJ_WKT2_2019_SIMPLIFIED;
2086 const QByteArray multiLineOption = QStringLiteral(
"MULTILINE=%1" ).arg( multiline ? QStringLiteral(
"YES" ) : QStringLiteral(
"NO" ) ).toLocal8Bit();
2087 const QByteArray indentatationWidthOption = QStringLiteral(
"INDENTATION_WIDTH=%1" ).arg( multiline ? QString::number( indentationWidth ) : QStringLiteral(
"0" ) ).toLocal8Bit();
2088 const char *
const options[] = {multiLineOption.constData(), indentatationWidthOption.constData(),
nullptr};
2091 if ( isDefaultPreferredFormat )
2094 d->mWktPreferred = res;
2106 QDomNode srsNode = node.namedItem( QStringLiteral(
"spatialrefsys" ) );
2108 if ( ! srsNode.isNull() )
2110 bool initialized =
false;
2113 long srsid = srsNode.namedItem( QStringLiteral(
"srsid" ) ).toElement().text().toLong( &ok );
2119 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
2120 if ( !node.isNull() )
2131 node = srsNode.namedItem( QStringLiteral(
"epsg" ) );
2132 if ( !node.isNull() )
2150 const QString
description = srsNode.namedItem( QStringLiteral(
"description" ) ).toElement().text();
2152 const QString wkt = srsNode.namedItem( QStringLiteral(
"wkt" ) ).toElement().text();
2153 initialized = createFromWktInternal( wkt,
description );
2158 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
2159 const QString proj4 = node.toElement().text();
2166 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
2167 const QString proj4 = node.toElement().text();
2168 if ( !proj4.trimmed().isEmpty() )
2169 setProjString( node.toElement().text() );
2171 node = srsNode.namedItem( QStringLiteral(
"srsid" ) );
2172 d->mSrsId = node.toElement().text().toLong();
2174 node = srsNode.namedItem( QStringLiteral(
"srid" ) );
2175 d->mSRID = node.toElement().text().toLong();
2177 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
2178 d->mAuthId = node.toElement().text();
2180 node = srsNode.namedItem( QStringLiteral(
"description" ) );
2181 d->mDescription = node.toElement().text();
2183 node = srsNode.namedItem( QStringLiteral(
"projectionacronym" ) );
2184 d->mProjectionAcronym = node.toElement().text();
2186 node = srsNode.namedItem( QStringLiteral(
"ellipsoidacronym" ) );
2187 d->mEllipsoidAcronym = node.toElement().text();
2189 node = srsNode.namedItem( QStringLiteral(
"geographicflag" ) );
2190 d->mIsGeographic = node.toElement().text() == QLatin1String(
"true" );
2192 d->mWktPreferred.clear();
2198 const QString epoch = srsNode.toElement().attribute( QStringLiteral(
"coordinateEpoch" ) );
2199 if ( !epoch.isEmpty() )
2201 bool epochOk =
false;
2202 d->mCoordinateEpoch = epoch.toDouble( &epochOk );
2204 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2208 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2211 mNativeFormat = qgsEnumKeyToValue<Qgis::CrsDefinitionFormat>( srsNode.toElement().attribute( QStringLiteral(
"nativeFormat" ) ),
Qgis::CrsDefinitionFormat::Wkt );
2216 d =
new QgsCoordinateReferenceSystemPrivate();
2224 QDomElement layerNode = node.toElement();
2225 QDomElement srsElement = doc.createElement( QStringLiteral(
"spatialrefsys" ) );
2227 srsElement.setAttribute( QStringLiteral(
"nativeFormat" ), qgsEnumValueToKey<Qgis::CrsDefinitionFormat>( mNativeFormat ) );
2229 if ( std::isfinite( d->mCoordinateEpoch ) )
2231 srsElement.setAttribute( QStringLiteral(
"coordinateEpoch" ), d->mCoordinateEpoch );
2234 QDomElement wktElement = doc.createElement( QStringLiteral(
"wkt" ) );
2236 srsElement.appendChild( wktElement );
2238 QDomElement proj4Element = doc.createElement( QStringLiteral(
"proj4" ) );
2239 proj4Element.appendChild( doc.createTextNode(
toProj() ) );
2240 srsElement.appendChild( proj4Element );
2242 QDomElement srsIdElement = doc.createElement( QStringLiteral(
"srsid" ) );
2243 srsIdElement.appendChild( doc.createTextNode( QString::number(
srsid() ) ) );
2244 srsElement.appendChild( srsIdElement );
2246 QDomElement sridElement = doc.createElement( QStringLiteral(
"srid" ) );
2247 sridElement.appendChild( doc.createTextNode( QString::number(
postgisSrid() ) ) );
2248 srsElement.appendChild( sridElement );
2250 QDomElement authidElement = doc.createElement( QStringLiteral(
"authid" ) );
2251 authidElement.appendChild( doc.createTextNode(
authid() ) );
2252 srsElement.appendChild( authidElement );
2254 QDomElement descriptionElement = doc.createElement( QStringLiteral(
"description" ) );
2255 descriptionElement.appendChild( doc.createTextNode(
description() ) );
2256 srsElement.appendChild( descriptionElement );
2258 QDomElement projectionAcronymElement = doc.createElement( QStringLiteral(
"projectionacronym" ) );
2259 projectionAcronymElement.appendChild( doc.createTextNode(
projectionAcronym() ) );
2260 srsElement.appendChild( projectionAcronymElement );
2262 QDomElement ellipsoidAcronymElement = doc.createElement( QStringLiteral(
"ellipsoidacronym" ) );
2263 ellipsoidAcronymElement.appendChild( doc.createTextNode(
ellipsoidAcronym() ) );
2264 srsElement.appendChild( ellipsoidAcronymElement );
2266 QDomElement geographicFlagElement = doc.createElement( QStringLiteral(
"geographicflag" ) );
2267 QString geoFlagText = QStringLiteral(
"false" );
2270 geoFlagText = QStringLiteral(
"true" );
2273 geographicFlagElement.appendChild( doc.createTextNode( geoFlagText ) );
2274 srsElement.appendChild( geographicFlagElement );
2276 layerNode.appendChild( srsElement );
2288QString QgsCoordinateReferenceSystem::projFromSrsId(
const int srsId )
2290 QString myDatabaseFileName;
2291 QString myProjString;
2292 QString mySql = QStringLiteral(
"select parameters from tbl_srs where srs_id = %1 order by deprecated" ).arg( srsId );
2301 QFileInfo myFileInfo;
2302 myFileInfo.setFile( myDatabaseFileName );
2303 if ( !myFileInfo.exists() )
2305 QgsDebugError( QStringLiteral(
"users qgis.db not found" ) );
2318 rc = openDatabase( myDatabaseFileName, database );
2324 statement = database.
prepare( mySql, rc );
2326 if ( rc == SQLITE_OK )
2328 if ( statement.
step() == SQLITE_ROW )
2334 return myProjString;
2341 myResult = database.
open_v2( path, SQLITE_OPEN_READONLY,
nullptr );
2343 myResult = database.
open( path );
2345 if ( myResult != SQLITE_OK )
2354 .arg( database.
errorMessage() ), QObject::tr(
"CRS" ) );
2361 sCustomSrsValidation = f;
2366 return sCustomSrsValidation;
2369void QgsCoordinateReferenceSystem::debugPrint()
2372 QgsDebugMsgLevel(
"* Valid : " + ( d->mIsValid ? QString(
"true" ) : QString(
"false" ) ), 1 );
2393 mValidationHint = html;
2398 return mValidationHint;
2408 mNativeFormat = format;
2413 return mNativeFormat;
2416long QgsCoordinateReferenceSystem::getRecordCount()
2421 long myRecordCount = 0;
2424 if ( myResult != SQLITE_OK )
2430 QString mySql = QStringLiteral(
"select count(*) from tbl_srs" );
2431 statement = database.
prepare( mySql, myResult );
2432 if ( myResult == SQLITE_OK )
2434 if ( statement.
step() == SQLITE_ROW )
2436 QString myRecordCountString = statement.
columnAsText( 0 );
2437 myRecordCount = myRecordCountString.toLong();
2440 return myRecordCount;
2446 bool isGeographic =
false;
2450 if ( !horizontalCrs )
2454 if ( coordinateSystem )
2456 const int axisCount = proj_cs_get_axis_count( pjContext, coordinateSystem.get() );
2457 if ( axisCount > 0 )
2459 const char *outUnitAuthName =
nullptr;
2460 const char *outUnitAuthCode =
nullptr;
2462 proj_cs_get_axis_info( pjContext, coordinateSystem.get(), 0,
2471 if ( outUnitAuthName && outUnitAuthCode )
2473 const char *unitCategory =
nullptr;
2474 if ( proj_uom_get_info_from_database( pjContext, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
2476 isGeographic = QString( unitCategory ).compare( QLatin1String(
"angular" ), Qt::CaseInsensitive ) == 0;
2481 return isGeographic;
2486 thread_local const QRegularExpression projRegExp( QStringLiteral(
"\\+proj=(\\S+)" ) );
2487 const QRegularExpressionMatch projMatch = projRegExp.match( proj );
2488 if ( !projMatch.hasMatch() )
2490 QgsDebugMsgLevel( QStringLiteral(
"no +proj argument found [%2]" ).arg( proj ), 2 );
2493 operation = projMatch.captured( 1 );
2495 const QRegularExpressionMatch ellipseMatch = projRegExp.match( proj );
2496 if ( ellipseMatch.hasMatch() )
2498 ellipsoid = ellipseMatch.captured( 1 );
2512bool QgsCoordinateReferenceSystem::loadFromAuthCode(
const QString &auth,
const QString &code )
2518 d->mIsValid =
false;
2519 d->mWktPreferred.clear();
2531 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2532 proj4 = proj4.trimmed();
2536 d->mWktPreferred.clear();
2537 d->mDescription = QString( proj_get_name(
crs.get() ) );
2538 d->mAuthId = QStringLiteral(
"%1:%2" ).arg( auth, code );
2540 d->mAxisInvertedDirty =
true;
2545 d->mEllipsoidAcronym.clear();
2546 d->setPj( std::move(
crs ) );
2548 const QString dbVals =
sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( auth, code ).toUpper() );
2549 if ( !dbVals.isEmpty() )
2551 const QStringList parts = dbVals.split(
',' );
2552 d->mSrsId = parts.at( 0 ).toInt();
2553 d->mSRID = parts.at( 1 ).toInt();
2561QList<long> QgsCoordinateReferenceSystem::userSrsIds()
2563 QList<long> results;
2567 QFileInfo myInfo( db );
2568 if ( !myInfo.exists() )
2578 int result = openDatabase( db, database );
2579 if ( result != SQLITE_OK )
2581 QgsDebugError(
"failed : " + db +
" could not be opened!" );
2585 QString sql = QStringLiteral(
"select srs_id from tbl_srs where srs_id >= %1" ).arg(
USER_CRS_START_ID );
2587 statement = database.
prepare( sql, rc );
2590 int ret = statement.
step();
2592 if ( ret == SQLITE_DONE )
2598 if ( ret == SQLITE_ROW )
2604 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
2612long QgsCoordinateReferenceSystem::matchToUserCrs()
const
2614 PJ *obj = d->threadLocalProjObject();
2618 const QList< long > ids = userSrsIds();
2619 for (
long id : ids )
2622 if ( candidate.
projObject() && proj_is_equivalent_to( obj, candidate.
projObject(), PJ_COMP_EQUIVALENT ) )
2630static void sync_db_proj_logger(
void * ,
int level,
const char *message )
2635 if ( level == PJ_LOG_ERROR )
2639 else if ( level == PJ_LOG_DEBUG )
2647 setlocale( LC_ALL,
"C" );
2650 int inserted = 0, updated = 0, deleted = 0, errors = 0;
2655 if ( database.
open( dbFilePath ) != SQLITE_OK )
2661 if ( sqlite3_exec( database.get(),
"BEGIN TRANSACTION",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2669 char *errMsg =
nullptr;
2671 bool createdTypeColumn =
false;
2672 if ( sqlite3_exec( database.get(),
"ALTER TABLE tbl_srs ADD COLUMN srs_type text",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2674 createdTypeColumn =
true;
2675 if ( sqlite3_exec( database.get(),
"CREATE INDEX srs_type ON tbl_srs(srs_type)",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2677 QgsDebugError( QStringLiteral(
"Could not create index for srs_type" ) );
2682 if ( sqlite3_exec( database.get(),
"create table tbl_info (proj_major INT, proj_minor INT, proj_patch INT)",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2684 QString sql = QStringLiteral(
"INSERT INTO tbl_info(proj_major, proj_minor, proj_patch) VALUES (%1, %2,%3)" )
2685 .arg( QString::number( PROJ_VERSION_MAJOR ),
2686 QString::number( PROJ_VERSION_MINOR ),
2687 QString::number( PROJ_VERSION_PATCH ) );
2688 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2690 QgsDebugError( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2693 errMsg ? errMsg :
"(unknown error)" ) );
2695 sqlite3_free( errMsg );
2702 QString sql = QStringLiteral(
"SELECT proj_major, proj_minor, proj_patch FROM tbl_info" );
2703 statement = database.
prepare( sql, result );
2704 if ( result != SQLITE_OK )
2709 if ( statement.
step() == SQLITE_ROW )
2714 if ( !createdTypeColumn && major == PROJ_VERSION_MAJOR && minor == PROJ_VERSION_MINOR && patch == PROJ_VERSION_PATCH )
2720 QgsDebugError( QStringLiteral(
"Could not retrieve previous CRS sync PROJ version number" ) );
2727 proj_log_func( pjContext,
nullptr, sync_db_proj_logger );
2729 PROJ_STRING_LIST authorities = proj_get_authorities_from_database( pjContext );
2731 int nextSrsId = 67218;
2732 int nextSrId = 520007218;
2733 for (
auto authIter = authorities; authIter && *authIter; ++authIter )
2735 const QString authority( *authIter );
2736 QgsDebugMsgLevel( QStringLiteral(
"Loading authority '%1'" ).arg( authority ), 2 );
2737 PROJ_STRING_LIST codes = proj_get_codes_from_database( pjContext, *authIter, PJ_TYPE_CRS,
true );
2739 QStringList allCodes;
2741 for (
auto codesIter = codes; codesIter && *codesIter; ++codesIter )
2743 const QString code( *codesIter );
2749 QgsDebugError( QStringLiteral(
"Could not load '%1:%2'" ).arg( authority, code ) );
2753 const PJ_TYPE pjType = proj_get_type(
crs.get( ) );
2755 QString srsTypeString;
2760 case PJ_TYPE_ELLIPSOID:
2761 case PJ_TYPE_PRIME_MERIDIAN:
2762 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
2763 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
2764 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
2765 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
2766 case PJ_TYPE_DATUM_ENSEMBLE:
2767 case PJ_TYPE_CONVERSION:
2768 case PJ_TYPE_TRANSFORMATION:
2769 case PJ_TYPE_CONCATENATED_OPERATION:
2770 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
2771 case PJ_TYPE_TEMPORAL_DATUM:
2772 case PJ_TYPE_ENGINEERING_DATUM:
2773 case PJ_TYPE_PARAMETRIC_DATUM:
2774 case PJ_TYPE_UNKNOWN:
2778 case PJ_TYPE_GEOGRAPHIC_CRS:
2781 case PJ_TYPE_GEODETIC_CRS:
2785 case PJ_TYPE_GEOCENTRIC_CRS:
2789 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
2793 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
2797 case PJ_TYPE_PROJECTED_CRS:
2801 case PJ_TYPE_COMPOUND_CRS:
2805 case PJ_TYPE_TEMPORAL_CRS:
2809 case PJ_TYPE_ENGINEERING_CRS:
2813 case PJ_TYPE_BOUND_CRS:
2817 case PJ_TYPE_VERTICAL_CRS:
2821#if PROJ_VERSION_MAJOR>9 || (PROJ_VERSION_MAJOR==9 && PROJ_VERSION_MINOR>=2)
2822 case PJ_TYPE_DERIVED_PROJECTED_CRS:
2825 case PJ_TYPE_COORDINATE_METADATA:
2828 case PJ_TYPE_OTHER_CRS:
2837 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2838 proj4 = proj4.trimmed();
2840 if ( proj4.isEmpty() )
2842 QgsDebugMsgLevel( QStringLiteral(
"No proj4 for '%1:%2'" ).arg( authority, code ), 2 );
2853 if ( translatedOperation.isEmpty() && !
operation.isEmpty() )
2855 std::cout << QStringLiteral(
"Operation needs translation in QgsCoordinateReferenceSystemUtils::translateProjection: %1" ).arg(
operation ).toLocal8Bit().constData() << std::endl;
2856 qFatal(
"aborted" );
2859 const bool deprecated = proj_is_deprecated(
crs.get() );
2860 const QString name( proj_get_name(
crs.get() ) );
2862 QString sql = QStringLiteral(
"SELECT parameters,description,deprecated,srs_type FROM tbl_srs WHERE auth_name='%1' AND auth_id='%2'" ).arg( authority, code );
2863 statement = database.
prepare( sql, result );
2864 if ( result != SQLITE_OK )
2873 bool dbSrsDeprecated = deprecated;
2874 if ( statement.
step() == SQLITE_ROW )
2878 dbSrsDeprecated = statement.
columnAsText( 2 ).toInt() != 0;
2882 if ( !dbSrsProj4.isEmpty() || !dbSrsDesc.isEmpty() )
2884 if ( proj4 != dbSrsProj4 || name != dbSrsDesc || deprecated != dbSrsDeprecated || dbSrsType != srsTypeString )
2887 sql = QStringLiteral(
"UPDATE tbl_srs SET parameters=%1,description=%2,deprecated=%3, srs_type=%4 WHERE auth_name=%5 AND auth_id=%6" )
2890 .arg( deprecated ? 1 : 0 )
2894 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2896 QgsDebugError( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2899 errMsg ? errMsg :
"(unknown error)" ) );
2901 sqlite3_free( errMsg );
2915 const QString dbVals =
sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( authority, code ) );
2918 if ( !dbVals.isEmpty() )
2920 const QStringList parts = dbVals.split(
',' );
2921 srsId = parts.at( 0 );
2922 srId = parts.at( 1 );
2924 if ( srId.isEmpty() )
2926 srId = QString::number( nextSrId );
2929 if ( srsId.isEmpty() )
2931 srsId = QString::number( nextSrsId );
2935 if ( !srsId.isEmpty() )
2937 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)" )
2947 .arg( deprecated ? 1 : 0 )
2952 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)" )
2961 .arg( deprecated ? 1 : 0 )
2966 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) == SQLITE_OK )
2972 qCritical(
"Could not execute: %s [%s/%s]\n",
2973 sql.toLocal8Bit().constData(),
2974 sqlite3_errmsg( database.get() ),
2975 errMsg ? errMsg :
"(unknown error)" );
2979 sqlite3_free( errMsg );
2984 proj_string_list_destroy( codes );
2986 const QString sql = QStringLiteral(
"DELETE FROM tbl_srs WHERE auth_name='%1' AND NOT auth_id IN (%2)" ).arg( authority, allCodes.join(
',' ) );
2987 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2989 deleted = sqlite3_changes( database.get() );
2994 qCritical(
"Could not execute: %s [%s]\n",
2995 sql.toLocal8Bit().constData(),
2996 sqlite3_errmsg( database.get() ) );
3000 proj_string_list_destroy( authorities );
3002 QString sql = QStringLiteral(
"UPDATE tbl_info set proj_major=%1,proj_minor=%2,proj_patch=%3" )
3003 .arg( QString::number( PROJ_VERSION_MAJOR ),
3004 QString::number( PROJ_VERSION_MINOR ),
3005 QString::number( PROJ_VERSION_PATCH ) );
3006 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
3008 QgsDebugError( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
3011 errMsg ? errMsg :
"(unknown error)" ) );
3013 sqlite3_free( errMsg );
3017 if ( sqlite3_exec( database.get(),
"COMMIT",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
3019 QgsDebugError( QStringLiteral(
"Could not commit transaction: %1 [%2]\n" ).arg(
3021 sqlite3_errmsg( database.get() ) )
3027 QgsDebugMsgLevel( QStringLiteral(
"CRS update (inserted:%1 updated:%2 deleted:%3 errors:%4)" ).arg( inserted ).arg( updated ).arg( deleted ).arg( errors ), 4 );
3035 return updated + inserted;
3038const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::stringCache()
3040 return *sStringCache();
3043const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::projCache()
3045 return *sProj4Cache();
3048const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::ogcCache()
3050 return *sOgcCache();
3053const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::wktCache()
3055 return *sWktCache();
3058const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srIdCache()
3060 return *sSrIdCache();
3063const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srsIdCache()
3065 return *sSrsIdCache();
3075 if (
PJ *obj = d->threadLocalProjObject() )
3121 if (
PJ *obj = d->threadLocalProjObject() )
3154 if (
PJ *obj = d->threadLocalProjObject() )
3165 if (
PJ *obj = d->threadLocalProjObject() )
3178 else if (
PJ *obj = d->threadLocalProjObject() )
3181 return geoCrs ? QStringLiteral(
"%1:%2" ).arg( proj_get_id_auth_name( geoCrs.get(), 0 ), proj_get_id_code( geoCrs.get(), 0 ) ) : QString();
3191 return d->threadLocalProjObject();
3204 d->mIsValid =
false;
3206 d->mWktPreferred.clear();
3213 switch ( proj_get_type(
object ) )
3215 case PJ_TYPE_GEODETIC_CRS:
3216 case PJ_TYPE_GEOCENTRIC_CRS:
3217 case PJ_TYPE_GEOGRAPHIC_CRS:
3218 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
3219 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
3220 case PJ_TYPE_VERTICAL_CRS:
3221 case PJ_TYPE_PROJECTED_CRS:
3222 case PJ_TYPE_COMPOUND_CRS:
3223 case PJ_TYPE_TEMPORAL_CRS:
3224 case PJ_TYPE_ENGINEERING_CRS:
3225 case PJ_TYPE_BOUND_CRS:
3226 case PJ_TYPE_OTHER_CRS:
3242 const QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
3243 const QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
3244 if ( !authName.isEmpty() && !authCode.isEmpty() && loadFromAuthCode( authName, authCode ) )
3252 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
3263 QStringList projections;
3265 projections.reserve( res.size() );
3268 projections << QString::number(
crs.
srsid() );
3295 sSrIdCacheLock()->lockForWrite();
3296 if ( !sDisableSrIdCache )
3299 sDisableSrIdCache =
true;
3300 sSrIdCache()->clear();
3302 sSrIdCacheLock()->unlock();
3304 sOgcLock()->lockForWrite();
3305 if ( !sDisableOgcCache )
3308 sDisableOgcCache =
true;
3309 sOgcCache()->clear();
3311 sOgcLock()->unlock();
3313 sProj4CacheLock()->lockForWrite();
3314 if ( !sDisableProjCache )
3317 sDisableProjCache =
true;
3318 sProj4Cache()->clear();
3320 sProj4CacheLock()->unlock();
3322 sCRSWktLock()->lockForWrite();
3323 if ( !sDisableWktCache )
3326 sDisableWktCache =
true;
3327 sWktCache()->clear();
3329 sCRSWktLock()->unlock();
3331 sCRSSrsIdLock()->lockForWrite();
3332 if ( !sDisableSrsIdCache )
3335 sDisableSrsIdCache =
true;
3336 sSrsIdCache()->clear();
3338 sCRSSrsIdLock()->unlock();
3340 sCrsStringLock()->lockForWrite();
3341 if ( !sDisableStringCache )
3344 sDisableStringCache =
true;
3345 sStringCache()->clear();
3347 sCrsStringLock()->unlock();
3356 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3359 if ( !c1.d->mIsValid && c2.d->mIsValid )
3362 if ( c1.d->mIsValid && !c2.d->mIsValid )
3368 if ( c1IsUser && !c2IsUser )
3371 if ( !c1IsUser && c2IsUser )
3374 if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
3376 if ( c1.d->mAuthId != c2.d->mAuthId )
3377 return c1.d->mAuthId > c2.d->mAuthId;
3385 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3388 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3391 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3394 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3397 return c1.d->mCoordinateEpoch > c2.d->mCoordinateEpoch;
3405 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3408 if ( c1.d->mIsValid && !c2.d->mIsValid )
3411 if ( !c1.d->mIsValid && c2.d->mIsValid )
3417 if ( !c1IsUser && c2IsUser )
3420 if ( c1IsUser && !c2IsUser )
3423 if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
3425 if ( c1.d->mAuthId != c2.d->mAuthId )
3426 return c1.d->mAuthId < c2.d->mAuthId;
3434 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3437 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3440 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3443 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3446 return c1.d->mCoordinateEpoch < c2.d->mCoordinateEpoch;
3451 return !( c1 < c2 );
3455 return !( c1 > c2 );
The Qgis class provides global constants for use throughout the application.
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.
@ YardsBritishSears1922Truncated
British yards (Sears 1922 truncated)
@ MilesUSSurvey
US Survey miles.
@ LinksBritishSears1922
British links (Sears 1922)
@ YardsBritishBenoit1895A
British yards (Benoit 1895 A)
@ LinksBritishBenoit1895A
British links (Benoit 1895 A)
@ Centimeters
Centimeters.
@ YardsIndian1975
Indian yards (1975)
@ FeetUSSurvey
US Survey feet.
@ Millimeters
Millimeters.
@ FeetBritishSears1922
British feet (Sears 1922)
@ YardsClarkes
Clarke's yards.
@ YardsIndian
Indian yards.
@ FeetBritishBenoit1895B
British feet (Benoit 1895 B)
@ Miles
Terrestrial miles.
@ LinksUSSurvey
US Survey links.
@ ChainsUSSurvey
US Survey chains.
@ FeetClarkes
Clarke's feet.
@ Unknown
Unknown distance unit.
@ FeetBritish1936
British feet (1936)
@ FeetIndian1962
Indian feet (1962)
@ YardsBritishSears1922
British yards (Sears 1922)
@ FeetIndian1937
Indian feet (1937)
@ YardsIndian1937
Indian yards (1937)
@ Degrees
Degrees, for planar geographic CRS distance measurements.
@ ChainsBritishBenoit1895B
British chains (Benoit 1895 B)
@ LinksBritishSears1922Truncated
British links (Sears 1922 truncated)
@ ChainsBritishBenoit1895A
British chains (Benoit 1895 A)
@ YardsBritishBenoit1895B
British yards (Benoit 1895 B)
@ FeetBritish1865
British feet (1865)
@ YardsIndian1962
Indian yards (1962)
@ FeetBritishSears1922Truncated
British feet (Sears 1922 truncated)
@ MetersGermanLegal
German legal meter.
@ LinksBritishBenoit1895B
British links (Benoit 1895 B)
@ ChainsInternational
International chains.
@ LinksInternational
International links.
@ ChainsBritishSears1922Truncated
British chains (Sears 1922 truncated)
@ FeetIndian
Indian (geodetic) feet.
@ NauticalMiles
Nautical miles.
@ ChainsClarkes
Clarke's chains.
@ LinksClarkes
Clarke's links.
@ ChainsBritishSears1922
British chains (Sears 1922)
@ FeetIndian1975
Indian feet (1975)
@ FeetGoldCoast
Gold Coast feet.
@ FeetBritishBenoit1895A
British feet (Benoit 1895 A)
@ 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
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
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)
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.
@ UNKNOWN
Unknown/unhandled flavor.
@ AUTH_CODE
E.g EPSG:4326.
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