21#include <nlohmann/json.hpp>
23#include <proj_constants.h>
24#include <proj_experimental.h>
50#include <QRegularExpression>
54#include "moc_qgscoordinatereferencesystem.cpp"
56using namespace Qt::StringLiterals;
59#include <ogr_srs_api.h>
71bool QgsCoordinateReferenceSystem::sDisableSrIdCache =
false;
75bool QgsCoordinateReferenceSystem::sDisableOgcCache =
false;
79bool QgsCoordinateReferenceSystem::sDisableProjCache =
false;
83bool QgsCoordinateReferenceSystem::sDisableWktCache =
false;
87bool QgsCoordinateReferenceSystem::sDisableSrsIdCache =
false;
91bool QgsCoordinateReferenceSystem::sDisableStringCache =
false;
100 if (
const char *proj4src = proj_as_proj_string(
QgsProjContext::get(), boundCrs.get(), PJ_PROJ_4,
nullptr ) )
102 return QString( proj4src );
119 d =
new QgsCoordinateReferenceSystemPrivate();
125 d =
new QgsCoordinateReferenceSystemPrivate();
133 , mValidationHint( srs.mValidationHint )
134 , mNativeFormat( srs.mNativeFormat )
143 mValidationHint = srs.mValidationHint;
144 mNativeFormat = srs.mNativeFormat;
154 const auto constDbs = dbs;
155 for (
const QString &db : constDbs )
157 QFileInfo myInfo( db );
158 if ( !myInfo.exists() )
168 int result = openDatabase( db, database );
169 if ( result != SQLITE_OK )
175 QString sql = u
"select srs_id from tbl_srs"_s;
177 statement = database.
prepare( sql, rc );
181 int ret = statement.
step();
183 if ( ret == SQLITE_DONE )
189 if ( ret == SQLITE_ROW )
195 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
200 std::sort( results.begin(), results.end() );
256 if ( horizontalObj && verticalObj )
263 QStringList formattedErrorList;
264 for (
const QString &rawError : std::as_const( errors ) )
266 QString formattedError = rawError;
267 formattedError.replace(
"proj_create_compound_crs: "_L1, QString() );
268 formattedErrorList.append( formattedError );
270 error = formattedErrorList.join(
'\n' );
278 if ( !ellipsoidParams.
valid )
326 if ( definition.isEmpty() )
330 if ( !sDisableStringCache )
332 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sStringCache()->constFind( definition );
333 if ( crsIt != sStringCache()->constEnd() )
336 *
this = crsIt.value();
343 const thread_local QRegularExpression reCrsId( u
"^(epsg|esri|osgeo|ignf|ogc|nkg|zangi|iau_2015|iau2000|postgis|internal|user)\\:(\\w+)$"_s, QRegularExpression::CaseInsensitiveOption );
344 QRegularExpressionMatch match = reCrsId.match( definition );
345 if ( match.capturedStart() == 0 )
347 QString authName = match.captured( 1 ).toLower();
348 if ( authName ==
"epsg"_L1 )
352 else if ( authName ==
"postgis"_L1 )
354 const long id = match.captured( 2 ).toLong();
359 else if ( authName ==
"esri"_L1
360 || authName ==
"osgeo"_L1
361 || authName ==
"ignf"_L1
362 || authName ==
"zangi"_L1
363 || authName ==
"iau2000"_L1
364 || authName ==
"ogc"_L1
365 || authName ==
"nkg"_L1
366 || authName ==
"iau_2015"_L1 )
372 const long id = match.captured( 2 ).toLong();
380 const thread_local QRegularExpression reCrsStr( u
"^(?:(wkt|proj4|proj)\\:)?(.+)$"_s, QRegularExpression::CaseInsensitiveOption );
381 match = reCrsStr.match( definition );
382 if ( match.capturedStart() == 0 )
384 if ( match.captured( 1 ).startsWith(
"proj"_L1, Qt::CaseInsensitive ) )
396 if ( !sDisableStringCache )
397 sStringCache()->insert( definition, *
this );
403 if ( definition.isEmpty() )
407 OGRSpatialReferenceH crs = OSRNewSpatialReference(
nullptr );
409 if ( OSRSetFromUserInput( crs, definition.toLocal8Bit().constData() ) == OGRERR_NONE )
412 OSRDestroySpatialReference( crs );
422 const char *configOld = CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" );
423 const char *configNew =
"GEOGCS";
425 if ( strcmp( configOld,
"" ) == 0 )
427 CPLSetConfigOption(
"GDAL_FIX_ESRI_WKT", configNew );
428 if ( strcmp( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) != 0 )
429 QgsLogger::warning( u
"GDAL_FIX_ESRI_WKT could not be set to %1 : %2"_s.arg( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) );
434 QgsDebugMsgLevel( u
"GDAL_FIX_ESRI_WKT was already set : %1"_s.arg( configNew ), 4 );
444 if ( !sDisableOgcCache )
446 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sOgcCache()->constFind( crs );
447 if ( crsIt != sOgcCache()->constEnd() )
450 *
this = crsIt.value();
456 QString wmsCrs = crs;
461 const QString authorityLower = authority.toLower();
467 if ( !sDisableOgcCache )
468 sOgcCache()->insert( crs, *
this );
474 wmsCrs = authority +
':' + code;
478 const QString legacyKey = wmsCrs.toLower();
479 for (
auto it = authIdToQgisSrsIdMap().constBegin(); it != authIdToQgisSrsIdMap().constEnd(); ++it )
481 if ( it.key().compare( legacyKey, Qt::CaseInsensitive ) == 0 )
483 const QStringList parts = it.key().split(
':' );
484 const QString auth = parts.at( 0 );
485 const QString code = parts.at( 1 );
486 if ( loadFromAuthCode( auth, code ) )
489 if ( !sDisableOgcCache )
490 sOgcCache()->insert( crs, *
this );
499 if ( !sDisableOgcCache )
500 sOgcCache()->insert( crs, *
this );
505 if ( wmsCrs.compare(
"CRS:27"_L1, Qt::CaseInsensitive ) == 0 || wmsCrs.compare(
"OGC:CRS27"_L1, Qt::CaseInsensitive ) == 0 )
512 if ( wmsCrs.compare(
"CRS:83"_L1, Qt::CaseInsensitive ) == 0 || wmsCrs.compare(
"OGC:CRS83"_L1, Qt::CaseInsensitive ) == 0 )
519 if ( wmsCrs.compare(
"CRS:84"_L1, Qt::CaseInsensitive ) == 0 || wmsCrs.compare(
"OGC:CRS84"_L1, Qt::CaseInsensitive ) == 0 )
523 d->mAxisInverted =
false;
524 d->mAxisInvertedDirty =
false;
528 if ( !sDisableOgcCache )
529 sOgcCache()->insert( crs, *
this );
536 if ( !authority.isEmpty() && !code.isEmpty() && loadFromAuthCode( authority, code ) )
539 if ( !sDisableOgcCache )
540 sOgcCache()->insert( crs, *
this );
545 if ( !sDisableOgcCache )
555 if ( d->mIsValid || !sCustomSrsValidation )
559 if ( sCustomSrsValidation )
560 sCustomSrsValidation( *
this );
566 if ( !sDisableSrIdCache )
568 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrIdCache()->constFind(
id );
569 if ( crsIt != sSrIdCache()->constEnd() )
572 *
this = crsIt.value();
579 for (
auto it = authIdToQgisSrsIdMap().constBegin(); it != authIdToQgisSrsIdMap().constEnd(); ++it )
581 if ( it.value().endsWith( u
",%1"_s.arg(
id ) ) )
583 const QStringList parts = it.key().split(
':' );
584 const QString auth = parts.at( 0 );
585 const QString code = parts.at( 1 );
586 if ( loadFromAuthCode( auth, code ) )
589 if ( !sDisableSrIdCache )
590 sSrIdCache()->insert(
id, *
this );
600 if ( !sDisableSrIdCache )
601 sSrIdCache()->insert(
id, *
this );
609 if ( !sDisableSrsIdCache )
611 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrsIdCache()->constFind(
id );
612 if ( crsIt != sSrsIdCache()->constEnd() )
615 *
this = crsIt.value();
622 for (
auto it = authIdToQgisSrsIdMap().constBegin(); it != authIdToQgisSrsIdMap().constEnd(); ++it )
624 if ( it.value().startsWith( QString::number(
id ) +
',' ) )
626 const QStringList parts = it.key().split(
':' );
627 const QString auth = parts.at( 0 );
628 const QString code = parts.at( 1 );
629 if ( loadFromAuthCode( auth, code ) )
632 if ( !sDisableSrsIdCache )
633 sSrsIdCache()->insert(
id, *
this );
642 if ( !sDisableSrsIdCache )
643 sSrsIdCache()->insert(
id, *
this );
647bool QgsCoordinateReferenceSystem::loadFromDatabase(
const QString &db,
const QString &expression,
const QString &value )
651 QgsDebugMsgLevel(
"load CRS from " + db +
" where " + expression +
" is " + value, 3 );
653 d->mWktPreferred.clear();
655 QFileInfo myInfo( db );
656 if ( !myInfo.exists() )
662 sqlite3_database_unique_ptr database;
663 sqlite3_statement_unique_ptr statement;
666 myResult = openDatabase( db, database );
667 if ( myResult != SQLITE_OK )
684 QString mySql =
"select srs_id,description,projection_acronym,"
685 "ellipsoid_acronym,parameters,srid,auth_name||':'||auth_id,is_geo,wkt "
686 "from tbl_srs where "
690 +
" order by deprecated";
691 statement = database.
prepare( mySql, myResult );
694 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
699 d->mEllipsoidAcronym.clear();
701 d->mWktPreferred.clear();
704 d->mIsGeographic = statement.
columnAsText( 7 ).toInt() != 0;
706 d->mAxisInvertedDirty =
true;
710 d->mAuthId = u
"USER:%1"_s.arg( d->mSrsId );
712 else if ( !d->mAuthId.startsWith(
"USER:"_L1, Qt::CaseInsensitive ) )
714 QStringList parts = d->mAuthId.split(
':' );
715 QString auth = parts.at( 0 );
716 QString code = parts.at( 1 );
723 d->mIsValid = d->hasPj();
726 setExtraProperties();
730 if ( !wkt.isEmpty() )
738 setProjString( d->mProj4 );
748void QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread(
PJ_CONTEXT *pj_context )
755 if ( !sDisableSrIdCache )
758 if ( !sDisableSrIdCache )
760 for (
auto it = sSrIdCache()->begin(); it != sSrIdCache()->end(); )
762 auto &v = it.value();
763 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
764 it = sSrIdCache()->erase( it );
770 if ( !sDisableOgcCache )
773 if ( !sDisableOgcCache )
775 for (
auto it = sOgcCache()->begin(); it != sOgcCache()->end(); )
777 auto &v = it.value();
778 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
779 it = sOgcCache()->erase( it );
785 if ( !sDisableProjCache )
788 if ( !sDisableProjCache )
790 for (
auto it = sProj4Cache()->begin(); it != sProj4Cache()->end(); )
792 auto &v = it.value();
793 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
794 it = sProj4Cache()->erase( it );
800 if ( !sDisableWktCache )
803 if ( !sDisableWktCache )
805 for (
auto it = sWktCache()->begin(); it != sWktCache()->end(); )
807 auto &v = it.value();
808 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
809 it = sWktCache()->erase( it );
815 if ( !sDisableSrsIdCache )
818 if ( !sDisableSrsIdCache )
820 for (
auto it = sSrsIdCache()->begin(); it != sSrsIdCache()->end(); )
822 auto &v = it.value();
823 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
824 it = sSrsIdCache()->erase( it );
830 if ( !sDisableStringCache )
833 if ( !sDisableStringCache )
835 for (
auto it = sStringCache()->begin(); it != sStringCache()->end(); )
837 auto &v = it.value();
838 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
839 it = sStringCache()->erase( it );
849 if ( d->mAxisInvertedDirty )
852 d->mAxisInvertedDirty =
false;
855 return d->mAxisInverted;
872 const thread_local QMap< Qgis::CrsAxisDirection, QString > mapping = {
914 QList< Qgis::CrsAxisDirection > res;
915 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
918 res.reserve( axisCount );
920 for (
int i = 0; i < axisCount; ++i )
922 const char *outDirection =
nullptr;
923 proj_cs_get_axis_info( context, pjCs.get(), i,
nullptr,
nullptr, &outDirection,
nullptr,
nullptr,
nullptr,
nullptr );
925 const thread_local QRegularExpression rx( u
"([^\\s]+).*"_s );
926 const QRegularExpressionMatch match = rx.match( QString( outDirection ) );
927 if ( !match.hasMatch() )
930 const QString direction = match.captured( 1 );
932 for (
auto it = mapping.constBegin(); it != mapping.constEnd(); ++it )
934 if ( it.value().compare( direction, Qt::CaseInsensitive ) == 0 )
949 return createFromWktInternal( wkt, QString() );
952bool QgsCoordinateReferenceSystem::createFromWktInternal(
const QString &wkt,
const QString &description )
960 if ( !sDisableWktCache )
962 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sWktCache()->constFind( wkt );
963 if ( crsIt != sWktCache()->constEnd() )
966 *
this = crsIt.value();
968 if ( !
description.isEmpty() && d->mDescription.isEmpty() )
973 sWktCache()->insert( wkt, *
this );
982 d->mWktPreferred.clear();
990 QgsCoordinateReferenceSystem::RecordMap record = getRecord(
"select * from tbl_srs where wkt=" +
QgsSqliteUtils::quotedString( wkt ) +
" order by deprecated" );
991 if ( !record.empty() )
993 long srsId = record[u
"srs_id"_s].toLong();
1001 setWktString( wkt );
1006 if ( d->mSrsId == 0 )
1009 long id = matchToUserCrs();
1018 if ( !sDisableWktCache )
1019 sWktCache()->insert( wkt, *
this );
1037 if ( projString.isEmpty() )
1042 if ( projString.trimmed().isEmpty() )
1044 d->mIsValid =
false;
1046 d->mWktPreferred.clear();
1051 if ( !sDisableProjCache )
1053 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sProj4Cache()->constFind( projString );
1054 if ( crsIt != sProj4Cache()->constEnd() )
1057 *
this = crsIt.value();
1071 QString myProj4String = projString.trimmed();
1072 myProj4String.remove( u
"+type=crs"_s );
1073 myProj4String = myProj4String.trimmed();
1075 d->mIsValid =
false;
1076 d->mWktPreferred.clear();
1081 const QString projCrsString = myProj4String + ( myProj4String.contains( u
"+type=crs"_s ) ? QString() : u
" +type=crs"_s );
1089 const QString
authid = u
"%1:%2"_s.arg( authName, authCode );
1093 if ( !sDisableProjCache )
1094 sProj4Cache()->insert( projString, *
this );
1101 QgsCoordinateReferenceSystem::RecordMap myRecord = getRecord(
"select * from tbl_srs where parameters=" +
QgsSqliteUtils::quotedString( myProj4String ) +
" order by deprecated" );
1103 if ( !myRecord.empty() )
1105 id = myRecord[u
"srs_id"_s].toLong();
1114 setProjString( myProj4String );
1117 id = matchToUserCrs();
1126 setProjString( myProj4String );
1130 if ( !sDisableProjCache )
1131 sProj4Cache()->insert( projString, *
this );
1137QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord(
const QString &sql )
1139 QString myDatabaseFileName;
1140 QgsCoordinateReferenceSystem::RecordMap myMap;
1141 QString myFieldName;
1142 QString myFieldValue;
1149 QFileInfo myInfo( myDatabaseFileName );
1150 if ( !myInfo.exists() )
1152 QgsDebugError(
"failed : " + myDatabaseFileName +
" does not exist!" );
1157 myResult = openDatabase( myDatabaseFileName, database );
1158 if ( myResult != SQLITE_OK )
1163 statement = database.
prepare( sql, myResult );
1165 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1169 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1171 myFieldName = statement.
columnName( myColNo );
1173 myMap[myFieldName] = myFieldValue;
1175 if ( statement.
step() != SQLITE_DONE )
1186 if ( myMap.empty() )
1189 QFileInfo myFileInfo;
1190 myFileInfo.setFile( myDatabaseFileName );
1191 if ( !myFileInfo.exists() )
1198 myResult = openDatabase( myDatabaseFileName, database );
1199 if ( myResult != SQLITE_OK )
1204 statement = database.
prepare( sql, myResult );
1206 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1210 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1212 myFieldName = statement.
columnName( myColNo );
1214 myMap[myFieldName] = myFieldValue;
1217 if ( statement.
step() != SQLITE_DONE )
1250 if ( d->mDescription.isNull() )
1256 return d->mDescription;
1263 if ( !
authid().isEmpty() )
1273 id =
isValid() ? QObject::tr(
"Custom CRS" ) : QObject::tr(
"Unknown CRS" );
1275 id = QObject::tr(
"Custom CRS: %1" )
1277 else if ( !
toProj().isEmpty() )
1279 if ( !
id.isEmpty() && !std::isnan( d->mCoordinateEpoch ) )
1287 if ( d->mProjectionAcronym.isNull() )
1293 return d->mProjectionAcronym;
1299 if ( d->mEllipsoidAcronym.isNull() )
1301 if (
PJ *obj = d->threadLocalProjObject() )
1306 const QString ellipsoidAuthName( proj_get_id_auth_name( ellipsoid.get(), 0 ) );
1307 const QString ellipsoidAuthCode( proj_get_id_code( ellipsoid.get(), 0 ) );
1308 if ( !ellipsoidAuthName.isEmpty() && !ellipsoidAuthCode.isEmpty() )
1309 d->mEllipsoidAcronym = u
"%1:%2"_s.arg( ellipsoidAuthName, ellipsoidAuthCode );
1312 double semiMajor, semiMinor, invFlattening;
1313 int semiMinorComputed = 0;
1314 if ( proj_ellipsoid_get_parameters(
QgsProjContext::get(), ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
1320 d->mEllipsoidAcronym.clear();
1325 return d->mEllipsoidAcronym;
1329 return d->mEllipsoidAcronym;
1343 if ( d->mProj4.isEmpty() )
1345 if (
PJ *obj = d->threadLocalProjObject() )
1351 return d->mProj4.trimmed();
1359 if (
PJ *obj = d->threadLocalProjObject() )
1361 const QByteArray multiLineOption = u
"MULTILINE=%1"_s.arg( multiline ? u
"YES"_s : u
"NO"_s ).toLocal8Bit();
1362 const QByteArray indentatationWidthOption = u
"INDENTATION_WIDTH=%1"_s.arg( multiline ? QString::number( indentationWidth ) : u
"0"_s ).toLocal8Bit();
1363 const QByteArray schemaOption = u
"SCHEMA=%1"_s.arg( schema ).toLocal8Bit();
1364 const char *
const options[] = { multiLineOption.constData(), indentatationWidthOption.constData(), schemaOption.constData(),
nullptr };
1367 return json ? std::string { json } : std::string {};
1378 switch ( d->mProjType )
1380 case PJ_TYPE_UNKNOWN:
1383 case PJ_TYPE_ELLIPSOID:
1384 case PJ_TYPE_PRIME_MERIDIAN:
1385 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
1386 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
1387 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
1388 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
1389 case PJ_TYPE_DATUM_ENSEMBLE:
1390 case PJ_TYPE_CONVERSION:
1391 case PJ_TYPE_TRANSFORMATION:
1392 case PJ_TYPE_CONCATENATED_OPERATION:
1393 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
1394 case PJ_TYPE_TEMPORAL_DATUM:
1395 case PJ_TYPE_ENGINEERING_DATUM:
1396 case PJ_TYPE_PARAMETRIC_DATUM:
1400 case PJ_TYPE_GEOGRAPHIC_CRS:
1404 case PJ_TYPE_GEODETIC_CRS:
1406 case PJ_TYPE_GEOCENTRIC_CRS:
1408 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
1410 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
1412 case PJ_TYPE_VERTICAL_CRS:
1414 case PJ_TYPE_PROJECTED_CRS:
1416 case PJ_TYPE_COMPOUND_CRS:
1418 case PJ_TYPE_TEMPORAL_CRS:
1420 case PJ_TYPE_ENGINEERING_CRS:
1422 case PJ_TYPE_BOUND_CRS:
1424 case PJ_TYPE_OTHER_CRS:
1426#if PROJ_VERSION_MAJOR > 9 || ( PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 2 )
1427 case PJ_TYPE_DERIVED_PROJECTED_CRS:
1429 case PJ_TYPE_COORDINATE_METADATA:
1443 return proj_is_deprecated( pj );
1448 return d->mIsGeographic;
1453 return d->mIsGeographicCrossingAntimeridian;
1473 return QString( proj_get_celestial_body_name( context, pj ) );
1478 if ( d->mCoordinateEpoch == epoch )
1484 d->mCoordinateEpoch = epoch;
1485 d->setPj( std::move( clone ) );
1490 return d->mCoordinateEpoch;
1509 res.mName = QString( proj_get_name( ensemble.get() ) );
1510 res.mAuthority = QString( proj_get_id_auth_name( ensemble.get(), 0 ) );
1511 res.mCode = QString( proj_get_id_code( ensemble.get(), 0 ) );
1512 res.mRemarks = QString( proj_get_remarks( ensemble.get() ) );
1513 res.mScope = QString( proj_get_scope( ensemble.get() ) );
1514 res.mAccuracy = proj_datum_ensemble_get_accuracy( context, ensemble.get() );
1516 const int memberCount = proj_datum_ensemble_get_member_count( context, ensemble.get() );
1517 for (
int i = 0; i < memberCount; ++i )
1524 details.mName = QString( proj_get_name( member.get() ) );
1525 details.mAuthority = QString( proj_get_id_auth_name( member.get(), 0 ) );
1526 details.mCode = QString( proj_get_id_code( member.get(), 0 ) );
1527 details.mRemarks = QString( proj_get_remarks( member.get() ) );
1528 details.mScope = QString( proj_get_scope( member.get() ) );
1530 res.mMembers << details;
1540 QString projString =
toProj();
1541 projString.replace(
"+type=crs"_L1, QString() );
1544 if ( !transformation )
1547 PJ_COORD coord = proj_coord( 0, 0, 0, HUGE_VAL );
1548 coord.uv.u = point.
x() * M_PI / 180.0;
1549 coord.uv.v = point.
y() * M_PI / 180.0;
1551 proj_errno_reset( transformation.get() );
1552 const PJ_FACTORS pjFactors = proj_factors( transformation.get(), coord );
1553 if ( proj_errno( transformation.get() ) )
1558 res.mIsValid =
true;
1559 res.mMeridionalScale = pjFactors.meridional_scale;
1560 res.mParallelScale = pjFactors.parallel_scale;
1561 res.mArealScale = pjFactors.areal_scale;
1562 res.mAngularDistortion = pjFactors.angular_distortion;
1563 res.mMeridianParallelAngle = pjFactors.meridian_parallel_angle * 180 / M_PI;
1564 res.mMeridianConvergence = pjFactors.meridian_convergence * 180 / M_PI;
1565 res.mTissotSemimajor = pjFactors.tissot_semimajor;
1566 res.mTissotSemiminor = pjFactors.tissot_semiminor;
1567 res.mDxDlam = pjFactors.dx_dlam;
1568 res.mDxDphi = pjFactors.dx_dphi;
1569 res.mDyDlam = pjFactors.dy_dlam;
1570 res.mDyDphi = pjFactors.dy_dphi;
1582 QString projString =
toProj();
1583 projString.replace(
"+type=crs"_L1, QString() );
1584 if ( projString.isEmpty() )
1588 if ( !transformation )
1591 PJ_PROJ_INFO info = proj_pj_info( transformation.get() );
1606 return d->mMapUnits;
1614 PJ *obj = d->threadLocalProjObject();
1619 double southLat = 0;
1621 double northLat = 0;
1623 if ( !proj_get_area_of_use(
QgsProjContext::get(), obj, &westLon, &southLat, &eastLon, &northLat,
nullptr ) )
1638 const auto parts {
authid().split(
':' ) };
1639 if ( parts.length() == 2 )
1641 if ( parts[0] ==
"EPSG"_L1 )
1642 return u
"http://www.opengis.net/def/crs/EPSG/0/%1"_s.arg( parts[1] );
1643 else if ( parts[0] ==
"OGC"_L1 )
1645 return u
"http://www.opengis.net/def/crs/OGC/1.3/%1"_s.arg( parts[1] );
1661 const auto parts {
authid().split(
':' ) };
1662 if ( parts.length() == 2 )
1664 if ( parts[0] ==
"EPSG"_L1 )
1665 return u
"urn:ogc:def:crs:EPSG::%1"_s.arg( parts[1] );
1666 else if ( parts[0] ==
"OGC"_L1 )
1668 return u
"urn:ogc:def:crs:OGC:1.3:%1"_s.arg( parts[1] );
1670 else if ( parts[0].startsWith(
"IAU"_L1 ) )
1672 if ( parts[0].contains(
"_"_L1 ) )
1674 const auto subParts = parts[0].split(
'_' );
1675 if ( subParts.length() == 2 )
1677 return u
"urn:ogc:def:crs:%1:%2:%3"_s.arg( subParts[0], subParts[1], parts[1] );
1682 return u
"urn:ogc:def:crs:%1::%2"_s.arg( parts[0], parts[1] );
1714void QgsCoordinateReferenceSystem::setProjString(
const QString &proj4String )
1717 d->mProj4 = proj4String;
1718 d->mWktPreferred.clear();
1721 QString trimmed = proj4String.trimmed();
1723 trimmed +=
" +type=crs"_L1;
1733 const int errNo = proj_context_errno( ctx );
1734 QgsDebugError( u
"proj string rejected: %1"_s.arg( proj_context_errno_string( ctx, errNo ) ) );
1736 d->mIsValid =
false;
1740 d->mEllipsoidAcronym.clear();
1745 setExtraProperties();
1748bool QgsCoordinateReferenceSystem::setWktString(
const QString &wkt )
1751 d->mIsValid =
false;
1752 d->mWktPreferred.clear();
1754 PROJ_STRING_LIST warnings =
nullptr;
1755 PROJ_STRING_LIST grammarErrors =
nullptr;
1763 QgsDebugMsgLevel( u
"\n---------------------------------------------------------------"_s, 2 );
1764 QgsDebugMsgLevel( u
"This CRS could *** NOT *** be set from the supplied Wkt "_s, 2 );
1766 for (
auto iter = warnings; iter && *iter; ++iter )
1770 for (
auto iter = grammarErrors; iter && *iter; ++iter )
1774 QgsDebugMsgLevel( u
"---------------------------------------------------------------\n"_s, 2 );
1776 proj_string_list_destroy( warnings );
1777 proj_string_list_destroy( grammarErrors );
1783 if ( !sDisableWktCache )
1784 sWktCache()->insert( wkt, *
this );
1792 QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
1793 QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
1795 if ( authName.isEmpty() || authCode.isEmpty() )
1801 if ( !authName.isEmpty() && !authCode.isEmpty() )
1804 if ( fromAuthCode.loadFromAuthCode( authName, authCode ) )
1806 *
this = fromAuthCode;
1808 if ( !sDisableWktCache )
1809 sWktCache()->insert( wkt, *
this );
1816 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
1818 setExtraProperties();
1824void QgsCoordinateReferenceSystem::setMapUnits()
1851 if ( !coordinateSystem )
1857 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
1858 if ( axisCount > 0 )
1860 const char *outUnitName =
nullptr;
1862 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
nullptr,
nullptr,
nullptr,
nullptr, &outUnitName,
nullptr,
nullptr );
1864 const QString unitName( outUnitName );
1868 if ( unitName.compare(
"degree"_L1, Qt::CaseInsensitive ) == 0
1869 || unitName.compare(
"degree minute second"_L1, Qt::CaseInsensitive ) == 0
1870 || unitName.compare(
"degree minute second hemisphere"_L1, Qt::CaseInsensitive ) == 0
1871 || unitName.compare(
"degree minute"_L1, Qt::CaseInsensitive ) == 0
1872 || unitName.compare(
"degree hemisphere"_L1, Qt::CaseInsensitive ) == 0
1873 || unitName.compare(
"degree minute hemisphere"_L1, Qt::CaseInsensitive ) == 0
1874 || unitName.compare(
"hemisphere degree"_L1, Qt::CaseInsensitive ) == 0
1875 || unitName.compare(
"hemisphere degree minute"_L1, Qt::CaseInsensitive ) == 0
1876 || unitName.compare(
"hemisphere degree minute second"_L1, Qt::CaseInsensitive ) == 0
1877 || unitName.compare(
"degree (supplier to define representation)"_L1, Qt::CaseInsensitive ) == 0 )
1879 else if ( unitName.compare(
"metre"_L1, Qt::CaseInsensitive ) == 0 || unitName.compare(
'm'_L1, Qt::CaseInsensitive ) == 0 || unitName.compare(
"meter"_L1, Qt::CaseInsensitive ) == 0 )
1881 else if ( unitName.compare(
"US survey foot"_L1, Qt::CaseInsensitive ) == 0 )
1883 else if ( unitName.compare(
"foot"_L1, Qt::CaseInsensitive ) == 0 )
1885 else if ( unitName.compare(
"British yard (Sears 1922)"_L1, Qt::CaseInsensitive ) == 0 )
1887 else if ( unitName.compare(
"British yard (Sears 1922 truncated)"_L1, Qt::CaseInsensitive ) == 0 )
1889 else if ( unitName.compare(
"British foot (Sears 1922)"_L1, Qt::CaseInsensitive ) == 0 )
1891 else if ( unitName.compare(
"British foot (Sears 1922 truncated)"_L1, Qt::CaseInsensitive ) == 0 )
1893 else if ( unitName.compare(
"British chain (Sears 1922)"_L1, Qt::CaseInsensitive ) == 0 )
1895 else if ( unitName.compare(
"British chain (Sears 1922 truncated)"_L1, Qt::CaseInsensitive ) == 0 )
1897 else if ( unitName.compare(
"British link (Sears 1922)"_L1, Qt::CaseInsensitive ) == 0 )
1899 else if ( unitName.compare(
"British link (Sears 1922 truncated)"_L1, Qt::CaseInsensitive ) == 0 )
1901 else if ( unitName.compare(
"British yard (Benoit 1895 A)"_L1, Qt::CaseInsensitive ) == 0 )
1903 else if ( unitName.compare(
"British foot (Benoit 1895 A)"_L1, Qt::CaseInsensitive ) == 0 )
1905 else if ( unitName.compare(
"British chain (Benoit 1895 A)"_L1, Qt::CaseInsensitive ) == 0 )
1907 else if ( unitName.compare(
"British link (Benoit 1895 A)"_L1, Qt::CaseInsensitive ) == 0 )
1909 else if ( unitName.compare(
"British yard (Benoit 1895 B)"_L1, Qt::CaseInsensitive ) == 0 )
1911 else if ( unitName.compare(
"British foot (Benoit 1895 B)"_L1, Qt::CaseInsensitive ) == 0 )
1913 else if ( unitName.compare(
"British chain (Benoit 1895 B)"_L1, Qt::CaseInsensitive ) == 0 )
1915 else if ( unitName.compare(
"British link (Benoit 1895 B)"_L1, Qt::CaseInsensitive ) == 0 )
1917 else if ( unitName.compare(
"British foot (1865)"_L1, Qt::CaseInsensitive ) == 0 )
1919 else if ( unitName.compare(
"British foot (1936)"_L1, Qt::CaseInsensitive ) == 0 )
1921 else if ( unitName.compare(
"Indian foot"_L1, Qt::CaseInsensitive ) == 0 )
1923 else if ( unitName.compare(
"Indian foot (1937)"_L1, Qt::CaseInsensitive ) == 0 )
1925 else if ( unitName.compare(
"Indian foot (1962)"_L1, Qt::CaseInsensitive ) == 0 )
1927 else if ( unitName.compare(
"Indian foot (1975)"_L1, Qt::CaseInsensitive ) == 0 )
1929 else if ( unitName.compare(
"Indian yard"_L1, Qt::CaseInsensitive ) == 0 )
1931 else if ( unitName.compare(
"Indian yard (1937)"_L1, Qt::CaseInsensitive ) == 0 )
1933 else if ( unitName.compare(
"Indian yard (1962)"_L1, Qt::CaseInsensitive ) == 0 )
1935 else if ( unitName.compare(
"Indian yard (1975)"_L1, Qt::CaseInsensitive ) == 0 )
1937 else if ( unitName.compare(
"Gold Coast foot"_L1, Qt::CaseInsensitive ) == 0 )
1939 else if ( unitName.compare(
"Clarke's foot"_L1, Qt::CaseInsensitive ) == 0 )
1941 else if ( unitName.compare(
"Clarke's yard"_L1, Qt::CaseInsensitive ) == 0 )
1943 else if ( unitName.compare(
"Clarke's chain"_L1, Qt::CaseInsensitive ) == 0 )
1945 else if ( unitName.compare(
"Clarke's link"_L1, Qt::CaseInsensitive ) == 0 )
1947 else if ( unitName.compare(
"kilometre"_L1, Qt::CaseInsensitive ) == 0 )
1949 else if ( unitName.compare(
"centimetre"_L1, Qt::CaseInsensitive ) == 0 )
1951 else if ( unitName.compare(
"millimetre"_L1, Qt::CaseInsensitive ) == 0 )
1953 else if ( unitName.compare(
"Statute mile"_L1, Qt::CaseInsensitive ) == 0 )
1955 else if ( unitName.compare(
"nautical mile"_L1, Qt::CaseInsensitive ) == 0 )
1957 else if ( unitName.compare(
"yard"_L1, Qt::CaseInsensitive ) == 0 )
1959 else if ( unitName.compare(
"fathom"_L1, Qt::CaseInsensitive ) == 0 )
1961 else if ( unitName.compare(
"US survey chain"_L1, Qt::CaseInsensitive ) == 0 )
1963 else if ( unitName.compare(
"chain"_L1, Qt::CaseInsensitive ) == 0 )
1965 else if ( unitName.compare(
"link"_L1, Qt::CaseInsensitive ) == 0 )
1967 else if ( unitName.compare(
"US survey link"_L1, Qt::CaseInsensitive ) == 0 )
1969 else if ( unitName.compare(
"US survey mile"_L1, Qt::CaseInsensitive ) == 0 )
1971 else if ( unitName.compare(
"German legal metre"_L1, Qt::CaseInsensitive ) == 0 )
1985void QgsCoordinateReferenceSystem::setExtraProperties()
1987 d->mIsGeographicCrossingAntimeridian =
false;
1992 if ( d->mIsGeographic && ( d->mAuthId !=
"EPSG:4326"_L1 ) )
1994 if (
PJ *obj = d->threadLocalProjObject() )
1997 double southLat = 0;
1999 double northLat = 0;
2000 if ( proj_get_area_of_use(
QgsProjContext::get(), obj, &westLon, &southLat, &eastLon, &northLat,
nullptr ) )
2002 d->mIsGeographicCrossingAntimeridian = westLon > eastLon;
2012 if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull() || !d->mIsValid )
2015 "QgsCoordinateReferenceSystem::findMatchingProj will only "
2016 "work if prj acr ellipsoid acr and proj4string are set"
2017 " and the current projection is valid!",
2029 QString mySql = QString(
2030 "select srs_id,parameters from tbl_srs where "
2031 "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated"
2038 myResult = openDatabase( myDatabaseFileName, database );
2039 if ( myResult != SQLITE_OK )
2044 statement = database.
prepare( mySql, myResult );
2045 if ( myResult == SQLITE_OK )
2047 while ( statement.
step() == SQLITE_ROW )
2051 if (
toProj() == myProj4String.trimmed() )
2053 return mySrsId.toLong();
2064 myResult = openDatabase( myDatabaseFileName, database );
2065 if ( myResult != SQLITE_OK )
2070 statement = database.
prepare( mySql, myResult );
2072 if ( myResult == SQLITE_OK )
2074 while ( statement.
step() == SQLITE_ROW )
2078 if (
toProj() == myProj4String.trimmed() )
2080 return mySrsId.toLong();
2094 if ( !d->mIsValid && !srs.d->mIsValid )
2097 if ( !d->mIsValid || !srs.d->mIsValid )
2105 if ( isUser != otherIsUser )
2109 if ( !isUser && ( !d->mAuthId.isEmpty() || !srs.d->mAuthId.isEmpty() ) )
2110 return d->mAuthId == srs.d->mAuthId;
2117 return !( *
this == srs );
2122 if (
PJ *obj = d->threadLocalProjObject() )
2125 if ( isDefaultPreferredFormat && !d->mWktPreferred.isEmpty() )
2128 return d->mWktPreferred;
2131 PJ_WKT_TYPE
type = PJ_WKT1_GDAL;
2135 type = PJ_WKT1_GDAL;
2138 type = PJ_WKT1_ESRI;
2141 type = PJ_WKT2_2015;
2144 type = PJ_WKT2_2015_SIMPLIFIED;
2147 type = PJ_WKT2_2019;
2150 type = PJ_WKT2_2019_SIMPLIFIED;
2154 const QByteArray multiLineOption = u
"MULTILINE=%1"_s.arg( multiline ? u
"YES"_s : u
"NO"_s ).toLocal8Bit();
2155 const QByteArray indentatationWidthOption = u
"INDENTATION_WIDTH=%1"_s.arg( multiline ? QString::number( indentationWidth ) : u
"0"_s ).toLocal8Bit();
2156 const char *
const options[] = { multiLineOption.constData(), indentatationWidthOption.constData(),
nullptr };
2159 if ( isDefaultPreferredFormat )
2162 d->mWktPreferred = res;
2174 QDomNode srsNode = node.namedItem( u
"spatialrefsys"_s );
2176 if ( !srsNode.isNull() )
2178 bool initialized =
false;
2181 long srsid = srsNode.namedItem( u
"srsid"_s ).toElement().text().toLong( &ok );
2187 node = srsNode.namedItem( u
"authid"_s );
2188 if ( !node.isNull() )
2199 node = srsNode.namedItem( u
"epsg"_s );
2200 if ( !node.isNull() )
2218 const QString
description = srsNode.namedItem( u
"description"_s ).toElement().text();
2220 const QString wkt = srsNode.namedItem( u
"wkt"_s ).toElement().text();
2221 initialized = createFromWktInternal( wkt,
description );
2226 node = srsNode.namedItem( u
"proj4"_s );
2227 const QString proj4 = node.toElement().text();
2234 node = srsNode.namedItem( u
"proj4"_s );
2235 const QString proj4 = node.toElement().text();
2236 if ( !proj4.trimmed().isEmpty() )
2237 setProjString( node.toElement().text() );
2239 node = srsNode.namedItem( u
"srsid"_s );
2240 d->mSrsId = node.toElement().text().toLong();
2242 node = srsNode.namedItem( u
"srid"_s );
2243 d->mSRID = node.toElement().text().toLong();
2245 node = srsNode.namedItem( u
"authid"_s );
2246 d->mAuthId = node.toElement().text();
2248 node = srsNode.namedItem( u
"description"_s );
2249 d->mDescription = node.toElement().text();
2251 node = srsNode.namedItem( u
"projectionacronym"_s );
2252 d->mProjectionAcronym = node.toElement().text();
2254 node = srsNode.namedItem( u
"ellipsoidacronym"_s );
2255 d->mEllipsoidAcronym = node.toElement().text();
2257 node = srsNode.namedItem( u
"geographicflag"_s );
2258 d->mIsGeographic = node.toElement().text() ==
"true"_L1;
2260 d->mWktPreferred.clear();
2264 setExtraProperties();
2267 const QString epoch = srsNode.toElement().attribute( u
"coordinateEpoch"_s );
2268 if ( !epoch.isEmpty() )
2270 bool epochOk =
false;
2271 d->mCoordinateEpoch = epoch.toDouble( &epochOk );
2273 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2277 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2282 const QDomNode topoBaseCrsNode = srsNode.namedItem( u
"topocentricBaseCrs"_s );
2283 if ( !topoBaseCrsNode.isNull() )
2285 d->mTopocentricBaseCrs = std::make_unique<QgsCoordinateReferenceSystem>();
2286 d->mTopocentricBaseCrs->readXml( topoBaseCrsNode );
2292 d =
new QgsCoordinateReferenceSystemPrivate();
2300 QDomElement layerNode = node.toElement();
2301 QDomElement srsElement = doc.createElement( u
"spatialrefsys"_s );
2305 if ( std::isfinite( d->mCoordinateEpoch ) )
2307 srsElement.setAttribute( u
"coordinateEpoch"_s, d->mCoordinateEpoch );
2310 QDomElement wktElement = doc.createElement( u
"wkt"_s );
2312 srsElement.appendChild( wktElement );
2314 QDomElement proj4Element = doc.createElement( u
"proj4"_s );
2315 proj4Element.appendChild( doc.createTextNode(
toProj() ) );
2316 srsElement.appendChild( proj4Element );
2318 QDomElement srsIdElement = doc.createElement( u
"srsid"_s );
2319 srsIdElement.appendChild( doc.createTextNode( QString::number(
srsid() ) ) );
2320 srsElement.appendChild( srsIdElement );
2322 QDomElement sridElement = doc.createElement( u
"srid"_s );
2323 sridElement.appendChild( doc.createTextNode( QString::number(
postgisSrid() ) ) );
2324 srsElement.appendChild( sridElement );
2326 QDomElement authidElement = doc.createElement( u
"authid"_s );
2327 authidElement.appendChild( doc.createTextNode(
authid() ) );
2328 srsElement.appendChild( authidElement );
2330 QDomElement descriptionElement = doc.createElement( u
"description"_s );
2331 descriptionElement.appendChild( doc.createTextNode(
description() ) );
2332 srsElement.appendChild( descriptionElement );
2334 QDomElement projectionAcronymElement = doc.createElement( u
"projectionacronym"_s );
2335 projectionAcronymElement.appendChild( doc.createTextNode(
projectionAcronym() ) );
2336 srsElement.appendChild( projectionAcronymElement );
2338 QDomElement ellipsoidAcronymElement = doc.createElement( u
"ellipsoidacronym"_s );
2339 ellipsoidAcronymElement.appendChild( doc.createTextNode(
ellipsoidAcronym() ) );
2340 srsElement.appendChild( ellipsoidAcronymElement );
2342 QDomElement geographicFlagElement = doc.createElement( u
"geographicflag"_s );
2343 QString geoFlagText = u
"false"_s;
2346 geoFlagText = u
"true"_s;
2349 geographicFlagElement.appendChild( doc.createTextNode( geoFlagText ) );
2350 srsElement.appendChild( geographicFlagElement );
2352 if ( d->mTopocentricBaseCrs && d->mTopocentricBaseCrs->isValid() )
2354 QDomElement topoBaseCrsElement = doc.createElement( u
"topocentricBaseCrs"_s );
2355 d->mTopocentricBaseCrs->writeXml( topoBaseCrsElement, doc );
2356 srsElement.appendChild( topoBaseCrsElement );
2359 layerNode.appendChild( srsElement );
2371QString QgsCoordinateReferenceSystem::projFromSrsId(
const int srsId )
2373 QString myDatabaseFileName;
2374 QString myProjString;
2375 QString mySql = u
"select parameters from tbl_srs where srs_id = %1 order by deprecated"_s.arg( srsId );
2384 QFileInfo myFileInfo;
2385 myFileInfo.setFile( myDatabaseFileName );
2386 if ( !myFileInfo.exists() )
2397 sqlite3_database_unique_ptr database;
2398 sqlite3_statement_unique_ptr statement;
2401 rc = openDatabase( myDatabaseFileName, database );
2407 statement = database.
prepare( mySql, rc );
2409 if ( rc == SQLITE_OK )
2411 if ( statement.
step() == SQLITE_ROW )
2417 return myProjString;
2424 myResult = database.
open_v2( path, SQLITE_OPEN_READONLY,
nullptr );
2426 myResult = database.
open( path );
2428 if ( myResult != SQLITE_OK )
2441 sCustomSrsValidation = f;
2446 return sCustomSrsValidation;
2449void QgsCoordinateReferenceSystem::debugPrint()
2452 QgsDebugMsgLevel(
"* Valid : " + ( d->mIsValid ? QString(
"true" ) : QString(
"false" ) ), 1 );
2473 mValidationHint = html;
2478 return mValidationHint;
2488 mNativeFormat = format;
2493 return mNativeFormat;
2496long QgsCoordinateReferenceSystem::getRecordCount()
2501 long myRecordCount = 0;
2504 if ( myResult != SQLITE_OK )
2510 QString mySql = u
"select count(*) from tbl_srs"_s;
2511 statement = database.
prepare( mySql, myResult );
2512 if ( myResult == SQLITE_OK )
2514 if ( statement.
step() == SQLITE_ROW )
2516 QString myRecordCountString = statement.
columnAsText( 0 );
2517 myRecordCount = myRecordCountString.toLong();
2520 return myRecordCount;
2526 bool isGeographic =
false;
2530 if ( !horizontalCrs )
2534 if ( coordinateSystem )
2536 const int axisCount = proj_cs_get_axis_count( pjContext, coordinateSystem.get() );
2537 if ( axisCount > 0 )
2539 const char *outUnitAuthName =
nullptr;
2540 const char *outUnitAuthCode =
nullptr;
2542 proj_cs_get_axis_info( pjContext, coordinateSystem.get(), 0,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr, &outUnitAuthName, &outUnitAuthCode );
2544 if ( outUnitAuthName && outUnitAuthCode )
2546 const char *unitCategory =
nullptr;
2547 if ( proj_uom_get_info_from_database( pjContext, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
2549 isGeographic = QString( unitCategory ).compare(
"angular"_L1, Qt::CaseInsensitive ) == 0;
2554 return isGeographic;
2559 thread_local const QRegularExpression projRegExp( u
"\\+proj=(\\S+)"_s );
2560 const QRegularExpressionMatch projMatch = projRegExp.match( proj );
2561 if ( !projMatch.hasMatch() )
2566 operation = projMatch.captured( 1 );
2568 const QRegularExpressionMatch ellipseMatch = projRegExp.match( proj );
2569 if ( ellipseMatch.hasMatch() )
2571 ellipsoid = ellipseMatch.captured( 1 );
2585bool QgsCoordinateReferenceSystem::loadFromAuthCode(
const QString &auth,
const QString &code )
2591 d->mIsValid =
false;
2592 d->mWktPreferred.clear();
2595 QgsProjUtils::proj_pj_unique_ptr crs( proj_create_from_database( pjContext, auth.toUtf8().constData(), code.toUtf8().constData(), PJ_CATEGORY_CRS,
false,
nullptr ) );
2604 proj4.replace(
"+type=crs"_L1, QString() );
2605 proj4 = proj4.trimmed();
2609 d->mWktPreferred.clear();
2610 d->mDescription = QString( proj_get_name( crs.get() ) );
2611 d->mAuthId = u
"%1:%2"_s.arg( auth, code );
2613 d->mAxisInvertedDirty =
true;
2618 d->mEllipsoidAcronym.clear();
2619 d->setPj( std::move( crs ) );
2621 const QString dbVals = authIdToQgisSrsIdMap().value( u
"%1:%2"_s.arg( auth, code ).toUpper() );
2622 if ( !dbVals.isEmpty() )
2624 const QStringList parts = dbVals.split(
',' );
2625 d->mSrsId = parts.at( 0 ).toInt();
2626 d->mSRID = parts.at( 1 ).toInt();
2630 setExtraProperties();
2635QList<long> QgsCoordinateReferenceSystem::userSrsIds()
2637 QList<long> results;
2641 QFileInfo myInfo( db );
2642 if ( !myInfo.exists() )
2648 sqlite3_database_unique_ptr database;
2649 sqlite3_statement_unique_ptr statement;
2652 int result = openDatabase( db, database );
2653 if ( result != SQLITE_OK )
2655 QgsDebugError(
"failed : " + db +
" could not be opened!" );
2661 statement = database.
prepare( sql, rc );
2664 int ret = statement.
step();
2666 if ( ret == SQLITE_DONE )
2672 if ( ret == SQLITE_ROW )
2678 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
2686long QgsCoordinateReferenceSystem::matchToUserCrs()
const
2688 PJ *obj = d->threadLocalProjObject();
2692 const QList< long > ids = userSrsIds();
2693 for (
long id : ids )
2696 if ( candidate.
projObject() && proj_is_equivalent_to( obj, candidate.
projObject(), PJ_COMP_EQUIVALENT ) )
2704static void sync_db_proj_logger(
void * ,
int level,
const char *message )
2709 if ( level == PJ_LOG_ERROR )
2713 else if ( level == PJ_LOG_DEBUG )
2721 setlocale( LC_ALL,
"C" );
2724 int inserted = 0, updated = 0, deleted = 0, errors = 0;
2729 if ( database.
open( dbFilePath ) != SQLITE_OK )
2735 if ( sqlite3_exec( database.get(),
"BEGIN TRANSACTION",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2743 char *errMsg =
nullptr;
2745 bool createdTypeColumn =
false;
2746 if ( sqlite3_exec( database.get(),
"ALTER TABLE tbl_srs ADD COLUMN srs_type text",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2748 createdTypeColumn =
true;
2749 if ( sqlite3_exec( database.get(),
"CREATE INDEX srs_type ON tbl_srs(srs_type)",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2756 if ( sqlite3_exec( database.get(),
"create table tbl_info (proj_major INT, proj_minor INT, proj_patch INT)",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2758 QString sql = u
"INSERT INTO tbl_info(proj_major, proj_minor, proj_patch) VALUES (%1, %2,%3)"_s
2759 .arg( QString::number( PROJ_VERSION_MAJOR ), QString::number( PROJ_VERSION_MINOR ), QString::number( PROJ_VERSION_PATCH ) );
2760 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2762 QgsDebugError( u
"Could not execute: %1 [%2/%3]\n"_s.arg( sql, database.
errorMessage(), errMsg ? errMsg :
"(unknown error)" ) );
2764 sqlite3_free( errMsg );
2771 QString sql = u
"SELECT proj_major, proj_minor, proj_patch FROM tbl_info"_s;
2772 statement = database.
prepare( sql, result );
2773 if ( result != SQLITE_OK )
2778 if ( statement.
step() == SQLITE_ROW )
2783 if ( !createdTypeColumn && major == PROJ_VERSION_MAJOR && minor == PROJ_VERSION_MINOR && patch == PROJ_VERSION_PATCH )
2789 QgsDebugError( u
"Could not retrieve previous CRS sync PROJ version number"_s );
2796 proj_log_func( pjContext,
nullptr, sync_db_proj_logger );
2798 PROJ_STRING_LIST authorities = proj_get_authorities_from_database( pjContext );
2800 int nextSrsId = 67218;
2801 int nextSrId = 520007218;
2802 for (
auto authIter = authorities; authIter && *authIter; ++authIter )
2804 const QString authority( *authIter );
2806 PROJ_STRING_LIST codes = proj_get_codes_from_database( pjContext, *authIter, PJ_TYPE_CRS,
true );
2808 QStringList allCodes;
2810 for (
auto codesIter = codes; codesIter && *codesIter; ++codesIter )
2812 const QString code( *codesIter );
2818 QgsDebugError( u
"Could not load '%1:%2'"_s.arg( authority, code ) );
2822 const PJ_TYPE pjType = proj_get_type( crs.get() );
2824 QString srsTypeString;
2829 case PJ_TYPE_ELLIPSOID:
2830 case PJ_TYPE_PRIME_MERIDIAN:
2831 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
2832 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
2833 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
2834 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
2835 case PJ_TYPE_DATUM_ENSEMBLE:
2836 case PJ_TYPE_CONVERSION:
2837 case PJ_TYPE_TRANSFORMATION:
2838 case PJ_TYPE_CONCATENATED_OPERATION:
2839 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
2840 case PJ_TYPE_TEMPORAL_DATUM:
2841 case PJ_TYPE_ENGINEERING_DATUM:
2842 case PJ_TYPE_PARAMETRIC_DATUM:
2843 case PJ_TYPE_UNKNOWN:
2847 case PJ_TYPE_GEOGRAPHIC_CRS:
2850 case PJ_TYPE_GEODETIC_CRS:
2854 case PJ_TYPE_GEOCENTRIC_CRS:
2858 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
2862 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
2866 case PJ_TYPE_PROJECTED_CRS:
2870 case PJ_TYPE_COMPOUND_CRS:
2874 case PJ_TYPE_TEMPORAL_CRS:
2878 case PJ_TYPE_ENGINEERING_CRS:
2882 case PJ_TYPE_BOUND_CRS:
2886 case PJ_TYPE_VERTICAL_CRS:
2890#if PROJ_VERSION_MAJOR > 9 || ( PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 2 )
2891 case PJ_TYPE_DERIVED_PROJECTED_CRS:
2894 case PJ_TYPE_COORDINATE_METADATA:
2897 case PJ_TYPE_OTHER_CRS:
2906 proj4.replace(
"+type=crs"_L1, QString() );
2907 proj4 = proj4.trimmed();
2909 if ( proj4.isEmpty() )
2922 if ( translatedOperation.isEmpty() && !
operation.isEmpty() )
2924 std::cout << u
"Operation needs translation in QgsCoordinateReferenceSystemUtils::translateProjection: %1"_s.arg(
operation ).toLocal8Bit().constData() << std::endl;
2925 qFatal(
"aborted" );
2928 const bool deprecated = proj_is_deprecated( crs.get() );
2929 const QString name( proj_get_name( crs.get() ) );
2931 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 );
2932 statement = database.
prepare( sql, result );
2933 if ( result != SQLITE_OK )
2942 QString dbOperation;
2943 bool dbSrsDeprecated = deprecated;
2944 if ( statement.
step() == SQLITE_ROW )
2948 dbSrsDeprecated = statement.
columnAsText( 2 ).toInt() != 0;
2953 if ( !dbSrsProj4.isEmpty() || !dbSrsDesc.isEmpty() )
2955 if ( proj4 != dbSrsProj4 || name != dbSrsDesc || deprecated != dbSrsDeprecated || dbSrsType != srsTypeString || dbOperation !=
operation )
2958 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 ) )
2960 .arg( deprecated ? 1 : 0 )
2963 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2965 QgsDebugError( u
"Could not execute: %1 [%2/%3]\n"_s.arg( sql, database.
errorMessage(), errMsg ? errMsg :
"(unknown error)" ) );
2967 sqlite3_free( errMsg );
2981 const QString dbVals = QgsCoordinateReferenceSystem::authIdToQgisSrsIdMap().value( u
"%1:%2"_s.arg( authority, code ) );
2984 if ( !dbVals.isEmpty() )
2986 const QStringList parts = dbVals.split(
',' );
2987 srsId = parts.at( 0 );
2988 srId = parts.at( 1 );
2990 if ( srId.isEmpty() )
2992 srId = QString::number( nextSrId );
2995 if ( srsId.isEmpty() )
2997 srsId = QString::number( nextSrsId );
3001 if ( !srsId.isEmpty() )
3003 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
3010 .arg( deprecated ? 1 : 0 )
3015 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
3021 .arg( deprecated ? 1 : 0 )
3026 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) == SQLITE_OK )
3032 qCritical(
"Could not execute: %s [%s/%s]\n", sql.toLocal8Bit().constData(), sqlite3_errmsg( database.get() ), errMsg ? errMsg :
"(unknown error)" );
3036 sqlite3_free( errMsg );
3041 proj_string_list_destroy( codes );
3043 const QString sql = u
"DELETE FROM tbl_srs WHERE auth_name='%1' AND NOT auth_id IN (%2)"_s.arg( authority, allCodes.join(
',' ) );
3044 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
3046 deleted = sqlite3_changes( database.get() );
3051 qCritical(
"Could not execute: %s [%s]\n", sql.toLocal8Bit().constData(), sqlite3_errmsg( database.get() ) );
3054 proj_string_list_destroy( authorities );
3056 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 ) );
3057 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
3059 QgsDebugError( u
"Could not execute: %1 [%2/%3]\n"_s.arg( sql, database.
errorMessage(), errMsg ? errMsg :
"(unknown error)" ) );
3061 sqlite3_free( errMsg );
3065 if ( sqlite3_exec( database.get(),
"COMMIT",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
3072 QgsDebugMsgLevel( u
"CRS update (inserted:%1 updated:%2 deleted:%3 errors:%4)"_s.arg( inserted ).arg( updated ).arg( deleted ).arg( errors ), 4 );
3080 return updated + inserted;
3083const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::stringCache()
3085 return *sStringCache();
3088const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::projCache()
3090 return *sProj4Cache();
3093const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::ogcCache()
3095 return *sOgcCache();
3098const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::wktCache()
3100 return *sWktCache();
3103const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srIdCache()
3105 return *sSrIdCache();
3108const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srsIdCache()
3110 return *sSrsIdCache();
3120 if (
PJ *obj = d->threadLocalProjObject() )
3127 const PJ_TYPE pjType = proj_get_type( geoCrs.get() );
3128 if ( pjType == PJ_TYPE_GEOCENTRIC_CRS )
3137 if ( !geoGraphicCrs )
3164 if (
PJ *obj = d->threadLocalProjObject() )
3218 if (
PJ *obj = d->threadLocalProjObject() )
3251 if (
PJ *obj = d->threadLocalProjObject() )
3262 if (
PJ *obj = d->threadLocalProjObject() )
3275 else if (
PJ *obj = d->threadLocalProjObject() )
3278 return geoCrs ? u
"%1:%2"_s.arg( proj_get_id_auth_name( geoCrs.get(), 0 ), proj_get_id_code( geoCrs.get(), 0 ) ) : QString();
3296 if ( !proj_crs_is_derived( ctx, pj ) )
3303 const int paramCount = proj_coordoperation_get_param_count( ctx, conversion.get() );
3304 bool hasLat =
false, hasLon =
false;
3306 for (
int i = 0; i < paramCount; i++ )
3308 const char *paramName =
nullptr, *authName =
nullptr, *code =
nullptr, *valueString =
nullptr, *unitName =
nullptr, *unitAuthName =
nullptr, *unitCode =
nullptr, *unitCategory =
nullptr;
3310 double unitConvFactor = 1.0;
3311 if ( !proj_coordoperation_get_param( ctx, conversion.get(), i, ¶mName, &authName, &code, &value, &valueString, &unitConvFactor, &unitName, &unitAuthName, &unitCode, &unitCategory ) )
3314 const int paramCode = QString( code ).toInt();
3316 if ( paramCode == EPSG_CODE_PARAMETER_LATITUDE_TOPOGRAPHIC_ORIGIN )
3318 latDeg = value * unitConvFactor * 180.0 / M_PI;
3321 else if ( paramCode == EPSG_CODE_PARAMETER_LONGITUDE_TOPOGRAPHIC_ORIGIN )
3323 lonDeg = value * unitConvFactor * 180.0 / M_PI;
3328 return hasLat && hasLon;
3344 double lat = 0.0, lon = 0.0;
3346 if ( isTopocentric )
3348 baseCrs.reset( proj_get_source_crs( ctx, pj ) );
3357 if ( proj_get_type( geodeticCrs.get() ) == PJ_TYPE_GEOGRAPHIC_2D_CRS )
3360 if ( geodetic3DCrs )
3361 geodeticCrs = std::move( geodetic3DCrs );
3364 if ( proj_get_type( geodeticCrs.get() ) == PJ_TYPE_GEOCENTRIC_CRS )
3371 geodeticCrs.reset( proj_create_geographic_crs_from_datum( ctx,
nullptr, datum ? datum.get() :
datumEnsemble.get(), cs.get() ) );
3376 const char *projWkt = proj_as_wkt( ctx, geodeticCrs.get(), PJ_WKT2_2019,
nullptr );
3380 QString baseWkt = QString::fromUtf8( projWkt );
3381 if ( baseWkt.startsWith(
"GEOGCRS["_L1 ) )
3382 baseWkt.replace( 0, 8,
"BASEGEOGCRS["_L1 );
3386 const QString wkt = u
"GEODCRS[\"Topocentric\","
3388 "DERIVINGCONVERSION[\"Geographic/topocentric\","
3389 " METHOD[\"Geographic/topocentric conversions\",ID[\"EPSG\",9837]],"
3390 " PARAMETER[\"Latitude of topocentric origin\",%2,"
3391 " ANGLEUNIT[\"degree\",0.0174532925199433],ID[\"EPSG\",8834]],"
3392 " PARAMETER[\"Longitude of topocentric origin\",%3,"
3393 " ANGLEUNIT[\"degree\",0.0174532925199433],ID[\"EPSG\",8835]],"
3394 " PARAMETER[\"Ellipsoidal height of topocentric origin\",0,"
3395 " LENGTHUNIT[\"metre\",1],ID[\"EPSG\",8836]]],"
3397 "AXIS[\"(X)\",east,ORDER[1],LENGTHUNIT[\"metre\",1]],"
3398 "AXIS[\"(Y)\",north,ORDER[2],LENGTHUNIT[\"metre\",1]],"
3399 "AXIS[\"(Z)\",up,ORDER[3],LENGTHUNIT[\"metre\",1]]]"_s.arg( baseWkt )
3408 if ( isTopocentric )
3409 crs.d->mTopocentricBaseCrs = std::make_unique<QgsCoordinateReferenceSystem>( *d->mTopocentricBaseCrs );
3411 crs.d->mTopocentricBaseCrs = std::make_unique<QgsCoordinateReferenceSystem>( *
this );
3423 return d->threadLocalProjObject();
3436 d->mIsValid =
false;
3438 d->mWktPreferred.clear();
3445 switch ( proj_get_type(
object ) )
3447 case PJ_TYPE_GEODETIC_CRS:
3448 case PJ_TYPE_GEOCENTRIC_CRS:
3449 case PJ_TYPE_GEOGRAPHIC_CRS:
3450 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
3451 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
3452 case PJ_TYPE_VERTICAL_CRS:
3453 case PJ_TYPE_PROJECTED_CRS:
3454 case PJ_TYPE_COMPOUND_CRS:
3455 case PJ_TYPE_TEMPORAL_CRS:
3456 case PJ_TYPE_ENGINEERING_CRS:
3457 case PJ_TYPE_BOUND_CRS:
3458 case PJ_TYPE_OTHER_CRS:
3474 const QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
3475 const QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
3476 if ( !authName.isEmpty() && !authCode.isEmpty() &&
createFromOgcWmsCrs( u
"%1:%2"_s.arg( authName, authCode ) ) )
3484 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
3487 setExtraProperties();
3496 QStringList projections;
3498 projections.reserve( res.size() );
3501 projections << QString::number( crs.srsid() );
3528 sSrIdCacheLock()->lockForWrite();
3529 if ( !sDisableSrIdCache )
3532 sDisableSrIdCache =
true;
3533 sSrIdCache()->clear();
3535 sSrIdCacheLock()->unlock();
3537 sOgcLock()->lockForWrite();
3538 if ( !sDisableOgcCache )
3541 sDisableOgcCache =
true;
3542 sOgcCache()->clear();
3544 sOgcLock()->unlock();
3546 sProj4CacheLock()->lockForWrite();
3547 if ( !sDisableProjCache )
3550 sDisableProjCache =
true;
3551 sProj4Cache()->clear();
3553 sProj4CacheLock()->unlock();
3555 sCRSWktLock()->lockForWrite();
3556 if ( !sDisableWktCache )
3559 sDisableWktCache =
true;
3560 sWktCache()->clear();
3562 sCRSWktLock()->unlock();
3564 sCRSSrsIdLock()->lockForWrite();
3565 if ( !sDisableSrsIdCache )
3568 sDisableSrsIdCache =
true;
3569 sSrsIdCache()->clear();
3571 sCRSSrsIdLock()->unlock();
3573 sCrsStringLock()->lockForWrite();
3574 if ( !sDisableStringCache )
3577 sDisableStringCache =
true;
3578 sStringCache()->clear();
3580 sCrsStringLock()->unlock();
3599 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3602 if ( !c1.d->mIsValid && c2.d->mIsValid )
3605 if ( c1.d->mIsValid && !c2.d->mIsValid )
3611 if ( c1IsUser && !c2IsUser )
3614 if ( !c1IsUser && c2IsUser )
3617 if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
3619 if ( c1.d->mAuthId != c2.d->mAuthId )
3620 return c1.d->mAuthId > c2.d->mAuthId;
3628 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3631 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3634 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3637 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3640 return c1.d->mCoordinateEpoch > c2.d->mCoordinateEpoch;
3648 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3651 if ( c1.d->mIsValid && !c2.d->mIsValid )
3654 if ( !c1.d->mIsValid && c2.d->mIsValid )
3660 if ( !c1IsUser && c2IsUser )
3663 if ( c1IsUser && !c2IsUser )
3666 if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
3668 if ( c1.d->mAuthId != c2.d->mAuthId )
3669 return c1.d->mAuthId < c2.d->mAuthId;
3677 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3680 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3683 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3686 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3689 return c1.d->mCoordinateEpoch < c2.d->mCoordinateEpoch;
3694 return !( c1 < c2 );
3698 return !( c1 > c2 );
3703 QMap<QString, QString> map;
3707 QFile file( jsonPath );
3708 if ( !file.open( QIODevice::ReadOnly ) )
3710 QgsDebugError( u
"Failed to open the coordinate reference legacy map JSON file: %1"_s.arg( jsonPath ) );
3714 const QByteArray jsonContent = file.readAll();
3717 const json mapJson = json::parse( jsonContent.toStdString() );
3719 if ( !mapJson.is_object() )
3721 QgsDebugError( u
"Failed to parse coordinate reference legacy map JSON, expected object."_s );
3725 for (
auto &item : mapJson.items() )
3727 if ( item.value().is_string() )
3729 QString key = QString::fromStdString( item.key() );
3730 QString value = QString::fromStdString( item.value().get<std::string>() );
3732 map.insert( key, value );
3737 catch ( nlohmann::json::exception &ex )
3739 QgsDebugError( u
"Failed to parse the coordinate reference legacy map JSON: %1"_s.arg( ex.what() ) );
3746const QMap<QString, QString> &QgsCoordinateReferenceSystem::authIdToQgisSrsIdMap()
3749 return sAuthIdToQgisSrsIdMap;
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 QString pkgDataPath()
Returns the common root path of all application data directories.
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 toTopocentricCrs(double latitude, double longitude) const
Constructs a topocentric CRS derived from this CRS with origin at latitude, longitude in decimal degr...
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)
QgsCoordinateReferenceSystem topocentricBaseCrs() const
Returns the base CRS for this topocentric CRS, or an invalid CRS if this is not a topocentric CRS or ...
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.
bool isGeographicCrossingAntimeridian() const
Returns true if the CRS is a geographic CRS (using lat/lon coordinates) where the area of use crosses...
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.
bool topocentricOrigin(double &latitude, double &longitude) const
Returns the topocentric origin of a topocentric compatible 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)
QMap< QString, QString > loadAuthIdToQgisSrsIdMapFromJson()
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 &)
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.