29 #include <QDomElement>
31 #include <QRegularExpression>
32 #include <QTextStream>
51 #include <proj_experimental.h>
54 #include <ogr_srs_api.h>
55 #include <cpl_error.h>
66 bool QgsCoordinateReferenceSystem::sDisableSrIdCache =
false;
70 bool QgsCoordinateReferenceSystem::sDisableOgcCache =
false;
74 bool QgsCoordinateReferenceSystem::sDisableProjCache =
false;
78 bool QgsCoordinateReferenceSystem::sDisableWktCache =
false;
82 bool QgsCoordinateReferenceSystem::sDisableSrsIdCache =
false;
86 bool 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() );
248 if ( horizontalObj && verticalObj )
279 QgsDebugError( QStringLiteral(
"Unexpected case reached!" ) );
286 if ( definition.isEmpty() )
290 if ( !sDisableStringCache )
292 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sStringCache()->constFind( definition );
293 if ( crsIt != sStringCache()->constEnd() )
296 *
this = crsIt.value();
303 const thread_local QRegularExpression reCrsId( QStringLiteral(
"^(epsg|esri|osgeo|ignf|ogc|nkg|zangi|iau_2015|iau2000|postgis|internal|user)\\:(\\w+)$" ), QRegularExpression::CaseInsensitiveOption );
304 QRegularExpressionMatch match = reCrsId.match( definition );
305 if ( match.capturedStart() == 0 )
307 QString authName = match.captured( 1 ).toLower();
308 if ( authName == QLatin1String(
"epsg" ) )
312 else if ( authName == QLatin1String(
"postgis" ) )
314 const long id = match.captured( 2 ).toLong();
319 else if ( authName == QLatin1String(
"esri" )
320 || authName == QLatin1String(
"osgeo" )
321 || authName == QLatin1String(
"ignf" )
322 || authName == QLatin1String(
"zangi" )
323 || authName == QLatin1String(
"iau2000" )
324 || authName == QLatin1String(
"ogc" )
325 || authName == QLatin1String(
"nkg" )
326 || authName == QLatin1String(
"iau_2015" )
333 const long id = match.captured( 2 ).toLong();
341 const thread_local QRegularExpression reCrsStr( QStringLiteral(
"^(?:(wkt|proj4|proj)\\:)?(.+)$" ), QRegularExpression::CaseInsensitiveOption );
342 match = reCrsStr.match( definition );
343 if ( match.capturedStart() == 0 )
345 if ( match.captured( 1 ).startsWith( QLatin1String(
"proj" ), Qt::CaseInsensitive ) )
357 if ( !sDisableStringCache )
358 sStringCache()->insert( definition, *
this );
364 if ( definition.isEmpty() )
370 if ( OSRSetFromUserInput(
crs, definition.toLocal8Bit().constData() ) == OGRERR_NONE )
373 OSRDestroySpatialReference(
crs );
383 const char *configOld = CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" );
384 const char *configNew =
"GEOGCS";
386 if ( strcmp( configOld,
"" ) == 0 )
388 CPLSetConfigOption(
"GDAL_FIX_ESRI_WKT", configNew );
389 if ( strcmp( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) != 0 )
391 .arg( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) );
392 QgsDebugMsgLevel( QStringLiteral(
"set GDAL_FIX_ESRI_WKT : %1" ).arg( configNew ), 4 );
396 QgsDebugMsgLevel( QStringLiteral(
"GDAL_FIX_ESRI_WKT was already set : %1" ).arg( configNew ), 4 );
406 if ( !sDisableOgcCache )
408 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sOgcCache()->constFind(
crs );
409 if ( crsIt != sOgcCache()->constEnd() )
412 *
this = crsIt.value();
418 QString wmsCrs =
crs;
423 const QString authorityLower = authority.toLower();
425 ( authorityLower == QLatin1String(
"user" ) ||
426 authorityLower == QLatin1String(
"custom" ) ||
427 authorityLower == QLatin1String(
"qgis" ) ) )
432 if ( !sDisableOgcCache )
433 sOgcCache()->insert(
crs, *
this );
439 wmsCrs = authority +
':' + code;
443 const QString legacyKey = wmsCrs.toLower();
446 if ( it.key().compare( legacyKey, Qt::CaseInsensitive ) == 0 )
448 const QStringList parts = it.key().split(
':' );
449 const QString auth = parts.at( 0 );
450 const QString code = parts.at( 1 );
451 if ( loadFromAuthCode( auth, code ) )
454 if ( !sDisableOgcCache )
455 sOgcCache()->insert(
crs, *
this );
464 if ( !sDisableOgcCache )
465 sOgcCache()->insert(
crs, *
this );
470 if ( wmsCrs.compare( QLatin1String(
"CRS:27" ), Qt::CaseInsensitive ) == 0 ||
471 wmsCrs.compare( QLatin1String(
"OGC:CRS27" ), Qt::CaseInsensitive ) == 0 )
478 if ( wmsCrs.compare( QLatin1String(
"CRS:83" ), Qt::CaseInsensitive ) == 0 ||
479 wmsCrs.compare( QLatin1String(
"OGC:CRS83" ), Qt::CaseInsensitive ) == 0 )
486 if ( wmsCrs.compare( QLatin1String(
"CRS:84" ), Qt::CaseInsensitive ) == 0 ||
487 wmsCrs.compare( QLatin1String(
"OGC:CRS84" ), Qt::CaseInsensitive ) == 0 )
491 d->mAxisInverted =
false;
492 d->mAxisInvertedDirty =
false;
496 if ( !sDisableOgcCache )
497 sOgcCache()->insert(
crs, *
this );
504 if ( !authority.isEmpty() && !code.isEmpty() && loadFromAuthCode( authority, code ) )
507 if ( !sDisableOgcCache )
508 sOgcCache()->insert(
crs, *
this );
513 if ( !sDisableOgcCache )
523 if ( d->mIsValid || !sCustomSrsValidation )
527 if ( sCustomSrsValidation )
528 sCustomSrsValidation( *
this );
534 if ( !sDisableSrIdCache )
536 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrIdCache()->constFind(
id );
537 if ( crsIt != sSrIdCache()->constEnd() )
540 *
this = crsIt.value();
549 if ( it.value().endsWith( QStringLiteral(
",%1" ).arg(
id ) ) )
551 const QStringList parts = it.key().split(
':' );
552 const QString auth = parts.at( 0 );
553 const QString code = parts.at( 1 );
554 if ( loadFromAuthCode( auth, code ) )
557 if ( !sDisableSrIdCache )
558 sSrIdCache()->insert(
id, *
this );
568 if ( !sDisableSrIdCache )
569 sSrIdCache()->insert(
id, *
this );
577 if ( !sDisableSrsIdCache )
579 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrsIdCache()->constFind(
id );
580 if ( crsIt != sSrsIdCache()->constEnd() )
583 *
this = crsIt.value();
592 if ( it.value().startsWith( QString::number(
id ) +
',' ) )
594 const QStringList parts = it.key().split(
':' );
595 const QString auth = parts.at( 0 );
596 const QString code = parts.at( 1 );
597 if ( loadFromAuthCode( auth, code ) )
600 if ( !sDisableSrsIdCache )
601 sSrsIdCache()->insert(
id, *
this );
609 QStringLiteral(
"srs_id" ), QString::number(
id ) );
612 if ( !sDisableSrsIdCache )
613 sSrsIdCache()->insert(
id, *
this );
617 bool QgsCoordinateReferenceSystem::loadFromDatabase(
const QString &db,
const QString &expression,
const QString &value )
621 QgsDebugMsgLevel(
"load CRS from " + db +
" where " + expression +
" is " + value, 3 );
623 d->mWktPreferred.clear();
625 QFileInfo myInfo( db );
626 if ( !myInfo.exists() )
636 myResult = openDatabase( db, database );
637 if ( myResult != SQLITE_OK )
654 QString mySql =
"select srs_id,description,projection_acronym,"
655 "ellipsoid_acronym,parameters,srid,auth_name||':'||auth_id,is_geo,wkt "
657 statement = database.
prepare( mySql, myResult );
660 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
665 d->mEllipsoidAcronym.clear();
667 d->mWktPreferred.clear();
670 d->mIsGeographic = statement.
columnAsText( 7 ).toInt() != 0;
672 d->mAxisInvertedDirty =
true;
674 if ( d->mSrsId >=
USER_CRS_START_ID && ( d->mAuthId.isEmpty() || d->mAuthId == QChar(
':' ) ) )
676 d->mAuthId = QStringLiteral(
"USER:%1" ).arg( d->mSrsId );
678 else if ( !d->mAuthId.startsWith( QLatin1String(
"USER:" ), Qt::CaseInsensitive ) )
680 QStringList parts = d->mAuthId.split(
':' );
681 QString auth = parts.at( 0 );
682 QString code = parts.at( 1 );
689 d->mIsValid = d->hasPj();
695 if ( !wkt.isEmpty() )
703 setProjString( d->mProj4 );
713 void QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread(
PJ_CONTEXT *pj_context )
720 if ( !sDisableSrIdCache )
723 if ( !sDisableSrIdCache )
725 for (
auto it = sSrIdCache()->begin(); it != sSrIdCache()->end(); )
727 auto &v = it.value();
728 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
729 it = sSrIdCache()->erase( it );
735 if ( !sDisableOgcCache )
738 if ( !sDisableOgcCache )
740 for (
auto it = sOgcCache()->begin(); it != sOgcCache()->end(); )
742 auto &v = it.value();
743 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
744 it = sOgcCache()->erase( it );
750 if ( !sDisableProjCache )
753 if ( !sDisableProjCache )
755 for (
auto it = sProj4Cache()->begin(); it != sProj4Cache()->end(); )
757 auto &v = it.value();
758 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
759 it = sProj4Cache()->erase( it );
765 if ( !sDisableWktCache )
768 if ( !sDisableWktCache )
770 for (
auto it = sWktCache()->begin(); it != sWktCache()->end(); )
772 auto &v = it.value();
773 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
774 it = sWktCache()->erase( it );
780 if ( !sDisableSrsIdCache )
783 if ( !sDisableSrsIdCache )
785 for (
auto it = sSrsIdCache()->begin(); it != sSrsIdCache()->end(); )
787 auto &v = it.value();
788 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
789 it = sSrsIdCache()->erase( it );
795 if ( !sDisableStringCache )
798 if ( !sDisableStringCache )
800 for (
auto it = sStringCache()->begin(); it != sStringCache()->end(); )
802 auto &v = it.value();
803 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
804 it = sStringCache()->erase( it );
814 if ( d->mAxisInvertedDirty )
817 d->mAxisInvertedDirty =
false;
820 return d->mAxisInverted;
834 const thread_local QMap< Qgis::CrsAxisDirection, QString > mapping =
877 QList< Qgis::CrsAxisDirection > res;
878 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
881 res.reserve( axisCount );
883 for (
int i = 0; i < axisCount; ++i )
885 const char *outDirection =
nullptr;
886 proj_cs_get_axis_info( context, pjCs.get(), i,
896 const thread_local QRegularExpression rx( QStringLiteral(
"([^\\s]+).*" ) );
897 const QRegularExpressionMatch match = rx.match( QString( outDirection ) );
898 if ( !match.hasMatch() )
901 const QString direction = match.captured( 1 );
903 for (
auto it = mapping.constBegin(); it != mapping.constEnd(); ++it )
905 if ( it.value().compare( direction, Qt::CaseInsensitive ) == 0 )
920 return createFromWktInternal( wkt, QString() );
923 bool QgsCoordinateReferenceSystem::createFromWktInternal(
const QString &wkt,
const QString &description )
931 if ( !sDisableWktCache )
933 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sWktCache()->constFind( wkt );
934 if ( crsIt != sWktCache()->constEnd() )
937 *
this = crsIt.value();
939 if ( !
description.isEmpty() && d->mDescription.isEmpty() )
944 sWktCache()->insert( wkt, *
this );
953 d->mWktPreferred.clear();
956 QgsDebugMsgLevel( QStringLiteral(
"theWkt is uninitialized, operation failed" ), 4 );
961 QgsCoordinateReferenceSystem::RecordMap record = getRecord(
"select * from tbl_srs where wkt=" +
QgsSqliteUtils::quotedString( wkt ) +
" order by deprecated" );
962 if ( !record.empty() )
964 long srsId = record[QStringLiteral(
"srs_id" )].toLong();
977 if ( d->mSrsId == 0 )
980 long id = matchToUserCrs();
989 if ( !sDisableWktCache )
990 sWktCache()->insert( wkt, *
this );
1008 if ( projString.isEmpty() )
1013 if ( projString.trimmed().isEmpty() )
1015 d->mIsValid =
false;
1017 d->mWktPreferred.clear();
1022 if ( !sDisableProjCache )
1024 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sProj4Cache()->constFind( projString );
1025 if ( crsIt != sProj4Cache()->constEnd() )
1028 *
this = crsIt.value();
1042 QString myProj4String = projString.trimmed();
1043 myProj4String.remove( QStringLiteral(
"+type=crs" ) );
1044 myProj4String = myProj4String.trimmed();
1046 d->mIsValid =
false;
1047 d->mWktPreferred.clear();
1052 const QString projCrsString = myProj4String + ( myProj4String.contains( QStringLiteral(
"+type=crs" ) ) ? QString() : QStringLiteral(
" +type=crs" ) );
1060 const QString
authid = QStringLiteral(
"%1:%2" ).arg( authName, authCode );
1064 if ( !sDisableProjCache )
1065 sProj4Cache()->insert( projString, *
this );
1072 QgsCoordinateReferenceSystem::RecordMap myRecord = getRecord(
"select * from tbl_srs where parameters=" +
QgsSqliteUtils::quotedString( myProj4String ) +
" order by deprecated" );
1074 if ( !myRecord.empty() )
1076 id = myRecord[QStringLiteral(
"srs_id" )].toLong();
1085 setProjString( myProj4String );
1088 id = matchToUserCrs();
1097 setProjString( myProj4String );
1101 if ( !sDisableProjCache )
1102 sProj4Cache()->insert( projString, *
this );
1108 QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord(
const QString &sql )
1110 QString myDatabaseFileName;
1111 QgsCoordinateReferenceSystem::RecordMap myMap;
1112 QString myFieldName;
1113 QString myFieldValue;
1120 QFileInfo myInfo( myDatabaseFileName );
1121 if ( !myInfo.exists() )
1123 QgsDebugError(
"failed : " + myDatabaseFileName +
" does not exist!" );
1128 myResult = openDatabase( myDatabaseFileName, database );
1129 if ( myResult != SQLITE_OK )
1134 statement = database.
prepare( sql, myResult );
1136 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1140 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1142 myFieldName = statement.
columnName( myColNo );
1144 myMap[myFieldName] = myFieldValue;
1146 if ( statement.
step() != SQLITE_DONE )
1148 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1157 if ( myMap.empty() )
1160 QFileInfo myFileInfo;
1161 myFileInfo.setFile( myDatabaseFileName );
1162 if ( !myFileInfo.exists() )
1164 QgsDebugError( QStringLiteral(
"user qgis.db not found" ) );
1169 myResult = openDatabase( myDatabaseFileName, database );
1170 if ( myResult != SQLITE_OK )
1175 statement = database.
prepare( sql, myResult );
1177 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1181 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1183 myFieldName = statement.
columnName( myColNo );
1185 myMap[myFieldName] = myFieldValue;
1188 if ( statement.
step() != SQLITE_DONE )
1190 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1221 if ( d->mDescription.isNull() )
1227 return d->mDescription;
1234 if ( !
authid().isEmpty() )
1244 id =
isValid() ? QObject::tr(
"Custom CRS" ) : QObject::tr(
"Unknown CRS" );
1246 id = QObject::tr(
"Custom CRS: %1" ).arg(
1249 else if ( !
toProj().isEmpty() )
1252 if ( !
id.isEmpty() && !std::isnan( d->mCoordinateEpoch ) )
1253 id += QStringLiteral(
" @ %1" ).arg( d->mCoordinateEpoch );
1260 if ( d->mProjectionAcronym.isNull() )
1266 return d->mProjectionAcronym;
1272 if ( d->mEllipsoidAcronym.isNull() )
1274 if (
PJ *obj = d->threadLocalProjObject() )
1279 const QString ellipsoidAuthName( proj_get_id_auth_name( ellipsoid.get(), 0 ) );
1280 const QString ellipsoidAuthCode( proj_get_id_code( ellipsoid.get(), 0 ) );
1281 if ( !ellipsoidAuthName.isEmpty() && !ellipsoidAuthCode.isEmpty() )
1282 d->mEllipsoidAcronym = QStringLiteral(
"%1:%2" ).arg( ellipsoidAuthName, ellipsoidAuthCode );
1285 double semiMajor, semiMinor, invFlattening;
1286 int semiMinorComputed = 0;
1287 if ( proj_ellipsoid_get_parameters(
QgsProjContext::get(), ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
1289 d->mEllipsoidAcronym = QStringLiteral(
"PARAMETER:%1:%2" ).arg(
qgsDoubleToString( semiMajor ),
1294 d->mEllipsoidAcronym.clear();
1299 return d->mEllipsoidAcronym;
1303 return d->mEllipsoidAcronym;
1317 if ( d->mProj4.isEmpty() )
1319 if (
PJ *obj = d->threadLocalProjObject() )
1325 return d->mProj4.trimmed();
1331 switch ( d->mProjType )
1333 case PJ_TYPE_UNKNOWN:
1336 case PJ_TYPE_ELLIPSOID:
1337 case PJ_TYPE_PRIME_MERIDIAN:
1338 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
1339 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
1340 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
1341 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
1342 case PJ_TYPE_DATUM_ENSEMBLE:
1343 case PJ_TYPE_CONVERSION:
1344 case PJ_TYPE_TRANSFORMATION:
1345 case PJ_TYPE_CONCATENATED_OPERATION:
1346 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
1347 case PJ_TYPE_TEMPORAL_DATUM:
1348 case PJ_TYPE_ENGINEERING_DATUM:
1349 case PJ_TYPE_PARAMETRIC_DATUM:
1353 case PJ_TYPE_GEOGRAPHIC_CRS:
1357 case PJ_TYPE_GEODETIC_CRS:
1359 case PJ_TYPE_GEOCENTRIC_CRS:
1361 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
1363 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
1365 case PJ_TYPE_VERTICAL_CRS:
1367 case PJ_TYPE_PROJECTED_CRS:
1369 case PJ_TYPE_COMPOUND_CRS:
1371 case PJ_TYPE_TEMPORAL_CRS:
1373 case PJ_TYPE_ENGINEERING_CRS:
1375 case PJ_TYPE_BOUND_CRS:
1377 case PJ_TYPE_OTHER_CRS:
1379 #if PROJ_VERSION_MAJOR>9 || (PROJ_VERSION_MAJOR==9 && PROJ_VERSION_MINOR>=2)
1380 case PJ_TYPE_DERIVED_PROJECTED_CRS:
1382 case PJ_TYPE_COORDINATE_METADATA:
1396 return proj_is_deprecated( pj );
1401 return d->mIsGeographic;
1419 #if PROJ_VERSION_MAJOR>8 || (PROJ_VERSION_MAJOR==8 && PROJ_VERSION_MINOR>=1)
1422 return QString( proj_get_celestial_body_name( context, pj ) );
1424 throw QgsNotSupportedException( QObject::tr(
"Retrieving celestial body requires a QGIS build based on PROJ 8.1 or later" ) );
1430 if ( d->mCoordinateEpoch == epoch )
1436 d->mCoordinateEpoch = epoch;
1437 d->setPj( std::move( clone ) );
1442 return d->mCoordinateEpoch;
1454 #if PROJ_VERSION_MAJOR>=8
1462 res.mName = QString( proj_get_name( ensemble.get() ) );
1463 res.mAuthority = QString( proj_get_id_auth_name( ensemble.get(), 0 ) );
1464 res.mCode = QString( proj_get_id_code( ensemble.get(), 0 ) );
1465 res.mRemarks = QString( proj_get_remarks( ensemble.get() ) );
1466 res.mScope = QString( proj_get_scope( ensemble.get() ) );
1467 res.mAccuracy = proj_datum_ensemble_get_accuracy( context, ensemble.get() );
1469 const int memberCount = proj_datum_ensemble_get_member_count( context, ensemble.get() );
1470 for (
int i = 0; i < memberCount; ++i )
1477 details.mName = QString( proj_get_name( member.get() ) );
1478 details.mAuthority = QString( proj_get_id_auth_name( member.get(), 0 ) );
1479 details.mCode = QString( proj_get_id_code( member.get(), 0 ) );
1480 details.mRemarks = QString( proj_get_remarks( member.get() ) );
1481 details.mScope = QString( proj_get_scope( member.get() ) );
1483 res.mMembers << details;
1487 throw QgsNotSupportedException( QObject::tr(
"Calculating datum ensembles requires a QGIS build based on PROJ 8.0 or later" ) );
1496 QString projString =
toProj();
1497 projString.replace( QLatin1String(
"+type=crs" ), QString() );
1500 if ( !transformation )
1503 PJ_COORD coord = proj_coord( 0, 0, 0, HUGE_VAL );
1504 coord.uv.u = point.
x() * M_PI / 180.0;
1505 coord.uv.v = point.
y() * M_PI / 180.0;
1507 proj_errno_reset( transformation.get() );
1508 const PJ_FACTORS pjFactors = proj_factors( transformation.get(), coord );
1509 if ( proj_errno( transformation.get() ) )
1514 res.mIsValid =
true;
1515 res.mMeridionalScale = pjFactors.meridional_scale;
1516 res.mParallelScale = pjFactors.parallel_scale;
1517 res.mArealScale = pjFactors.areal_scale;
1518 res.mAngularDistortion = pjFactors.angular_distortion;
1519 res.mMeridianParallelAngle = pjFactors.meridian_parallel_angle * 180 / M_PI;
1520 res.mMeridianConvergence = pjFactors.meridian_convergence * 180 / M_PI;
1521 res.mTissotSemimajor = pjFactors.tissot_semimajor;
1522 res.mTissotSemiminor = pjFactors.tissot_semiminor;
1523 res.mDxDlam = pjFactors.dx_dlam;
1524 res.mDxDphi = pjFactors.dx_dphi;
1525 res.mDyDlam = pjFactors.dy_dlam;
1526 res.mDyDphi = pjFactors.dy_dphi;
1538 QString projString =
toProj();
1539 projString.replace( QLatin1String(
"+type=crs" ), QString() );
1540 if ( projString.isEmpty() )
1544 if ( !transformation )
1547 PJ_PROJ_INFO info = proj_pj_info( transformation.get() );
1562 return d->mMapUnits;
1570 PJ *obj = d->threadLocalProjObject();
1575 double southLat = 0;
1577 double northLat = 0;
1580 &westLon, &southLat, &eastLon, &northLat,
nullptr ) )
1595 const auto parts {
authid().split(
':' ) };
1596 if ( parts.length() == 2 )
1598 if ( parts[0] == QLatin1String(
"EPSG" ) )
1599 return QStringLiteral(
"http://www.opengis.net/def/crs/EPSG/0/%1" ).arg( parts[1] ) ;
1600 else if ( parts[0] == QLatin1String(
"OGC" ) )
1602 return QStringLiteral(
"http://www.opengis.net/def/crs/OGC/1.3/%1" ).arg( parts[1] ) ;
1606 QgsMessageLog::logMessage( QStringLiteral(
"Error converting published CRS to URI %1: (not OGC or EPSG)" ).arg(
authid() ), QStringLiteral(
"CRS" ), Qgis::MessageLevel::Critical );
1611 QgsMessageLog::logMessage( QStringLiteral(
"Error converting published CRS to URI: %1" ).arg(
authid() ), QStringLiteral(
"CRS" ), Qgis::MessageLevel::Critical );
1632 void QgsCoordinateReferenceSystem::setProjString(
const QString &proj4String )
1635 d->mProj4 = proj4String;
1636 d->mWktPreferred.clear();
1639 QString trimmed = proj4String.trimmed();
1641 trimmed += QLatin1String(
" +type=crs" );
1651 const int errNo = proj_context_errno( ctx );
1652 QgsDebugError( QStringLiteral(
"proj string rejected: %1" ).arg( proj_errno_string( errNo ) ) );
1654 d->mIsValid =
false;
1658 d->mEllipsoidAcronym.clear();
1665 bool QgsCoordinateReferenceSystem::setWktString(
const QString &wkt )
1668 d->mIsValid =
false;
1669 d->mWktPreferred.clear();
1671 PROJ_STRING_LIST warnings =
nullptr;
1672 PROJ_STRING_LIST grammarErrors =
nullptr;
1680 QgsDebugMsgLevel( QStringLiteral(
"\n---------------------------------------------------------------" ), 2 );
1681 QgsDebugMsgLevel( QStringLiteral(
"This CRS could *** NOT *** be set from the supplied Wkt " ), 2 );
1683 for (
auto iter = warnings; iter && *iter; ++iter )
1685 for (
auto iter = grammarErrors; iter && *iter; ++iter )
1687 QgsDebugMsgLevel( QStringLiteral(
"---------------------------------------------------------------\n" ), 2 );
1689 proj_string_list_destroy( warnings );
1690 proj_string_list_destroy( grammarErrors );
1696 if ( !sDisableWktCache )
1697 sWktCache()->insert( wkt, *
this );
1704 QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
1705 QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
1707 if ( authName.isEmpty() || authCode.isEmpty() )
1713 if ( !authName.isEmpty() && !authCode.isEmpty() )
1715 if ( loadFromAuthCode( authName, authCode ) )
1718 if ( !sDisableWktCache )
1719 sWktCache()->insert( wkt, *
this );
1727 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
1735 void QgsCoordinateReferenceSystem::setMapUnits()
1762 if ( !coordinateSystem )
1768 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
1769 if ( axisCount > 0 )
1771 const char *outUnitName =
nullptr;
1773 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
1782 const QString unitName( outUnitName );
1786 if ( unitName.compare( QLatin1String(
"degree" ), Qt::CaseInsensitive ) == 0 ||
1787 unitName.compare( QLatin1String(
"degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1788 unitName.compare( QLatin1String(
"degree minute second hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1789 unitName.compare( QLatin1String(
"degree minute" ), Qt::CaseInsensitive ) == 0 ||
1790 unitName.compare( QLatin1String(
"degree hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1791 unitName.compare( QLatin1String(
"degree minute hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1792 unitName.compare( QLatin1String(
"hemisphere degree" ), Qt::CaseInsensitive ) == 0 ||
1793 unitName.compare( QLatin1String(
"hemisphere degree minute" ), Qt::CaseInsensitive ) == 0 ||
1794 unitName.compare( QLatin1String(
"hemisphere degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1795 unitName.compare( QLatin1String(
"degree (supplier to define representation)" ), Qt::CaseInsensitive ) == 0 )
1797 else if ( unitName.compare( QLatin1String(
"metre" ), Qt::CaseInsensitive ) == 0
1798 || unitName.compare( QLatin1String(
"m" ), Qt::CaseInsensitive ) == 0
1799 || unitName.compare( QLatin1String(
"meter" ), Qt::CaseInsensitive ) == 0 )
1802 else if ( unitName.compare( QLatin1String(
"US survey foot" ), Qt::CaseInsensitive ) == 0 ||
1803 unitName.compare( QLatin1String(
"foot" ), Qt::CaseInsensitive ) == 0 )
1805 else if ( unitName.compare( QLatin1String(
"kilometre" ), Qt::CaseInsensitive ) == 0 )
1807 else if ( unitName.compare( QLatin1String(
"centimetre" ), Qt::CaseInsensitive ) == 0 )
1809 else if ( unitName.compare( QLatin1String(
"millimetre" ), Qt::CaseInsensitive ) == 0 )
1811 else if ( unitName.compare( QLatin1String(
"Statute mile" ), Qt::CaseInsensitive ) == 0 )
1813 else if ( unitName.compare( QLatin1String(
"nautical mile" ), Qt::CaseInsensitive ) == 0 )
1815 else if ( unitName.compare( QLatin1String(
"yard" ), Qt::CaseInsensitive ) == 0 )
1832 if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull()
1835 QgsDebugMsgLevel(
"QgsCoordinateReferenceSystem::findMatchingProj will only "
1836 "work if prj acr ellipsoid acr and proj4string are set"
1837 " and the current projection is valid!", 4 );
1847 QString mySql = QString(
"select srs_id,parameters from tbl_srs where "
1848 "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated" )
1855 myResult = openDatabase( myDatabaseFileName, database );
1856 if ( myResult != SQLITE_OK )
1861 statement = database.
prepare( mySql, myResult );
1862 if ( myResult == SQLITE_OK )
1865 while ( statement.
step() == SQLITE_ROW )
1869 if (
toProj() == myProj4String.trimmed() )
1871 return mySrsId.toLong();
1882 myResult = openDatabase( myDatabaseFileName, database );
1883 if ( myResult != SQLITE_OK )
1888 statement = database.
prepare( mySql, myResult );
1890 if ( myResult == SQLITE_OK )
1892 while ( statement.
step() == SQLITE_ROW )
1896 if (
toProj() == myProj4String.trimmed() )
1898 return mySrsId.toLong();
1912 if ( !d->mIsValid && !srs.d->mIsValid )
1915 if ( !d->mIsValid || !srs.d->mIsValid )
1923 if ( isUser != otherIsUser )
1927 if ( !isUser && ( !d->mAuthId.isEmpty() || !srs.d->mAuthId.isEmpty() ) )
1928 return d->mAuthId == srs.d->mAuthId;
1935 return !( *
this == srs );
1940 if (
PJ *obj = d->threadLocalProjObject() )
1943 if ( isDefaultPreferredFormat && !d->mWktPreferred.isEmpty() )
1946 return d->mWktPreferred;
1949 PJ_WKT_TYPE
type = PJ_WKT1_GDAL;
1953 type = PJ_WKT1_GDAL;
1956 type = PJ_WKT1_ESRI;
1959 type = PJ_WKT2_2015;
1962 type = PJ_WKT2_2015_SIMPLIFIED;
1965 type = PJ_WKT2_2019;
1968 type = PJ_WKT2_2019_SIMPLIFIED;
1972 const QByteArray multiLineOption = QStringLiteral(
"MULTILINE=%1" ).arg( multiline ? QStringLiteral(
"YES" ) : QStringLiteral(
"NO" ) ).toLocal8Bit();
1973 const QByteArray indentatationWidthOption = QStringLiteral(
"INDENTATION_WIDTH=%1" ).arg( multiline ? QString::number( indentationWidth ) : QStringLiteral(
"0" ) ).toLocal8Bit();
1974 const char *
const options[] = {multiLineOption.constData(), indentatationWidthOption.constData(),
nullptr};
1977 if ( isDefaultPreferredFormat )
1980 d->mWktPreferred = res;
1992 QDomNode srsNode = node.namedItem( QStringLiteral(
"spatialrefsys" ) );
1994 if ( ! srsNode.isNull() )
1996 bool initialized =
false;
1999 long srsid = srsNode.namedItem( QStringLiteral(
"srsid" ) ).toElement().text().toLong( &ok );
2005 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
2006 if ( !node.isNull() )
2017 node = srsNode.namedItem( QStringLiteral(
"epsg" ) );
2018 if ( !node.isNull() )
2036 const QString
description = srsNode.namedItem( QStringLiteral(
"description" ) ).toElement().text();
2038 const QString wkt = srsNode.namedItem( QStringLiteral(
"wkt" ) ).toElement().text();
2039 initialized = createFromWktInternal( wkt,
description );
2044 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
2045 const QString proj4 = node.toElement().text();
2052 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
2053 const QString proj4 = node.toElement().text();
2054 if ( !proj4.trimmed().isEmpty() )
2055 setProjString( node.toElement().text() );
2057 node = srsNode.namedItem( QStringLiteral(
"srsid" ) );
2058 d->mSrsId = node.toElement().text().toLong();
2060 node = srsNode.namedItem( QStringLiteral(
"srid" ) );
2061 d->mSRID = node.toElement().text().toLong();
2063 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
2064 d->mAuthId = node.toElement().text();
2066 node = srsNode.namedItem( QStringLiteral(
"description" ) );
2067 d->mDescription = node.toElement().text();
2069 node = srsNode.namedItem( QStringLiteral(
"projectionacronym" ) );
2070 d->mProjectionAcronym = node.toElement().text();
2072 node = srsNode.namedItem( QStringLiteral(
"ellipsoidacronym" ) );
2073 d->mEllipsoidAcronym = node.toElement().text();
2075 node = srsNode.namedItem( QStringLiteral(
"geographicflag" ) );
2076 d->mIsGeographic = node.toElement().text() == QLatin1String(
"true" );
2078 d->mWktPreferred.clear();
2084 const QString epoch = srsNode.toElement().attribute( QStringLiteral(
"coordinateEpoch" ) );
2085 if ( !epoch.isEmpty() )
2087 bool epochOk =
false;
2088 d->mCoordinateEpoch = epoch.toDouble( &epochOk );
2090 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2094 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2097 mNativeFormat = qgsEnumKeyToValue<Qgis::CrsDefinitionFormat>( srsNode.toElement().attribute( QStringLiteral(
"nativeFormat" ) ),
Qgis::CrsDefinitionFormat::Wkt );
2102 d =
new QgsCoordinateReferenceSystemPrivate();
2110 QDomElement layerNode = node.toElement();
2111 QDomElement srsElement = doc.createElement( QStringLiteral(
"spatialrefsys" ) );
2113 srsElement.setAttribute( QStringLiteral(
"nativeFormat" ), qgsEnumValueToKey<Qgis::CrsDefinitionFormat>( mNativeFormat ) );
2115 if ( std::isfinite( d->mCoordinateEpoch ) )
2117 srsElement.setAttribute( QStringLiteral(
"coordinateEpoch" ), d->mCoordinateEpoch );
2120 QDomElement wktElement = doc.createElement( QStringLiteral(
"wkt" ) );
2122 srsElement.appendChild( wktElement );
2124 QDomElement proj4Element = doc.createElement( QStringLiteral(
"proj4" ) );
2125 proj4Element.appendChild( doc.createTextNode(
toProj() ) );
2126 srsElement.appendChild( proj4Element );
2128 QDomElement srsIdElement = doc.createElement( QStringLiteral(
"srsid" ) );
2129 srsIdElement.appendChild( doc.createTextNode( QString::number(
srsid() ) ) );
2130 srsElement.appendChild( srsIdElement );
2132 QDomElement sridElement = doc.createElement( QStringLiteral(
"srid" ) );
2133 sridElement.appendChild( doc.createTextNode( QString::number(
postgisSrid() ) ) );
2134 srsElement.appendChild( sridElement );
2136 QDomElement authidElement = doc.createElement( QStringLiteral(
"authid" ) );
2137 authidElement.appendChild( doc.createTextNode(
authid() ) );
2138 srsElement.appendChild( authidElement );
2140 QDomElement descriptionElement = doc.createElement( QStringLiteral(
"description" ) );
2141 descriptionElement.appendChild( doc.createTextNode(
description() ) );
2142 srsElement.appendChild( descriptionElement );
2144 QDomElement projectionAcronymElement = doc.createElement( QStringLiteral(
"projectionacronym" ) );
2145 projectionAcronymElement.appendChild( doc.createTextNode(
projectionAcronym() ) );
2146 srsElement.appendChild( projectionAcronymElement );
2148 QDomElement ellipsoidAcronymElement = doc.createElement( QStringLiteral(
"ellipsoidacronym" ) );
2149 ellipsoidAcronymElement.appendChild( doc.createTextNode(
ellipsoidAcronym() ) );
2150 srsElement.appendChild( ellipsoidAcronymElement );
2152 QDomElement geographicFlagElement = doc.createElement( QStringLiteral(
"geographicflag" ) );
2153 QString geoFlagText = QStringLiteral(
"false" );
2156 geoFlagText = QStringLiteral(
"true" );
2159 geographicFlagElement.appendChild( doc.createTextNode( geoFlagText ) );
2160 srsElement.appendChild( geographicFlagElement );
2162 layerNode.appendChild( srsElement );
2174 QString QgsCoordinateReferenceSystem::projFromSrsId(
const int srsId )
2176 QString myDatabaseFileName;
2177 QString myProjString;
2178 QString mySql = QStringLiteral(
"select parameters from tbl_srs where srs_id = %1 order by deprecated" ).arg( srsId );
2187 QFileInfo myFileInfo;
2188 myFileInfo.setFile( myDatabaseFileName );
2189 if ( !myFileInfo.exists() )
2191 QgsDebugError( QStringLiteral(
"users qgis.db not found" ) );
2204 rc = openDatabase( myDatabaseFileName, database );
2210 statement = database.
prepare( mySql, rc );
2212 if ( rc == SQLITE_OK )
2214 if ( statement.
step() == SQLITE_ROW )
2220 return myProjString;
2227 myResult = database.
open_v2( path, SQLITE_OPEN_READONLY,
nullptr );
2229 myResult = database.
open( path );
2231 if ( myResult != SQLITE_OK )
2240 .arg( database.
errorMessage() ), QObject::tr(
"CRS" ) );
2247 sCustomSrsValidation = f;
2252 return sCustomSrsValidation;
2255 void QgsCoordinateReferenceSystem::debugPrint()
2258 QgsDebugMsgLevel(
"* Valid : " + ( d->mIsValid ? QString(
"true" ) : QString(
"false" ) ), 1 );
2279 mValidationHint = html;
2284 return mValidationHint;
2294 mNativeFormat = format;
2299 return mNativeFormat;
2302 long QgsCoordinateReferenceSystem::getRecordCount()
2307 long myRecordCount = 0;
2310 if ( myResult != SQLITE_OK )
2316 QString mySql = QStringLiteral(
"select count(*) from tbl_srs" );
2317 statement = database.
prepare( mySql, myResult );
2318 if ( myResult == SQLITE_OK )
2320 if ( statement.
step() == SQLITE_ROW )
2322 QString myRecordCountString = statement.
columnAsText( 0 );
2323 myRecordCount = myRecordCountString.toLong();
2326 return myRecordCount;
2332 bool isGeographic =
false;
2336 if ( !horizontalCrs )
2340 if ( coordinateSystem )
2342 const int axisCount = proj_cs_get_axis_count( pjContext, coordinateSystem.get() );
2343 if ( axisCount > 0 )
2345 const char *outUnitAuthName =
nullptr;
2346 const char *outUnitAuthCode =
nullptr;
2348 proj_cs_get_axis_info( pjContext, coordinateSystem.get(), 0,
2357 if ( outUnitAuthName && outUnitAuthCode )
2359 const char *unitCategory =
nullptr;
2360 if ( proj_uom_get_info_from_database( pjContext, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
2362 isGeographic = QString( unitCategory ).compare( QLatin1String(
"angular" ), Qt::CaseInsensitive ) == 0;
2367 return isGeographic;
2372 thread_local
const QRegularExpression projRegExp( QStringLiteral(
"\\+proj=(\\S+)" ) );
2373 const QRegularExpressionMatch projMatch = projRegExp.match( proj );
2374 if ( !projMatch.hasMatch() )
2376 QgsDebugMsgLevel( QStringLiteral(
"no +proj argument found [%2]" ).arg( proj ), 2 );
2379 operation = projMatch.captured( 1 );
2381 const QRegularExpressionMatch ellipseMatch = projRegExp.match( proj );
2382 if ( ellipseMatch.hasMatch() )
2384 ellipsoid = ellipseMatch.captured( 1 );
2398 bool QgsCoordinateReferenceSystem::loadFromAuthCode(
const QString &auth,
const QString &code )
2404 d->mIsValid =
false;
2405 d->mWktPreferred.clear();
2417 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2418 proj4 = proj4.trimmed();
2422 d->mWktPreferred.clear();
2423 d->mDescription = QString( proj_get_name(
crs.get() ) );
2424 d->mAuthId = QStringLiteral(
"%1:%2" ).arg( auth, code );
2426 d->mAxisInvertedDirty =
true;
2431 d->mEllipsoidAcronym.clear();
2432 d->setPj( std::move(
crs ) );
2434 const QString dbVals =
sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( auth, code ).toUpper() );
2435 if ( !dbVals.isEmpty() )
2437 const QStringList parts = dbVals.split(
',' );
2438 d->mSrsId = parts.at( 0 ).toInt();
2439 d->mSRID = parts.at( 1 ).toInt();
2447 QList<long> QgsCoordinateReferenceSystem::userSrsIds()
2449 QList<long> results;
2453 QFileInfo myInfo( db );
2454 if ( !myInfo.exists() )
2464 int result = openDatabase( db, database );
2465 if ( result != SQLITE_OK )
2467 QgsDebugError(
"failed : " + db +
" could not be opened!" );
2471 QString sql = QStringLiteral(
"select srs_id from tbl_srs where srs_id >= %1" ).arg(
USER_CRS_START_ID );
2473 statement = database.
prepare( sql, rc );
2476 int ret = statement.
step();
2478 if ( ret == SQLITE_DONE )
2484 if ( ret == SQLITE_ROW )
2490 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
2498 long QgsCoordinateReferenceSystem::matchToUserCrs()
const
2500 PJ *obj = d->threadLocalProjObject();
2504 const QList< long > ids = userSrsIds();
2505 for (
long id : ids )
2508 if ( candidate.
projObject() && proj_is_equivalent_to( obj, candidate.
projObject(), PJ_COMP_EQUIVALENT ) )
2516 static void sync_db_proj_logger(
void * ,
int level,
const char *message )
2521 if ( level == PJ_LOG_ERROR )
2525 else if ( level == PJ_LOG_DEBUG )
2533 setlocale( LC_ALL,
"C" );
2536 int inserted = 0, updated = 0, deleted = 0, errors = 0;
2541 if ( database.
open( dbFilePath ) != SQLITE_OK )
2547 if ( sqlite3_exec( database.get(),
"BEGIN TRANSACTION",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2555 char *errMsg =
nullptr;
2557 bool createdTypeColumn =
false;
2558 if ( sqlite3_exec( database.get(),
"ALTER TABLE tbl_srs ADD COLUMN srs_type text",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2560 createdTypeColumn =
true;
2561 if ( sqlite3_exec( database.get(),
"CREATE INDEX srs_type ON tbl_srs(srs_type)",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2563 QgsDebugError( QStringLiteral(
"Could not create index for srs_type" ) );
2568 if ( sqlite3_exec( database.get(),
"create table tbl_info (proj_major INT, proj_minor INT, proj_patch INT)",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2570 QString sql = QStringLiteral(
"INSERT INTO tbl_info(proj_major, proj_minor, proj_patch) VALUES (%1, %2,%3)" )
2571 .arg( QString::number( PROJ_VERSION_MAJOR ),
2572 QString::number( PROJ_VERSION_MINOR ),
2573 QString::number( PROJ_VERSION_PATCH ) );
2574 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2576 QgsDebugError( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2579 errMsg ? errMsg :
"(unknown error)" ) );
2581 sqlite3_free( errMsg );
2588 QString sql = QStringLiteral(
"SELECT proj_major, proj_minor, proj_patch FROM tbl_info" );
2589 statement = database.
prepare( sql, result );
2590 if ( result != SQLITE_OK )
2595 if ( statement.
step() == SQLITE_ROW )
2600 if ( !createdTypeColumn && major == PROJ_VERSION_MAJOR && minor == PROJ_VERSION_MINOR && patch == PROJ_VERSION_PATCH )
2606 QgsDebugError( QStringLiteral(
"Could not retrieve previous CRS sync PROJ version number" ) );
2613 proj_log_func( pjContext,
nullptr, sync_db_proj_logger );
2615 PROJ_STRING_LIST authorities = proj_get_authorities_from_database( pjContext );
2617 int nextSrsId = 67218;
2618 int nextSrId = 520007218;
2619 for (
auto authIter = authorities; authIter && *authIter; ++authIter )
2621 const QString authority( *authIter );
2622 QgsDebugMsgLevel( QStringLiteral(
"Loading authority '%1'" ).arg( authority ), 2 );
2623 PROJ_STRING_LIST codes = proj_get_codes_from_database( pjContext, *authIter, PJ_TYPE_CRS,
true );
2625 QStringList allCodes;
2627 for (
auto codesIter = codes; codesIter && *codesIter; ++codesIter )
2629 const QString code( *codesIter );
2635 QgsDebugError( QStringLiteral(
"Could not load '%1:%2'" ).arg( authority, code ) );
2639 const PJ_TYPE pjType = proj_get_type(
crs.get( ) );
2641 QString srsTypeString;
2646 case PJ_TYPE_ELLIPSOID:
2647 case PJ_TYPE_PRIME_MERIDIAN:
2648 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
2649 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
2650 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
2651 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
2652 case PJ_TYPE_DATUM_ENSEMBLE:
2653 case PJ_TYPE_CONVERSION:
2654 case PJ_TYPE_TRANSFORMATION:
2655 case PJ_TYPE_CONCATENATED_OPERATION:
2656 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
2657 case PJ_TYPE_TEMPORAL_DATUM:
2658 case PJ_TYPE_ENGINEERING_DATUM:
2659 case PJ_TYPE_PARAMETRIC_DATUM:
2660 case PJ_TYPE_UNKNOWN:
2664 case PJ_TYPE_GEOGRAPHIC_CRS:
2667 case PJ_TYPE_GEODETIC_CRS:
2671 case PJ_TYPE_GEOCENTRIC_CRS:
2675 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
2679 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
2683 case PJ_TYPE_PROJECTED_CRS:
2687 case PJ_TYPE_COMPOUND_CRS:
2691 case PJ_TYPE_TEMPORAL_CRS:
2695 case PJ_TYPE_ENGINEERING_CRS:
2699 case PJ_TYPE_BOUND_CRS:
2703 case PJ_TYPE_VERTICAL_CRS:
2707 #if PROJ_VERSION_MAJOR>9 || (PROJ_VERSION_MAJOR==9 && PROJ_VERSION_MINOR>=2)
2708 case PJ_TYPE_DERIVED_PROJECTED_CRS:
2711 case PJ_TYPE_COORDINATE_METADATA:
2714 case PJ_TYPE_OTHER_CRS:
2723 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2724 proj4 = proj4.trimmed();
2726 if ( proj4.isEmpty() )
2728 QgsDebugMsgLevel( QStringLiteral(
"No proj4 for '%1:%2'" ).arg( authority, code ), 2 );
2739 if ( translatedOperation.isEmpty() && !
operation.isEmpty() )
2741 std::cout << QStringLiteral(
"Operation needs translation in QgsCoordinateReferenceSystemUtils::translateProjection: %1" ).arg(
operation ).toLocal8Bit().constData() << std::endl;
2742 qFatal(
"aborted" );
2745 const bool deprecated = proj_is_deprecated(
crs.get() );
2746 const QString name( proj_get_name(
crs.get() ) );
2748 QString sql = QStringLiteral(
"SELECT parameters,description,deprecated,srs_type FROM tbl_srs WHERE auth_name='%1' AND auth_id='%2'" ).arg( authority, code );
2749 statement = database.
prepare( sql, result );
2750 if ( result != SQLITE_OK )
2759 bool dbSrsDeprecated = deprecated;
2760 if ( statement.
step() == SQLITE_ROW )
2764 dbSrsDeprecated = statement.
columnAsText( 2 ).toInt() != 0;
2768 if ( !dbSrsProj4.isEmpty() || !dbSrsDesc.isEmpty() )
2770 if ( proj4 != dbSrsProj4 || name != dbSrsDesc || deprecated != dbSrsDeprecated || dbSrsType != srsTypeString )
2773 sql = QStringLiteral(
"UPDATE tbl_srs SET parameters=%1,description=%2,deprecated=%3, srs_type=%4 WHERE auth_name=%5 AND auth_id=%6" )
2776 .arg( deprecated ? 1 : 0 )
2780 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2782 QgsDebugError( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2785 errMsg ? errMsg :
"(unknown error)" ) );
2787 sqlite3_free( errMsg );
2801 const QString dbVals =
sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( authority, code ) );
2804 if ( !dbVals.isEmpty() )
2806 const QStringList parts = dbVals.split(
',' );
2807 srsId = parts.at( 0 );
2808 srId = parts.at( 1 );
2810 if ( srId.isEmpty() )
2812 srId = QString::number( nextSrId );
2815 if ( srsId.isEmpty() )
2817 srsId = QString::number( nextSrsId );
2821 if ( !srsId.isEmpty() )
2823 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)" )
2833 .arg( deprecated ? 1 : 0 )
2838 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)" )
2847 .arg( deprecated ? 1 : 0 )
2852 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) == SQLITE_OK )
2858 qCritical(
"Could not execute: %s [%s/%s]\n",
2859 sql.toLocal8Bit().constData(),
2860 sqlite3_errmsg( database.get() ),
2861 errMsg ? errMsg :
"(unknown error)" );
2865 sqlite3_free( errMsg );
2870 proj_string_list_destroy( codes );
2872 const QString sql = QStringLiteral(
"DELETE FROM tbl_srs WHERE auth_name='%1' AND NOT auth_id IN (%2)" ).arg( authority, allCodes.join(
',' ) );
2873 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2875 deleted = sqlite3_changes( database.get() );
2880 qCritical(
"Could not execute: %s [%s]\n",
2881 sql.toLocal8Bit().constData(),
2882 sqlite3_errmsg( database.get() ) );
2886 proj_string_list_destroy( authorities );
2888 QString sql = QStringLiteral(
"UPDATE tbl_info set proj_major=%1,proj_minor=%2,proj_patch=%3" )
2889 .arg( QString::number( PROJ_VERSION_MAJOR ),
2890 QString::number( PROJ_VERSION_MINOR ),
2891 QString::number( PROJ_VERSION_PATCH ) );
2892 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2894 QgsDebugError( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2897 errMsg ? errMsg :
"(unknown error)" ) );
2899 sqlite3_free( errMsg );
2903 if ( sqlite3_exec( database.get(),
"COMMIT",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2905 QgsDebugError( QStringLiteral(
"Could not commit transaction: %1 [%2]\n" ).arg(
2907 sqlite3_errmsg( database.get() ) )
2913 QgsDebugMsgLevel( QStringLiteral(
"CRS update (inserted:%1 updated:%2 deleted:%3 errors:%4)" ).arg( inserted ).arg( updated ).arg( deleted ).arg( errors ), 4 );
2921 return updated + inserted;
2924 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::stringCache()
2926 return *sStringCache();
2929 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::projCache()
2931 return *sProj4Cache();
2934 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::ogcCache()
2936 return *sOgcCache();
2939 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::wktCache()
2941 return *sWktCache();
2944 const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srIdCache()
2946 return *sSrIdCache();
2949 const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srsIdCache()
2951 return *sSrsIdCache();
2961 if (
PJ *obj = d->threadLocalProjObject() )
3007 if (
PJ *obj = d->threadLocalProjObject() )
3040 if (
PJ *obj = d->threadLocalProjObject() )
3055 else if (
PJ *obj = d->threadLocalProjObject() )
3058 return geoCrs ? QStringLiteral(
"%1:%2" ).arg( proj_get_id_auth_name( geoCrs.get(), 0 ), proj_get_id_code( geoCrs.get(), 0 ) ) : QString();
3068 return d->threadLocalProjObject();
3081 d->mIsValid =
false;
3083 d->mWktPreferred.clear();
3090 switch ( proj_get_type(
object ) )
3092 case PJ_TYPE_GEODETIC_CRS:
3093 case PJ_TYPE_GEOCENTRIC_CRS:
3094 case PJ_TYPE_GEOGRAPHIC_CRS:
3095 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
3096 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
3097 case PJ_TYPE_VERTICAL_CRS:
3098 case PJ_TYPE_PROJECTED_CRS:
3099 case PJ_TYPE_COMPOUND_CRS:
3100 case PJ_TYPE_TEMPORAL_CRS:
3101 case PJ_TYPE_ENGINEERING_CRS:
3102 case PJ_TYPE_BOUND_CRS:
3103 case PJ_TYPE_OTHER_CRS:
3119 const QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
3120 const QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
3121 if ( !authName.isEmpty() && !authCode.isEmpty() && loadFromAuthCode( authName, authCode ) )
3129 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
3140 QStringList projections;
3142 projections.reserve( res.size() );
3145 projections << QString::number(
crs.
srsid() );
3172 sSrIdCacheLock()->lockForWrite();
3173 if ( !sDisableSrIdCache )
3176 sDisableSrIdCache =
true;
3177 sSrIdCache()->clear();
3179 sSrIdCacheLock()->unlock();
3181 sOgcLock()->lockForWrite();
3182 if ( !sDisableOgcCache )
3185 sDisableOgcCache =
true;
3186 sOgcCache()->clear();
3188 sOgcLock()->unlock();
3190 sProj4CacheLock()->lockForWrite();
3191 if ( !sDisableProjCache )
3194 sDisableProjCache =
true;
3195 sProj4Cache()->clear();
3197 sProj4CacheLock()->unlock();
3199 sCRSWktLock()->lockForWrite();
3200 if ( !sDisableWktCache )
3203 sDisableWktCache =
true;
3204 sWktCache()->clear();
3206 sCRSWktLock()->unlock();
3208 sCRSSrsIdLock()->lockForWrite();
3209 if ( !sDisableSrsIdCache )
3212 sDisableSrsIdCache =
true;
3213 sSrsIdCache()->clear();
3215 sCRSSrsIdLock()->unlock();
3217 sCrsStringLock()->lockForWrite();
3218 if ( !sDisableStringCache )
3221 sDisableStringCache =
true;
3222 sStringCache()->clear();
3224 sCrsStringLock()->unlock();
3233 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3236 if ( !c1.d->mIsValid && c2.d->mIsValid )
3239 if ( c1.d->mIsValid && !c2.d->mIsValid )
3245 if ( c1IsUser && !c2IsUser )
3248 if ( !c1IsUser && c2IsUser )
3251 if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
3253 if ( c1.d->mAuthId != c2.d->mAuthId )
3254 return c1.d->mAuthId > c2.d->mAuthId;
3262 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3265 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3268 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3271 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3274 return c1.d->mCoordinateEpoch > c2.d->mCoordinateEpoch;
3282 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3285 if ( c1.d->mIsValid && !c2.d->mIsValid )
3288 if ( !c1.d->mIsValid && c2.d->mIsValid )
3294 if ( !c1IsUser && c2IsUser )
3297 if ( c1IsUser && !c2IsUser )
3300 if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
3302 if ( c1.d->mAuthId != c2.d->mAuthId )
3303 return c1.d->mAuthId < c2.d->mAuthId;
3311 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3314 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3317 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3320 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3323 return c1.d->mCoordinateEpoch < c2.d->mCoordinateEpoch;
3328 return !( c1 < c2 );
3332 return !( c1 > c2 );
CrsIdentifierType
Available identifier string types for representing coordinate reference systems.
@ ShortString
A heavily abbreviated string, for use when a compact representation is required.
@ MediumString
A medium-length string, recommended for general purpose use.
DistanceUnit
Units of distance.
@ Centimeters
Centimeters.
@ Millimeters
Millimeters.
@ Miles
Terrestrial miles.
@ Unknown
Unknown distance unit.
@ Degrees
Degrees, for planar geographic CRS distance measurements.
@ NauticalMiles
Nautical miles.
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.
Q_GADGET Qgis::DistanceUnit mapUnits
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 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...
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.
static QgsCoordinateReferenceSystem createCompoundCrs(const QgsCoordinateReferenceSystem &horizontalCrs, const QgsCoordinateReferenceSystem &verticalCrs)
Given a horizontal and vertical CRS, attempts to create a compound CRS from them.
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.
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 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 proj_pj_unique_ptr createCompoundCrs(const PJ *horizontalCrs, const PJ *verticalCrs)
Given a PROJ horizontal and vertical CRS, attempt to create a compound CRS from them.
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