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