22#include <proj_experimental.h>
49#include <QRegularExpression>
53#include "moc_qgscoordinatereferencesystem.cpp"
55using namespace Qt::StringLiterals;
58#include <ogr_srs_api.h>
70bool QgsCoordinateReferenceSystem::sDisableSrIdCache =
false;
74bool QgsCoordinateReferenceSystem::sDisableOgcCache =
false;
78bool QgsCoordinateReferenceSystem::sDisableProjCache =
false;
82bool QgsCoordinateReferenceSystem::sDisableWktCache =
false;
86bool QgsCoordinateReferenceSystem::sDisableSrsIdCache =
false;
90bool QgsCoordinateReferenceSystem::sDisableStringCache =
false;
99 if (
const char *proj4src = proj_as_proj_string(
QgsProjContext::get(), boundCrs.get(), PJ_PROJ_4,
nullptr ) )
101 return QString( proj4src );
118 d =
new QgsCoordinateReferenceSystemPrivate();
124 d =
new QgsCoordinateReferenceSystemPrivate();
132 , mValidationHint( srs.mValidationHint )
133 , mNativeFormat( srs.mNativeFormat )
142 mValidationHint = srs.mValidationHint;
143 mNativeFormat = srs.mNativeFormat;
153 const auto constDbs = dbs;
154 for (
const QString &db : constDbs )
156 QFileInfo myInfo( db );
157 if ( !myInfo.exists() )
167 int result = openDatabase( db, database );
168 if ( result != SQLITE_OK )
174 QString sql = u
"select srs_id from tbl_srs"_s;
176 statement = database.
prepare( sql, rc );
180 int ret = statement.
step();
182 if ( ret == SQLITE_DONE )
188 if ( ret == SQLITE_ROW )
194 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
199 std::sort( results.begin(), results.end() );
255 if ( horizontalObj && verticalObj )
262 QStringList formattedErrorList;
263 for (
const QString &rawError : std::as_const( errors ) )
265 QString formattedError = rawError;
266 formattedError.replace(
"proj_create_compound_crs: "_L1, QString() );
267 formattedErrorList.append( formattedError );
269 error = formattedErrorList.join(
'\n' );
277 if ( !ellipsoidParams.
valid )
325 if ( definition.isEmpty() )
329 if ( !sDisableStringCache )
331 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sStringCache()->constFind( definition );
332 if ( crsIt != sStringCache()->constEnd() )
335 *
this = crsIt.value();
342 const thread_local QRegularExpression reCrsId( u
"^(epsg|esri|osgeo|ignf|ogc|nkg|zangi|iau_2015|iau2000|postgis|internal|user)\\:(\\w+)$"_s, QRegularExpression::CaseInsensitiveOption );
343 QRegularExpressionMatch match = reCrsId.match( definition );
344 if ( match.capturedStart() == 0 )
346 QString authName = match.captured( 1 ).toLower();
347 if ( authName ==
"epsg"_L1 )
351 else if ( authName ==
"postgis"_L1 )
353 const long id = match.captured( 2 ).toLong();
358 else if ( authName ==
"esri"_L1
359 || authName ==
"osgeo"_L1
360 || authName ==
"ignf"_L1
361 || authName ==
"zangi"_L1
362 || authName ==
"iau2000"_L1
363 || authName ==
"ogc"_L1
364 || authName ==
"nkg"_L1
365 || authName ==
"iau_2015"_L1 )
371 const long id = match.captured( 2 ).toLong();
379 const thread_local QRegularExpression reCrsStr( u
"^(?:(wkt|proj4|proj)\\:)?(.+)$"_s, QRegularExpression::CaseInsensitiveOption );
380 match = reCrsStr.match( definition );
381 if ( match.capturedStart() == 0 )
383 if ( match.captured( 1 ).startsWith(
"proj"_L1, Qt::CaseInsensitive ) )
395 if ( !sDisableStringCache )
396 sStringCache()->insert( definition, *
this );
402 if ( definition.isEmpty() )
406 OGRSpatialReferenceH crs = OSRNewSpatialReference(
nullptr );
408 if ( OSRSetFromUserInput( crs, definition.toLocal8Bit().constData() ) == OGRERR_NONE )
411 OSRDestroySpatialReference( crs );
421 const char *configOld = CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" );
422 const char *configNew =
"GEOGCS";
424 if ( strcmp( configOld,
"" ) == 0 )
426 CPLSetConfigOption(
"GDAL_FIX_ESRI_WKT", configNew );
427 if ( strcmp( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) != 0 )
428 QgsLogger::warning( u
"GDAL_FIX_ESRI_WKT could not be set to %1 : %2"_s.arg( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) );
433 QgsDebugMsgLevel( u
"GDAL_FIX_ESRI_WKT was already set : %1"_s.arg( configNew ), 4 );
443 if ( !sDisableOgcCache )
445 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sOgcCache()->constFind( crs );
446 if ( crsIt != sOgcCache()->constEnd() )
449 *
this = crsIt.value();
455 QString wmsCrs = crs;
460 const QString authorityLower = authority.toLower();
466 if ( !sDisableOgcCache )
467 sOgcCache()->insert( crs, *
this );
473 wmsCrs = authority +
':' + code;
477 const QString legacyKey = wmsCrs.toLower();
480 if ( it.key().compare( legacyKey, Qt::CaseInsensitive ) == 0 )
482 const QStringList parts = it.key().split(
':' );
483 const QString auth = parts.at( 0 );
484 const QString code = parts.at( 1 );
485 if ( loadFromAuthCode( auth, code ) )
488 if ( !sDisableOgcCache )
489 sOgcCache()->insert( crs, *
this );
498 if ( !sDisableOgcCache )
499 sOgcCache()->insert( crs, *
this );
504 if ( wmsCrs.compare(
"CRS:27"_L1, Qt::CaseInsensitive ) == 0 || wmsCrs.compare(
"OGC:CRS27"_L1, Qt::CaseInsensitive ) == 0 )
511 if ( wmsCrs.compare(
"CRS:83"_L1, Qt::CaseInsensitive ) == 0 || wmsCrs.compare(
"OGC:CRS83"_L1, Qt::CaseInsensitive ) == 0 )
518 if ( wmsCrs.compare(
"CRS:84"_L1, Qt::CaseInsensitive ) == 0 || wmsCrs.compare(
"OGC:CRS84"_L1, Qt::CaseInsensitive ) == 0 )
522 d->mAxisInverted =
false;
523 d->mAxisInvertedDirty =
false;
527 if ( !sDisableOgcCache )
528 sOgcCache()->insert( crs, *
this );
535 if ( !authority.isEmpty() && !code.isEmpty() && loadFromAuthCode( authority, code ) )
538 if ( !sDisableOgcCache )
539 sOgcCache()->insert( crs, *
this );
544 if ( !sDisableOgcCache )
554 if ( d->mIsValid || !sCustomSrsValidation )
558 if ( sCustomSrsValidation )
559 sCustomSrsValidation( *
this );
565 if ( !sDisableSrIdCache )
567 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrIdCache()->constFind(
id );
568 if ( crsIt != sSrIdCache()->constEnd() )
571 *
this = crsIt.value();
580 if ( it.value().endsWith( u
",%1"_s.arg(
id ) ) )
582 const QStringList parts = it.key().split(
':' );
583 const QString auth = parts.at( 0 );
584 const QString code = parts.at( 1 );
585 if ( loadFromAuthCode( auth, code ) )
588 if ( !sDisableSrIdCache )
589 sSrIdCache()->insert(
id, *
this );
599 if ( !sDisableSrIdCache )
600 sSrIdCache()->insert(
id, *
this );
608 if ( !sDisableSrsIdCache )
610 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrsIdCache()->constFind(
id );
611 if ( crsIt != sSrsIdCache()->constEnd() )
614 *
this = crsIt.value();
623 if ( it.value().startsWith( QString::number(
id ) +
',' ) )
625 const QStringList parts = it.key().split(
':' );
626 const QString auth = parts.at( 0 );
627 const QString code = parts.at( 1 );
628 if ( loadFromAuthCode( auth, code ) )
631 if ( !sDisableSrsIdCache )
632 sSrsIdCache()->insert(
id, *
this );
641 if ( !sDisableSrsIdCache )
642 sSrsIdCache()->insert(
id, *
this );
646bool QgsCoordinateReferenceSystem::loadFromDatabase(
const QString &db,
const QString &expression,
const QString &value )
650 QgsDebugMsgLevel(
"load CRS from " + db +
" where " + expression +
" is " + value, 3 );
652 d->mWktPreferred.clear();
654 QFileInfo myInfo( db );
655 if ( !myInfo.exists() )
661 sqlite3_database_unique_ptr database;
662 sqlite3_statement_unique_ptr statement;
665 myResult = openDatabase( db, database );
666 if ( myResult != SQLITE_OK )
683 QString mySql =
"select srs_id,description,projection_acronym,"
684 "ellipsoid_acronym,parameters,srid,auth_name||':'||auth_id,is_geo,wkt "
685 "from tbl_srs where "
689 +
" order by deprecated";
690 statement = database.
prepare( mySql, myResult );
693 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
698 d->mEllipsoidAcronym.clear();
700 d->mWktPreferred.clear();
703 d->mIsGeographic = statement.
columnAsText( 7 ).toInt() != 0;
705 d->mAxisInvertedDirty =
true;
709 d->mAuthId = u
"USER:%1"_s.arg( d->mSrsId );
711 else if ( !d->mAuthId.startsWith(
"USER:"_L1, Qt::CaseInsensitive ) )
713 QStringList parts = d->mAuthId.split(
':' );
714 QString auth = parts.at( 0 );
715 QString code = parts.at( 1 );
722 d->mIsValid = d->hasPj();
728 if ( !wkt.isEmpty() )
736 setProjString( d->mProj4 );
746void QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread(
PJ_CONTEXT *pj_context )
753 if ( !sDisableSrIdCache )
756 if ( !sDisableSrIdCache )
758 for (
auto it = sSrIdCache()->begin(); it != sSrIdCache()->end(); )
760 auto &v = it.value();
761 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
762 it = sSrIdCache()->erase( it );
768 if ( !sDisableOgcCache )
771 if ( !sDisableOgcCache )
773 for (
auto it = sOgcCache()->begin(); it != sOgcCache()->end(); )
775 auto &v = it.value();
776 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
777 it = sOgcCache()->erase( it );
783 if ( !sDisableProjCache )
786 if ( !sDisableProjCache )
788 for (
auto it = sProj4Cache()->begin(); it != sProj4Cache()->end(); )
790 auto &v = it.value();
791 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
792 it = sProj4Cache()->erase( it );
798 if ( !sDisableWktCache )
801 if ( !sDisableWktCache )
803 for (
auto it = sWktCache()->begin(); it != sWktCache()->end(); )
805 auto &v = it.value();
806 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
807 it = sWktCache()->erase( it );
813 if ( !sDisableSrsIdCache )
816 if ( !sDisableSrsIdCache )
818 for (
auto it = sSrsIdCache()->begin(); it != sSrsIdCache()->end(); )
820 auto &v = it.value();
821 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
822 it = sSrsIdCache()->erase( it );
828 if ( !sDisableStringCache )
831 if ( !sDisableStringCache )
833 for (
auto it = sStringCache()->begin(); it != sStringCache()->end(); )
835 auto &v = it.value();
836 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
837 it = sStringCache()->erase( it );
847 if ( d->mAxisInvertedDirty )
850 d->mAxisInvertedDirty =
false;
853 return d->mAxisInverted;
870 const thread_local QMap< Qgis::CrsAxisDirection, QString > mapping = {
912 QList< Qgis::CrsAxisDirection > res;
913 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
916 res.reserve( axisCount );
918 for (
int i = 0; i < axisCount; ++i )
920 const char *outDirection =
nullptr;
921 proj_cs_get_axis_info( context, pjCs.get(), i,
nullptr,
nullptr, &outDirection,
nullptr,
nullptr,
nullptr,
nullptr );
923 const thread_local QRegularExpression rx( u
"([^\\s]+).*"_s );
924 const QRegularExpressionMatch match = rx.match( QString( outDirection ) );
925 if ( !match.hasMatch() )
928 const QString direction = match.captured( 1 );
930 for (
auto it = mapping.constBegin(); it != mapping.constEnd(); ++it )
932 if ( it.value().compare( direction, Qt::CaseInsensitive ) == 0 )
947 return createFromWktInternal( wkt, QString() );
950bool QgsCoordinateReferenceSystem::createFromWktInternal(
const QString &wkt,
const QString &description )
958 if ( !sDisableWktCache )
960 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sWktCache()->constFind( wkt );
961 if ( crsIt != sWktCache()->constEnd() )
964 *
this = crsIt.value();
966 if ( !
description.isEmpty() && d->mDescription.isEmpty() )
971 sWktCache()->insert( wkt, *
this );
980 d->mWktPreferred.clear();
988 QgsCoordinateReferenceSystem::RecordMap record = getRecord(
"select * from tbl_srs where wkt=" +
QgsSqliteUtils::quotedString( wkt ) +
" order by deprecated" );
989 if ( !record.empty() )
991 long srsId = record[u
"srs_id"_s].toLong();
1004 if ( d->mSrsId == 0 )
1007 long id = matchToUserCrs();
1016 if ( !sDisableWktCache )
1017 sWktCache()->insert( wkt, *
this );
1035 if ( projString.isEmpty() )
1040 if ( projString.trimmed().isEmpty() )
1042 d->mIsValid =
false;
1044 d->mWktPreferred.clear();
1049 if ( !sDisableProjCache )
1051 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sProj4Cache()->constFind( projString );
1052 if ( crsIt != sProj4Cache()->constEnd() )
1055 *
this = crsIt.value();
1069 QString myProj4String = projString.trimmed();
1070 myProj4String.remove( u
"+type=crs"_s );
1071 myProj4String = myProj4String.trimmed();
1073 d->mIsValid =
false;
1074 d->mWktPreferred.clear();
1079 const QString projCrsString = myProj4String + ( myProj4String.contains( u
"+type=crs"_s ) ? QString() : u
" +type=crs"_s );
1087 const QString
authid = u
"%1:%2"_s.arg( authName, authCode );
1091 if ( !sDisableProjCache )
1092 sProj4Cache()->insert( projString, *
this );
1099 QgsCoordinateReferenceSystem::RecordMap myRecord = getRecord(
"select * from tbl_srs where parameters=" +
QgsSqliteUtils::quotedString( myProj4String ) +
" order by deprecated" );
1101 if ( !myRecord.empty() )
1103 id = myRecord[u
"srs_id"_s].toLong();
1112 setProjString( myProj4String );
1115 id = matchToUserCrs();
1124 setProjString( myProj4String );
1128 if ( !sDisableProjCache )
1129 sProj4Cache()->insert( projString, *
this );
1135QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord(
const QString &sql )
1137 QString myDatabaseFileName;
1138 QgsCoordinateReferenceSystem::RecordMap myMap;
1139 QString myFieldName;
1140 QString myFieldValue;
1147 QFileInfo myInfo( myDatabaseFileName );
1148 if ( !myInfo.exists() )
1150 QgsDebugError(
"failed : " + myDatabaseFileName +
" does not exist!" );
1155 myResult = openDatabase( myDatabaseFileName, database );
1156 if ( myResult != SQLITE_OK )
1161 statement = database.
prepare( sql, myResult );
1163 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1167 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1169 myFieldName = statement.
columnName( myColNo );
1171 myMap[myFieldName] = myFieldValue;
1173 if ( statement.
step() != SQLITE_DONE )
1184 if ( myMap.empty() )
1187 QFileInfo myFileInfo;
1188 myFileInfo.setFile( myDatabaseFileName );
1189 if ( !myFileInfo.exists() )
1196 myResult = openDatabase( myDatabaseFileName, database );
1197 if ( myResult != SQLITE_OK )
1202 statement = database.
prepare( sql, myResult );
1204 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1208 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1210 myFieldName = statement.
columnName( myColNo );
1212 myMap[myFieldName] = myFieldValue;
1215 if ( statement.
step() != SQLITE_DONE )
1248 if ( d->mDescription.isNull() )
1254 return d->mDescription;
1261 if ( !
authid().isEmpty() )
1271 id =
isValid() ? QObject::tr(
"Custom CRS" ) : QObject::tr(
"Unknown CRS" );
1273 id = QObject::tr(
"Custom CRS: %1" )
1275 else if ( !
toProj().isEmpty() )
1277 if ( !
id.isEmpty() && !std::isnan( d->mCoordinateEpoch ) )
1285 if ( d->mProjectionAcronym.isNull() )
1291 return d->mProjectionAcronym;
1297 if ( d->mEllipsoidAcronym.isNull() )
1299 if (
PJ *obj = d->threadLocalProjObject() )
1304 const QString ellipsoidAuthName( proj_get_id_auth_name( ellipsoid.get(), 0 ) );
1305 const QString ellipsoidAuthCode( proj_get_id_code( ellipsoid.get(), 0 ) );
1306 if ( !ellipsoidAuthName.isEmpty() && !ellipsoidAuthCode.isEmpty() )
1307 d->mEllipsoidAcronym = u
"%1:%2"_s.arg( ellipsoidAuthName, ellipsoidAuthCode );
1310 double semiMajor, semiMinor, invFlattening;
1311 int semiMinorComputed = 0;
1312 if ( proj_ellipsoid_get_parameters(
QgsProjContext::get(), ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
1318 d->mEllipsoidAcronym.clear();
1323 return d->mEllipsoidAcronym;
1327 return d->mEllipsoidAcronym;
1341 if ( d->mProj4.isEmpty() )
1343 if (
PJ *obj = d->threadLocalProjObject() )
1349 return d->mProj4.trimmed();
1357 if (
PJ *obj = d->threadLocalProjObject() )
1359 const QByteArray multiLineOption = u
"MULTILINE=%1"_s.arg( multiline ? u
"YES"_s : u
"NO"_s ).toLocal8Bit();
1360 const QByteArray indentatationWidthOption = u
"INDENTATION_WIDTH=%1"_s.arg( multiline ? QString::number( indentationWidth ) : u
"0"_s ).toLocal8Bit();
1361 const QByteArray schemaOption = u
"SCHEMA=%1"_s.arg( schema ).toLocal8Bit();
1362 const char *
const options[] = { multiLineOption.constData(), indentatationWidthOption.constData(), schemaOption.constData(),
nullptr };
1365 return json ? std::string { json } : std::string {};
1376 switch ( d->mProjType )
1378 case PJ_TYPE_UNKNOWN:
1381 case PJ_TYPE_ELLIPSOID:
1382 case PJ_TYPE_PRIME_MERIDIAN:
1383 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
1384 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
1385 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
1386 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
1387 case PJ_TYPE_DATUM_ENSEMBLE:
1388 case PJ_TYPE_CONVERSION:
1389 case PJ_TYPE_TRANSFORMATION:
1390 case PJ_TYPE_CONCATENATED_OPERATION:
1391 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
1392 case PJ_TYPE_TEMPORAL_DATUM:
1393 case PJ_TYPE_ENGINEERING_DATUM:
1394 case PJ_TYPE_PARAMETRIC_DATUM:
1398 case PJ_TYPE_GEOGRAPHIC_CRS:
1402 case PJ_TYPE_GEODETIC_CRS:
1404 case PJ_TYPE_GEOCENTRIC_CRS:
1406 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
1408 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
1410 case PJ_TYPE_VERTICAL_CRS:
1412 case PJ_TYPE_PROJECTED_CRS:
1414 case PJ_TYPE_COMPOUND_CRS:
1416 case PJ_TYPE_TEMPORAL_CRS:
1418 case PJ_TYPE_ENGINEERING_CRS:
1420 case PJ_TYPE_BOUND_CRS:
1422 case PJ_TYPE_OTHER_CRS:
1424#if PROJ_VERSION_MAJOR > 9 || ( PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 2 )
1425 case PJ_TYPE_DERIVED_PROJECTED_CRS:
1427 case PJ_TYPE_COORDINATE_METADATA:
1441 return proj_is_deprecated( pj );
1446 return d->mIsGeographic;
1466 return QString( proj_get_celestial_body_name( context, pj ) );
1471 if ( d->mCoordinateEpoch == epoch )
1477 d->mCoordinateEpoch = epoch;
1478 d->setPj( std::move( clone ) );
1483 return d->mCoordinateEpoch;
1502 res.mName = QString( proj_get_name( ensemble.get() ) );
1503 res.mAuthority = QString( proj_get_id_auth_name( ensemble.get(), 0 ) );
1504 res.mCode = QString( proj_get_id_code( ensemble.get(), 0 ) );
1505 res.mRemarks = QString( proj_get_remarks( ensemble.get() ) );
1506 res.mScope = QString( proj_get_scope( ensemble.get() ) );
1507 res.mAccuracy = proj_datum_ensemble_get_accuracy( context, ensemble.get() );
1509 const int memberCount = proj_datum_ensemble_get_member_count( context, ensemble.get() );
1510 for (
int i = 0; i < memberCount; ++i )
1517 details.mName = QString( proj_get_name( member.get() ) );
1518 details.mAuthority = QString( proj_get_id_auth_name( member.get(), 0 ) );
1519 details.mCode = QString( proj_get_id_code( member.get(), 0 ) );
1520 details.mRemarks = QString( proj_get_remarks( member.get() ) );
1521 details.mScope = QString( proj_get_scope( member.get() ) );
1523 res.mMembers << details;
1533 QString projString =
toProj();
1534 projString.replace(
"+type=crs"_L1, QString() );
1537 if ( !transformation )
1540 PJ_COORD coord = proj_coord( 0, 0, 0, HUGE_VAL );
1541 coord.uv.u = point.
x() * M_PI / 180.0;
1542 coord.uv.v = point.
y() * M_PI / 180.0;
1544 proj_errno_reset( transformation.get() );
1545 const PJ_FACTORS pjFactors = proj_factors( transformation.get(), coord );
1546 if ( proj_errno( transformation.get() ) )
1551 res.mIsValid =
true;
1552 res.mMeridionalScale = pjFactors.meridional_scale;
1553 res.mParallelScale = pjFactors.parallel_scale;
1554 res.mArealScale = pjFactors.areal_scale;
1555 res.mAngularDistortion = pjFactors.angular_distortion;
1556 res.mMeridianParallelAngle = pjFactors.meridian_parallel_angle * 180 / M_PI;
1557 res.mMeridianConvergence = pjFactors.meridian_convergence * 180 / M_PI;
1558 res.mTissotSemimajor = pjFactors.tissot_semimajor;
1559 res.mTissotSemiminor = pjFactors.tissot_semiminor;
1560 res.mDxDlam = pjFactors.dx_dlam;
1561 res.mDxDphi = pjFactors.dx_dphi;
1562 res.mDyDlam = pjFactors.dy_dlam;
1563 res.mDyDphi = pjFactors.dy_dphi;
1575 QString projString =
toProj();
1576 projString.replace(
"+type=crs"_L1, QString() );
1577 if ( projString.isEmpty() )
1581 if ( !transformation )
1584 PJ_PROJ_INFO info = proj_pj_info( transformation.get() );
1599 return d->mMapUnits;
1607 PJ *obj = d->threadLocalProjObject();
1612 double southLat = 0;
1614 double northLat = 0;
1616 if ( !proj_get_area_of_use(
QgsProjContext::get(), obj, &westLon, &southLat, &eastLon, &northLat,
nullptr ) )
1631 const auto parts {
authid().split(
':' ) };
1632 if ( parts.length() == 2 )
1634 if ( parts[0] ==
"EPSG"_L1 )
1635 return u
"http://www.opengis.net/def/crs/EPSG/0/%1"_s.arg( parts[1] );
1636 else if ( parts[0] ==
"OGC"_L1 )
1638 return u
"http://www.opengis.net/def/crs/OGC/1.3/%1"_s.arg( parts[1] );
1654 const auto parts {
authid().split(
':' ) };
1655 if ( parts.length() == 2 )
1657 if ( parts[0] ==
"EPSG"_L1 )
1658 return u
"urn:ogc:def:crs:EPSG::%1"_s.arg( parts[1] );
1659 else if ( parts[0] ==
"OGC"_L1 )
1661 return u
"urn:ogc:def:crs:OGC:1.3:%1"_s.arg( parts[1] );
1663 else if ( parts[0].startsWith(
"IAU"_L1 ) )
1665 if ( parts[0].contains(
"_"_L1 ) )
1667 const auto subParts = parts[0].split(
'_' );
1668 if ( subParts.length() == 2 )
1670 return u
"urn:ogc:def:crs:%1:%2:%3"_s.arg( subParts[0], subParts[1], parts[1] );
1675 return u
"urn:ogc:def:crs:%1::%2"_s.arg( parts[0], parts[1] );
1707void QgsCoordinateReferenceSystem::setProjString(
const QString &proj4String )
1710 d->mProj4 = proj4String;
1711 d->mWktPreferred.clear();
1714 QString trimmed = proj4String.trimmed();
1716 trimmed +=
" +type=crs"_L1;
1726 const int errNo = proj_context_errno( ctx );
1727 QgsDebugError( u
"proj string rejected: %1"_s.arg( proj_context_errno_string( ctx, errNo ) ) );
1729 d->mIsValid =
false;
1733 d->mEllipsoidAcronym.clear();
1740bool QgsCoordinateReferenceSystem::setWktString(
const QString &wkt )
1743 d->mIsValid =
false;
1744 d->mWktPreferred.clear();
1746 PROJ_STRING_LIST warnings =
nullptr;
1747 PROJ_STRING_LIST grammarErrors =
nullptr;
1755 QgsDebugMsgLevel( u
"\n---------------------------------------------------------------"_s, 2 );
1756 QgsDebugMsgLevel( u
"This CRS could *** NOT *** be set from the supplied Wkt "_s, 2 );
1758 for (
auto iter = warnings; iter && *iter; ++iter )
1762 for (
auto iter = grammarErrors; iter && *iter; ++iter )
1766 QgsDebugMsgLevel( u
"---------------------------------------------------------------\n"_s, 2 );
1768 proj_string_list_destroy( warnings );
1769 proj_string_list_destroy( grammarErrors );
1775 if ( !sDisableWktCache )
1776 sWktCache()->insert( wkt, *
this );
1784 QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
1785 QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
1787 if ( authName.isEmpty() || authCode.isEmpty() )
1793 if ( !authName.isEmpty() && !authCode.isEmpty() )
1796 if ( fromAuthCode.loadFromAuthCode( authName, authCode ) )
1798 *
this = fromAuthCode;
1800 if ( !sDisableWktCache )
1801 sWktCache()->insert( wkt, *
this );
1808 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
1815void QgsCoordinateReferenceSystem::setMapUnits()
1842 if ( !coordinateSystem )
1848 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
1849 if ( axisCount > 0 )
1851 const char *outUnitName =
nullptr;
1853 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
nullptr,
nullptr,
nullptr,
nullptr, &outUnitName,
nullptr,
nullptr );
1855 const QString unitName( outUnitName );
1859 if ( unitName.compare(
"degree"_L1, Qt::CaseInsensitive ) == 0
1860 || unitName.compare(
"degree minute second"_L1, Qt::CaseInsensitive ) == 0
1861 || unitName.compare(
"degree minute second hemisphere"_L1, Qt::CaseInsensitive ) == 0
1862 || unitName.compare(
"degree minute"_L1, Qt::CaseInsensitive ) == 0
1863 || unitName.compare(
"degree hemisphere"_L1, Qt::CaseInsensitive ) == 0
1864 || unitName.compare(
"degree minute hemisphere"_L1, Qt::CaseInsensitive ) == 0
1865 || unitName.compare(
"hemisphere degree"_L1, Qt::CaseInsensitive ) == 0
1866 || unitName.compare(
"hemisphere degree minute"_L1, Qt::CaseInsensitive ) == 0
1867 || unitName.compare(
"hemisphere degree minute second"_L1, Qt::CaseInsensitive ) == 0
1868 || unitName.compare(
"degree (supplier to define representation)"_L1, Qt::CaseInsensitive ) == 0 )
1870 else if ( unitName.compare(
"metre"_L1, Qt::CaseInsensitive ) == 0 || unitName.compare(
'm'_L1, Qt::CaseInsensitive ) == 0 || unitName.compare(
"meter"_L1, Qt::CaseInsensitive ) == 0 )
1872 else if ( unitName.compare(
"US survey foot"_L1, Qt::CaseInsensitive ) == 0 )
1874 else if ( unitName.compare(
"foot"_L1, Qt::CaseInsensitive ) == 0 )
1876 else if ( unitName.compare(
"British yard (Sears 1922)"_L1, Qt::CaseInsensitive ) == 0 )
1878 else if ( unitName.compare(
"British yard (Sears 1922 truncated)"_L1, Qt::CaseInsensitive ) == 0 )
1880 else if ( unitName.compare(
"British foot (Sears 1922)"_L1, Qt::CaseInsensitive ) == 0 )
1882 else if ( unitName.compare(
"British foot (Sears 1922 truncated)"_L1, Qt::CaseInsensitive ) == 0 )
1884 else if ( unitName.compare(
"British chain (Sears 1922)"_L1, Qt::CaseInsensitive ) == 0 )
1886 else if ( unitName.compare(
"British chain (Sears 1922 truncated)"_L1, Qt::CaseInsensitive ) == 0 )
1888 else if ( unitName.compare(
"British link (Sears 1922)"_L1, Qt::CaseInsensitive ) == 0 )
1890 else if ( unitName.compare(
"British link (Sears 1922 truncated)"_L1, Qt::CaseInsensitive ) == 0 )
1892 else if ( unitName.compare(
"British yard (Benoit 1895 A)"_L1, Qt::CaseInsensitive ) == 0 )
1894 else if ( unitName.compare(
"British foot (Benoit 1895 A)"_L1, Qt::CaseInsensitive ) == 0 )
1896 else if ( unitName.compare(
"British chain (Benoit 1895 A)"_L1, Qt::CaseInsensitive ) == 0 )
1898 else if ( unitName.compare(
"British link (Benoit 1895 A)"_L1, Qt::CaseInsensitive ) == 0 )
1900 else if ( unitName.compare(
"British yard (Benoit 1895 B)"_L1, Qt::CaseInsensitive ) == 0 )
1902 else if ( unitName.compare(
"British foot (Benoit 1895 B)"_L1, Qt::CaseInsensitive ) == 0 )
1904 else if ( unitName.compare(
"British chain (Benoit 1895 B)"_L1, Qt::CaseInsensitive ) == 0 )
1906 else if ( unitName.compare(
"British link (Benoit 1895 B)"_L1, Qt::CaseInsensitive ) == 0 )
1908 else if ( unitName.compare(
"British foot (1865)"_L1, Qt::CaseInsensitive ) == 0 )
1910 else if ( unitName.compare(
"British foot (1936)"_L1, Qt::CaseInsensitive ) == 0 )
1912 else if ( unitName.compare(
"Indian foot"_L1, Qt::CaseInsensitive ) == 0 )
1914 else if ( unitName.compare(
"Indian foot (1937)"_L1, Qt::CaseInsensitive ) == 0 )
1916 else if ( unitName.compare(
"Indian foot (1962)"_L1, Qt::CaseInsensitive ) == 0 )
1918 else if ( unitName.compare(
"Indian foot (1975)"_L1, Qt::CaseInsensitive ) == 0 )
1920 else if ( unitName.compare(
"Indian yard"_L1, Qt::CaseInsensitive ) == 0 )
1922 else if ( unitName.compare(
"Indian yard (1937)"_L1, Qt::CaseInsensitive ) == 0 )
1924 else if ( unitName.compare(
"Indian yard (1962)"_L1, Qt::CaseInsensitive ) == 0 )
1926 else if ( unitName.compare(
"Indian yard (1975)"_L1, Qt::CaseInsensitive ) == 0 )
1928 else if ( unitName.compare(
"Gold Coast foot"_L1, Qt::CaseInsensitive ) == 0 )
1930 else if ( unitName.compare(
"Clarke's foot"_L1, Qt::CaseInsensitive ) == 0 )
1932 else if ( unitName.compare(
"Clarke's yard"_L1, Qt::CaseInsensitive ) == 0 )
1934 else if ( unitName.compare(
"Clarke's chain"_L1, Qt::CaseInsensitive ) == 0 )
1936 else if ( unitName.compare(
"Clarke's link"_L1, Qt::CaseInsensitive ) == 0 )
1938 else if ( unitName.compare(
"kilometre"_L1, Qt::CaseInsensitive ) == 0 )
1940 else if ( unitName.compare(
"centimetre"_L1, Qt::CaseInsensitive ) == 0 )
1942 else if ( unitName.compare(
"millimetre"_L1, Qt::CaseInsensitive ) == 0 )
1944 else if ( unitName.compare(
"Statute mile"_L1, Qt::CaseInsensitive ) == 0 )
1946 else if ( unitName.compare(
"nautical mile"_L1, Qt::CaseInsensitive ) == 0 )
1948 else if ( unitName.compare(
"yard"_L1, Qt::CaseInsensitive ) == 0 )
1950 else if ( unitName.compare(
"fathom"_L1, Qt::CaseInsensitive ) == 0 )
1952 else if ( unitName.compare(
"US survey chain"_L1, Qt::CaseInsensitive ) == 0 )
1954 else if ( unitName.compare(
"chain"_L1, Qt::CaseInsensitive ) == 0 )
1956 else if ( unitName.compare(
"link"_L1, Qt::CaseInsensitive ) == 0 )
1958 else if ( unitName.compare(
"US survey link"_L1, Qt::CaseInsensitive ) == 0 )
1960 else if ( unitName.compare(
"US survey mile"_L1, Qt::CaseInsensitive ) == 0 )
1962 else if ( unitName.compare(
"German legal metre"_L1, Qt::CaseInsensitive ) == 0 )
1979 if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull() || !d->mIsValid )
1982 "QgsCoordinateReferenceSystem::findMatchingProj will only "
1983 "work if prj acr ellipsoid acr and proj4string are set"
1984 " and the current projection is valid!",
1996 QString mySql = QString(
1997 "select srs_id,parameters from tbl_srs where "
1998 "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated"
2005 myResult = openDatabase( myDatabaseFileName, database );
2006 if ( myResult != SQLITE_OK )
2011 statement = database.
prepare( mySql, myResult );
2012 if ( myResult == SQLITE_OK )
2014 while ( statement.
step() == SQLITE_ROW )
2018 if (
toProj() == myProj4String.trimmed() )
2020 return mySrsId.toLong();
2031 myResult = openDatabase( myDatabaseFileName, database );
2032 if ( myResult != SQLITE_OK )
2037 statement = database.
prepare( mySql, myResult );
2039 if ( myResult == SQLITE_OK )
2041 while ( statement.
step() == SQLITE_ROW )
2045 if (
toProj() == myProj4String.trimmed() )
2047 return mySrsId.toLong();
2061 if ( !d->mIsValid && !srs.d->mIsValid )
2064 if ( !d->mIsValid || !srs.d->mIsValid )
2072 if ( isUser != otherIsUser )
2076 if ( !isUser && ( !d->mAuthId.isEmpty() || !srs.d->mAuthId.isEmpty() ) )
2077 return d->mAuthId == srs.d->mAuthId;
2084 return !( *
this == srs );
2089 if (
PJ *obj = d->threadLocalProjObject() )
2092 if ( isDefaultPreferredFormat && !d->mWktPreferred.isEmpty() )
2095 return d->mWktPreferred;
2098 PJ_WKT_TYPE
type = PJ_WKT1_GDAL;
2102 type = PJ_WKT1_GDAL;
2105 type = PJ_WKT1_ESRI;
2108 type = PJ_WKT2_2015;
2111 type = PJ_WKT2_2015_SIMPLIFIED;
2114 type = PJ_WKT2_2019;
2117 type = PJ_WKT2_2019_SIMPLIFIED;
2121 const QByteArray multiLineOption = u
"MULTILINE=%1"_s.arg( multiline ? u
"YES"_s : u
"NO"_s ).toLocal8Bit();
2122 const QByteArray indentatationWidthOption = u
"INDENTATION_WIDTH=%1"_s.arg( multiline ? QString::number( indentationWidth ) : u
"0"_s ).toLocal8Bit();
2123 const char *
const options[] = { multiLineOption.constData(), indentatationWidthOption.constData(),
nullptr };
2126 if ( isDefaultPreferredFormat )
2129 d->mWktPreferred = res;
2141 QDomNode srsNode = node.namedItem( u
"spatialrefsys"_s );
2143 if ( !srsNode.isNull() )
2145 bool initialized =
false;
2148 long srsid = srsNode.namedItem( u
"srsid"_s ).toElement().text().toLong( &ok );
2154 node = srsNode.namedItem( u
"authid"_s );
2155 if ( !node.isNull() )
2166 node = srsNode.namedItem( u
"epsg"_s );
2167 if ( !node.isNull() )
2185 const QString
description = srsNode.namedItem( u
"description"_s ).toElement().text();
2187 const QString wkt = srsNode.namedItem( u
"wkt"_s ).toElement().text();
2188 initialized = createFromWktInternal( wkt,
description );
2193 node = srsNode.namedItem( u
"proj4"_s );
2194 const QString proj4 = node.toElement().text();
2201 node = srsNode.namedItem( u
"proj4"_s );
2202 const QString proj4 = node.toElement().text();
2203 if ( !proj4.trimmed().isEmpty() )
2204 setProjString( node.toElement().text() );
2206 node = srsNode.namedItem( u
"srsid"_s );
2207 d->mSrsId = node.toElement().text().toLong();
2209 node = srsNode.namedItem( u
"srid"_s );
2210 d->mSRID = node.toElement().text().toLong();
2212 node = srsNode.namedItem( u
"authid"_s );
2213 d->mAuthId = node.toElement().text();
2215 node = srsNode.namedItem( u
"description"_s );
2216 d->mDescription = node.toElement().text();
2218 node = srsNode.namedItem( u
"projectionacronym"_s );
2219 d->mProjectionAcronym = node.toElement().text();
2221 node = srsNode.namedItem( u
"ellipsoidacronym"_s );
2222 d->mEllipsoidAcronym = node.toElement().text();
2224 node = srsNode.namedItem( u
"geographicflag"_s );
2225 d->mIsGeographic = node.toElement().text() ==
"true"_L1;
2227 d->mWktPreferred.clear();
2233 const QString epoch = srsNode.toElement().attribute( u
"coordinateEpoch"_s );
2234 if ( !epoch.isEmpty() )
2236 bool epochOk =
false;
2237 d->mCoordinateEpoch = epoch.toDouble( &epochOk );
2239 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2243 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2251 d =
new QgsCoordinateReferenceSystemPrivate();
2259 QDomElement layerNode = node.toElement();
2260 QDomElement srsElement = doc.createElement( u
"spatialrefsys"_s );
2264 if ( std::isfinite( d->mCoordinateEpoch ) )
2266 srsElement.setAttribute( u
"coordinateEpoch"_s, d->mCoordinateEpoch );
2269 QDomElement wktElement = doc.createElement( u
"wkt"_s );
2271 srsElement.appendChild( wktElement );
2273 QDomElement proj4Element = doc.createElement( u
"proj4"_s );
2274 proj4Element.appendChild( doc.createTextNode(
toProj() ) );
2275 srsElement.appendChild( proj4Element );
2277 QDomElement srsIdElement = doc.createElement( u
"srsid"_s );
2278 srsIdElement.appendChild( doc.createTextNode( QString::number(
srsid() ) ) );
2279 srsElement.appendChild( srsIdElement );
2281 QDomElement sridElement = doc.createElement( u
"srid"_s );
2282 sridElement.appendChild( doc.createTextNode( QString::number(
postgisSrid() ) ) );
2283 srsElement.appendChild( sridElement );
2285 QDomElement authidElement = doc.createElement( u
"authid"_s );
2286 authidElement.appendChild( doc.createTextNode(
authid() ) );
2287 srsElement.appendChild( authidElement );
2289 QDomElement descriptionElement = doc.createElement( u
"description"_s );
2290 descriptionElement.appendChild( doc.createTextNode(
description() ) );
2291 srsElement.appendChild( descriptionElement );
2293 QDomElement projectionAcronymElement = doc.createElement( u
"projectionacronym"_s );
2294 projectionAcronymElement.appendChild( doc.createTextNode(
projectionAcronym() ) );
2295 srsElement.appendChild( projectionAcronymElement );
2297 QDomElement ellipsoidAcronymElement = doc.createElement( u
"ellipsoidacronym"_s );
2298 ellipsoidAcronymElement.appendChild( doc.createTextNode(
ellipsoidAcronym() ) );
2299 srsElement.appendChild( ellipsoidAcronymElement );
2301 QDomElement geographicFlagElement = doc.createElement( u
"geographicflag"_s );
2302 QString geoFlagText = u
"false"_s;
2305 geoFlagText = u
"true"_s;
2308 geographicFlagElement.appendChild( doc.createTextNode( geoFlagText ) );
2309 srsElement.appendChild( geographicFlagElement );
2311 layerNode.appendChild( srsElement );
2323QString QgsCoordinateReferenceSystem::projFromSrsId(
const int srsId )
2325 QString myDatabaseFileName;
2326 QString myProjString;
2327 QString mySql = u
"select parameters from tbl_srs where srs_id = %1 order by deprecated"_s.arg( srsId );
2336 QFileInfo myFileInfo;
2337 myFileInfo.setFile( myDatabaseFileName );
2338 if ( !myFileInfo.exists() )
2349 sqlite3_database_unique_ptr database;
2350 sqlite3_statement_unique_ptr statement;
2353 rc = openDatabase( myDatabaseFileName, database );
2359 statement = database.
prepare( mySql, rc );
2361 if ( rc == SQLITE_OK )
2363 if ( statement.
step() == SQLITE_ROW )
2369 return myProjString;
2376 myResult = database.
open_v2( path, SQLITE_OPEN_READONLY,
nullptr );
2378 myResult = database.
open( path );
2380 if ( myResult != SQLITE_OK )
2393 sCustomSrsValidation = f;
2398 return sCustomSrsValidation;
2401void QgsCoordinateReferenceSystem::debugPrint()
2404 QgsDebugMsgLevel(
"* Valid : " + ( d->mIsValid ? QString(
"true" ) : QString(
"false" ) ), 1 );
2425 mValidationHint = html;
2430 return mValidationHint;
2440 mNativeFormat = format;
2445 return mNativeFormat;
2448long QgsCoordinateReferenceSystem::getRecordCount()
2453 long myRecordCount = 0;
2456 if ( myResult != SQLITE_OK )
2462 QString mySql = u
"select count(*) from tbl_srs"_s;
2463 statement = database.
prepare( mySql, myResult );
2464 if ( myResult == SQLITE_OK )
2466 if ( statement.
step() == SQLITE_ROW )
2468 QString myRecordCountString = statement.
columnAsText( 0 );
2469 myRecordCount = myRecordCountString.toLong();
2472 return myRecordCount;
2478 bool isGeographic =
false;
2482 if ( !horizontalCrs )
2486 if ( coordinateSystem )
2488 const int axisCount = proj_cs_get_axis_count( pjContext, coordinateSystem.get() );
2489 if ( axisCount > 0 )
2491 const char *outUnitAuthName =
nullptr;
2492 const char *outUnitAuthCode =
nullptr;
2494 proj_cs_get_axis_info( pjContext, coordinateSystem.get(), 0,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr, &outUnitAuthName, &outUnitAuthCode );
2496 if ( outUnitAuthName && outUnitAuthCode )
2498 const char *unitCategory =
nullptr;
2499 if ( proj_uom_get_info_from_database( pjContext, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
2501 isGeographic = QString( unitCategory ).compare(
"angular"_L1, Qt::CaseInsensitive ) == 0;
2506 return isGeographic;
2511 thread_local const QRegularExpression projRegExp( u
"\\+proj=(\\S+)"_s );
2512 const QRegularExpressionMatch projMatch = projRegExp.match( proj );
2513 if ( !projMatch.hasMatch() )
2518 operation = projMatch.captured( 1 );
2520 const QRegularExpressionMatch ellipseMatch = projRegExp.match( proj );
2521 if ( ellipseMatch.hasMatch() )
2523 ellipsoid = ellipseMatch.captured( 1 );
2537bool QgsCoordinateReferenceSystem::loadFromAuthCode(
const QString &auth,
const QString &code )
2543 d->mIsValid =
false;
2544 d->mWktPreferred.clear();
2547 QgsProjUtils::proj_pj_unique_ptr crs( proj_create_from_database( pjContext, auth.toUtf8().constData(), code.toUtf8().constData(), PJ_CATEGORY_CRS,
false,
nullptr ) );
2556 proj4.replace(
"+type=crs"_L1, QString() );
2557 proj4 = proj4.trimmed();
2561 d->mWktPreferred.clear();
2562 d->mDescription = QString( proj_get_name( crs.get() ) );
2563 d->mAuthId = u
"%1:%2"_s.arg( auth, code );
2565 d->mAxisInvertedDirty =
true;
2570 d->mEllipsoidAcronym.clear();
2571 d->setPj( std::move( crs ) );
2574 if ( !dbVals.isEmpty() )
2576 const QStringList parts = dbVals.split(
',' );
2577 d->mSrsId = parts.at( 0 ).toInt();
2578 d->mSRID = parts.at( 1 ).toInt();
2586QList<long> QgsCoordinateReferenceSystem::userSrsIds()
2588 QList<long> results;
2592 QFileInfo myInfo( db );
2593 if ( !myInfo.exists() )
2599 sqlite3_database_unique_ptr database;
2600 sqlite3_statement_unique_ptr statement;
2603 int result = openDatabase( db, database );
2604 if ( result != SQLITE_OK )
2606 QgsDebugError(
"failed : " + db +
" could not be opened!" );
2612 statement = database.
prepare( sql, rc );
2615 int ret = statement.
step();
2617 if ( ret == SQLITE_DONE )
2623 if ( ret == SQLITE_ROW )
2629 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
2637long QgsCoordinateReferenceSystem::matchToUserCrs()
const
2639 PJ *obj = d->threadLocalProjObject();
2643 const QList< long > ids = userSrsIds();
2644 for (
long id : ids )
2647 if ( candidate.
projObject() && proj_is_equivalent_to( obj, candidate.
projObject(), PJ_COMP_EQUIVALENT ) )
2655static void sync_db_proj_logger(
void * ,
int level,
const char *message )
2660 if ( level == PJ_LOG_ERROR )
2664 else if ( level == PJ_LOG_DEBUG )
2672 setlocale( LC_ALL,
"C" );
2675 int inserted = 0, updated = 0, deleted = 0, errors = 0;
2680 if ( database.
open( dbFilePath ) != SQLITE_OK )
2686 if ( sqlite3_exec( database.get(),
"BEGIN TRANSACTION",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2694 char *errMsg =
nullptr;
2696 bool createdTypeColumn =
false;
2697 if ( sqlite3_exec( database.get(),
"ALTER TABLE tbl_srs ADD COLUMN srs_type text",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2699 createdTypeColumn =
true;
2700 if ( sqlite3_exec( database.get(),
"CREATE INDEX srs_type ON tbl_srs(srs_type)",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2707 if ( sqlite3_exec( database.get(),
"create table tbl_info (proj_major INT, proj_minor INT, proj_patch INT)",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2709 QString sql = u
"INSERT INTO tbl_info(proj_major, proj_minor, proj_patch) VALUES (%1, %2,%3)"_s
2710 .arg( QString::number( PROJ_VERSION_MAJOR ), QString::number( PROJ_VERSION_MINOR ), QString::number( PROJ_VERSION_PATCH ) );
2711 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2713 QgsDebugError( u
"Could not execute: %1 [%2/%3]\n"_s.arg( sql, database.
errorMessage(), errMsg ? errMsg :
"(unknown error)" ) );
2715 sqlite3_free( errMsg );
2722 QString sql = u
"SELECT proj_major, proj_minor, proj_patch FROM tbl_info"_s;
2723 statement = database.
prepare( sql, result );
2724 if ( result != SQLITE_OK )
2729 if ( statement.
step() == SQLITE_ROW )
2734 if ( !createdTypeColumn && major == PROJ_VERSION_MAJOR && minor == PROJ_VERSION_MINOR && patch == PROJ_VERSION_PATCH )
2740 QgsDebugError( u
"Could not retrieve previous CRS sync PROJ version number"_s );
2747 proj_log_func( pjContext,
nullptr, sync_db_proj_logger );
2749 PROJ_STRING_LIST authorities = proj_get_authorities_from_database( pjContext );
2751 int nextSrsId = 67218;
2752 int nextSrId = 520007218;
2753 for (
auto authIter = authorities; authIter && *authIter; ++authIter )
2755 const QString authority( *authIter );
2757 PROJ_STRING_LIST codes = proj_get_codes_from_database( pjContext, *authIter, PJ_TYPE_CRS,
true );
2759 QStringList allCodes;
2761 for (
auto codesIter = codes; codesIter && *codesIter; ++codesIter )
2763 const QString code( *codesIter );
2769 QgsDebugError( u
"Could not load '%1:%2'"_s.arg( authority, code ) );
2773 const PJ_TYPE pjType = proj_get_type( crs.get() );
2775 QString srsTypeString;
2780 case PJ_TYPE_ELLIPSOID:
2781 case PJ_TYPE_PRIME_MERIDIAN:
2782 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
2783 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
2784 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
2785 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
2786 case PJ_TYPE_DATUM_ENSEMBLE:
2787 case PJ_TYPE_CONVERSION:
2788 case PJ_TYPE_TRANSFORMATION:
2789 case PJ_TYPE_CONCATENATED_OPERATION:
2790 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
2791 case PJ_TYPE_TEMPORAL_DATUM:
2792 case PJ_TYPE_ENGINEERING_DATUM:
2793 case PJ_TYPE_PARAMETRIC_DATUM:
2794 case PJ_TYPE_UNKNOWN:
2798 case PJ_TYPE_GEOGRAPHIC_CRS:
2801 case PJ_TYPE_GEODETIC_CRS:
2805 case PJ_TYPE_GEOCENTRIC_CRS:
2809 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
2813 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
2817 case PJ_TYPE_PROJECTED_CRS:
2821 case PJ_TYPE_COMPOUND_CRS:
2825 case PJ_TYPE_TEMPORAL_CRS:
2829 case PJ_TYPE_ENGINEERING_CRS:
2833 case PJ_TYPE_BOUND_CRS:
2837 case PJ_TYPE_VERTICAL_CRS:
2841#if PROJ_VERSION_MAJOR > 9 || ( PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 2 )
2842 case PJ_TYPE_DERIVED_PROJECTED_CRS:
2845 case PJ_TYPE_COORDINATE_METADATA:
2848 case PJ_TYPE_OTHER_CRS:
2857 proj4.replace(
"+type=crs"_L1, QString() );
2858 proj4 = proj4.trimmed();
2860 if ( proj4.isEmpty() )
2873 if ( translatedOperation.isEmpty() && !
operation.isEmpty() )
2875 std::cout << u
"Operation needs translation in QgsCoordinateReferenceSystemUtils::translateProjection: %1"_s.arg(
operation ).toLocal8Bit().constData() << std::endl;
2876 qFatal(
"aborted" );
2879 const bool deprecated = proj_is_deprecated( crs.get() );
2880 const QString name( proj_get_name( crs.get() ) );
2882 QString sql = u
"SELECT parameters,description,deprecated,srs_type,projection_acronym FROM tbl_srs WHERE auth_name='%1' AND auth_id='%2'"_s.arg( authority, code );
2883 statement = database.
prepare( sql, result );
2884 if ( result != SQLITE_OK )
2893 QString dbOperation;
2894 bool dbSrsDeprecated = deprecated;
2895 if ( statement.
step() == SQLITE_ROW )
2899 dbSrsDeprecated = statement.
columnAsText( 2 ).toInt() != 0;
2904 if ( !dbSrsProj4.isEmpty() || !dbSrsDesc.isEmpty() )
2906 if ( proj4 != dbSrsProj4 || name != dbSrsDesc || deprecated != dbSrsDeprecated || dbSrsType != srsTypeString || dbOperation !=
operation )
2909 sql = u
"UPDATE tbl_srs SET parameters=%1,description=%2,deprecated=%3, srs_type=%4,projection_acronym=%5 WHERE auth_name=%6 AND auth_id=%7"_s.arg(
QgsSqliteUtils::quotedString( proj4 ) )
2911 .arg( deprecated ? 1 : 0 )
2914 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2916 QgsDebugError( u
"Could not execute: %1 [%2/%3]\n"_s.arg( sql, database.
errorMessage(), errMsg ? errMsg :
"(unknown error)" ) );
2918 sqlite3_free( errMsg );
2935 if ( !dbVals.isEmpty() )
2937 const QStringList parts = dbVals.split(
',' );
2938 srsId = parts.at( 0 );
2939 srId = parts.at( 1 );
2941 if ( srId.isEmpty() )
2943 srId = QString::number( nextSrId );
2946 if ( srsId.isEmpty() )
2948 srsId = QString::number( nextSrsId );
2952 if ( !srsId.isEmpty() )
2954 sql = u
"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)"_s
2961 .arg( deprecated ? 1 : 0 )
2966 sql = u
"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)"_s
2972 .arg( deprecated ? 1 : 0 )
2977 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) == SQLITE_OK )
2983 qCritical(
"Could not execute: %s [%s/%s]\n", sql.toLocal8Bit().constData(), sqlite3_errmsg( database.get() ), errMsg ? errMsg :
"(unknown error)" );
2987 sqlite3_free( errMsg );
2992 proj_string_list_destroy( codes );
2994 const QString sql = u
"DELETE FROM tbl_srs WHERE auth_name='%1' AND NOT auth_id IN (%2)"_s.arg( authority, allCodes.join(
',' ) );
2995 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2997 deleted = sqlite3_changes( database.get() );
3002 qCritical(
"Could not execute: %s [%s]\n", sql.toLocal8Bit().constData(), sqlite3_errmsg( database.get() ) );
3005 proj_string_list_destroy( authorities );
3007 QString sql = u
"UPDATE tbl_info set proj_major=%1,proj_minor=%2,proj_patch=%3"_s.arg( QString::number( PROJ_VERSION_MAJOR ), QString::number( PROJ_VERSION_MINOR ), QString::number( PROJ_VERSION_PATCH ) );
3008 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
3010 QgsDebugError( u
"Could not execute: %1 [%2/%3]\n"_s.arg( sql, database.
errorMessage(), errMsg ? errMsg :
"(unknown error)" ) );
3012 sqlite3_free( errMsg );
3016 if ( sqlite3_exec( database.get(),
"COMMIT",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
3023 QgsDebugMsgLevel( u
"CRS update (inserted:%1 updated:%2 deleted:%3 errors:%4)"_s.arg( inserted ).arg( updated ).arg( deleted ).arg( errors ), 4 );
3031 return updated + inserted;
3034const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::stringCache()
3036 return *sStringCache();
3039const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::projCache()
3041 return *sProj4Cache();
3044const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::ogcCache()
3046 return *sOgcCache();
3049const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::wktCache()
3051 return *sWktCache();
3054const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srIdCache()
3056 return *sSrIdCache();
3059const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srsIdCache()
3061 return *sSrsIdCache();
3071 if (
PJ *obj = d->threadLocalProjObject() )
3078 const PJ_TYPE pjType = proj_get_type( geoCrs.get() );
3079 if ( pjType == PJ_TYPE_GEOCENTRIC_CRS )
3088 if ( !geoGraphicCrs )
3115 if (
PJ *obj = d->threadLocalProjObject() )
3169 if (
PJ *obj = d->threadLocalProjObject() )
3202 if (
PJ *obj = d->threadLocalProjObject() )
3213 if (
PJ *obj = d->threadLocalProjObject() )
3226 else if (
PJ *obj = d->threadLocalProjObject() )
3229 return geoCrs ? u
"%1:%2"_s.arg( proj_get_id_auth_name( geoCrs.get(), 0 ), proj_get_id_code( geoCrs.get(), 0 ) ) : QString();
3239 return d->threadLocalProjObject();
3252 d->mIsValid =
false;
3254 d->mWktPreferred.clear();
3261 switch ( proj_get_type(
object ) )
3263 case PJ_TYPE_GEODETIC_CRS:
3264 case PJ_TYPE_GEOCENTRIC_CRS:
3265 case PJ_TYPE_GEOGRAPHIC_CRS:
3266 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
3267 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
3268 case PJ_TYPE_VERTICAL_CRS:
3269 case PJ_TYPE_PROJECTED_CRS:
3270 case PJ_TYPE_COMPOUND_CRS:
3271 case PJ_TYPE_TEMPORAL_CRS:
3272 case PJ_TYPE_ENGINEERING_CRS:
3273 case PJ_TYPE_BOUND_CRS:
3274 case PJ_TYPE_OTHER_CRS:
3290 const QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
3291 const QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
3292 if ( !authName.isEmpty() && !authCode.isEmpty() &&
createFromOgcWmsCrs( u
"%1:%2"_s.arg( authName, authCode ) ) )
3300 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
3311 QStringList projections;
3313 projections.reserve( res.size() );
3316 projections << QString::number( crs.srsid() );
3343 sSrIdCacheLock()->lockForWrite();
3344 if ( !sDisableSrIdCache )
3347 sDisableSrIdCache =
true;
3348 sSrIdCache()->clear();
3350 sSrIdCacheLock()->unlock();
3352 sOgcLock()->lockForWrite();
3353 if ( !sDisableOgcCache )
3356 sDisableOgcCache =
true;
3357 sOgcCache()->clear();
3359 sOgcLock()->unlock();
3361 sProj4CacheLock()->lockForWrite();
3362 if ( !sDisableProjCache )
3365 sDisableProjCache =
true;
3366 sProj4Cache()->clear();
3368 sProj4CacheLock()->unlock();
3370 sCRSWktLock()->lockForWrite();
3371 if ( !sDisableWktCache )
3374 sDisableWktCache =
true;
3375 sWktCache()->clear();
3377 sCRSWktLock()->unlock();
3379 sCRSSrsIdLock()->lockForWrite();
3380 if ( !sDisableSrsIdCache )
3383 sDisableSrsIdCache =
true;
3384 sSrsIdCache()->clear();
3386 sCRSSrsIdLock()->unlock();
3388 sCrsStringLock()->lockForWrite();
3389 if ( !sDisableStringCache )
3392 sDisableStringCache =
true;
3393 sStringCache()->clear();
3395 sCrsStringLock()->unlock();
3414 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3417 if ( !c1.d->mIsValid && c2.d->mIsValid )
3420 if ( c1.d->mIsValid && !c2.d->mIsValid )
3426 if ( c1IsUser && !c2IsUser )
3429 if ( !c1IsUser && c2IsUser )
3432 if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
3434 if ( c1.d->mAuthId != c2.d->mAuthId )
3435 return c1.d->mAuthId > c2.d->mAuthId;
3443 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3446 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3449 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3452 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3455 return c1.d->mCoordinateEpoch > c2.d->mCoordinateEpoch;
3463 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3466 if ( c1.d->mIsValid && !c2.d->mIsValid )
3469 if ( !c1.d->mIsValid && c2.d->mIsValid )
3475 if ( !c1IsUser && c2IsUser )
3478 if ( c1IsUser && !c2IsUser )
3481 if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
3483 if ( c1.d->mAuthId != c2.d->mAuthId )
3484 return c1.d->mAuthId < c2.d->mAuthId;
3492 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3495 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3498 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3501 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3504 return c1.d->mCoordinateEpoch < c2.d->mCoordinateEpoch;
3509 return !( c1 < c2 );
3513 return !( c1 > c2 );
CrsIdentifierType
Available identifier string types for representing coordinate reference systems.
@ ShortString
A heavily abbreviated string, for use when a compact representation is required.
@ MediumString
A medium-length string, recommended for general purpose use.
DistanceUnit
Units of distance.
@ YardsBritishSears1922Truncated
British yards (Sears 1922 truncated).
@ MilesUSSurvey
US Survey miles.
@ LinksBritishSears1922
British links (Sears 1922).
@ YardsBritishBenoit1895A
British yards (Benoit 1895 A).
@ LinksBritishBenoit1895A
British links (Benoit 1895 A).
@ Centimeters
Centimeters.
@ YardsIndian1975
Indian yards (1975).
@ FeetUSSurvey
US Survey feet.
@ Millimeters
Millimeters.
@ FeetBritishSears1922
British feet (Sears 1922).
@ YardsClarkes
Clarke's yards.
@ YardsIndian
Indian yards.
@ FeetBritishBenoit1895B
British feet (Benoit 1895 B).
@ Miles
Terrestrial miles.
@ LinksUSSurvey
US Survey links.
@ ChainsUSSurvey
US Survey chains.
@ FeetClarkes
Clarke's feet.
@ Unknown
Unknown distance unit.
@ FeetBritish1936
British feet (1936).
@ FeetIndian1962
Indian feet (1962).
@ YardsBritishSears1922
British yards (Sears 1922).
@ FeetIndian1937
Indian feet (1937).
@ YardsIndian1937
Indian yards (1937).
@ Degrees
Degrees, for planar geographic CRS distance measurements.
@ ChainsBritishBenoit1895B
British chains (Benoit 1895 B).
@ LinksBritishSears1922Truncated
British links (Sears 1922 truncated).
@ ChainsBritishBenoit1895A
British chains (Benoit 1895 A).
@ YardsBritishBenoit1895B
British yards (Benoit 1895 B).
@ FeetBritish1865
British feet (1865).
@ YardsIndian1962
Indian yards (1962).
@ FeetBritishSears1922Truncated
British feet (Sears 1922 truncated).
@ MetersGermanLegal
German legal meter.
@ LinksBritishBenoit1895B
British links (Benoit 1895 B).
@ ChainsInternational
International chains.
@ LinksInternational
International links.
@ ChainsBritishSears1922Truncated
British chains (Sears 1922 truncated).
@ FeetIndian
Indian (geodetic) feet.
@ NauticalMiles
Nautical miles.
@ ChainsClarkes
Clarke's chains.
@ LinksClarkes
Clarke's links.
@ ChainsBritishSears1922
British chains (Sears 1922).
@ FeetIndian1975
Indian feet (1975).
@ FeetGoldCoast
Gold Coast feet.
@ FeetBritishBenoit1895A
British feet (Benoit 1895 A).
@ Critical
Critical/error message.
CrsType
Coordinate reference system types.
@ Compound
Compound (horizontal + vertical) CRS.
@ Projected
Projected CRS.
@ DerivedProjected
Derived projected CRS.
@ Engineering
Engineering CRS.
@ Geographic3d
3D geopraphic CRS
@ Geographic2d
2D geographic CRS
@ Geocentric
Geocentric CRS.
CrsDefinitionFormat
CRS definition formats.
@ Wkt
WKT format (always recommended over proj string format).
static const int USER_CRS_START_ID
Minimum ID number for a user-defined projection.
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.
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
bool createFromOgcWmsCrs(const QString &crs)
Sets this CRS to the given OGC WMS-format Coordinate Reference Systems.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
bool createFromWkt(const QString &wkt)
Sets this CRS using a WKT definition.
bool createFromString(const QString &definition)
Set up this CRS from a string definition.
bool hasVerticalAxis() const
Returns true if the CRS has a vertical axis.
void validate()
Perform some validation on this CRS.
~QgsCoordinateReferenceSystem()
QgsRectangle bounds() const
Returns the approximate bounds for the region the CRS is usable within.
QString toProj() const
Returns a Proj string representation of this CRS.
static CUSTOM_CRS_VALIDATION customCrsValidation()
Gets custom function.
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
static QgsCoordinateReferenceSystem createCompoundCrs(const QgsCoordinateReferenceSystem &horizontalCrs, const QgsCoordinateReferenceSystem &verticalCrs, QString &error)
Given a horizontal and vertical CRS, attempts to create a compound CRS from them.
static Q_INVOKABLE QgsCoordinateReferenceSystem fromEpsgId(long epsg)
Creates a CRS from a given EPSG ID.
Q_DECL_DEPRECATED bool createFromProj4(const QString &projString)
Sets this CRS by passing it a PROJ style formatted string.
static Q_DECL_DEPRECATED QStringList recentProjections()
Returns a list of recently used projections.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
QString toOgcUri() const
Returns the crs as OGC URI (format: http://www.opengis.net/def/crs/OGC/1.3/CRS84) Returns an empty st...
QString toOgcUrn() const
Returns the crs as OGC URN (format: urn:ogc:def:crs:OGC:1.3:CRS84) Returns an empty string on failure...
static void setCustomCrsValidation(CUSTOM_CRS_VALIDATION f)
Sets custom function to force valid CRS.
bool isEarthCrs() const
Returns true if the CRS is associated with the Earth.
static Q_DECL_DEPRECATED void pushRecentCoordinateReferenceSystem(const QgsCoordinateReferenceSystem &crs)
Pushes a recently used CRS to the top of the recent CRS list.
long postgisSrid() const
Returns PostGIS SRID for the CRS.
QgsCoordinateReferenceSystem horizontalCrs() const
Returns the horizontal CRS associated with this CRS object.
Q_DECL_DEPRECATED long findMatchingProj()
Walks the CRS databases (both system and user database) trying to match stored PROJ string to a datab...
static QList< long > validSrsIds()
Returns a list of all valid SRS IDs present in the CRS database.
QgsProjectionFactors factors(const QgsPoint &point) const
Calculate various cartographic properties, such as scale factors, angular distortion and meridian con...
void setValidationHint(const QString &html)
Set user hint for validation.
Q_DECL_DEPRECATED QString toProj4() const
Returns a Proj string representation of this CRS.
bool operator==(const QgsCoordinateReferenceSystem &srs) const
QString projectionAcronym() const
Returns the projection acronym for the projection used by the CRS.
QString userFriendlyIdentifier(Qgis::CrsIdentifierType type=Qgis::CrsIdentifierType::MediumString) const
Returns a user friendly identifier for the CRS.
Qgis::CrsDefinitionFormat nativeFormat() const
Returns the native format for the CRS definition.
CrsType
Enumeration of types of IDs accepted in createFromId() method.
@ InternalCrsId
Internal ID used by QGIS in the local SQLite database.
@ PostgisCrsId
SRID used in PostGIS. DEPRECATED – DO NOT USE.
bool createFromUserInput(const QString &definition)
Set up this CRS from various text formats.
QgsCoordinateReferenceSystem()
Constructs an invalid CRS object.
static int syncDatabase()
Update proj.4 parameters in our database from proj.4.
bool operator!=(const QgsCoordinateReferenceSystem &srs) const
void setNativeFormat(Qgis::CrsDefinitionFormat format)
Sets the native format for the CRS definition.
bool createFromProj(const QString &projString, bool identify=true)
Sets this CRS by passing it a PROJ style formatted string.
QgsCoordinateReferenceSystem verticalCrs() const
Returns the vertical CRS associated with this CRS object.
static Q_DECL_DEPRECATED void removeRecentCoordinateReferenceSystem(const QgsCoordinateReferenceSystem &crs)
Removes a CRS from the list of recently used CRS.
QgsDatumEnsemble datumEnsemble() const
Attempts to retrieve datum ensemble details from the CRS.
QgsCoordinateReferenceSystem toGeographicCrs() const
Returns the geographic CRS associated with this CRS object.
bool isDynamic() const
Returns true if the CRS is a dynamic CRS.
bool createFromSrsId(long srsId)
Sets this CRS by lookup of internal QGIS CRS ID in the CRS database.
Q_DECL_DEPRECATED bool createFromId(long id, CrsType type=PostgisCrsId)
Sets this CRS by lookup of the given ID in the CRS database.
static QgsCoordinateReferenceSystem fromProjObject(PJ *object)
Constructs a QgsCoordinateReferenceSystem from a PROJ PJ object.
static void invalidateCache(bool disableCache=false)
Clears the internal cache used to initialize QgsCoordinateReferenceSystem objects.
QgsCoordinateReferenceSystem & operator=(const QgsCoordinateReferenceSystem &srs)
bool isSameCelestialBody(const QgsCoordinateReferenceSystem &other) const
Returns true if other crs is associated with the same celestial body.
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.
std::string toJsonString(bool multiline=false, int indentationWidth=4, const QString &schema=QString()) const
Returns a JSON string representation of this 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 createGeocentricCrs(const QString &ellipsoid)
Creates a geocentric CRS given an ellipsoid definition.
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,...
QgsCoordinateReferenceSystem toGeocentricCrs() const
Returns a new geocentric CRS based on this CRS object.
QList< Qgis::CrsAxisDirection > axisOrdering() const
Returns an ordered list of the axis directions reflecting the native axis order for the CRS.
long srsid() const
Returns the internal CRS ID, if available.
Qgis::CrsType type() const
Returns the type of the CRS.
Qgis::DistanceUnit mapUnits
bool hasAxisInverted() const
Returns whether the axis order is inverted for the CRS compared to the order east/north (longitude/la...
bool createFromProjObject(PJ *object)
Sets this CRS by passing it a PROJ PJ object, corresponding to a PROJ CRS object.
bool isDeprecated() const
Returns true if the CRS is considered deprecated.
static Q_DECL_DEPRECATED QgsCoordinateReferenceSystem fromProj4(const QString &proj4)
Creates a CRS from a proj style formatted string.
Contains information about a member of a datum ensemble.
Contains information about a datum ensemble.
static EllipsoidParameters ellipsoidParameters(const QString &ellipsoid)
Returns the parameters for the specified ellipsoid.
Sets the current locale to the c locale for the lifetime of the object.
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, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
@ UNKNOWN
Unknown/unhandled flavor.
@ AUTH_CODE
E.g EPSG:4326.
static CRSFlavor parseCrsName(const QString &crsName, QString &authority, QString &code)
Parse a CRS name in one of the flavors of OGC services, and decompose it as authority and code.
static QString OGRSpatialReferenceToWkt(OGRSpatialReferenceH srs)
Returns a WKT string corresponding to the specified OGR srs object.
Point geometry type, with support for z-dimension and m-values.
static PJ_CONTEXT * get()
Returns a thread local instance of a proj context, safe for use in the current thread.
Contains information about a PROJ operation.
static proj_pj_unique_ptr crsToHorizontalCrs(const PJ *crs)
Given a PROJ crs (which may be a compound or bound crs, or some other type), extract the horizontal c...
@ FlagMatchBoundCrsToUnderlyingSourceCrs
Allow matching a BoundCRS object to its underlying SourceCRS.
static proj_pj_unique_ptr createCompoundCrs(const PJ *horizontalCrs, const PJ *verticalCrs, QStringList *errors=nullptr)
Given a PROJ horizontal and vertical CRS, attempt to create a compound CRS from them.
static bool isDynamic(const PJ *crs)
Returns true if the given proj coordinate system is a dynamic CRS.
static proj_pj_unique_ptr unboundCrs(const PJ *crs)
Given a PROJ crs (which may be a compound or bound crs, or some other type), ensure that it is not a ...
static bool identifyCrs(const PJ *crs, QString &authName, QString &authCode, IdentifyFlags flags=IdentifyFlags())
Attempts to identify a crs, matching it to a known authority and code within an acceptable level of t...
static bool hasVerticalAxis(const PJ *crs)
Returns true if a PROJ crs has a vertical axis.
static proj_pj_unique_ptr crsToVerticalCrs(const PJ *crs)
Given a PROJ crs (which may be a compound crs, or some other type), extract the vertical crs from it.
static proj_pj_unique_ptr crsToDatumEnsemble(const PJ *crs)
Given a PROJ crs, attempt to retrieve the datum ensemble from it.
std::unique_ptr< PJ, ProjPJDeleter > proj_pj_unique_ptr
Scoped Proj PJ object.
static bool axisOrderIsSwapped(const PJ *crs)
Returns true if the given proj coordinate system uses requires y/x coordinate order instead of x/y.
Contains various cartographic properties, such as scale factors, angular distortion and meridian conv...
A convenience class that simplifies locking and unlocking QReadWriteLocks.
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.
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
#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.
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(* CUSTOM_CRS_VALIDATION)(QgsCoordinateReferenceSystem &)
const QMap< QString, QString > sAuthIdToQgisSrsIdMap
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
Contains parameters for an ellipsoid.
double semiMajor
Semi-major axis, in meters.
bool valid
Whether ellipsoid parameters are valid.
double inverseFlattening
Inverse flattening.