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() );
266 QgsDebugError( QStringLiteral(
"Unexpected case reached!" ) );
273 if ( definition.isEmpty() )
277 if ( !sDisableStringCache )
279 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sStringCache()->constFind( definition );
280 if ( crsIt != sStringCache()->constEnd() )
283 *
this = crsIt.value();
290 const thread_local QRegularExpression reCrsId( QStringLiteral(
"^(epsg|esri|osgeo|ignf|ogc|nkg|zangi|iau_2015|iau2000|postgis|internal|user)\\:(\\w+)$" ), QRegularExpression::CaseInsensitiveOption );
291 QRegularExpressionMatch match = reCrsId.match( definition );
292 if ( match.capturedStart() == 0 )
294 QString authName = match.captured( 1 ).toLower();
295 if ( authName == QLatin1String(
"epsg" ) )
299 else if ( authName == QLatin1String(
"postgis" ) )
301 const long id = match.captured( 2 ).toLong();
306 else if ( authName == QLatin1String(
"esri" )
307 || authName == QLatin1String(
"osgeo" )
308 || authName == QLatin1String(
"ignf" )
309 || authName == QLatin1String(
"zangi" )
310 || authName == QLatin1String(
"iau2000" )
311 || authName == QLatin1String(
"ogc" )
312 || authName == QLatin1String(
"nkg" )
313 || authName == QLatin1String(
"iau_2015" )
320 const long id = match.captured( 2 ).toLong();
328 const thread_local QRegularExpression reCrsStr( QStringLiteral(
"^(?:(wkt|proj4|proj)\\:)?(.+)$" ), QRegularExpression::CaseInsensitiveOption );
329 match = reCrsStr.match( definition );
330 if ( match.capturedStart() == 0 )
332 if ( match.captured( 1 ).startsWith( QLatin1String(
"proj" ), Qt::CaseInsensitive ) )
344 if ( !sDisableStringCache )
345 sStringCache()->insert( definition, *
this );
351 if ( definition.isEmpty() )
357 if ( OSRSetFromUserInput(
crs, definition.toLocal8Bit().constData() ) == OGRERR_NONE )
360 OSRDestroySpatialReference(
crs );
370 const char *configOld = CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" );
371 const char *configNew =
"GEOGCS";
373 if ( strcmp( configOld,
"" ) == 0 )
375 CPLSetConfigOption(
"GDAL_FIX_ESRI_WKT", configNew );
376 if ( strcmp( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) != 0 )
378 .arg( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) );
379 QgsDebugMsgLevel( QStringLiteral(
"set GDAL_FIX_ESRI_WKT : %1" ).arg( configNew ), 4 );
383 QgsDebugMsgLevel( QStringLiteral(
"GDAL_FIX_ESRI_WKT was already set : %1" ).arg( configNew ), 4 );
393 if ( !sDisableOgcCache )
395 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sOgcCache()->constFind(
crs );
396 if ( crsIt != sOgcCache()->constEnd() )
399 *
this = crsIt.value();
405 QString wmsCrs =
crs;
410 const QString authorityLower = authority.toLower();
412 ( authorityLower == QLatin1String(
"user" ) ||
413 authorityLower == QLatin1String(
"custom" ) ||
414 authorityLower == QLatin1String(
"qgis" ) ) )
419 if ( !sDisableOgcCache )
420 sOgcCache()->insert(
crs, *
this );
426 wmsCrs = authority +
':' + code;
430 const QString legacyKey = wmsCrs.toLower();
433 if ( it.key().compare( legacyKey, Qt::CaseInsensitive ) == 0 )
435 const QStringList parts = it.key().split(
':' );
436 const QString auth = parts.at( 0 );
437 const QString code = parts.at( 1 );
438 if ( loadFromAuthCode( auth, code ) )
441 if ( !sDisableOgcCache )
442 sOgcCache()->insert(
crs, *
this );
451 if ( !sDisableOgcCache )
452 sOgcCache()->insert(
crs, *
this );
457 if ( wmsCrs.compare( QLatin1String(
"CRS:27" ), Qt::CaseInsensitive ) == 0 ||
458 wmsCrs.compare( QLatin1String(
"OGC:CRS27" ), Qt::CaseInsensitive ) == 0 )
465 if ( wmsCrs.compare( QLatin1String(
"CRS:83" ), Qt::CaseInsensitive ) == 0 ||
466 wmsCrs.compare( QLatin1String(
"OGC:CRS83" ), Qt::CaseInsensitive ) == 0 )
473 if ( wmsCrs.compare( QLatin1String(
"CRS:84" ), Qt::CaseInsensitive ) == 0 ||
474 wmsCrs.compare( QLatin1String(
"OGC:CRS84" ), Qt::CaseInsensitive ) == 0 )
478 d->mAxisInverted =
false;
479 d->mAxisInvertedDirty =
false;
483 if ( !sDisableOgcCache )
484 sOgcCache()->insert(
crs, *
this );
491 if ( !authority.isEmpty() && !code.isEmpty() && loadFromAuthCode( authority, code ) )
494 if ( !sDisableOgcCache )
495 sOgcCache()->insert(
crs, *
this );
500 if ( !sDisableOgcCache )
510 if ( d->mIsValid || !sCustomSrsValidation )
514 if ( sCustomSrsValidation )
515 sCustomSrsValidation( *
this );
521 if ( !sDisableSrIdCache )
523 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrIdCache()->constFind(
id );
524 if ( crsIt != sSrIdCache()->constEnd() )
527 *
this = crsIt.value();
536 if ( it.value().endsWith( QStringLiteral(
",%1" ).arg(
id ) ) )
538 const QStringList parts = it.key().split(
':' );
539 const QString auth = parts.at( 0 );
540 const QString code = parts.at( 1 );
541 if ( loadFromAuthCode( auth, code ) )
544 if ( !sDisableSrIdCache )
545 sSrIdCache()->insert(
id, *
this );
555 if ( !sDisableSrIdCache )
556 sSrIdCache()->insert(
id, *
this );
564 if ( !sDisableSrsIdCache )
566 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrsIdCache()->constFind(
id );
567 if ( crsIt != sSrsIdCache()->constEnd() )
570 *
this = crsIt.value();
579 if ( it.value().startsWith( QString::number(
id ) +
',' ) )
581 const QStringList parts = it.key().split(
':' );
582 const QString auth = parts.at( 0 );
583 const QString code = parts.at( 1 );
584 if ( loadFromAuthCode( auth, code ) )
587 if ( !sDisableSrsIdCache )
588 sSrsIdCache()->insert(
id, *
this );
596 QStringLiteral(
"srs_id" ), QString::number(
id ) );
599 if ( !sDisableSrsIdCache )
600 sSrsIdCache()->insert(
id, *
this );
604bool QgsCoordinateReferenceSystem::loadFromDatabase(
const QString &db,
const QString &expression,
const QString &value )
608 QgsDebugMsgLevel(
"load CRS from " + db +
" where " + expression +
" is " + value, 3 );
610 d->mWktPreferred.clear();
612 QFileInfo myInfo( db );
613 if ( !myInfo.exists() )
623 myResult = openDatabase( db, database );
624 if ( myResult != SQLITE_OK )
641 QString mySql =
"select srs_id,description,projection_acronym,"
642 "ellipsoid_acronym,parameters,srid,auth_name||':'||auth_id,is_geo,wkt "
644 statement = database.
prepare( mySql, myResult );
647 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
652 d->mEllipsoidAcronym.clear();
654 d->mWktPreferred.clear();
657 d->mIsGeographic = statement.
columnAsText( 7 ).toInt() != 0;
659 d->mAxisInvertedDirty =
true;
661 if ( d->mSrsId >=
USER_CRS_START_ID && ( d->mAuthId.isEmpty() || d->mAuthId == QChar(
':' ) ) )
663 d->mAuthId = QStringLiteral(
"USER:%1" ).arg( d->mSrsId );
665 else if ( !d->mAuthId.startsWith( QLatin1String(
"USER:" ), Qt::CaseInsensitive ) )
667 QStringList parts = d->mAuthId.split(
':' );
668 QString auth = parts.at( 0 );
669 QString code = parts.at( 1 );
676 d->mIsValid = d->hasPj();
682 if ( !wkt.isEmpty() )
690 setProjString( d->mProj4 );
700void QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread(
PJ_CONTEXT *pj_context )
707 if ( !sDisableSrIdCache )
710 if ( !sDisableSrIdCache )
712 for (
auto it = sSrIdCache()->begin(); it != sSrIdCache()->end(); )
714 auto &v = it.value();
715 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
716 it = sSrIdCache()->erase( it );
722 if ( !sDisableOgcCache )
725 if ( !sDisableOgcCache )
727 for (
auto it = sOgcCache()->begin(); it != sOgcCache()->end(); )
729 auto &v = it.value();
730 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
731 it = sOgcCache()->erase( it );
737 if ( !sDisableProjCache )
740 if ( !sDisableProjCache )
742 for (
auto it = sProj4Cache()->begin(); it != sProj4Cache()->end(); )
744 auto &v = it.value();
745 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
746 it = sProj4Cache()->erase( it );
752 if ( !sDisableWktCache )
755 if ( !sDisableWktCache )
757 for (
auto it = sWktCache()->begin(); it != sWktCache()->end(); )
759 auto &v = it.value();
760 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
761 it = sWktCache()->erase( it );
767 if ( !sDisableSrsIdCache )
770 if ( !sDisableSrsIdCache )
772 for (
auto it = sSrsIdCache()->begin(); it != sSrsIdCache()->end(); )
774 auto &v = it.value();
775 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
776 it = sSrsIdCache()->erase( it );
782 if ( !sDisableStringCache )
785 if ( !sDisableStringCache )
787 for (
auto it = sStringCache()->begin(); it != sStringCache()->end(); )
789 auto &v = it.value();
790 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
791 it = sStringCache()->erase( it );
801 if ( d->mAxisInvertedDirty )
804 d->mAxisInvertedDirty =
false;
807 return d->mAxisInverted;
821 const thread_local QMap< Qgis::CrsAxisDirection, QString > mapping =
864 QList< Qgis::CrsAxisDirection > res;
865 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
868 res.reserve( axisCount );
870 for (
int i = 0; i < axisCount; ++i )
872 const char *outDirection =
nullptr;
873 proj_cs_get_axis_info( context, pjCs.get(), i,
883 const thread_local QRegularExpression rx( QStringLiteral(
"([^\\s]+).*" ) );
884 const QRegularExpressionMatch match = rx.match( QString( outDirection ) );
885 if ( !match.hasMatch() )
888 const QString direction = match.captured( 1 );
890 for (
auto it = mapping.constBegin(); it != mapping.constEnd(); ++it )
892 if ( it.value().compare( direction, Qt::CaseInsensitive ) == 0 )
907 return createFromWktInternal( wkt, QString() );
910bool QgsCoordinateReferenceSystem::createFromWktInternal(
const QString &wkt,
const QString &description )
918 if ( !sDisableWktCache )
920 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sWktCache()->constFind( wkt );
921 if ( crsIt != sWktCache()->constEnd() )
924 *
this = crsIt.value();
926 if ( !
description.isEmpty() && d->mDescription.isEmpty() )
931 sWktCache()->insert( wkt, *
this );
940 d->mWktPreferred.clear();
943 QgsDebugMsgLevel( QStringLiteral(
"theWkt is uninitialized, operation failed" ), 4 );
948 QgsCoordinateReferenceSystem::RecordMap record = getRecord(
"select * from tbl_srs where wkt=" +
QgsSqliteUtils::quotedString( wkt ) +
" order by deprecated" );
949 if ( !record.empty() )
951 long srsId = record[QStringLiteral(
"srs_id" )].toLong();
964 if ( d->mSrsId == 0 )
967 long id = matchToUserCrs();
976 if ( !sDisableWktCache )
977 sWktCache()->insert( wkt, *
this );
995 if ( projString.isEmpty() )
1000 if ( projString.trimmed().isEmpty() )
1002 d->mIsValid =
false;
1004 d->mWktPreferred.clear();
1009 if ( !sDisableProjCache )
1011 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sProj4Cache()->constFind( projString );
1012 if ( crsIt != sProj4Cache()->constEnd() )
1015 *
this = crsIt.value();
1029 QString myProj4String = projString.trimmed();
1030 myProj4String.remove( QStringLiteral(
"+type=crs" ) );
1031 myProj4String = myProj4String.trimmed();
1033 d->mIsValid =
false;
1034 d->mWktPreferred.clear();
1039 const QString projCrsString = myProj4String + ( myProj4String.contains( QStringLiteral(
"+type=crs" ) ) ? QString() : QStringLiteral(
" +type=crs" ) );
1047 const QString
authid = QStringLiteral(
"%1:%2" ).arg( authName, authCode );
1051 if ( !sDisableProjCache )
1052 sProj4Cache()->insert( projString, *
this );
1059 QgsCoordinateReferenceSystem::RecordMap myRecord = getRecord(
"select * from tbl_srs where parameters=" +
QgsSqliteUtils::quotedString( myProj4String ) +
" order by deprecated" );
1061 if ( !myRecord.empty() )
1063 id = myRecord[QStringLiteral(
"srs_id" )].toLong();
1072 setProjString( myProj4String );
1075 id = matchToUserCrs();
1084 setProjString( myProj4String );
1088 if ( !sDisableProjCache )
1089 sProj4Cache()->insert( projString, *
this );
1095QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord(
const QString &sql )
1097 QString myDatabaseFileName;
1098 QgsCoordinateReferenceSystem::RecordMap myMap;
1099 QString myFieldName;
1100 QString myFieldValue;
1107 QFileInfo myInfo( myDatabaseFileName );
1108 if ( !myInfo.exists() )
1110 QgsDebugError(
"failed : " + myDatabaseFileName +
" does not exist!" );
1115 myResult = openDatabase( myDatabaseFileName, database );
1116 if ( myResult != SQLITE_OK )
1121 statement = database.
prepare( sql, myResult );
1123 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1127 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1129 myFieldName = statement.
columnName( myColNo );
1131 myMap[myFieldName] = myFieldValue;
1133 if ( statement.
step() != SQLITE_DONE )
1135 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1144 if ( myMap.empty() )
1147 QFileInfo myFileInfo;
1148 myFileInfo.setFile( myDatabaseFileName );
1149 if ( !myFileInfo.exists() )
1151 QgsDebugError( QStringLiteral(
"user qgis.db not found" ) );
1156 myResult = openDatabase( myDatabaseFileName, database );
1157 if ( myResult != SQLITE_OK )
1162 statement = database.
prepare( sql, myResult );
1164 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1168 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1170 myFieldName = statement.
columnName( myColNo );
1172 myMap[myFieldName] = myFieldValue;
1175 if ( statement.
step() != SQLITE_DONE )
1177 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1208 if ( d->mDescription.isNull() )
1214 return d->mDescription;
1221 if ( !
authid().isEmpty() )
1231 id =
isValid() ? QObject::tr(
"Custom CRS" ) : QObject::tr(
"Unknown CRS" );
1233 id = QObject::tr(
"Custom CRS: %1" ).arg(
1236 else if ( !
toProj().isEmpty() )
1237 id = QObject::tr(
"Custom CRS: %1" ).arg(
type ==
MediumString ? (
toProj().left( 50 ) + QString( QChar( 0x2026 ) ) )
1239 if ( !
id.isEmpty() && !std::isnan( d->mCoordinateEpoch ) )
1240 id += QStringLiteral(
" @ %1" ).arg( d->mCoordinateEpoch );
1247 if ( d->mProjectionAcronym.isNull() )
1253 return d->mProjectionAcronym;
1259 if ( d->mEllipsoidAcronym.isNull() )
1261 if (
PJ *obj = d->threadLocalProjObject() )
1266 const QString ellipsoidAuthName( proj_get_id_auth_name( ellipsoid.get(), 0 ) );
1267 const QString ellipsoidAuthCode( proj_get_id_code( ellipsoid.get(), 0 ) );
1268 if ( !ellipsoidAuthName.isEmpty() && !ellipsoidAuthCode.isEmpty() )
1269 d->mEllipsoidAcronym = QStringLiteral(
"%1:%2" ).arg( ellipsoidAuthName, ellipsoidAuthCode );
1272 double semiMajor, semiMinor, invFlattening;
1273 int semiMinorComputed = 0;
1274 if ( proj_ellipsoid_get_parameters(
QgsProjContext::get(), ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
1276 d->mEllipsoidAcronym = QStringLiteral(
"PARAMETER:%1:%2" ).arg(
qgsDoubleToString( semiMajor ),
1281 d->mEllipsoidAcronym.clear();
1286 return d->mEllipsoidAcronym;
1290 return d->mEllipsoidAcronym;
1304 if ( d->mProj4.isEmpty() )
1306 if (
PJ *obj = d->threadLocalProjObject() )
1312 return d->mProj4.trimmed();
1318 switch ( d->mProjType )
1320 case PJ_TYPE_UNKNOWN:
1323 case PJ_TYPE_ELLIPSOID:
1324 case PJ_TYPE_PRIME_MERIDIAN:
1325 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
1326 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
1327 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
1328 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
1329 case PJ_TYPE_DATUM_ENSEMBLE:
1330 case PJ_TYPE_CONVERSION:
1331 case PJ_TYPE_TRANSFORMATION:
1332 case PJ_TYPE_CONCATENATED_OPERATION:
1333 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
1334 case PJ_TYPE_TEMPORAL_DATUM:
1335 case PJ_TYPE_ENGINEERING_DATUM:
1336 case PJ_TYPE_PARAMETRIC_DATUM:
1340 case PJ_TYPE_GEOGRAPHIC_CRS:
1344 case PJ_TYPE_GEODETIC_CRS:
1346 case PJ_TYPE_GEOCENTRIC_CRS:
1348 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
1350 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
1352 case PJ_TYPE_VERTICAL_CRS:
1354 case PJ_TYPE_PROJECTED_CRS:
1356 case PJ_TYPE_COMPOUND_CRS:
1358 case PJ_TYPE_TEMPORAL_CRS:
1360 case PJ_TYPE_ENGINEERING_CRS:
1362 case PJ_TYPE_BOUND_CRS:
1364 case PJ_TYPE_OTHER_CRS:
1366#if PROJ_VERSION_MAJOR>9 || (PROJ_VERSION_MAJOR==9 && PROJ_VERSION_MINOR>=2)
1367 case PJ_TYPE_DERIVED_PROJECTED_CRS:
1369 case PJ_TYPE_COORDINATE_METADATA:
1379 return d->mIsGeographic;
1397#if PROJ_VERSION_MAJOR>8 || (PROJ_VERSION_MAJOR==8 && PROJ_VERSION_MINOR>=1)
1400 return QString( proj_get_celestial_body_name( context, pj ) );
1402 throw QgsNotSupportedException( QObject::tr(
"Retrieving celestial body requires a QGIS build based on PROJ 8.1 or later" ) );
1408 if ( d->mCoordinateEpoch == epoch )
1414 d->mCoordinateEpoch = epoch;
1415 d->setPj( std::move( clone ) );
1420 return d->mCoordinateEpoch;
1432#if PROJ_VERSION_MAJOR>=8
1440 res.mName = QString( proj_get_name( ensemble.get() ) );
1441 res.mAuthority = QString( proj_get_id_auth_name( ensemble.get(), 0 ) );
1442 res.mCode = QString( proj_get_id_code( ensemble.get(), 0 ) );
1443 res.mRemarks = QString( proj_get_remarks( ensemble.get() ) );
1444 res.mScope = QString( proj_get_scope( ensemble.get() ) );
1445 res.mAccuracy = proj_datum_ensemble_get_accuracy( context, ensemble.get() );
1447 const int memberCount = proj_datum_ensemble_get_member_count( context, ensemble.get() );
1448 for (
int i = 0; i < memberCount; ++i )
1455 details.mName = QString( proj_get_name( member.get() ) );
1456 details.mAuthority = QString( proj_get_id_auth_name( member.get(), 0 ) );
1457 details.mCode = QString( proj_get_id_code( member.get(), 0 ) );
1458 details.mRemarks = QString( proj_get_remarks( member.get() ) );
1459 details.mScope = QString( proj_get_scope( member.get() ) );
1461 res.mMembers << details;
1465 throw QgsNotSupportedException( QObject::tr(
"Calculating datum ensembles requires a QGIS build based on PROJ 8.0 or later" ) );
1474 QString projString =
toProj();
1475 projString.replace( QLatin1String(
"+type=crs" ), QString() );
1478 if ( !transformation )
1481 PJ_COORD coord = proj_coord( 0, 0, 0, HUGE_VAL );
1482 coord.uv.u = point.
x() * M_PI / 180.0;
1483 coord.uv.v = point.
y() * M_PI / 180.0;
1485 proj_errno_reset( transformation.get() );
1486 const PJ_FACTORS pjFactors = proj_factors( transformation.get(), coord );
1487 if ( proj_errno( transformation.get() ) )
1492 res.mIsValid =
true;
1493 res.mMeridionalScale = pjFactors.meridional_scale;
1494 res.mParallelScale = pjFactors.parallel_scale;
1495 res.mArealScale = pjFactors.areal_scale;
1496 res.mAngularDistortion = pjFactors.angular_distortion;
1497 res.mMeridianParallelAngle = pjFactors.meridian_parallel_angle * 180 / M_PI;
1498 res.mMeridianConvergence = pjFactors.meridian_convergence * 180 / M_PI;
1499 res.mTissotSemimajor = pjFactors.tissot_semimajor;
1500 res.mTissotSemiminor = pjFactors.tissot_semiminor;
1501 res.mDxDlam = pjFactors.dx_dlam;
1502 res.mDxDphi = pjFactors.dx_dphi;
1503 res.mDyDlam = pjFactors.dy_dlam;
1504 res.mDyDphi = pjFactors.dy_dphi;
1516 QString projString =
toProj();
1517 projString.replace( QLatin1String(
"+type=crs" ), QString() );
1518 if ( projString.isEmpty() )
1522 if ( !transformation )
1525 PJ_PROJ_INFO info = proj_pj_info( transformation.get() );
1540 return d->mMapUnits;
1548 PJ *obj = d->threadLocalProjObject();
1553 double southLat = 0;
1555 double northLat = 0;
1558 &westLon, &southLat, &eastLon, &northLat,
nullptr ) )
1573 const auto parts {
authid().split(
':' ) };
1574 if ( parts.length() == 2 )
1576 if ( parts[0] == QLatin1String(
"EPSG" ) )
1577 return QStringLiteral(
"http://www.opengis.net/def/crs/EPSG/0/%1" ).arg( parts[1] ) ;
1578 else if ( parts[0] == QLatin1String(
"OGC" ) )
1580 return QStringLiteral(
"http://www.opengis.net/def/crs/OGC/1.3/%1" ).arg( parts[1] ) ;
1610void QgsCoordinateReferenceSystem::setProjString(
const QString &proj4String )
1613 d->mProj4 = proj4String;
1614 d->mWktPreferred.clear();
1617 QString trimmed = proj4String.trimmed();
1619 trimmed += QLatin1String(
" +type=crs" );
1629 const int errNo = proj_context_errno( ctx );
1630 QgsDebugError( QStringLiteral(
"proj string rejected: %1" ).arg( proj_errno_string( errNo ) ) );
1632 d->mIsValid =
false;
1636 d->mEllipsoidAcronym.clear();
1643bool QgsCoordinateReferenceSystem::setWktString(
const QString &wkt )
1646 d->mIsValid =
false;
1647 d->mWktPreferred.clear();
1649 PROJ_STRING_LIST warnings =
nullptr;
1650 PROJ_STRING_LIST grammerErrors =
nullptr;
1658 QgsDebugMsgLevel( QStringLiteral(
"\n---------------------------------------------------------------" ), 2 );
1659 QgsDebugMsgLevel( QStringLiteral(
"This CRS could *** NOT *** be set from the supplied Wkt " ), 2 );
1661 for (
auto iter = warnings; iter && *iter; ++iter )
1663 for (
auto iter = grammerErrors; iter && *iter; ++iter )
1665 QgsDebugMsgLevel( QStringLiteral(
"---------------------------------------------------------------\n" ), 2 );
1667 proj_string_list_destroy( warnings );
1668 proj_string_list_destroy( grammerErrors );
1674 if ( !sDisableWktCache )
1675 sWktCache()->insert( wkt, *
this );
1682 QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
1683 QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
1685 if ( authName.isEmpty() || authCode.isEmpty() )
1691 if ( !authName.isEmpty() && !authCode.isEmpty() )
1693 if ( loadFromAuthCode( authName, authCode ) )
1696 if ( !sDisableWktCache )
1697 sWktCache()->insert( wkt, *
this );
1705 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
1713void QgsCoordinateReferenceSystem::setMapUnits()
1740 if ( !coordinateSystem )
1746 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
1747 if ( axisCount > 0 )
1749 const char *outUnitName =
nullptr;
1751 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
1760 const QString unitName( outUnitName );
1764 if ( unitName.compare( QLatin1String(
"degree" ), Qt::CaseInsensitive ) == 0 ||
1765 unitName.compare( QLatin1String(
"degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1766 unitName.compare( QLatin1String(
"degree minute second hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1767 unitName.compare( QLatin1String(
"degree minute" ), Qt::CaseInsensitive ) == 0 ||
1768 unitName.compare( QLatin1String(
"degree hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1769 unitName.compare( QLatin1String(
"degree minute hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1770 unitName.compare( QLatin1String(
"hemisphere degree" ), Qt::CaseInsensitive ) == 0 ||
1771 unitName.compare( QLatin1String(
"hemisphere degree minute" ), Qt::CaseInsensitive ) == 0 ||
1772 unitName.compare( QLatin1String(
"hemisphere degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1773 unitName.compare( QLatin1String(
"degree (supplier to define representation)" ), Qt::CaseInsensitive ) == 0 )
1775 else if ( unitName.compare( QLatin1String(
"metre" ), Qt::CaseInsensitive ) == 0
1776 || unitName.compare( QLatin1String(
"m" ), Qt::CaseInsensitive ) == 0
1777 || unitName.compare( QLatin1String(
"meter" ), Qt::CaseInsensitive ) == 0 )
1780 else if ( unitName.compare( QLatin1String(
"US survey foot" ), Qt::CaseInsensitive ) == 0 ||
1781 unitName.compare( QLatin1String(
"foot" ), Qt::CaseInsensitive ) == 0 )
1783 else if ( unitName.compare( QLatin1String(
"kilometre" ), Qt::CaseInsensitive ) == 0 )
1785 else if ( unitName.compare( QLatin1String(
"centimetre" ), Qt::CaseInsensitive ) == 0 )
1787 else if ( unitName.compare( QLatin1String(
"millimetre" ), Qt::CaseInsensitive ) == 0 )
1789 else if ( unitName.compare( QLatin1String(
"Statute mile" ), Qt::CaseInsensitive ) == 0 )
1791 else if ( unitName.compare( QLatin1String(
"nautical mile" ), Qt::CaseInsensitive ) == 0 )
1793 else if ( unitName.compare( QLatin1String(
"yard" ), Qt::CaseInsensitive ) == 0 )
1810 if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull()
1813 QgsDebugMsgLevel(
"QgsCoordinateReferenceSystem::findMatchingProj will only "
1814 "work if prj acr ellipsoid acr and proj4string are set"
1815 " and the current projection is valid!", 4 );
1825 QString mySql = QString(
"select srs_id,parameters from tbl_srs where "
1826 "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated" )
1833 myResult = openDatabase( myDatabaseFileName, database );
1834 if ( myResult != SQLITE_OK )
1839 statement = database.
prepare( mySql, myResult );
1840 if ( myResult == SQLITE_OK )
1843 while ( statement.
step() == SQLITE_ROW )
1847 if (
toProj() == myProj4String.trimmed() )
1849 return mySrsId.toLong();
1860 myResult = openDatabase( myDatabaseFileName, database );
1861 if ( myResult != SQLITE_OK )
1866 statement = database.
prepare( mySql, myResult );
1868 if ( myResult == SQLITE_OK )
1870 while ( statement.
step() == SQLITE_ROW )
1874 if (
toProj() == myProj4String.trimmed() )
1876 return mySrsId.toLong();
1890 if ( !d->mIsValid && !srs.d->mIsValid )
1893 if ( !d->mIsValid || !srs.d->mIsValid )
1901 if ( isUser != otherIsUser )
1905 if ( !isUser && ( !d->mAuthId.isEmpty() || !srs.d->mAuthId.isEmpty() ) )
1906 return d->mAuthId == srs.d->mAuthId;
1913 return !( *
this == srs );
1918 if (
PJ *obj = d->threadLocalProjObject() )
1920 const bool isDefaultPreferredFormat = variant ==
WKT_PREFERRED && !multiline;
1921 if ( isDefaultPreferredFormat && !d->mWktPreferred.isEmpty() )
1924 return d->mWktPreferred;
1927 PJ_WKT_TYPE
type = PJ_WKT1_GDAL;
1931 type = PJ_WKT1_GDAL;
1934 type = PJ_WKT1_ESRI;
1937 type = PJ_WKT2_2015;
1940 type = PJ_WKT2_2015_SIMPLIFIED;
1943 type = PJ_WKT2_2019;
1946 type = PJ_WKT2_2019_SIMPLIFIED;
1950 const QByteArray multiLineOption = QStringLiteral(
"MULTILINE=%1" ).arg( multiline ? QStringLiteral(
"YES" ) : QStringLiteral(
"NO" ) ).toLocal8Bit();
1951 const QByteArray indentatationWidthOption = QStringLiteral(
"INDENTATION_WIDTH=%1" ).arg( multiline ? QString::number( indentationWidth ) : QStringLiteral(
"0" ) ).toLocal8Bit();
1952 const char *
const options[] = {multiLineOption.constData(), indentatationWidthOption.constData(),
nullptr};
1955 if ( isDefaultPreferredFormat )
1958 d->mWktPreferred = res;
1970 QDomNode srsNode = node.namedItem( QStringLiteral(
"spatialrefsys" ) );
1972 if ( ! srsNode.isNull() )
1974 bool initialized =
false;
1977 long srsid = srsNode.namedItem( QStringLiteral(
"srsid" ) ).toElement().text().toLong( &ok );
1983 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
1984 if ( !node.isNull() )
1995 node = srsNode.namedItem( QStringLiteral(
"epsg" ) );
1996 if ( !node.isNull() )
2014 const QString
description = srsNode.namedItem( QStringLiteral(
"description" ) ).toElement().text();
2016 const QString wkt = srsNode.namedItem( QStringLiteral(
"wkt" ) ).toElement().text();
2017 initialized = createFromWktInternal( wkt,
description );
2022 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
2023 const QString proj4 = node.toElement().text();
2030 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
2031 const QString proj4 = node.toElement().text();
2032 if ( !proj4.trimmed().isEmpty() )
2033 setProjString( node.toElement().text() );
2035 node = srsNode.namedItem( QStringLiteral(
"srsid" ) );
2036 d->mSrsId = node.toElement().text().toLong();
2038 node = srsNode.namedItem( QStringLiteral(
"srid" ) );
2039 d->mSRID = node.toElement().text().toLong();
2041 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
2042 d->mAuthId = node.toElement().text();
2044 node = srsNode.namedItem( QStringLiteral(
"description" ) );
2045 d->mDescription = node.toElement().text();
2047 node = srsNode.namedItem( QStringLiteral(
"projectionacronym" ) );
2048 d->mProjectionAcronym = node.toElement().text();
2050 node = srsNode.namedItem( QStringLiteral(
"ellipsoidacronym" ) );
2051 d->mEllipsoidAcronym = node.toElement().text();
2053 node = srsNode.namedItem( QStringLiteral(
"geographicflag" ) );
2054 d->mIsGeographic = node.toElement().text() == QLatin1String(
"true" );
2056 d->mWktPreferred.clear();
2062 const QString epoch = srsNode.toElement().attribute( QStringLiteral(
"coordinateEpoch" ) );
2063 if ( !epoch.isEmpty() )
2065 bool epochOk =
false;
2066 d->mCoordinateEpoch = epoch.toDouble( &epochOk );
2068 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2072 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2075 mNativeFormat = qgsEnumKeyToValue<Qgis::CrsDefinitionFormat>( srsNode.toElement().attribute( QStringLiteral(
"nativeFormat" ) ),
Qgis::CrsDefinitionFormat::Wkt );
2080 d =
new QgsCoordinateReferenceSystemPrivate();
2088 QDomElement layerNode = node.toElement();
2089 QDomElement srsElement = doc.createElement( QStringLiteral(
"spatialrefsys" ) );
2091 srsElement.setAttribute( QStringLiteral(
"nativeFormat" ), qgsEnumValueToKey<Qgis::CrsDefinitionFormat>( mNativeFormat ) );
2093 if ( std::isfinite( d->mCoordinateEpoch ) )
2095 srsElement.setAttribute( QStringLiteral(
"coordinateEpoch" ), d->mCoordinateEpoch );
2098 QDomElement wktElement = doc.createElement( QStringLiteral(
"wkt" ) );
2100 srsElement.appendChild( wktElement );
2102 QDomElement proj4Element = doc.createElement( QStringLiteral(
"proj4" ) );
2103 proj4Element.appendChild( doc.createTextNode(
toProj() ) );
2104 srsElement.appendChild( proj4Element );
2106 QDomElement srsIdElement = doc.createElement( QStringLiteral(
"srsid" ) );
2107 srsIdElement.appendChild( doc.createTextNode( QString::number(
srsid() ) ) );
2108 srsElement.appendChild( srsIdElement );
2110 QDomElement sridElement = doc.createElement( QStringLiteral(
"srid" ) );
2111 sridElement.appendChild( doc.createTextNode( QString::number(
postgisSrid() ) ) );
2112 srsElement.appendChild( sridElement );
2114 QDomElement authidElement = doc.createElement( QStringLiteral(
"authid" ) );
2115 authidElement.appendChild( doc.createTextNode(
authid() ) );
2116 srsElement.appendChild( authidElement );
2118 QDomElement descriptionElement = doc.createElement( QStringLiteral(
"description" ) );
2119 descriptionElement.appendChild( doc.createTextNode(
description() ) );
2120 srsElement.appendChild( descriptionElement );
2122 QDomElement projectionAcronymElement = doc.createElement( QStringLiteral(
"projectionacronym" ) );
2123 projectionAcronymElement.appendChild( doc.createTextNode(
projectionAcronym() ) );
2124 srsElement.appendChild( projectionAcronymElement );
2126 QDomElement ellipsoidAcronymElement = doc.createElement( QStringLiteral(
"ellipsoidacronym" ) );
2127 ellipsoidAcronymElement.appendChild( doc.createTextNode(
ellipsoidAcronym() ) );
2128 srsElement.appendChild( ellipsoidAcronymElement );
2130 QDomElement geographicFlagElement = doc.createElement( QStringLiteral(
"geographicflag" ) );
2131 QString geoFlagText = QStringLiteral(
"false" );
2134 geoFlagText = QStringLiteral(
"true" );
2137 geographicFlagElement.appendChild( doc.createTextNode( geoFlagText ) );
2138 srsElement.appendChild( geographicFlagElement );
2140 layerNode.appendChild( srsElement );
2152QString QgsCoordinateReferenceSystem::projFromSrsId(
const int srsId )
2154 QString myDatabaseFileName;
2155 QString myProjString;
2156 QString mySql = QStringLiteral(
"select parameters from tbl_srs where srs_id = %1 order by deprecated" ).arg( srsId );
2165 QFileInfo myFileInfo;
2166 myFileInfo.setFile( myDatabaseFileName );
2167 if ( !myFileInfo.exists() )
2169 QgsDebugError( QStringLiteral(
"users qgis.db not found" ) );
2182 rc = openDatabase( myDatabaseFileName, database );
2188 statement = database.
prepare( mySql, rc );
2190 if ( rc == SQLITE_OK )
2192 if ( statement.
step() == SQLITE_ROW )
2198 return myProjString;
2205 myResult = database.
open_v2( path, SQLITE_OPEN_READONLY,
nullptr );
2207 myResult = database.
open( path );
2209 if ( myResult != SQLITE_OK )
2218 .arg( database.
errorMessage() ), QObject::tr(
"CRS" ) );
2225 sCustomSrsValidation = f;
2230 return sCustomSrsValidation;
2233void QgsCoordinateReferenceSystem::debugPrint()
2236 QgsDebugMsgLevel(
"* Valid : " + ( d->mIsValid ? QString(
"true" ) : QString(
"false" ) ), 1 );
2257 mValidationHint = html;
2262 return mValidationHint;
2272 mNativeFormat = format;
2277 return mNativeFormat;
2280long QgsCoordinateReferenceSystem::getRecordCount()
2285 long myRecordCount = 0;
2288 if ( myResult != SQLITE_OK )
2294 QString mySql = QStringLiteral(
"select count(*) from tbl_srs" );
2295 statement = database.
prepare( mySql, myResult );
2296 if ( myResult == SQLITE_OK )
2298 if ( statement.
step() == SQLITE_ROW )
2300 QString myRecordCountString = statement.
columnAsText( 0 );
2301 myRecordCount = myRecordCountString.toLong();
2304 return myRecordCount;
2310 bool isGeographic =
false;
2312 if ( coordinateSystem )
2314 const int axisCount = proj_cs_get_axis_count( pjContext, coordinateSystem.get() );
2315 if ( axisCount > 0 )
2317 const char *outUnitAuthName =
nullptr;
2318 const char *outUnitAuthCode =
nullptr;
2320 proj_cs_get_axis_info( pjContext, coordinateSystem.get(), 0,
2329 if ( outUnitAuthName && outUnitAuthCode )
2331 const char *unitCategory =
nullptr;
2332 if ( proj_uom_get_info_from_database( pjContext, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
2334 isGeographic = QString( unitCategory ).compare( QLatin1String(
"angular" ), Qt::CaseInsensitive ) == 0;
2339 return isGeographic;
2344 thread_local const QRegularExpression projRegExp( QStringLiteral(
"\\+proj=(\\S+)" ) );
2345 const QRegularExpressionMatch projMatch = projRegExp.match( proj );
2346 if ( !projMatch.hasMatch() )
2348 QgsDebugMsgLevel( QStringLiteral(
"no +proj argument found [%2]" ).arg( proj ), 2 );
2351 operation = projMatch.captured( 1 );
2353 const QRegularExpressionMatch ellipseMatch = projRegExp.match( proj );
2354 if ( ellipseMatch.hasMatch() )
2356 ellipsoid = ellipseMatch.captured( 1 );
2370bool QgsCoordinateReferenceSystem::loadFromAuthCode(
const QString &auth,
const QString &code )
2376 d->mIsValid =
false;
2377 d->mWktPreferred.clear();
2389 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2390 proj4 = proj4.trimmed();
2394 d->mWktPreferred.clear();
2395 d->mDescription = QString( proj_get_name(
crs.get() ) );
2396 d->mAuthId = QStringLiteral(
"%1:%2" ).arg( auth, code );
2398 d->mAxisInvertedDirty =
true;
2403 d->mEllipsoidAcronym.clear();
2404 d->setPj( std::move(
crs ) );
2406 const QString dbVals =
sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( auth, code ).toUpper() );
2407 if ( !dbVals.isEmpty() )
2409 const QStringList parts = dbVals.split(
',' );
2410 d->mSrsId = parts.at( 0 ).toInt();
2411 d->mSRID = parts.at( 1 ).toInt();
2419QList<long> QgsCoordinateReferenceSystem::userSrsIds()
2421 QList<long> results;
2425 QFileInfo myInfo( db );
2426 if ( !myInfo.exists() )
2436 int result = openDatabase( db, database );
2437 if ( result != SQLITE_OK )
2439 QgsDebugError(
"failed : " + db +
" could not be opened!" );
2443 QString sql = QStringLiteral(
"select srs_id from tbl_srs where srs_id >= %1" ).arg(
USER_CRS_START_ID );
2445 statement = database.
prepare( sql, rc );
2448 int ret = statement.
step();
2450 if ( ret == SQLITE_DONE )
2456 if ( ret == SQLITE_ROW )
2462 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
2470long QgsCoordinateReferenceSystem::matchToUserCrs()
const
2472 PJ *obj = d->threadLocalProjObject();
2476 const QList< long > ids = userSrsIds();
2477 for (
long id : ids )
2480 if ( candidate.
projObject() && proj_is_equivalent_to( obj, candidate.
projObject(), PJ_COMP_EQUIVALENT ) )
2488static void sync_db_proj_logger(
void * ,
int level,
const char *message )
2493 if ( level == PJ_LOG_ERROR )
2497 else if ( level == PJ_LOG_DEBUG )
2505 setlocale( LC_ALL,
"C" );
2508 int inserted = 0, updated = 0, deleted = 0, errors = 0;
2513 if ( database.
open( dbFilePath ) != SQLITE_OK )
2519 if ( sqlite3_exec( database.get(),
"BEGIN TRANSACTION",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2527 char *errMsg =
nullptr;
2529 bool createdTypeColumn =
false;
2530 if ( sqlite3_exec( database.get(),
"ALTER TABLE tbl_srs ADD COLUMN srs_type text",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2532 createdTypeColumn =
true;
2533 if ( sqlite3_exec( database.get(),
"CREATE INDEX srs_type ON tbl_srs(srs_type)",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2535 QgsDebugError( QStringLiteral(
"Could not create index for srs_type" ) );
2540 if ( sqlite3_exec( database.get(),
"create table tbl_info (proj_major INT, proj_minor INT, proj_patch INT)",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2542 QString sql = QStringLiteral(
"INSERT INTO tbl_info(proj_major, proj_minor, proj_patch) VALUES (%1, %2,%3)" )
2543 .arg( QString::number( PROJ_VERSION_MAJOR ),
2544 QString::number( PROJ_VERSION_MINOR ),
2545 QString::number( PROJ_VERSION_PATCH ) );
2546 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2548 QgsDebugError( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2551 errMsg ? errMsg :
"(unknown error)" ) );
2553 sqlite3_free( errMsg );
2560 QString sql = QStringLiteral(
"SELECT proj_major, proj_minor, proj_patch FROM tbl_info" );
2561 statement = database.
prepare( sql, result );
2562 if ( result != SQLITE_OK )
2567 if ( statement.
step() == SQLITE_ROW )
2572 if ( !createdTypeColumn && major == PROJ_VERSION_MAJOR && minor == PROJ_VERSION_MINOR && patch == PROJ_VERSION_PATCH )
2578 QgsDebugError( QStringLiteral(
"Could not retrieve previous CRS sync PROJ version number" ) );
2585 proj_log_func( pjContext,
nullptr, sync_db_proj_logger );
2587 PROJ_STRING_LIST authorities = proj_get_authorities_from_database( pjContext );
2589 int nextSrsId = 63561;
2590 int nextSrId = 520003561;
2591 for (
auto authIter = authorities; authIter && *authIter; ++authIter )
2593 const QString authority( *authIter );
2594 QgsDebugMsgLevel( QStringLiteral(
"Loading authority '%1'" ).arg( authority ), 2 );
2595 PROJ_STRING_LIST codes = proj_get_codes_from_database( pjContext, *authIter, PJ_TYPE_CRS,
true );
2597 QStringList allCodes;
2599 for (
auto codesIter = codes; codesIter && *codesIter; ++codesIter )
2601 const QString code( *codesIter );
2607 QgsDebugError( QStringLiteral(
"Could not load '%1:%2'" ).arg( authority, code ) );
2611 const PJ_TYPE pjType = proj_get_type(
crs.get( ) );
2613 QString srsTypeString;
2618 case PJ_TYPE_ELLIPSOID:
2619 case PJ_TYPE_PRIME_MERIDIAN:
2620 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
2621 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
2622 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
2623 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
2624 case PJ_TYPE_DATUM_ENSEMBLE:
2625 case PJ_TYPE_CONVERSION:
2626 case PJ_TYPE_TRANSFORMATION:
2627 case PJ_TYPE_CONCATENATED_OPERATION:
2628 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
2629 case PJ_TYPE_TEMPORAL_DATUM:
2630 case PJ_TYPE_ENGINEERING_DATUM:
2631 case PJ_TYPE_PARAMETRIC_DATUM:
2632 case PJ_TYPE_UNKNOWN:
2636 case PJ_TYPE_GEOGRAPHIC_CRS:
2639 case PJ_TYPE_GEODETIC_CRS:
2643 case PJ_TYPE_GEOCENTRIC_CRS:
2647 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
2651 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
2655 case PJ_TYPE_PROJECTED_CRS:
2659 case PJ_TYPE_COMPOUND_CRS:
2663 case PJ_TYPE_TEMPORAL_CRS:
2667 case PJ_TYPE_ENGINEERING_CRS:
2671 case PJ_TYPE_BOUND_CRS:
2675 case PJ_TYPE_VERTICAL_CRS:
2679#if PROJ_VERSION_MAJOR>9 || (PROJ_VERSION_MAJOR==9 && PROJ_VERSION_MINOR>=2)
2680 case PJ_TYPE_DERIVED_PROJECTED_CRS:
2683 case PJ_TYPE_COORDINATE_METADATA:
2686 case PJ_TYPE_OTHER_CRS:
2695 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2696 proj4 = proj4.trimmed();
2698 if ( proj4.isEmpty() )
2700 QgsDebugMsgLevel( QStringLiteral(
"No proj4 for '%1:%2'" ).arg( authority, code ), 2 );
2711 if ( translatedOperation.isEmpty() && !
operation.isEmpty() )
2713 std::cout << QStringLiteral(
"Operation needs translation in QgsCoordinateReferenceSystemUtils::translateProjection: %1" ).arg(
operation ).toLocal8Bit().constData() << std::endl;
2714 qFatal(
"aborted" );
2717 const bool deprecated = proj_is_deprecated(
crs.get() );
2718 const QString name( proj_get_name(
crs.get() ) );
2720 QString sql = QStringLiteral(
"SELECT parameters,description,deprecated,srs_type FROM tbl_srs WHERE auth_name='%1' AND auth_id='%2'" ).arg( authority, code );
2721 statement = database.
prepare( sql, result );
2722 if ( result != SQLITE_OK )
2731 bool dbSrsDeprecated = deprecated;
2732 if ( statement.
step() == SQLITE_ROW )
2736 dbSrsDeprecated = statement.
columnAsText( 2 ).toInt() != 0;
2740 if ( !dbSrsProj4.isEmpty() || !dbSrsDesc.isEmpty() )
2742 if ( proj4 != dbSrsProj4 || name != dbSrsDesc || deprecated != dbSrsDeprecated || dbSrsType != srsTypeString )
2745 sql = QStringLiteral(
"UPDATE tbl_srs SET parameters=%1,description=%2,deprecated=%3, srs_type=%4 WHERE auth_name=%5 AND auth_id=%6" )
2748 .arg( deprecated ? 1 : 0 )
2752 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2754 QgsDebugError( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2757 errMsg ? errMsg :
"(unknown error)" ) );
2759 sqlite3_free( errMsg );
2773 const QString dbVals =
sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( authority, code ) );
2776 if ( !dbVals.isEmpty() )
2778 const QStringList parts = dbVals.split(
',' );
2779 srsId = parts.at( 0 );
2780 srId = parts.at( 1 );
2782 if ( srId.isEmpty() )
2784 srId = QString::number( nextSrId );
2787 if ( srsId.isEmpty() )
2789 srsId = QString::number( nextSrsId );
2793 if ( !srsId.isEmpty() )
2795 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)" )
2805 .arg( deprecated ? 1 : 0 )
2810 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)" )
2819 .arg( deprecated ? 1 : 0 )
2824 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) == SQLITE_OK )
2830 qCritical(
"Could not execute: %s [%s/%s]\n",
2831 sql.toLocal8Bit().constData(),
2832 sqlite3_errmsg( database.get() ),
2833 errMsg ? errMsg :
"(unknown error)" );
2837 sqlite3_free( errMsg );
2842 proj_string_list_destroy( codes );
2844 const QString sql = QStringLiteral(
"DELETE FROM tbl_srs WHERE auth_name='%1' AND NOT auth_id IN (%2)" ).arg( authority, allCodes.join(
',' ) );
2845 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2847 deleted = sqlite3_changes( database.get() );
2852 qCritical(
"Could not execute: %s [%s]\n",
2853 sql.toLocal8Bit().constData(),
2854 sqlite3_errmsg( database.get() ) );
2858 proj_string_list_destroy( authorities );
2860 QString sql = QStringLiteral(
"UPDATE tbl_info set proj_major=%1,proj_minor=%2,proj_patch=%3" )
2861 .arg( QString::number( PROJ_VERSION_MAJOR ),
2862 QString::number( PROJ_VERSION_MINOR ),
2863 QString::number( PROJ_VERSION_PATCH ) );
2864 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2866 QgsDebugError( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2869 errMsg ? errMsg :
"(unknown error)" ) );
2871 sqlite3_free( errMsg );
2875 if ( sqlite3_exec( database.get(),
"COMMIT",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2877 QgsDebugError( QStringLiteral(
"Could not commit transaction: %1 [%2]\n" ).arg(
2879 sqlite3_errmsg( database.get() ) )
2885 QgsDebugMsgLevel( QStringLiteral(
"CRS update (inserted:%1 updated:%2 deleted:%3 errors:%4)" ).arg( inserted ).arg( updated ).arg( deleted ).arg( errors ), 4 );
2893 return updated + inserted;
2896const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::stringCache()
2898 return *sStringCache();
2901const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::projCache()
2903 return *sProj4Cache();
2906const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::ogcCache()
2908 return *sOgcCache();
2911const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::wktCache()
2913 return *sWktCache();
2916const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srIdCache()
2918 return *sSrIdCache();
2921const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srsIdCache()
2923 return *sSrsIdCache();
2933 if (
PJ *obj = d->threadLocalProjObject() )
2961 else if (
PJ *obj = d->threadLocalProjObject() )
2964 return geoCrs ? QStringLiteral(
"%1:%2" ).arg( proj_get_id_auth_name( geoCrs.get(), 0 ), proj_get_id_code( geoCrs.get(), 0 ) ) : QString();
2974 return d->threadLocalProjObject();
2987 d->mIsValid =
false;
2989 d->mWktPreferred.clear();
2996 switch ( proj_get_type(
object ) )
2998 case PJ_TYPE_GEODETIC_CRS:
2999 case PJ_TYPE_GEOCENTRIC_CRS:
3000 case PJ_TYPE_GEOGRAPHIC_CRS:
3001 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
3002 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
3003 case PJ_TYPE_VERTICAL_CRS:
3004 case PJ_TYPE_PROJECTED_CRS:
3005 case PJ_TYPE_COMPOUND_CRS:
3006 case PJ_TYPE_TEMPORAL_CRS:
3007 case PJ_TYPE_ENGINEERING_CRS:
3008 case PJ_TYPE_BOUND_CRS:
3009 case PJ_TYPE_OTHER_CRS:
3025 const QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
3026 const QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
3027 if ( !authName.isEmpty() && !authCode.isEmpty() && loadFromAuthCode( authName, authCode ) )
3035 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
3046 QStringList projections;
3048 projections.reserve( res.size() );
3051 projections << QString::number(
crs.
srsid() );
3058 QList<QgsCoordinateReferenceSystem> res;
3062 QStringList projectionsProj4 = settings.
value( QStringLiteral(
"UI/recentProjectionsProj4" ) ).toStringList();
3063 QStringList projectionsWkt = settings.
value( QStringLiteral(
"UI/recentProjectionsWkt" ) ).toStringList();
3064 QStringList projectionsAuthId = settings.
value( QStringLiteral(
"UI/recentProjectionsAuthId" ) ).toStringList();
3065 int max = std::max( projectionsAuthId.size(), std::max( projectionsProj4.size(), projectionsWkt.size() ) );
3067 for (
int i = 0; i < max; ++i )
3069 const QString proj = projectionsProj4.value( i );
3070 const QString wkt = projectionsWkt.value( i );
3071 const QString
authid = projectionsAuthId.value( i );
3094 recent.removeAll(
crs );
3095 recent.insert( 0,
crs );
3098 recent = recent.mid( 0, 30 );
3099 QStringList authids;
3100 authids.reserve( recent.size() );
3102 proj.reserve( recent.size() );
3104 wkt.reserve( recent.size() );
3107 authids <<
c.authid();
3113 settings.
setValue( QStringLiteral(
"UI/recentProjectionsAuthId" ), authids );
3114 settings.
setValue( QStringLiteral(
"UI/recentProjectionsWkt" ), wkt );
3115 settings.
setValue( QStringLiteral(
"UI/recentProjectionsProj4" ), proj );
3125 recent.removeAll(
crs );
3126 QStringList authids;
3127 authids.reserve( recent.size() );
3129 proj.reserve( recent.size() );
3131 wkt.reserve( recent.size() );
3134 authids <<
c.authid();
3139 settings.
setValue( QStringLiteral(
"UI/recentProjectionsAuthId" ), authids );
3140 settings.
setValue( QStringLiteral(
"UI/recentProjectionsWkt" ), wkt );
3141 settings.
setValue( QStringLiteral(
"UI/recentProjectionsProj4" ), proj );
3147 settings.
remove( QStringLiteral(
"UI/recentProjectionsAuthId" ) );
3148 settings.
remove( QStringLiteral(
"UI/recentProjectionsWkt" ) );
3149 settings.
remove( QStringLiteral(
"UI/recentProjectionsProj4" ) );
3154 sSrIdCacheLock()->lockForWrite();
3155 if ( !sDisableSrIdCache )
3158 sDisableSrIdCache =
true;
3159 sSrIdCache()->clear();
3161 sSrIdCacheLock()->unlock();
3163 sOgcLock()->lockForWrite();
3164 if ( !sDisableOgcCache )
3167 sDisableOgcCache =
true;
3168 sOgcCache()->clear();
3170 sOgcLock()->unlock();
3172 sProj4CacheLock()->lockForWrite();
3173 if ( !sDisableProjCache )
3176 sDisableProjCache =
true;
3177 sProj4Cache()->clear();
3179 sProj4CacheLock()->unlock();
3181 sCRSWktLock()->lockForWrite();
3182 if ( !sDisableWktCache )
3185 sDisableWktCache =
true;
3186 sWktCache()->clear();
3188 sCRSWktLock()->unlock();
3190 sCRSSrsIdLock()->lockForWrite();
3191 if ( !sDisableSrsIdCache )
3194 sDisableSrsIdCache =
true;
3195 sSrsIdCache()->clear();
3197 sCRSSrsIdLock()->unlock();
3199 sCrsStringLock()->lockForWrite();
3200 if ( !sDisableStringCache )
3203 sDisableStringCache =
true;
3204 sStringCache()->clear();
3206 sCrsStringLock()->unlock();
3215 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3218 if ( !c1.d->mIsValid && c2.d->mIsValid )
3221 if ( c1.d->mIsValid && !c2.d->mIsValid )
3227 if ( c1IsUser && !c2IsUser )
3230 if ( !c1IsUser && c2IsUser )
3233 if ( !c1IsUser && !c2IsUser )
3235 if ( c1.d->mAuthId != c2.d->mAuthId )
3236 return c1.d->mAuthId > c2.d->mAuthId;
3246 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3249 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3252 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3255 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3258 return c1.d->mCoordinateEpoch > c2.d->mCoordinateEpoch;
3266 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3269 if ( c1.d->mIsValid && !c2.d->mIsValid )
3272 if ( !c1.d->mIsValid && c2.d->mIsValid )
3278 if ( !c1IsUser && c2IsUser )
3281 if ( c1IsUser && !c2IsUser )
3284 if ( !c1IsUser && !c2IsUser )
3286 if ( c1.d->mAuthId != c2.d->mAuthId )
3287 return c1.d->mAuthId < c2.d->mAuthId;
3297 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3300 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3303 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3306 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3309 return c1.d->mCoordinateEpoch < c2.d->mCoordinateEpoch;
3314 return !( c1 < c2 );
3318 return !( c1 > c2 );
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
Data provider flags.
@ 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)
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.
long addUserCrs(const QgsCoordinateReferenceSystem &crs, const QString &name, Qgis::CrsDefinitionFormat nativeFormat=Qgis::CrsDefinitionFormat::Wkt)
Adds a new crs definition as a custom ("USER") CRS.
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.
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 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.
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.
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.
IdentifierType
Type of identifier string to create.
@ MediumString
A medium-length string, recommended for general purpose use.
@ ShortString
A heavily abbreviated string, for use when a compact representation is required.
static 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 QList< QgsCoordinateReferenceSystem > recentCoordinateReferenceSystems()
Returns a list of recently used CRS.
PJ * projObject() const
Returns the underlying PROJ PJ object corresponding to the CRS, or nullptr if the CRS is invalid.
QString userFriendlyIdentifier(IdentifierType type=MediumString) const
Returns a user friendly identifier for the CRS.
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 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.
WktVariant
WKT formatting variants, only used for builds based on Proj >= 6.
@ WKT1_GDAL
WKT1 as traditionally output by GDAL, deriving from OGC 01-009. A notable departure from WKT1_GDAL wi...
@ WKT2_2019_SIMPLIFIED
WKT2_2019 with the simplification rule of WKT2_SIMPLIFIED.
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
@ WKT2_2015
Full WKT2 string, conforming to ISO 19162:2015(E) / OGC 12-063r5 with all possible nodes and new keyw...
@ WKT2_2019
Full WKT2 string, conforming to ISO 19162:2019 / OGC 18-010, with all possible nodes and new keyword ...
@ WKT1_ESRI
WKT1 as traditionally output by ESRI software, deriving from OGC 99-049.
@ WKT2_2015_SIMPLIFIED
Same as WKT2_2015 with the following exceptions: UNIT keyword used. ID node only on top element....
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.
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
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.
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 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.
This class is a composition of two QSettings instances:
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void remove(const QString &key, QgsSettings::Section section=QgsSettings::NoSection)
Removes the setting key and any sub-settings of key in a section.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to 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.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#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