19#include "moc_qgscoordinatereferencesystem.cpp"
32#include <QRegularExpression>
52#include <proj_experimental.h>
55#include <ogr_srs_api.h>
67bool QgsCoordinateReferenceSystem::sDisableSrIdCache =
false;
71bool QgsCoordinateReferenceSystem::sDisableOgcCache =
false;
75bool QgsCoordinateReferenceSystem::sDisableProjCache =
false;
79bool QgsCoordinateReferenceSystem::sDisableWktCache =
false;
83bool QgsCoordinateReferenceSystem::sDisableSrsIdCache =
false;
87bool QgsCoordinateReferenceSystem::sDisableStringCache =
false;
96 if (
const char *proj4src = proj_as_proj_string(
QgsProjContext::get(), boundCrs.get(), PJ_PROJ_4,
nullptr ) )
98 return QString( proj4src );
115 d =
new QgsCoordinateReferenceSystemPrivate();
121 d =
new QgsCoordinateReferenceSystemPrivate();
129 , mValidationHint( srs.mValidationHint )
130 , mNativeFormat( srs.mNativeFormat )
137 mValidationHint = srs.mValidationHint;
138 mNativeFormat = srs.mNativeFormat;
148 const auto constDbs = dbs;
149 for (
const QString &db : constDbs )
151 QFileInfo myInfo( db );
152 if ( !myInfo.exists() )
162 int result = openDatabase( db, database );
163 if ( result != SQLITE_OK )
169 QString sql = QStringLiteral(
"select srs_id from tbl_srs" );
171 statement = database.
prepare( sql, rc );
175 int ret = statement.
step();
177 if ( ret == SQLITE_DONE )
183 if ( ret == SQLITE_ROW )
189 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
194 std::sort( results.begin(), results.end() );
250 if ( horizontalObj && verticalObj )
257 QStringList formattedErrorList;
258 for (
const QString &rawError : std::as_const( errors ) )
260 QString formattedError = rawError;
261 formattedError.replace( QLatin1String(
"proj_create_compound_crs: " ), QString() );
262 formattedErrorList.append( formattedError );
264 error = formattedErrorList.join(
'\n' );
291 QgsDebugError( QStringLiteral(
"Unexpected case reached!" ) );
298 if ( definition.isEmpty() )
302 if ( !sDisableStringCache )
304 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sStringCache()->constFind( definition );
305 if ( crsIt != sStringCache()->constEnd() )
308 *
this = crsIt.value();
315 const thread_local QRegularExpression reCrsId( QStringLiteral(
"^(epsg|esri|osgeo|ignf|ogc|nkg|zangi|iau_2015|iau2000|postgis|internal|user)\\:(\\w+)$" ), QRegularExpression::CaseInsensitiveOption );
316 QRegularExpressionMatch match = reCrsId.match( definition );
317 if ( match.capturedStart() == 0 )
319 QString authName = match.captured( 1 ).toLower();
320 if ( authName == QLatin1String(
"epsg" ) )
324 else if ( authName == QLatin1String(
"postgis" ) )
326 const long id = match.captured( 2 ).toLong();
331 else if ( authName == QLatin1String(
"esri" )
332 || authName == QLatin1String(
"osgeo" )
333 || authName == QLatin1String(
"ignf" )
334 || authName == QLatin1String(
"zangi" )
335 || authName == QLatin1String(
"iau2000" )
336 || authName == QLatin1String(
"ogc" )
337 || authName == QLatin1String(
"nkg" )
338 || authName == QLatin1String(
"iau_2015" )
345 const long id = match.captured( 2 ).toLong();
353 const thread_local QRegularExpression reCrsStr( QStringLiteral(
"^(?:(wkt|proj4|proj)\\:)?(.+)$" ), QRegularExpression::CaseInsensitiveOption );
354 match = reCrsStr.match( definition );
355 if ( match.capturedStart() == 0 )
357 if ( match.captured( 1 ).startsWith( QLatin1String(
"proj" ), Qt::CaseInsensitive ) )
369 if ( !sDisableStringCache )
370 sStringCache()->insert( definition, *
this );
376 if ( definition.isEmpty() )
382 if ( OSRSetFromUserInput(
crs, definition.toLocal8Bit().constData() ) == OGRERR_NONE )
385 OSRDestroySpatialReference(
crs );
395 const char *configOld = CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" );
396 const char *configNew =
"GEOGCS";
398 if ( strcmp( configOld,
"" ) == 0 )
400 CPLSetConfigOption(
"GDAL_FIX_ESRI_WKT", configNew );
401 if ( strcmp( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) != 0 )
403 .arg( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) );
404 QgsDebugMsgLevel( QStringLiteral(
"set GDAL_FIX_ESRI_WKT : %1" ).arg( configNew ), 4 );
408 QgsDebugMsgLevel( QStringLiteral(
"GDAL_FIX_ESRI_WKT was already set : %1" ).arg( configNew ), 4 );
418 if ( !sDisableOgcCache )
420 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sOgcCache()->constFind(
crs );
421 if ( crsIt != sOgcCache()->constEnd() )
424 *
this = crsIt.value();
430 QString wmsCrs =
crs;
435 const QString authorityLower = authority.toLower();
437 ( authorityLower == QLatin1String(
"user" ) ||
438 authorityLower == QLatin1String(
"custom" ) ||
439 authorityLower == QLatin1String(
"qgis" ) ) )
444 if ( !sDisableOgcCache )
445 sOgcCache()->insert(
crs, *
this );
451 wmsCrs = authority +
':' + code;
455 const QString legacyKey = wmsCrs.toLower();
458 if ( it.key().compare( legacyKey, Qt::CaseInsensitive ) == 0 )
460 const QStringList parts = it.key().split(
':' );
461 const QString auth = parts.at( 0 );
462 const QString code = parts.at( 1 );
463 if ( loadFromAuthCode( auth, code ) )
466 if ( !sDisableOgcCache )
467 sOgcCache()->insert(
crs, *
this );
476 if ( !sDisableOgcCache )
477 sOgcCache()->insert(
crs, *
this );
482 if ( wmsCrs.compare( QLatin1String(
"CRS:27" ), Qt::CaseInsensitive ) == 0 ||
483 wmsCrs.compare( QLatin1String(
"OGC:CRS27" ), Qt::CaseInsensitive ) == 0 )
490 if ( wmsCrs.compare( QLatin1String(
"CRS:83" ), Qt::CaseInsensitive ) == 0 ||
491 wmsCrs.compare( QLatin1String(
"OGC:CRS83" ), Qt::CaseInsensitive ) == 0 )
498 if ( wmsCrs.compare( QLatin1String(
"CRS:84" ), Qt::CaseInsensitive ) == 0 ||
499 wmsCrs.compare( QLatin1String(
"OGC:CRS84" ), Qt::CaseInsensitive ) == 0 )
503 d->mAxisInverted =
false;
504 d->mAxisInvertedDirty =
false;
508 if ( !sDisableOgcCache )
509 sOgcCache()->insert(
crs, *
this );
516 if ( !authority.isEmpty() && !code.isEmpty() && loadFromAuthCode( authority, code ) )
519 if ( !sDisableOgcCache )
520 sOgcCache()->insert(
crs, *
this );
525 if ( !sDisableOgcCache )
535 if ( d->mIsValid || !sCustomSrsValidation )
539 if ( sCustomSrsValidation )
540 sCustomSrsValidation( *
this );
546 if ( !sDisableSrIdCache )
548 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrIdCache()->constFind(
id );
549 if ( crsIt != sSrIdCache()->constEnd() )
552 *
this = crsIt.value();
561 if ( it.value().endsWith( QStringLiteral(
",%1" ).arg(
id ) ) )
563 const QStringList parts = it.key().split(
':' );
564 const QString auth = parts.at( 0 );
565 const QString code = parts.at( 1 );
566 if ( loadFromAuthCode( auth, code ) )
569 if ( !sDisableSrIdCache )
570 sSrIdCache()->insert(
id, *
this );
580 if ( !sDisableSrIdCache )
581 sSrIdCache()->insert(
id, *
this );
589 if ( !sDisableSrsIdCache )
591 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrsIdCache()->constFind(
id );
592 if ( crsIt != sSrsIdCache()->constEnd() )
595 *
this = crsIt.value();
604 if ( it.value().startsWith( QString::number(
id ) +
',' ) )
606 const QStringList parts = it.key().split(
':' );
607 const QString auth = parts.at( 0 );
608 const QString code = parts.at( 1 );
609 if ( loadFromAuthCode( auth, code ) )
612 if ( !sDisableSrsIdCache )
613 sSrsIdCache()->insert(
id, *
this );
621 QStringLiteral(
"srs_id" ), QString::number(
id ) );
624 if ( !sDisableSrsIdCache )
625 sSrsIdCache()->insert(
id, *
this );
629bool QgsCoordinateReferenceSystem::loadFromDatabase(
const QString &db,
const QString &expression,
const QString &value )
633 QgsDebugMsgLevel(
"load CRS from " + db +
" where " + expression +
" is " + value, 3 );
635 d->mWktPreferred.clear();
637 QFileInfo myInfo( db );
638 if ( !myInfo.exists() )
648 myResult = openDatabase( db, database );
649 if ( myResult != SQLITE_OK )
666 QString mySql =
"select srs_id,description,projection_acronym,"
667 "ellipsoid_acronym,parameters,srid,auth_name||':'||auth_id,is_geo,wkt "
669 statement = database.
prepare( mySql, myResult );
672 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
677 d->mEllipsoidAcronym.clear();
679 d->mWktPreferred.clear();
682 d->mIsGeographic = statement.
columnAsText( 7 ).toInt() != 0;
684 d->mAxisInvertedDirty =
true;
686 if ( d->mSrsId >=
USER_CRS_START_ID && ( d->mAuthId.isEmpty() || d->mAuthId == QChar(
':' ) ) )
688 d->mAuthId = QStringLiteral(
"USER:%1" ).arg( d->mSrsId );
690 else if ( !d->mAuthId.startsWith( QLatin1String(
"USER:" ), Qt::CaseInsensitive ) )
692 QStringList parts = d->mAuthId.split(
':' );
693 QString auth = parts.at( 0 );
694 QString code = parts.at( 1 );
701 d->mIsValid = d->hasPj();
707 if ( !wkt.isEmpty() )
715 setProjString( d->mProj4 );
725void QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread(
PJ_CONTEXT *pj_context )
732 if ( !sDisableSrIdCache )
735 if ( !sDisableSrIdCache )
737 for (
auto it = sSrIdCache()->begin(); it != sSrIdCache()->end(); )
739 auto &v = it.value();
740 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
741 it = sSrIdCache()->erase( it );
747 if ( !sDisableOgcCache )
750 if ( !sDisableOgcCache )
752 for (
auto it = sOgcCache()->begin(); it != sOgcCache()->end(); )
754 auto &v = it.value();
755 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
756 it = sOgcCache()->erase( it );
762 if ( !sDisableProjCache )
765 if ( !sDisableProjCache )
767 for (
auto it = sProj4Cache()->begin(); it != sProj4Cache()->end(); )
769 auto &v = it.value();
770 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
771 it = sProj4Cache()->erase( it );
777 if ( !sDisableWktCache )
780 if ( !sDisableWktCache )
782 for (
auto it = sWktCache()->begin(); it != sWktCache()->end(); )
784 auto &v = it.value();
785 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
786 it = sWktCache()->erase( it );
792 if ( !sDisableSrsIdCache )
795 if ( !sDisableSrsIdCache )
797 for (
auto it = sSrsIdCache()->begin(); it != sSrsIdCache()->end(); )
799 auto &v = it.value();
800 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
801 it = sSrsIdCache()->erase( it );
807 if ( !sDisableStringCache )
810 if ( !sDisableStringCache )
812 for (
auto it = sStringCache()->begin(); it != sStringCache()->end(); )
814 auto &v = it.value();
815 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
816 it = sStringCache()->erase( it );
826 if ( d->mAxisInvertedDirty )
829 d->mAxisInvertedDirty =
false;
832 return d->mAxisInverted;
849 const thread_local QMap< Qgis::CrsAxisDirection, QString > mapping =
892 QList< Qgis::CrsAxisDirection > res;
893 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
896 res.reserve( axisCount );
898 for (
int i = 0; i < axisCount; ++i )
900 const char *outDirection =
nullptr;
901 proj_cs_get_axis_info( context, pjCs.get(), i,
911 const thread_local QRegularExpression rx( QStringLiteral(
"([^\\s]+).*" ) );
912 const QRegularExpressionMatch match = rx.match( QString( outDirection ) );
913 if ( !match.hasMatch() )
916 const QString direction = match.captured( 1 );
918 for (
auto it = mapping.constBegin(); it != mapping.constEnd(); ++it )
920 if ( it.value().compare( direction, Qt::CaseInsensitive ) == 0 )
935 return createFromWktInternal( wkt, QString() );
938bool QgsCoordinateReferenceSystem::createFromWktInternal(
const QString &wkt,
const QString &description )
946 if ( !sDisableWktCache )
948 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sWktCache()->constFind( wkt );
949 if ( crsIt != sWktCache()->constEnd() )
952 *
this = crsIt.value();
954 if ( !
description.isEmpty() && d->mDescription.isEmpty() )
959 sWktCache()->insert( wkt, *
this );
968 d->mWktPreferred.clear();
971 QgsDebugMsgLevel( QStringLiteral(
"theWkt is uninitialized, operation failed" ), 4 );
976 QgsCoordinateReferenceSystem::RecordMap record = getRecord(
"select * from tbl_srs where wkt=" +
QgsSqliteUtils::quotedString( wkt ) +
" order by deprecated" );
977 if ( !record.empty() )
979 long srsId = record[QStringLiteral(
"srs_id" )].toLong();
992 if ( d->mSrsId == 0 )
995 long id = matchToUserCrs();
1004 if ( !sDisableWktCache )
1005 sWktCache()->insert( wkt, *
this );
1023 if ( projString.isEmpty() )
1028 if ( projString.trimmed().isEmpty() )
1030 d->mIsValid =
false;
1032 d->mWktPreferred.clear();
1037 if ( !sDisableProjCache )
1039 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sProj4Cache()->constFind( projString );
1040 if ( crsIt != sProj4Cache()->constEnd() )
1043 *
this = crsIt.value();
1057 QString myProj4String = projString.trimmed();
1058 myProj4String.remove( QStringLiteral(
"+type=crs" ) );
1059 myProj4String = myProj4String.trimmed();
1061 d->mIsValid =
false;
1062 d->mWktPreferred.clear();
1067 const QString projCrsString = myProj4String + ( myProj4String.contains( QStringLiteral(
"+type=crs" ) ) ? QString() : QStringLiteral(
" +type=crs" ) );
1075 const QString
authid = QStringLiteral(
"%1:%2" ).arg( authName, authCode );
1079 if ( !sDisableProjCache )
1080 sProj4Cache()->insert( projString, *
this );
1087 QgsCoordinateReferenceSystem::RecordMap myRecord = getRecord(
"select * from tbl_srs where parameters=" +
QgsSqliteUtils::quotedString( myProj4String ) +
" order by deprecated" );
1089 if ( !myRecord.empty() )
1091 id = myRecord[QStringLiteral(
"srs_id" )].toLong();
1100 setProjString( myProj4String );
1103 id = matchToUserCrs();
1112 setProjString( myProj4String );
1116 if ( !sDisableProjCache )
1117 sProj4Cache()->insert( projString, *
this );
1123QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord(
const QString &sql )
1125 QString myDatabaseFileName;
1126 QgsCoordinateReferenceSystem::RecordMap myMap;
1127 QString myFieldName;
1128 QString myFieldValue;
1135 QFileInfo myInfo( myDatabaseFileName );
1136 if ( !myInfo.exists() )
1138 QgsDebugError(
"failed : " + myDatabaseFileName +
" does not exist!" );
1143 myResult = openDatabase( myDatabaseFileName, database );
1144 if ( myResult != SQLITE_OK )
1149 statement = database.
prepare( sql, myResult );
1151 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1155 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1157 myFieldName = statement.
columnName( myColNo );
1159 myMap[myFieldName] = myFieldValue;
1161 if ( statement.
step() != SQLITE_DONE )
1163 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1172 if ( myMap.empty() )
1175 QFileInfo myFileInfo;
1176 myFileInfo.setFile( myDatabaseFileName );
1177 if ( !myFileInfo.exists() )
1179 QgsDebugError( QStringLiteral(
"user qgis.db not found" ) );
1184 myResult = openDatabase( myDatabaseFileName, database );
1185 if ( myResult != SQLITE_OK )
1190 statement = database.
prepare( sql, myResult );
1192 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1196 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1198 myFieldName = statement.
columnName( myColNo );
1200 myMap[myFieldName] = myFieldValue;
1203 if ( statement.
step() != SQLITE_DONE )
1205 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1236 if ( d->mDescription.isNull() )
1242 return d->mDescription;
1249 if ( !
authid().isEmpty() )
1259 id =
isValid() ? QObject::tr(
"Custom CRS" ) : QObject::tr(
"Unknown CRS" );
1261 id = QObject::tr(
"Custom CRS: %1" ).arg(
1264 else if ( !
toProj().isEmpty() )
1267 if ( !
id.isEmpty() && !std::isnan( d->mCoordinateEpoch ) )
1268 id += QStringLiteral(
" @ %1" ).arg(
qgsDoubleToString( d->mCoordinateEpoch, 3 ) );
1275 if ( d->mProjectionAcronym.isNull() )
1281 return d->mProjectionAcronym;
1287 if ( d->mEllipsoidAcronym.isNull() )
1289 if (
PJ *obj = d->threadLocalProjObject() )
1294 const QString ellipsoidAuthName( proj_get_id_auth_name( ellipsoid.get(), 0 ) );
1295 const QString ellipsoidAuthCode( proj_get_id_code( ellipsoid.get(), 0 ) );
1296 if ( !ellipsoidAuthName.isEmpty() && !ellipsoidAuthCode.isEmpty() )
1297 d->mEllipsoidAcronym = QStringLiteral(
"%1:%2" ).arg( ellipsoidAuthName, ellipsoidAuthCode );
1300 double semiMajor, semiMinor, invFlattening;
1301 int semiMinorComputed = 0;
1302 if ( proj_ellipsoid_get_parameters(
QgsProjContext::get(), ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
1304 d->mEllipsoidAcronym = QStringLiteral(
"PARAMETER:%1:%2" ).arg(
qgsDoubleToString( semiMajor ),
1309 d->mEllipsoidAcronym.clear();
1314 return d->mEllipsoidAcronym;
1318 return d->mEllipsoidAcronym;
1332 if ( d->mProj4.isEmpty() )
1334 if (
PJ *obj = d->threadLocalProjObject() )
1340 return d->mProj4.trimmed();
1346 switch ( d->mProjType )
1348 case PJ_TYPE_UNKNOWN:
1351 case PJ_TYPE_ELLIPSOID:
1352 case PJ_TYPE_PRIME_MERIDIAN:
1353 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
1354 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
1355 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
1356 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
1357 case PJ_TYPE_DATUM_ENSEMBLE:
1358 case PJ_TYPE_CONVERSION:
1359 case PJ_TYPE_TRANSFORMATION:
1360 case PJ_TYPE_CONCATENATED_OPERATION:
1361 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
1362 case PJ_TYPE_TEMPORAL_DATUM:
1363 case PJ_TYPE_ENGINEERING_DATUM:
1364 case PJ_TYPE_PARAMETRIC_DATUM:
1368 case PJ_TYPE_GEOGRAPHIC_CRS:
1372 case PJ_TYPE_GEODETIC_CRS:
1374 case PJ_TYPE_GEOCENTRIC_CRS:
1376 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
1378 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
1380 case PJ_TYPE_VERTICAL_CRS:
1382 case PJ_TYPE_PROJECTED_CRS:
1384 case PJ_TYPE_COMPOUND_CRS:
1386 case PJ_TYPE_TEMPORAL_CRS:
1388 case PJ_TYPE_ENGINEERING_CRS:
1390 case PJ_TYPE_BOUND_CRS:
1392 case PJ_TYPE_OTHER_CRS:
1394#if PROJ_VERSION_MAJOR>9 || (PROJ_VERSION_MAJOR==9 && PROJ_VERSION_MINOR>=2)
1395 case PJ_TYPE_DERIVED_PROJECTED_CRS:
1397 case PJ_TYPE_COORDINATE_METADATA:
1411 return proj_is_deprecated( pj );
1416 return d->mIsGeographic;
1434#if PROJ_VERSION_MAJOR>8 || (PROJ_VERSION_MAJOR==8 && PROJ_VERSION_MINOR>=1)
1437 return QString( proj_get_celestial_body_name( context, pj ) );
1439 throw QgsNotSupportedException( QObject::tr(
"Retrieving celestial body requires a QGIS build based on PROJ 8.1 or later" ) );
1445 if ( d->mCoordinateEpoch == epoch )
1451 d->mCoordinateEpoch = epoch;
1452 d->setPj( std::move( clone ) );
1457 return d->mCoordinateEpoch;
1469#if PROJ_VERSION_MAJOR>=8
1477 res.mName = QString( proj_get_name( ensemble.get() ) );
1478 res.mAuthority = QString( proj_get_id_auth_name( ensemble.get(), 0 ) );
1479 res.mCode = QString( proj_get_id_code( ensemble.get(), 0 ) );
1480 res.mRemarks = QString( proj_get_remarks( ensemble.get() ) );
1481 res.mScope = QString( proj_get_scope( ensemble.get() ) );
1482 res.mAccuracy = proj_datum_ensemble_get_accuracy( context, ensemble.get() );
1484 const int memberCount = proj_datum_ensemble_get_member_count( context, ensemble.get() );
1485 for (
int i = 0; i < memberCount; ++i )
1492 details.mName = QString( proj_get_name( member.get() ) );
1493 details.mAuthority = QString( proj_get_id_auth_name( member.get(), 0 ) );
1494 details.mCode = QString( proj_get_id_code( member.get(), 0 ) );
1495 details.mRemarks = QString( proj_get_remarks( member.get() ) );
1496 details.mScope = QString( proj_get_scope( member.get() ) );
1498 res.mMembers << details;
1502 throw QgsNotSupportedException( QObject::tr(
"Calculating datum ensembles requires a QGIS build based on PROJ 8.0 or later" ) );
1511 QString projString =
toProj();
1512 projString.replace( QLatin1String(
"+type=crs" ), QString() );
1515 if ( !transformation )
1518 PJ_COORD coord = proj_coord( 0, 0, 0, HUGE_VAL );
1519 coord.uv.u = point.
x() * M_PI / 180.0;
1520 coord.uv.v = point.
y() * M_PI / 180.0;
1522 proj_errno_reset( transformation.get() );
1523 const PJ_FACTORS pjFactors = proj_factors( transformation.get(), coord );
1524 if ( proj_errno( transformation.get() ) )
1529 res.mIsValid =
true;
1530 res.mMeridionalScale = pjFactors.meridional_scale;
1531 res.mParallelScale = pjFactors.parallel_scale;
1532 res.mArealScale = pjFactors.areal_scale;
1533 res.mAngularDistortion = pjFactors.angular_distortion;
1534 res.mMeridianParallelAngle = pjFactors.meridian_parallel_angle * 180 / M_PI;
1535 res.mMeridianConvergence = pjFactors.meridian_convergence * 180 / M_PI;
1536 res.mTissotSemimajor = pjFactors.tissot_semimajor;
1537 res.mTissotSemiminor = pjFactors.tissot_semiminor;
1538 res.mDxDlam = pjFactors.dx_dlam;
1539 res.mDxDphi = pjFactors.dx_dphi;
1540 res.mDyDlam = pjFactors.dy_dlam;
1541 res.mDyDphi = pjFactors.dy_dphi;
1553 QString projString =
toProj();
1554 projString.replace( QLatin1String(
"+type=crs" ), QString() );
1555 if ( projString.isEmpty() )
1559 if ( !transformation )
1562 PJ_PROJ_INFO info = proj_pj_info( transformation.get() );
1577 return d->mMapUnits;
1585 PJ *obj = d->threadLocalProjObject();
1590 double southLat = 0;
1592 double northLat = 0;
1595 &westLon, &southLat, &eastLon, &northLat,
nullptr ) )
1610 const auto parts {
authid().split(
':' ) };
1611 if ( parts.length() == 2 )
1613 if ( parts[0] == QLatin1String(
"EPSG" ) )
1614 return QStringLiteral(
"http://www.opengis.net/def/crs/EPSG/0/%1" ).arg( parts[1] ) ;
1615 else if ( parts[0] == QLatin1String(
"OGC" ) )
1617 return QStringLiteral(
"http://www.opengis.net/def/crs/OGC/1.3/%1" ).arg( parts[1] ) ;
1633 const auto parts {
authid().split(
':' ) };
1634 if ( parts.length() == 2 )
1636 if ( parts[0] == QLatin1String(
"EPSG" ) )
1637 return QStringLiteral(
"urn:ogc:def:crs:EPSG::%1" ).arg( parts[1] );
1638 else if ( parts[0] == QLatin1String(
"OGC" ) )
1640 return QStringLiteral(
"urn:ogc:def:crs:OGC:1.3:%1" ).arg( parts[1] );
1671void QgsCoordinateReferenceSystem::setProjString(
const QString &proj4String )
1674 d->mProj4 = proj4String;
1675 d->mWktPreferred.clear();
1678 QString trimmed = proj4String.trimmed();
1680 trimmed += QLatin1String(
" +type=crs" );
1690 const int errNo = proj_context_errno( ctx );
1691#if PROJ_VERSION_MAJOR>=8
1692 QgsDebugError( QStringLiteral(
"proj string rejected: %1" ).arg( proj_context_errno_string( ctx, errNo ) ) );
1694 QgsDebugError( QStringLiteral(
"proj string rejected: %1" ).arg( proj_errno_string( errNo ) ) );
1697 d->mIsValid =
false;
1701 d->mEllipsoidAcronym.clear();
1708bool QgsCoordinateReferenceSystem::setWktString(
const QString &wkt )
1711 d->mIsValid =
false;
1712 d->mWktPreferred.clear();
1714 PROJ_STRING_LIST warnings =
nullptr;
1715 PROJ_STRING_LIST grammarErrors =
nullptr;
1723 QgsDebugMsgLevel( QStringLiteral(
"\n---------------------------------------------------------------" ), 2 );
1724 QgsDebugMsgLevel( QStringLiteral(
"This CRS could *** NOT *** be set from the supplied Wkt " ), 2 );
1726 for (
auto iter = warnings; iter && *iter; ++iter )
1730 for (
auto iter = grammarErrors; iter && *iter; ++iter )
1734 QgsDebugMsgLevel( QStringLiteral(
"---------------------------------------------------------------\n" ), 2 );
1736 proj_string_list_destroy( warnings );
1737 proj_string_list_destroy( grammarErrors );
1743 if ( !sDisableWktCache )
1744 sWktCache()->insert( wkt, *
this );
1751 QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
1752 QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
1754 if ( authName.isEmpty() || authCode.isEmpty() )
1760 if ( !authName.isEmpty() && !authCode.isEmpty() )
1762 if ( loadFromAuthCode( authName, authCode ) )
1765 if ( !sDisableWktCache )
1766 sWktCache()->insert( wkt, *
this );
1774 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
1782void QgsCoordinateReferenceSystem::setMapUnits()
1809 if ( !coordinateSystem )
1815 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
1816 if ( axisCount > 0 )
1818 const char *outUnitName =
nullptr;
1820 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
1829 const QString unitName( outUnitName );
1833 if ( unitName.compare( QLatin1String(
"degree" ), Qt::CaseInsensitive ) == 0 ||
1834 unitName.compare( QLatin1String(
"degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1835 unitName.compare( QLatin1String(
"degree minute second hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1836 unitName.compare( QLatin1String(
"degree minute" ), Qt::CaseInsensitive ) == 0 ||
1837 unitName.compare( QLatin1String(
"degree hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1838 unitName.compare( QLatin1String(
"degree minute hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1839 unitName.compare( QLatin1String(
"hemisphere degree" ), Qt::CaseInsensitive ) == 0 ||
1840 unitName.compare( QLatin1String(
"hemisphere degree minute" ), Qt::CaseInsensitive ) == 0 ||
1841 unitName.compare( QLatin1String(
"hemisphere degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1842 unitName.compare( QLatin1String(
"degree (supplier to define representation)" ), Qt::CaseInsensitive ) == 0 )
1844 else if ( unitName.compare( QLatin1String(
"metre" ), Qt::CaseInsensitive ) == 0
1845 || unitName.compare( QLatin1String(
"m" ), Qt::CaseInsensitive ) == 0
1846 || unitName.compare( QLatin1String(
"meter" ), Qt::CaseInsensitive ) == 0 )
1848 else if ( unitName.compare( QLatin1String(
"US survey foot" ), Qt::CaseInsensitive ) == 0 )
1850 else if ( unitName.compare( QLatin1String(
"foot" ), Qt::CaseInsensitive ) == 0 )
1852 else if ( unitName.compare( QLatin1String(
"British yard (Sears 1922)" ), Qt::CaseInsensitive ) == 0 )
1854 else if ( unitName.compare( QLatin1String(
"British yard (Sears 1922 truncated)" ), Qt::CaseInsensitive ) == 0 )
1856 else if ( unitName.compare( QLatin1String(
"British foot (Sears 1922)" ), Qt::CaseInsensitive ) == 0 )
1858 else if ( unitName.compare( QLatin1String(
"British foot (Sears 1922 truncated)" ), Qt::CaseInsensitive ) == 0 )
1860 else if ( unitName.compare( QLatin1String(
"British chain (Sears 1922)" ), Qt::CaseInsensitive ) == 0 )
1862 else if ( unitName.compare( QLatin1String(
"British chain (Sears 1922 truncated)" ), Qt::CaseInsensitive ) == 0 )
1864 else if ( unitName.compare( QLatin1String(
"British link (Sears 1922)" ), Qt::CaseInsensitive ) == 0 )
1866 else if ( unitName.compare( QLatin1String(
"British link (Sears 1922 truncated)" ), Qt::CaseInsensitive ) == 0 )
1868 else if ( unitName.compare( QLatin1String(
"British yard (Benoit 1895 A)" ), Qt::CaseInsensitive ) == 0 )
1870 else if ( unitName.compare( QLatin1String(
"British foot (Benoit 1895 A)" ), Qt::CaseInsensitive ) == 0 )
1872 else if ( unitName.compare( QLatin1String(
"British chain (Benoit 1895 A)" ), Qt::CaseInsensitive ) == 0 )
1874 else if ( unitName.compare( QLatin1String(
"British link (Benoit 1895 A)" ), Qt::CaseInsensitive ) == 0 )
1876 else if ( unitName.compare( QLatin1String(
"British yard (Benoit 1895 B)" ), Qt::CaseInsensitive ) == 0 )
1878 else if ( unitName.compare( QLatin1String(
"British foot (Benoit 1895 B)" ), Qt::CaseInsensitive ) == 0 )
1880 else if ( unitName.compare( QLatin1String(
"British chain (Benoit 1895 B)" ), Qt::CaseInsensitive ) == 0 )
1882 else if ( unitName.compare( QLatin1String(
"British link (Benoit 1895 B)" ), Qt::CaseInsensitive ) == 0 )
1884 else if ( unitName.compare( QLatin1String(
"British foot (1865)" ), Qt::CaseInsensitive ) == 0 )
1886 else if ( unitName.compare( QLatin1String(
"British foot (1936)" ), Qt::CaseInsensitive ) == 0 )
1888 else if ( unitName.compare( QLatin1String(
"Indian foot" ), Qt::CaseInsensitive ) == 0 )
1890 else if ( unitName.compare( QLatin1String(
"Indian foot (1937)" ), Qt::CaseInsensitive ) == 0 )
1892 else if ( unitName.compare( QLatin1String(
"Indian foot (1962)" ), Qt::CaseInsensitive ) == 0 )
1894 else if ( unitName.compare( QLatin1String(
"Indian foot (1975)" ), Qt::CaseInsensitive ) == 0 )
1896 else if ( unitName.compare( QLatin1String(
"Indian yard" ), Qt::CaseInsensitive ) == 0 )
1898 else if ( unitName.compare( QLatin1String(
"Indian yard (1937)" ), Qt::CaseInsensitive ) == 0 )
1900 else if ( unitName.compare( QLatin1String(
"Indian yard (1962)" ), Qt::CaseInsensitive ) == 0 )
1902 else if ( unitName.compare( QLatin1String(
"Indian yard (1975)" ), Qt::CaseInsensitive ) == 0 )
1904 else if ( unitName.compare( QLatin1String(
"Gold Coast foot" ), Qt::CaseInsensitive ) == 0 )
1906 else if ( unitName.compare( QLatin1String(
"Clarke's foot" ), Qt::CaseInsensitive ) == 0 )
1908 else if ( unitName.compare( QLatin1String(
"Clarke's yard" ), Qt::CaseInsensitive ) == 0 )
1910 else if ( unitName.compare( QLatin1String(
"Clarke's chain" ), Qt::CaseInsensitive ) == 0 )
1912 else if ( unitName.compare( QLatin1String(
"Clarke's link" ), Qt::CaseInsensitive ) == 0 )
1914 else if ( unitName.compare( QLatin1String(
"kilometre" ), Qt::CaseInsensitive ) == 0 )
1916 else if ( unitName.compare( QLatin1String(
"centimetre" ), Qt::CaseInsensitive ) == 0 )
1918 else if ( unitName.compare( QLatin1String(
"millimetre" ), Qt::CaseInsensitive ) == 0 )
1920 else if ( unitName.compare( QLatin1String(
"Statute mile" ), Qt::CaseInsensitive ) == 0 )
1922 else if ( unitName.compare( QLatin1String(
"nautical mile" ), Qt::CaseInsensitive ) == 0 )
1924 else if ( unitName.compare( QLatin1String(
"yard" ), Qt::CaseInsensitive ) == 0 )
1926 else if ( unitName.compare( QLatin1String(
"fathom" ), Qt::CaseInsensitive ) == 0 )
1928 else if ( unitName.compare( QLatin1String(
"US survey chain" ), Qt::CaseInsensitive ) == 0 )
1930 else if ( unitName.compare( QLatin1String(
"chain" ), Qt::CaseInsensitive ) == 0 )
1932 else if ( unitName.compare( QLatin1String(
"link" ), Qt::CaseInsensitive ) == 0 )
1934 else if ( unitName.compare( QLatin1String(
"US survey link" ), Qt::CaseInsensitive ) == 0 )
1936 else if ( unitName.compare( QLatin1String(
"US survey mile" ), Qt::CaseInsensitive ) == 0 )
1938 else if ( unitName.compare( QLatin1String(
"German legal metre" ), Qt::CaseInsensitive ) == 0 )
1955 if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull()
1958 QgsDebugMsgLevel(
"QgsCoordinateReferenceSystem::findMatchingProj will only "
1959 "work if prj acr ellipsoid acr and proj4string are set"
1960 " and the current projection is valid!", 4 );
1970 QString mySql = QString(
"select srs_id,parameters from tbl_srs where "
1971 "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated" )
1978 myResult = openDatabase( myDatabaseFileName, database );
1979 if ( myResult != SQLITE_OK )
1984 statement = database.
prepare( mySql, myResult );
1985 if ( myResult == SQLITE_OK )
1988 while ( statement.
step() == SQLITE_ROW )
1992 if (
toProj() == myProj4String.trimmed() )
1994 return mySrsId.toLong();
2005 myResult = openDatabase( myDatabaseFileName, database );
2006 if ( myResult != SQLITE_OK )
2011 statement = database.
prepare( mySql, myResult );
2013 if ( myResult == SQLITE_OK )
2015 while ( statement.
step() == SQLITE_ROW )
2019 if (
toProj() == myProj4String.trimmed() )
2021 return mySrsId.toLong();
2035 if ( !d->mIsValid && !srs.d->mIsValid )
2038 if ( !d->mIsValid || !srs.d->mIsValid )
2046 if ( isUser != otherIsUser )
2050 if ( !isUser && ( !d->mAuthId.isEmpty() || !srs.d->mAuthId.isEmpty() ) )
2051 return d->mAuthId == srs.d->mAuthId;
2058 return !( *
this == srs );
2063 if (
PJ *obj = d->threadLocalProjObject() )
2066 if ( isDefaultPreferredFormat && !d->mWktPreferred.isEmpty() )
2069 return d->mWktPreferred;
2072 PJ_WKT_TYPE
type = PJ_WKT1_GDAL;
2076 type = PJ_WKT1_GDAL;
2079 type = PJ_WKT1_ESRI;
2082 type = PJ_WKT2_2015;
2085 type = PJ_WKT2_2015_SIMPLIFIED;
2088 type = PJ_WKT2_2019;
2091 type = PJ_WKT2_2019_SIMPLIFIED;
2095 const QByteArray multiLineOption = QStringLiteral(
"MULTILINE=%1" ).arg( multiline ? QStringLiteral(
"YES" ) : QStringLiteral(
"NO" ) ).toLocal8Bit();
2096 const QByteArray indentatationWidthOption = QStringLiteral(
"INDENTATION_WIDTH=%1" ).arg( multiline ? QString::number( indentationWidth ) : QStringLiteral(
"0" ) ).toLocal8Bit();
2097 const char *
const options[] = {multiLineOption.constData(), indentatationWidthOption.constData(),
nullptr};
2100 if ( isDefaultPreferredFormat )
2103 d->mWktPreferred = res;
2115 QDomNode srsNode = node.namedItem( QStringLiteral(
"spatialrefsys" ) );
2117 if ( ! srsNode.isNull() )
2119 bool initialized =
false;
2122 long srsid = srsNode.namedItem( QStringLiteral(
"srsid" ) ).toElement().text().toLong( &ok );
2128 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
2129 if ( !node.isNull() )
2140 node = srsNode.namedItem( QStringLiteral(
"epsg" ) );
2141 if ( !node.isNull() )
2159 const QString
description = srsNode.namedItem( QStringLiteral(
"description" ) ).toElement().text();
2161 const QString wkt = srsNode.namedItem( QStringLiteral(
"wkt" ) ).toElement().text();
2162 initialized = createFromWktInternal( wkt,
description );
2167 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
2168 const QString proj4 = node.toElement().text();
2175 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
2176 const QString proj4 = node.toElement().text();
2177 if ( !proj4.trimmed().isEmpty() )
2178 setProjString( node.toElement().text() );
2180 node = srsNode.namedItem( QStringLiteral(
"srsid" ) );
2181 d->mSrsId = node.toElement().text().toLong();
2183 node = srsNode.namedItem( QStringLiteral(
"srid" ) );
2184 d->mSRID = node.toElement().text().toLong();
2186 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
2187 d->mAuthId = node.toElement().text();
2189 node = srsNode.namedItem( QStringLiteral(
"description" ) );
2190 d->mDescription = node.toElement().text();
2192 node = srsNode.namedItem( QStringLiteral(
"projectionacronym" ) );
2193 d->mProjectionAcronym = node.toElement().text();
2195 node = srsNode.namedItem( QStringLiteral(
"ellipsoidacronym" ) );
2196 d->mEllipsoidAcronym = node.toElement().text();
2198 node = srsNode.namedItem( QStringLiteral(
"geographicflag" ) );
2199 d->mIsGeographic = node.toElement().text() == QLatin1String(
"true" );
2201 d->mWktPreferred.clear();
2207 const QString epoch = srsNode.toElement().attribute( QStringLiteral(
"coordinateEpoch" ) );
2208 if ( !epoch.isEmpty() )
2210 bool epochOk =
false;
2211 d->mCoordinateEpoch = epoch.toDouble( &epochOk );
2213 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2217 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2220 mNativeFormat = qgsEnumKeyToValue<Qgis::CrsDefinitionFormat>( srsNode.toElement().attribute( QStringLiteral(
"nativeFormat" ) ),
Qgis::CrsDefinitionFormat::Wkt );
2225 d =
new QgsCoordinateReferenceSystemPrivate();
2233 QDomElement layerNode = node.toElement();
2234 QDomElement srsElement = doc.createElement( QStringLiteral(
"spatialrefsys" ) );
2236 srsElement.setAttribute( QStringLiteral(
"nativeFormat" ), qgsEnumValueToKey<Qgis::CrsDefinitionFormat>( mNativeFormat ) );
2238 if ( std::isfinite( d->mCoordinateEpoch ) )
2240 srsElement.setAttribute( QStringLiteral(
"coordinateEpoch" ), d->mCoordinateEpoch );
2243 QDomElement wktElement = doc.createElement( QStringLiteral(
"wkt" ) );
2245 srsElement.appendChild( wktElement );
2247 QDomElement proj4Element = doc.createElement( QStringLiteral(
"proj4" ) );
2248 proj4Element.appendChild( doc.createTextNode(
toProj() ) );
2249 srsElement.appendChild( proj4Element );
2251 QDomElement srsIdElement = doc.createElement( QStringLiteral(
"srsid" ) );
2252 srsIdElement.appendChild( doc.createTextNode( QString::number(
srsid() ) ) );
2253 srsElement.appendChild( srsIdElement );
2255 QDomElement sridElement = doc.createElement( QStringLiteral(
"srid" ) );
2256 sridElement.appendChild( doc.createTextNode( QString::number(
postgisSrid() ) ) );
2257 srsElement.appendChild( sridElement );
2259 QDomElement authidElement = doc.createElement( QStringLiteral(
"authid" ) );
2260 authidElement.appendChild( doc.createTextNode(
authid() ) );
2261 srsElement.appendChild( authidElement );
2263 QDomElement descriptionElement = doc.createElement( QStringLiteral(
"description" ) );
2264 descriptionElement.appendChild( doc.createTextNode(
description() ) );
2265 srsElement.appendChild( descriptionElement );
2267 QDomElement projectionAcronymElement = doc.createElement( QStringLiteral(
"projectionacronym" ) );
2268 projectionAcronymElement.appendChild( doc.createTextNode(
projectionAcronym() ) );
2269 srsElement.appendChild( projectionAcronymElement );
2271 QDomElement ellipsoidAcronymElement = doc.createElement( QStringLiteral(
"ellipsoidacronym" ) );
2272 ellipsoidAcronymElement.appendChild( doc.createTextNode(
ellipsoidAcronym() ) );
2273 srsElement.appendChild( ellipsoidAcronymElement );
2275 QDomElement geographicFlagElement = doc.createElement( QStringLiteral(
"geographicflag" ) );
2276 QString geoFlagText = QStringLiteral(
"false" );
2279 geoFlagText = QStringLiteral(
"true" );
2282 geographicFlagElement.appendChild( doc.createTextNode( geoFlagText ) );
2283 srsElement.appendChild( geographicFlagElement );
2285 layerNode.appendChild( srsElement );
2297QString QgsCoordinateReferenceSystem::projFromSrsId(
const int srsId )
2299 QString myDatabaseFileName;
2300 QString myProjString;
2301 QString mySql = QStringLiteral(
"select parameters from tbl_srs where srs_id = %1 order by deprecated" ).arg( srsId );
2310 QFileInfo myFileInfo;
2311 myFileInfo.setFile( myDatabaseFileName );
2312 if ( !myFileInfo.exists() )
2314 QgsDebugError( QStringLiteral(
"users qgis.db not found" ) );
2327 rc = openDatabase( myDatabaseFileName, database );
2333 statement = database.
prepare( mySql, rc );
2335 if ( rc == SQLITE_OK )
2337 if ( statement.
step() == SQLITE_ROW )
2343 return myProjString;
2350 myResult = database.
open_v2( path, SQLITE_OPEN_READONLY,
nullptr );
2352 myResult = database.
open( path );
2354 if ( myResult != SQLITE_OK )
2363 .arg( database.
errorMessage() ), QObject::tr(
"CRS" ) );
2370 sCustomSrsValidation = f;
2375 return sCustomSrsValidation;
2378void QgsCoordinateReferenceSystem::debugPrint()
2381 QgsDebugMsgLevel(
"* Valid : " + ( d->mIsValid ? QString(
"true" ) : QString(
"false" ) ), 1 );
2402 mValidationHint = html;
2407 return mValidationHint;
2417 mNativeFormat = format;
2422 return mNativeFormat;
2425long QgsCoordinateReferenceSystem::getRecordCount()
2430 long myRecordCount = 0;
2433 if ( myResult != SQLITE_OK )
2439 QString mySql = QStringLiteral(
"select count(*) from tbl_srs" );
2440 statement = database.
prepare( mySql, myResult );
2441 if ( myResult == SQLITE_OK )
2443 if ( statement.
step() == SQLITE_ROW )
2445 QString myRecordCountString = statement.
columnAsText( 0 );
2446 myRecordCount = myRecordCountString.toLong();
2449 return myRecordCount;
2455 bool isGeographic =
false;
2459 if ( !horizontalCrs )
2463 if ( coordinateSystem )
2465 const int axisCount = proj_cs_get_axis_count( pjContext, coordinateSystem.get() );
2466 if ( axisCount > 0 )
2468 const char *outUnitAuthName =
nullptr;
2469 const char *outUnitAuthCode =
nullptr;
2471 proj_cs_get_axis_info( pjContext, coordinateSystem.get(), 0,
2480 if ( outUnitAuthName && outUnitAuthCode )
2482 const char *unitCategory =
nullptr;
2483 if ( proj_uom_get_info_from_database( pjContext, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
2485 isGeographic = QString( unitCategory ).compare( QLatin1String(
"angular" ), Qt::CaseInsensitive ) == 0;
2490 return isGeographic;
2495 thread_local const QRegularExpression projRegExp( QStringLiteral(
"\\+proj=(\\S+)" ) );
2496 const QRegularExpressionMatch projMatch = projRegExp.match( proj );
2497 if ( !projMatch.hasMatch() )
2499 QgsDebugMsgLevel( QStringLiteral(
"no +proj argument found [%2]" ).arg( proj ), 2 );
2502 operation = projMatch.captured( 1 );
2504 const QRegularExpressionMatch ellipseMatch = projRegExp.match( proj );
2505 if ( ellipseMatch.hasMatch() )
2507 ellipsoid = ellipseMatch.captured( 1 );
2521bool QgsCoordinateReferenceSystem::loadFromAuthCode(
const QString &auth,
const QString &code )
2527 d->mIsValid =
false;
2528 d->mWktPreferred.clear();
2540 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2541 proj4 = proj4.trimmed();
2545 d->mWktPreferred.clear();
2546 d->mDescription = QString( proj_get_name(
crs.get() ) );
2547 d->mAuthId = QStringLiteral(
"%1:%2" ).arg( auth, code );
2549 d->mAxisInvertedDirty =
true;
2554 d->mEllipsoidAcronym.clear();
2555 d->setPj( std::move(
crs ) );
2557 const QString dbVals =
sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( auth, code ).toUpper() );
2558 if ( !dbVals.isEmpty() )
2560 const QStringList parts = dbVals.split(
',' );
2561 d->mSrsId = parts.at( 0 ).toInt();
2562 d->mSRID = parts.at( 1 ).toInt();
2570QList<long> QgsCoordinateReferenceSystem::userSrsIds()
2572 QList<long> results;
2576 QFileInfo myInfo( db );
2577 if ( !myInfo.exists() )
2587 int result = openDatabase( db, database );
2588 if ( result != SQLITE_OK )
2590 QgsDebugError(
"failed : " + db +
" could not be opened!" );
2594 QString sql = QStringLiteral(
"select srs_id from tbl_srs where srs_id >= %1" ).arg(
USER_CRS_START_ID );
2596 statement = database.
prepare( sql, rc );
2599 int ret = statement.
step();
2601 if ( ret == SQLITE_DONE )
2607 if ( ret == SQLITE_ROW )
2613 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
2621long QgsCoordinateReferenceSystem::matchToUserCrs()
const
2623 PJ *obj = d->threadLocalProjObject();
2627 const QList< long > ids = userSrsIds();
2628 for (
long id : ids )
2631 if ( candidate.
projObject() && proj_is_equivalent_to( obj, candidate.
projObject(), PJ_COMP_EQUIVALENT ) )
2639static void sync_db_proj_logger(
void * ,
int level,
const char *message )
2644 if ( level == PJ_LOG_ERROR )
2648 else if ( level == PJ_LOG_DEBUG )
2656 setlocale( LC_ALL,
"C" );
2659 int inserted = 0, updated = 0, deleted = 0, errors = 0;
2664 if ( database.
open( dbFilePath ) != SQLITE_OK )
2670 if ( sqlite3_exec( database.get(),
"BEGIN TRANSACTION",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2678 char *errMsg =
nullptr;
2680 bool createdTypeColumn =
false;
2681 if ( sqlite3_exec( database.get(),
"ALTER TABLE tbl_srs ADD COLUMN srs_type text",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2683 createdTypeColumn =
true;
2684 if ( sqlite3_exec( database.get(),
"CREATE INDEX srs_type ON tbl_srs(srs_type)",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2686 QgsDebugError( QStringLiteral(
"Could not create index for srs_type" ) );
2691 if ( sqlite3_exec( database.get(),
"create table tbl_info (proj_major INT, proj_minor INT, proj_patch INT)",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2693 QString sql = QStringLiteral(
"INSERT INTO tbl_info(proj_major, proj_minor, proj_patch) VALUES (%1, %2,%3)" )
2694 .arg( QString::number( PROJ_VERSION_MAJOR ),
2695 QString::number( PROJ_VERSION_MINOR ),
2696 QString::number( PROJ_VERSION_PATCH ) );
2697 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2699 QgsDebugError( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2702 errMsg ? errMsg :
"(unknown error)" ) );
2704 sqlite3_free( errMsg );
2711 QString sql = QStringLiteral(
"SELECT proj_major, proj_minor, proj_patch FROM tbl_info" );
2712 statement = database.
prepare( sql, result );
2713 if ( result != SQLITE_OK )
2718 if ( statement.
step() == SQLITE_ROW )
2723 if ( !createdTypeColumn && major == PROJ_VERSION_MAJOR && minor == PROJ_VERSION_MINOR && patch == PROJ_VERSION_PATCH )
2729 QgsDebugError( QStringLiteral(
"Could not retrieve previous CRS sync PROJ version number" ) );
2736 proj_log_func( pjContext,
nullptr, sync_db_proj_logger );
2738 PROJ_STRING_LIST authorities = proj_get_authorities_from_database( pjContext );
2740 int nextSrsId = 67218;
2741 int nextSrId = 520007218;
2742 for (
auto authIter = authorities; authIter && *authIter; ++authIter )
2744 const QString authority( *authIter );
2745 QgsDebugMsgLevel( QStringLiteral(
"Loading authority '%1'" ).arg( authority ), 2 );
2746 PROJ_STRING_LIST codes = proj_get_codes_from_database( pjContext, *authIter, PJ_TYPE_CRS,
true );
2748 QStringList allCodes;
2750 for (
auto codesIter = codes; codesIter && *codesIter; ++codesIter )
2752 const QString code( *codesIter );
2758 QgsDebugError( QStringLiteral(
"Could not load '%1:%2'" ).arg( authority, code ) );
2762 const PJ_TYPE pjType = proj_get_type(
crs.get( ) );
2764 QString srsTypeString;
2769 case PJ_TYPE_ELLIPSOID:
2770 case PJ_TYPE_PRIME_MERIDIAN:
2771 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
2772 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
2773 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
2774 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
2775 case PJ_TYPE_DATUM_ENSEMBLE:
2776 case PJ_TYPE_CONVERSION:
2777 case PJ_TYPE_TRANSFORMATION:
2778 case PJ_TYPE_CONCATENATED_OPERATION:
2779 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
2780 case PJ_TYPE_TEMPORAL_DATUM:
2781 case PJ_TYPE_ENGINEERING_DATUM:
2782 case PJ_TYPE_PARAMETRIC_DATUM:
2783 case PJ_TYPE_UNKNOWN:
2787 case PJ_TYPE_GEOGRAPHIC_CRS:
2790 case PJ_TYPE_GEODETIC_CRS:
2794 case PJ_TYPE_GEOCENTRIC_CRS:
2798 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
2802 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
2806 case PJ_TYPE_PROJECTED_CRS:
2810 case PJ_TYPE_COMPOUND_CRS:
2814 case PJ_TYPE_TEMPORAL_CRS:
2818 case PJ_TYPE_ENGINEERING_CRS:
2822 case PJ_TYPE_BOUND_CRS:
2826 case PJ_TYPE_VERTICAL_CRS:
2830#if PROJ_VERSION_MAJOR>9 || (PROJ_VERSION_MAJOR==9 && PROJ_VERSION_MINOR>=2)
2831 case PJ_TYPE_DERIVED_PROJECTED_CRS:
2834 case PJ_TYPE_COORDINATE_METADATA:
2837 case PJ_TYPE_OTHER_CRS:
2846 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2847 proj4 = proj4.trimmed();
2849 if ( proj4.isEmpty() )
2851 QgsDebugMsgLevel( QStringLiteral(
"No proj4 for '%1:%2'" ).arg( authority, code ), 2 );
2862 if ( translatedOperation.isEmpty() && !
operation.isEmpty() )
2864 std::cout << QStringLiteral(
"Operation needs translation in QgsCoordinateReferenceSystemUtils::translateProjection: %1" ).arg(
operation ).toLocal8Bit().constData() << std::endl;
2865 qFatal(
"aborted" );
2868 const bool deprecated = proj_is_deprecated(
crs.get() );
2869 const QString name( proj_get_name(
crs.get() ) );
2871 QString sql = QStringLiteral(
"SELECT parameters,description,deprecated,srs_type FROM tbl_srs WHERE auth_name='%1' AND auth_id='%2'" ).arg( authority, code );
2872 statement = database.
prepare( sql, result );
2873 if ( result != SQLITE_OK )
2882 bool dbSrsDeprecated = deprecated;
2883 if ( statement.
step() == SQLITE_ROW )
2887 dbSrsDeprecated = statement.
columnAsText( 2 ).toInt() != 0;
2891 if ( !dbSrsProj4.isEmpty() || !dbSrsDesc.isEmpty() )
2893 if ( proj4 != dbSrsProj4 || name != dbSrsDesc || deprecated != dbSrsDeprecated || dbSrsType != srsTypeString )
2896 sql = QStringLiteral(
"UPDATE tbl_srs SET parameters=%1,description=%2,deprecated=%3, srs_type=%4 WHERE auth_name=%5 AND auth_id=%6" )
2899 .arg( deprecated ? 1 : 0 )
2903 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2905 QgsDebugError( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2908 errMsg ? errMsg :
"(unknown error)" ) );
2910 sqlite3_free( errMsg );
2924 const QString dbVals =
sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( authority, code ) );
2927 if ( !dbVals.isEmpty() )
2929 const QStringList parts = dbVals.split(
',' );
2930 srsId = parts.at( 0 );
2931 srId = parts.at( 1 );
2933 if ( srId.isEmpty() )
2935 srId = QString::number( nextSrId );
2938 if ( srsId.isEmpty() )
2940 srsId = QString::number( nextSrsId );
2944 if ( !srsId.isEmpty() )
2946 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)" )
2956 .arg( deprecated ? 1 : 0 )
2961 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)" )
2970 .arg( deprecated ? 1 : 0 )
2975 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) == SQLITE_OK )
2981 qCritical(
"Could not execute: %s [%s/%s]\n",
2982 sql.toLocal8Bit().constData(),
2983 sqlite3_errmsg( database.get() ),
2984 errMsg ? errMsg :
"(unknown error)" );
2988 sqlite3_free( errMsg );
2993 proj_string_list_destroy( codes );
2995 const QString sql = QStringLiteral(
"DELETE FROM tbl_srs WHERE auth_name='%1' AND NOT auth_id IN (%2)" ).arg( authority, allCodes.join(
',' ) );
2996 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2998 deleted = sqlite3_changes( database.get() );
3003 qCritical(
"Could not execute: %s [%s]\n",
3004 sql.toLocal8Bit().constData(),
3005 sqlite3_errmsg( database.get() ) );
3009 proj_string_list_destroy( authorities );
3011 QString sql = QStringLiteral(
"UPDATE tbl_info set proj_major=%1,proj_minor=%2,proj_patch=%3" )
3012 .arg( QString::number( PROJ_VERSION_MAJOR ),
3013 QString::number( PROJ_VERSION_MINOR ),
3014 QString::number( PROJ_VERSION_PATCH ) );
3015 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
3017 QgsDebugError( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
3020 errMsg ? errMsg :
"(unknown error)" ) );
3022 sqlite3_free( errMsg );
3026 if ( sqlite3_exec( database.get(),
"COMMIT",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
3028 QgsDebugError( QStringLiteral(
"Could not commit transaction: %1 [%2]\n" ).arg(
3030 sqlite3_errmsg( database.get() ) )
3036 QgsDebugMsgLevel( QStringLiteral(
"CRS update (inserted:%1 updated:%2 deleted:%3 errors:%4)" ).arg( inserted ).arg( updated ).arg( deleted ).arg( errors ), 4 );
3044 return updated + inserted;
3047const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::stringCache()
3049 return *sStringCache();
3052const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::projCache()
3054 return *sProj4Cache();
3057const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::ogcCache()
3059 return *sOgcCache();
3062const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::wktCache()
3064 return *sWktCache();
3067const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srIdCache()
3069 return *sSrIdCache();
3072const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srsIdCache()
3074 return *sSrsIdCache();
3084 if (
PJ *obj = d->threadLocalProjObject() )
3130 if (
PJ *obj = d->threadLocalProjObject() )
3163 if (
PJ *obj = d->threadLocalProjObject() )
3174 if (
PJ *obj = d->threadLocalProjObject() )
3187 else if (
PJ *obj = d->threadLocalProjObject() )
3190 return geoCrs ? QStringLiteral(
"%1:%2" ).arg( proj_get_id_auth_name( geoCrs.get(), 0 ), proj_get_id_code( geoCrs.get(), 0 ) ) : QString();
3200 return d->threadLocalProjObject();
3213 d->mIsValid =
false;
3215 d->mWktPreferred.clear();
3222 switch ( proj_get_type(
object ) )
3224 case PJ_TYPE_GEODETIC_CRS:
3225 case PJ_TYPE_GEOCENTRIC_CRS:
3226 case PJ_TYPE_GEOGRAPHIC_CRS:
3227 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
3228 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
3229 case PJ_TYPE_VERTICAL_CRS:
3230 case PJ_TYPE_PROJECTED_CRS:
3231 case PJ_TYPE_COMPOUND_CRS:
3232 case PJ_TYPE_TEMPORAL_CRS:
3233 case PJ_TYPE_ENGINEERING_CRS:
3234 case PJ_TYPE_BOUND_CRS:
3235 case PJ_TYPE_OTHER_CRS:
3251 const QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
3252 const QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
3253 if ( !authName.isEmpty() && !authCode.isEmpty() &&
createFromOgcWmsCrs( QStringLiteral(
"%1:%2" ).arg( authName, authCode ) ) )
3261 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
3272 QStringList projections;
3274 projections.reserve( res.size() );
3277 projections << QString::number(
crs.
srsid() );
3304 sSrIdCacheLock()->lockForWrite();
3305 if ( !sDisableSrIdCache )
3308 sDisableSrIdCache =
true;
3309 sSrIdCache()->clear();
3311 sSrIdCacheLock()->unlock();
3313 sOgcLock()->lockForWrite();
3314 if ( !sDisableOgcCache )
3317 sDisableOgcCache =
true;
3318 sOgcCache()->clear();
3320 sOgcLock()->unlock();
3322 sProj4CacheLock()->lockForWrite();
3323 if ( !sDisableProjCache )
3326 sDisableProjCache =
true;
3327 sProj4Cache()->clear();
3329 sProj4CacheLock()->unlock();
3331 sCRSWktLock()->lockForWrite();
3332 if ( !sDisableWktCache )
3335 sDisableWktCache =
true;
3336 sWktCache()->clear();
3338 sCRSWktLock()->unlock();
3340 sCRSSrsIdLock()->lockForWrite();
3341 if ( !sDisableSrsIdCache )
3344 sDisableSrsIdCache =
true;
3345 sSrsIdCache()->clear();
3347 sCRSSrsIdLock()->unlock();
3349 sCrsStringLock()->lockForWrite();
3350 if ( !sDisableStringCache )
3353 sDisableStringCache =
true;
3354 sStringCache()->clear();
3356 sCrsStringLock()->unlock();
3365 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3368 if ( !c1.d->mIsValid && c2.d->mIsValid )
3371 if ( c1.d->mIsValid && !c2.d->mIsValid )
3377 if ( c1IsUser && !c2IsUser )
3380 if ( !c1IsUser && c2IsUser )
3383 if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
3385 if ( c1.d->mAuthId != c2.d->mAuthId )
3386 return c1.d->mAuthId > c2.d->mAuthId;
3394 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3397 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3400 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3403 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3406 return c1.d->mCoordinateEpoch > c2.d->mCoordinateEpoch;
3414 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3417 if ( c1.d->mIsValid && !c2.d->mIsValid )
3420 if ( !c1.d->mIsValid && c2.d->mIsValid )
3426 if ( !c1IsUser && c2IsUser )
3429 if ( c1IsUser && !c2IsUser )
3432 if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
3434 if ( c1.d->mAuthId != c2.d->mAuthId )
3435 return c1.d->mAuthId < c2.d->mAuthId;
3443 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3446 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3449 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3452 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3455 return c1.d->mCoordinateEpoch < c2.d->mCoordinateEpoch;
3460 return !( c1 < c2 );
3464 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.
@ 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