22#include <proj_constants.h>
23#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();
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();
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();
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();
729 if ( !wkt.isEmpty() )
737 setProjString( d->mProj4 );
747void QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread(
PJ_CONTEXT *pj_context )
754 if ( !sDisableSrIdCache )
757 if ( !sDisableSrIdCache )
759 for (
auto it = sSrIdCache()->begin(); it != sSrIdCache()->end(); )
761 auto &v = it.value();
762 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
763 it = sSrIdCache()->erase( it );
769 if ( !sDisableOgcCache )
772 if ( !sDisableOgcCache )
774 for (
auto it = sOgcCache()->begin(); it != sOgcCache()->end(); )
776 auto &v = it.value();
777 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
778 it = sOgcCache()->erase( it );
784 if ( !sDisableProjCache )
787 if ( !sDisableProjCache )
789 for (
auto it = sProj4Cache()->begin(); it != sProj4Cache()->end(); )
791 auto &v = it.value();
792 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
793 it = sProj4Cache()->erase( it );
799 if ( !sDisableWktCache )
802 if ( !sDisableWktCache )
804 for (
auto it = sWktCache()->begin(); it != sWktCache()->end(); )
806 auto &v = it.value();
807 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
808 it = sWktCache()->erase( it );
814 if ( !sDisableSrsIdCache )
817 if ( !sDisableSrsIdCache )
819 for (
auto it = sSrsIdCache()->begin(); it != sSrsIdCache()->end(); )
821 auto &v = it.value();
822 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
823 it = sSrsIdCache()->erase( it );
829 if ( !sDisableStringCache )
832 if ( !sDisableStringCache )
834 for (
auto it = sStringCache()->begin(); it != sStringCache()->end(); )
836 auto &v = it.value();
837 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
838 it = sStringCache()->erase( it );
848 if ( d->mAxisInvertedDirty )
851 d->mAxisInvertedDirty =
false;
854 return d->mAxisInverted;
871 const thread_local QMap< Qgis::CrsAxisDirection, QString > mapping = {
913 QList< Qgis::CrsAxisDirection > res;
914 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
917 res.reserve( axisCount );
919 for (
int i = 0; i < axisCount; ++i )
921 const char *outDirection =
nullptr;
922 proj_cs_get_axis_info( context, pjCs.get(), i,
nullptr,
nullptr, &outDirection,
nullptr,
nullptr,
nullptr,
nullptr );
924 const thread_local QRegularExpression rx( u
"([^\\s]+).*"_s );
925 const QRegularExpressionMatch match = rx.match( QString( outDirection ) );
926 if ( !match.hasMatch() )
929 const QString direction = match.captured( 1 );
931 for (
auto it = mapping.constBegin(); it != mapping.constEnd(); ++it )
933 if ( it.value().compare( direction, Qt::CaseInsensitive ) == 0 )
948 return createFromWktInternal( wkt, QString() );
951bool QgsCoordinateReferenceSystem::createFromWktInternal(
const QString &wkt,
const QString &description )
959 if ( !sDisableWktCache )
961 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sWktCache()->constFind( wkt );
962 if ( crsIt != sWktCache()->constEnd() )
965 *
this = crsIt.value();
967 if ( !
description.isEmpty() && d->mDescription.isEmpty() )
972 sWktCache()->insert( wkt, *
this );
981 d->mWktPreferred.clear();
989 QgsCoordinateReferenceSystem::RecordMap record = getRecord(
"select * from tbl_srs where wkt=" +
QgsSqliteUtils::quotedString( wkt ) +
" order by deprecated" );
990 if ( !record.empty() )
992 long srsId = record[u
"srs_id"_s].toLong();
1000 setWktString( wkt );
1005 if ( d->mSrsId == 0 )
1008 long id = matchToUserCrs();
1017 if ( !sDisableWktCache )
1018 sWktCache()->insert( wkt, *
this );
1036 if ( projString.isEmpty() )
1041 if ( projString.trimmed().isEmpty() )
1043 d->mIsValid =
false;
1045 d->mWktPreferred.clear();
1050 if ( !sDisableProjCache )
1052 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sProj4Cache()->constFind( projString );
1053 if ( crsIt != sProj4Cache()->constEnd() )
1056 *
this = crsIt.value();
1070 QString myProj4String = projString.trimmed();
1071 myProj4String.remove( u
"+type=crs"_s );
1072 myProj4String = myProj4String.trimmed();
1074 d->mIsValid =
false;
1075 d->mWktPreferred.clear();
1080 const QString projCrsString = myProj4String + ( myProj4String.contains( u
"+type=crs"_s ) ? QString() : u
" +type=crs"_s );
1088 const QString
authid = u
"%1:%2"_s.arg( authName, authCode );
1092 if ( !sDisableProjCache )
1093 sProj4Cache()->insert( projString, *
this );
1100 QgsCoordinateReferenceSystem::RecordMap myRecord = getRecord(
"select * from tbl_srs where parameters=" +
QgsSqliteUtils::quotedString( myProj4String ) +
" order by deprecated" );
1102 if ( !myRecord.empty() )
1104 id = myRecord[u
"srs_id"_s].toLong();
1113 setProjString( myProj4String );
1116 id = matchToUserCrs();
1125 setProjString( myProj4String );
1129 if ( !sDisableProjCache )
1130 sProj4Cache()->insert( projString, *
this );
1136QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord(
const QString &sql )
1138 QString myDatabaseFileName;
1139 QgsCoordinateReferenceSystem::RecordMap myMap;
1140 QString myFieldName;
1141 QString myFieldValue;
1148 QFileInfo myInfo( myDatabaseFileName );
1149 if ( !myInfo.exists() )
1151 QgsDebugError(
"failed : " + myDatabaseFileName +
" does not exist!" );
1156 myResult = openDatabase( myDatabaseFileName, database );
1157 if ( myResult != SQLITE_OK )
1162 statement = database.
prepare( sql, myResult );
1164 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1168 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1170 myFieldName = statement.
columnName( myColNo );
1172 myMap[myFieldName] = myFieldValue;
1174 if ( statement.
step() != SQLITE_DONE )
1185 if ( myMap.empty() )
1188 QFileInfo myFileInfo;
1189 myFileInfo.setFile( myDatabaseFileName );
1190 if ( !myFileInfo.exists() )
1197 myResult = openDatabase( myDatabaseFileName, database );
1198 if ( myResult != SQLITE_OK )
1203 statement = database.
prepare( sql, myResult );
1205 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1209 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1211 myFieldName = statement.
columnName( myColNo );
1213 myMap[myFieldName] = myFieldValue;
1216 if ( statement.
step() != SQLITE_DONE )
1249 if ( d->mDescription.isNull() )
1255 return d->mDescription;
1262 if ( !
authid().isEmpty() )
1272 id =
isValid() ? QObject::tr(
"Custom CRS" ) : QObject::tr(
"Unknown CRS" );
1274 id = QObject::tr(
"Custom CRS: %1" )
1276 else if ( !
toProj().isEmpty() )
1278 if ( !
id.isEmpty() && !std::isnan( d->mCoordinateEpoch ) )
1286 if ( d->mProjectionAcronym.isNull() )
1292 return d->mProjectionAcronym;
1298 if ( d->mEllipsoidAcronym.isNull() )
1300 if (
PJ *obj = d->threadLocalProjObject() )
1305 const QString ellipsoidAuthName( proj_get_id_auth_name( ellipsoid.get(), 0 ) );
1306 const QString ellipsoidAuthCode( proj_get_id_code( ellipsoid.get(), 0 ) );
1307 if ( !ellipsoidAuthName.isEmpty() && !ellipsoidAuthCode.isEmpty() )
1308 d->mEllipsoidAcronym = u
"%1:%2"_s.arg( ellipsoidAuthName, ellipsoidAuthCode );
1311 double semiMajor, semiMinor, invFlattening;
1312 int semiMinorComputed = 0;
1313 if ( proj_ellipsoid_get_parameters(
QgsProjContext::get(), ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
1319 d->mEllipsoidAcronym.clear();
1324 return d->mEllipsoidAcronym;
1328 return d->mEllipsoidAcronym;
1342 if ( d->mProj4.isEmpty() )
1344 if (
PJ *obj = d->threadLocalProjObject() )
1350 return d->mProj4.trimmed();
1358 if (
PJ *obj = d->threadLocalProjObject() )
1360 const QByteArray multiLineOption = u
"MULTILINE=%1"_s.arg( multiline ? u
"YES"_s : u
"NO"_s ).toLocal8Bit();
1361 const QByteArray indentatationWidthOption = u
"INDENTATION_WIDTH=%1"_s.arg( multiline ? QString::number( indentationWidth ) : u
"0"_s ).toLocal8Bit();
1362 const QByteArray schemaOption = u
"SCHEMA=%1"_s.arg( schema ).toLocal8Bit();
1363 const char *
const options[] = { multiLineOption.constData(), indentatationWidthOption.constData(), schemaOption.constData(),
nullptr };
1366 return json ? std::string { json } : std::string {};
1377 switch ( d->mProjType )
1379 case PJ_TYPE_UNKNOWN:
1382 case PJ_TYPE_ELLIPSOID:
1383 case PJ_TYPE_PRIME_MERIDIAN:
1384 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
1385 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
1386 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
1387 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
1388 case PJ_TYPE_DATUM_ENSEMBLE:
1389 case PJ_TYPE_CONVERSION:
1390 case PJ_TYPE_TRANSFORMATION:
1391 case PJ_TYPE_CONCATENATED_OPERATION:
1392 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
1393 case PJ_TYPE_TEMPORAL_DATUM:
1394 case PJ_TYPE_ENGINEERING_DATUM:
1395 case PJ_TYPE_PARAMETRIC_DATUM:
1399 case PJ_TYPE_GEOGRAPHIC_CRS:
1403 case PJ_TYPE_GEODETIC_CRS:
1405 case PJ_TYPE_GEOCENTRIC_CRS:
1407 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
1409 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
1411 case PJ_TYPE_VERTICAL_CRS:
1413 case PJ_TYPE_PROJECTED_CRS:
1415 case PJ_TYPE_COMPOUND_CRS:
1417 case PJ_TYPE_TEMPORAL_CRS:
1419 case PJ_TYPE_ENGINEERING_CRS:
1421 case PJ_TYPE_BOUND_CRS:
1423 case PJ_TYPE_OTHER_CRS:
1425#if PROJ_VERSION_MAJOR > 9 || ( PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 2 )
1426 case PJ_TYPE_DERIVED_PROJECTED_CRS:
1428 case PJ_TYPE_COORDINATE_METADATA:
1442 return proj_is_deprecated( pj );
1447 return d->mIsGeographic;
1467 return QString( proj_get_celestial_body_name( context, pj ) );
1472 if ( d->mCoordinateEpoch == epoch )
1478 d->mCoordinateEpoch = epoch;
1479 d->setPj( std::move( clone ) );
1484 return d->mCoordinateEpoch;
1503 res.mName = QString( proj_get_name( ensemble.get() ) );
1504 res.mAuthority = QString( proj_get_id_auth_name( ensemble.get(), 0 ) );
1505 res.mCode = QString( proj_get_id_code( ensemble.get(), 0 ) );
1506 res.mRemarks = QString( proj_get_remarks( ensemble.get() ) );
1507 res.mScope = QString( proj_get_scope( ensemble.get() ) );
1508 res.mAccuracy = proj_datum_ensemble_get_accuracy( context, ensemble.get() );
1510 const int memberCount = proj_datum_ensemble_get_member_count( context, ensemble.get() );
1511 for (
int i = 0; i < memberCount; ++i )
1518 details.mName = QString( proj_get_name( member.get() ) );
1519 details.mAuthority = QString( proj_get_id_auth_name( member.get(), 0 ) );
1520 details.mCode = QString( proj_get_id_code( member.get(), 0 ) );
1521 details.mRemarks = QString( proj_get_remarks( member.get() ) );
1522 details.mScope = QString( proj_get_scope( member.get() ) );
1524 res.mMembers << details;
1534 QString projString =
toProj();
1535 projString.replace(
"+type=crs"_L1, QString() );
1538 if ( !transformation )
1541 PJ_COORD coord = proj_coord( 0, 0, 0, HUGE_VAL );
1542 coord.uv.u = point.
x() * M_PI / 180.0;
1543 coord.uv.v = point.
y() * M_PI / 180.0;
1545 proj_errno_reset( transformation.get() );
1546 const PJ_FACTORS pjFactors = proj_factors( transformation.get(), coord );
1547 if ( proj_errno( transformation.get() ) )
1552 res.mIsValid =
true;
1553 res.mMeridionalScale = pjFactors.meridional_scale;
1554 res.mParallelScale = pjFactors.parallel_scale;
1555 res.mArealScale = pjFactors.areal_scale;
1556 res.mAngularDistortion = pjFactors.angular_distortion;
1557 res.mMeridianParallelAngle = pjFactors.meridian_parallel_angle * 180 / M_PI;
1558 res.mMeridianConvergence = pjFactors.meridian_convergence * 180 / M_PI;
1559 res.mTissotSemimajor = pjFactors.tissot_semimajor;
1560 res.mTissotSemiminor = pjFactors.tissot_semiminor;
1561 res.mDxDlam = pjFactors.dx_dlam;
1562 res.mDxDphi = pjFactors.dx_dphi;
1563 res.mDyDlam = pjFactors.dy_dlam;
1564 res.mDyDphi = pjFactors.dy_dphi;
1576 QString projString =
toProj();
1577 projString.replace(
"+type=crs"_L1, QString() );
1578 if ( projString.isEmpty() )
1582 if ( !transformation )
1585 PJ_PROJ_INFO info = proj_pj_info( transformation.get() );
1600 return d->mMapUnits;
1608 PJ *obj = d->threadLocalProjObject();
1613 double southLat = 0;
1615 double northLat = 0;
1617 if ( !proj_get_area_of_use(
QgsProjContext::get(), obj, &westLon, &southLat, &eastLon, &northLat,
nullptr ) )
1632 const auto parts {
authid().split(
':' ) };
1633 if ( parts.length() == 2 )
1635 if ( parts[0] ==
"EPSG"_L1 )
1636 return u
"http://www.opengis.net/def/crs/EPSG/0/%1"_s.arg( parts[1] );
1637 else if ( parts[0] ==
"OGC"_L1 )
1639 return u
"http://www.opengis.net/def/crs/OGC/1.3/%1"_s.arg( parts[1] );
1655 const auto parts {
authid().split(
':' ) };
1656 if ( parts.length() == 2 )
1658 if ( parts[0] ==
"EPSG"_L1 )
1659 return u
"urn:ogc:def:crs:EPSG::%1"_s.arg( parts[1] );
1660 else if ( parts[0] ==
"OGC"_L1 )
1662 return u
"urn:ogc:def:crs:OGC:1.3:%1"_s.arg( parts[1] );
1664 else if ( parts[0].startsWith(
"IAU"_L1 ) )
1666 if ( parts[0].contains(
"_"_L1 ) )
1668 const auto subParts = parts[0].split(
'_' );
1669 if ( subParts.length() == 2 )
1671 return u
"urn:ogc:def:crs:%1:%2:%3"_s.arg( subParts[0], subParts[1], parts[1] );
1676 return u
"urn:ogc:def:crs:%1::%2"_s.arg( parts[0], parts[1] );
1708void QgsCoordinateReferenceSystem::setProjString(
const QString &proj4String )
1711 d->mProj4 = proj4String;
1712 d->mWktPreferred.clear();
1715 QString trimmed = proj4String.trimmed();
1717 trimmed +=
" +type=crs"_L1;
1727 const int errNo = proj_context_errno( ctx );
1728 QgsDebugError( u
"proj string rejected: %1"_s.arg( proj_context_errno_string( ctx, errNo ) ) );
1730 d->mIsValid =
false;
1734 d->mEllipsoidAcronym.clear();
1741bool QgsCoordinateReferenceSystem::setWktString(
const QString &wkt )
1744 d->mIsValid =
false;
1745 d->mWktPreferred.clear();
1747 PROJ_STRING_LIST warnings =
nullptr;
1748 PROJ_STRING_LIST grammarErrors =
nullptr;
1756 QgsDebugMsgLevel( u
"\n---------------------------------------------------------------"_s, 2 );
1757 QgsDebugMsgLevel( u
"This CRS could *** NOT *** be set from the supplied Wkt "_s, 2 );
1759 for (
auto iter = warnings; iter && *iter; ++iter )
1763 for (
auto iter = grammarErrors; iter && *iter; ++iter )
1767 QgsDebugMsgLevel( u
"---------------------------------------------------------------\n"_s, 2 );
1769 proj_string_list_destroy( warnings );
1770 proj_string_list_destroy( grammarErrors );
1776 if ( !sDisableWktCache )
1777 sWktCache()->insert( wkt, *
this );
1785 QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
1786 QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
1788 if ( authName.isEmpty() || authCode.isEmpty() )
1794 if ( !authName.isEmpty() && !authCode.isEmpty() )
1797 if ( fromAuthCode.loadFromAuthCode( authName, authCode ) )
1799 *
this = fromAuthCode;
1801 if ( !sDisableWktCache )
1802 sWktCache()->insert( wkt, *
this );
1809 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
1816void QgsCoordinateReferenceSystem::setMapUnits()
1843 if ( !coordinateSystem )
1849 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
1850 if ( axisCount > 0 )
1852 const char *outUnitName =
nullptr;
1854 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
nullptr,
nullptr,
nullptr,
nullptr, &outUnitName,
nullptr,
nullptr );
1856 const QString unitName( outUnitName );
1860 if ( unitName.compare(
"degree"_L1, Qt::CaseInsensitive ) == 0
1861 || unitName.compare(
"degree minute second"_L1, Qt::CaseInsensitive ) == 0
1862 || unitName.compare(
"degree minute second hemisphere"_L1, Qt::CaseInsensitive ) == 0
1863 || unitName.compare(
"degree minute"_L1, Qt::CaseInsensitive ) == 0
1864 || unitName.compare(
"degree hemisphere"_L1, Qt::CaseInsensitive ) == 0
1865 || unitName.compare(
"degree minute hemisphere"_L1, Qt::CaseInsensitive ) == 0
1866 || unitName.compare(
"hemisphere degree"_L1, Qt::CaseInsensitive ) == 0
1867 || unitName.compare(
"hemisphere degree minute"_L1, Qt::CaseInsensitive ) == 0
1868 || unitName.compare(
"hemisphere degree minute second"_L1, Qt::CaseInsensitive ) == 0
1869 || unitName.compare(
"degree (supplier to define representation)"_L1, Qt::CaseInsensitive ) == 0 )
1871 else if ( unitName.compare(
"metre"_L1, Qt::CaseInsensitive ) == 0 || unitName.compare(
'm'_L1, Qt::CaseInsensitive ) == 0 || unitName.compare(
"meter"_L1, Qt::CaseInsensitive ) == 0 )
1873 else if ( unitName.compare(
"US survey foot"_L1, Qt::CaseInsensitive ) == 0 )
1875 else if ( unitName.compare(
"foot"_L1, Qt::CaseInsensitive ) == 0 )
1877 else if ( unitName.compare(
"British yard (Sears 1922)"_L1, Qt::CaseInsensitive ) == 0 )
1879 else if ( unitName.compare(
"British yard (Sears 1922 truncated)"_L1, Qt::CaseInsensitive ) == 0 )
1881 else if ( unitName.compare(
"British foot (Sears 1922)"_L1, Qt::CaseInsensitive ) == 0 )
1883 else if ( unitName.compare(
"British foot (Sears 1922 truncated)"_L1, Qt::CaseInsensitive ) == 0 )
1885 else if ( unitName.compare(
"British chain (Sears 1922)"_L1, Qt::CaseInsensitive ) == 0 )
1887 else if ( unitName.compare(
"British chain (Sears 1922 truncated)"_L1, Qt::CaseInsensitive ) == 0 )
1889 else if ( unitName.compare(
"British link (Sears 1922)"_L1, Qt::CaseInsensitive ) == 0 )
1891 else if ( unitName.compare(
"British link (Sears 1922 truncated)"_L1, Qt::CaseInsensitive ) == 0 )
1893 else if ( unitName.compare(
"British yard (Benoit 1895 A)"_L1, Qt::CaseInsensitive ) == 0 )
1895 else if ( unitName.compare(
"British foot (Benoit 1895 A)"_L1, Qt::CaseInsensitive ) == 0 )
1897 else if ( unitName.compare(
"British chain (Benoit 1895 A)"_L1, Qt::CaseInsensitive ) == 0 )
1899 else if ( unitName.compare(
"British link (Benoit 1895 A)"_L1, Qt::CaseInsensitive ) == 0 )
1901 else if ( unitName.compare(
"British yard (Benoit 1895 B)"_L1, Qt::CaseInsensitive ) == 0 )
1903 else if ( unitName.compare(
"British foot (Benoit 1895 B)"_L1, Qt::CaseInsensitive ) == 0 )
1905 else if ( unitName.compare(
"British chain (Benoit 1895 B)"_L1, Qt::CaseInsensitive ) == 0 )
1907 else if ( unitName.compare(
"British link (Benoit 1895 B)"_L1, Qt::CaseInsensitive ) == 0 )
1909 else if ( unitName.compare(
"British foot (1865)"_L1, Qt::CaseInsensitive ) == 0 )
1911 else if ( unitName.compare(
"British foot (1936)"_L1, Qt::CaseInsensitive ) == 0 )
1913 else if ( unitName.compare(
"Indian foot"_L1, Qt::CaseInsensitive ) == 0 )
1915 else if ( unitName.compare(
"Indian foot (1937)"_L1, Qt::CaseInsensitive ) == 0 )
1917 else if ( unitName.compare(
"Indian foot (1962)"_L1, Qt::CaseInsensitive ) == 0 )
1919 else if ( unitName.compare(
"Indian foot (1975)"_L1, Qt::CaseInsensitive ) == 0 )
1921 else if ( unitName.compare(
"Indian yard"_L1, Qt::CaseInsensitive ) == 0 )
1923 else if ( unitName.compare(
"Indian yard (1937)"_L1, Qt::CaseInsensitive ) == 0 )
1925 else if ( unitName.compare(
"Indian yard (1962)"_L1, Qt::CaseInsensitive ) == 0 )
1927 else if ( unitName.compare(
"Indian yard (1975)"_L1, Qt::CaseInsensitive ) == 0 )
1929 else if ( unitName.compare(
"Gold Coast foot"_L1, Qt::CaseInsensitive ) == 0 )
1931 else if ( unitName.compare(
"Clarke's foot"_L1, Qt::CaseInsensitive ) == 0 )
1933 else if ( unitName.compare(
"Clarke's yard"_L1, Qt::CaseInsensitive ) == 0 )
1935 else if ( unitName.compare(
"Clarke's chain"_L1, Qt::CaseInsensitive ) == 0 )
1937 else if ( unitName.compare(
"Clarke's link"_L1, Qt::CaseInsensitive ) == 0 )
1939 else if ( unitName.compare(
"kilometre"_L1, Qt::CaseInsensitive ) == 0 )
1941 else if ( unitName.compare(
"centimetre"_L1, Qt::CaseInsensitive ) == 0 )
1943 else if ( unitName.compare(
"millimetre"_L1, Qt::CaseInsensitive ) == 0 )
1945 else if ( unitName.compare(
"Statute mile"_L1, Qt::CaseInsensitive ) == 0 )
1947 else if ( unitName.compare(
"nautical mile"_L1, Qt::CaseInsensitive ) == 0 )
1949 else if ( unitName.compare(
"yard"_L1, Qt::CaseInsensitive ) == 0 )
1951 else if ( unitName.compare(
"fathom"_L1, Qt::CaseInsensitive ) == 0 )
1953 else if ( unitName.compare(
"US survey chain"_L1, Qt::CaseInsensitive ) == 0 )
1955 else if ( unitName.compare(
"chain"_L1, Qt::CaseInsensitive ) == 0 )
1957 else if ( unitName.compare(
"link"_L1, Qt::CaseInsensitive ) == 0 )
1959 else if ( unitName.compare(
"US survey link"_L1, Qt::CaseInsensitive ) == 0 )
1961 else if ( unitName.compare(
"US survey mile"_L1, Qt::CaseInsensitive ) == 0 )
1963 else if ( unitName.compare(
"German legal metre"_L1, Qt::CaseInsensitive ) == 0 )
1980 if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull() || !d->mIsValid )
1983 "QgsCoordinateReferenceSystem::findMatchingProj will only "
1984 "work if prj acr ellipsoid acr and proj4string are set"
1985 " and the current projection is valid!",
1997 QString mySql = QString(
1998 "select srs_id,parameters from tbl_srs where "
1999 "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated"
2006 myResult = openDatabase( myDatabaseFileName, database );
2007 if ( myResult != SQLITE_OK )
2012 statement = database.
prepare( mySql, myResult );
2013 if ( myResult == SQLITE_OK )
2015 while ( statement.
step() == SQLITE_ROW )
2019 if (
toProj() == myProj4String.trimmed() )
2021 return mySrsId.toLong();
2032 myResult = openDatabase( myDatabaseFileName, database );
2033 if ( myResult != SQLITE_OK )
2038 statement = database.
prepare( mySql, myResult );
2040 if ( myResult == SQLITE_OK )
2042 while ( statement.
step() == SQLITE_ROW )
2046 if (
toProj() == myProj4String.trimmed() )
2048 return mySrsId.toLong();
2062 if ( !d->mIsValid && !srs.d->mIsValid )
2065 if ( !d->mIsValid || !srs.d->mIsValid )
2073 if ( isUser != otherIsUser )
2077 if ( !isUser && ( !d->mAuthId.isEmpty() || !srs.d->mAuthId.isEmpty() ) )
2078 return d->mAuthId == srs.d->mAuthId;
2085 return !( *
this == srs );
2090 if (
PJ *obj = d->threadLocalProjObject() )
2093 if ( isDefaultPreferredFormat && !d->mWktPreferred.isEmpty() )
2096 return d->mWktPreferred;
2099 PJ_WKT_TYPE
type = PJ_WKT1_GDAL;
2103 type = PJ_WKT1_GDAL;
2106 type = PJ_WKT1_ESRI;
2109 type = PJ_WKT2_2015;
2112 type = PJ_WKT2_2015_SIMPLIFIED;
2115 type = PJ_WKT2_2019;
2118 type = PJ_WKT2_2019_SIMPLIFIED;
2122 const QByteArray multiLineOption = u
"MULTILINE=%1"_s.arg( multiline ? u
"YES"_s : u
"NO"_s ).toLocal8Bit();
2123 const QByteArray indentatationWidthOption = u
"INDENTATION_WIDTH=%1"_s.arg( multiline ? QString::number( indentationWidth ) : u
"0"_s ).toLocal8Bit();
2124 const char *
const options[] = { multiLineOption.constData(), indentatationWidthOption.constData(),
nullptr };
2127 if ( isDefaultPreferredFormat )
2130 d->mWktPreferred = res;
2142 QDomNode srsNode = node.namedItem( u
"spatialrefsys"_s );
2144 if ( !srsNode.isNull() )
2146 bool initialized =
false;
2149 long srsid = srsNode.namedItem( u
"srsid"_s ).toElement().text().toLong( &ok );
2155 node = srsNode.namedItem( u
"authid"_s );
2156 if ( !node.isNull() )
2167 node = srsNode.namedItem( u
"epsg"_s );
2168 if ( !node.isNull() )
2186 const QString
description = srsNode.namedItem( u
"description"_s ).toElement().text();
2188 const QString wkt = srsNode.namedItem( u
"wkt"_s ).toElement().text();
2189 initialized = createFromWktInternal( wkt,
description );
2194 node = srsNode.namedItem( u
"proj4"_s );
2195 const QString proj4 = node.toElement().text();
2202 node = srsNode.namedItem( u
"proj4"_s );
2203 const QString proj4 = node.toElement().text();
2204 if ( !proj4.trimmed().isEmpty() )
2205 setProjString( node.toElement().text() );
2207 node = srsNode.namedItem( u
"srsid"_s );
2208 d->mSrsId = node.toElement().text().toLong();
2210 node = srsNode.namedItem( u
"srid"_s );
2211 d->mSRID = node.toElement().text().toLong();
2213 node = srsNode.namedItem( u
"authid"_s );
2214 d->mAuthId = node.toElement().text();
2216 node = srsNode.namedItem( u
"description"_s );
2217 d->mDescription = node.toElement().text();
2219 node = srsNode.namedItem( u
"projectionacronym"_s );
2220 d->mProjectionAcronym = node.toElement().text();
2222 node = srsNode.namedItem( u
"ellipsoidacronym"_s );
2223 d->mEllipsoidAcronym = node.toElement().text();
2225 node = srsNode.namedItem( u
"geographicflag"_s );
2226 d->mIsGeographic = node.toElement().text() ==
"true"_L1;
2228 d->mWktPreferred.clear();
2234 const QString epoch = srsNode.toElement().attribute( u
"coordinateEpoch"_s );
2235 if ( !epoch.isEmpty() )
2237 bool epochOk =
false;
2238 d->mCoordinateEpoch = epoch.toDouble( &epochOk );
2240 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2244 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2249 const QDomNode topoBaseCrsNode = srsNode.namedItem( u
"topocentricBaseCrs"_s );
2250 if ( !topoBaseCrsNode.isNull() )
2252 d->mTopocentricBaseCrs = std::make_unique<QgsCoordinateReferenceSystem>();
2253 d->mTopocentricBaseCrs->readXml( topoBaseCrsNode );
2259 d =
new QgsCoordinateReferenceSystemPrivate();
2267 QDomElement layerNode = node.toElement();
2268 QDomElement srsElement = doc.createElement( u
"spatialrefsys"_s );
2272 if ( std::isfinite( d->mCoordinateEpoch ) )
2274 srsElement.setAttribute( u
"coordinateEpoch"_s, d->mCoordinateEpoch );
2277 QDomElement wktElement = doc.createElement( u
"wkt"_s );
2279 srsElement.appendChild( wktElement );
2281 QDomElement proj4Element = doc.createElement( u
"proj4"_s );
2282 proj4Element.appendChild( doc.createTextNode(
toProj() ) );
2283 srsElement.appendChild( proj4Element );
2285 QDomElement srsIdElement = doc.createElement( u
"srsid"_s );
2286 srsIdElement.appendChild( doc.createTextNode( QString::number(
srsid() ) ) );
2287 srsElement.appendChild( srsIdElement );
2289 QDomElement sridElement = doc.createElement( u
"srid"_s );
2290 sridElement.appendChild( doc.createTextNode( QString::number(
postgisSrid() ) ) );
2291 srsElement.appendChild( sridElement );
2293 QDomElement authidElement = doc.createElement( u
"authid"_s );
2294 authidElement.appendChild( doc.createTextNode(
authid() ) );
2295 srsElement.appendChild( authidElement );
2297 QDomElement descriptionElement = doc.createElement( u
"description"_s );
2298 descriptionElement.appendChild( doc.createTextNode(
description() ) );
2299 srsElement.appendChild( descriptionElement );
2301 QDomElement projectionAcronymElement = doc.createElement( u
"projectionacronym"_s );
2302 projectionAcronymElement.appendChild( doc.createTextNode(
projectionAcronym() ) );
2303 srsElement.appendChild( projectionAcronymElement );
2305 QDomElement ellipsoidAcronymElement = doc.createElement( u
"ellipsoidacronym"_s );
2306 ellipsoidAcronymElement.appendChild( doc.createTextNode(
ellipsoidAcronym() ) );
2307 srsElement.appendChild( ellipsoidAcronymElement );
2309 QDomElement geographicFlagElement = doc.createElement( u
"geographicflag"_s );
2310 QString geoFlagText = u
"false"_s;
2313 geoFlagText = u
"true"_s;
2316 geographicFlagElement.appendChild( doc.createTextNode( geoFlagText ) );
2317 srsElement.appendChild( geographicFlagElement );
2319 if ( d->mTopocentricBaseCrs && d->mTopocentricBaseCrs->isValid() )
2321 QDomElement topoBaseCrsElement = doc.createElement( u
"topocentricBaseCrs"_s );
2322 d->mTopocentricBaseCrs->writeXml( topoBaseCrsElement, doc );
2323 srsElement.appendChild( topoBaseCrsElement );
2326 layerNode.appendChild( srsElement );
2338QString QgsCoordinateReferenceSystem::projFromSrsId(
const int srsId )
2340 QString myDatabaseFileName;
2341 QString myProjString;
2342 QString mySql = u
"select parameters from tbl_srs where srs_id = %1 order by deprecated"_s.arg( srsId );
2351 QFileInfo myFileInfo;
2352 myFileInfo.setFile( myDatabaseFileName );
2353 if ( !myFileInfo.exists() )
2364 sqlite3_database_unique_ptr database;
2365 sqlite3_statement_unique_ptr statement;
2368 rc = openDatabase( myDatabaseFileName, database );
2374 statement = database.
prepare( mySql, rc );
2376 if ( rc == SQLITE_OK )
2378 if ( statement.
step() == SQLITE_ROW )
2384 return myProjString;
2391 myResult = database.
open_v2( path, SQLITE_OPEN_READONLY,
nullptr );
2393 myResult = database.
open( path );
2395 if ( myResult != SQLITE_OK )
2408 sCustomSrsValidation = f;
2413 return sCustomSrsValidation;
2416void QgsCoordinateReferenceSystem::debugPrint()
2419 QgsDebugMsgLevel(
"* Valid : " + ( d->mIsValid ? QString(
"true" ) : QString(
"false" ) ), 1 );
2440 mValidationHint = html;
2445 return mValidationHint;
2455 mNativeFormat = format;
2460 return mNativeFormat;
2463long QgsCoordinateReferenceSystem::getRecordCount()
2468 long myRecordCount = 0;
2471 if ( myResult != SQLITE_OK )
2477 QString mySql = u
"select count(*) from tbl_srs"_s;
2478 statement = database.
prepare( mySql, myResult );
2479 if ( myResult == SQLITE_OK )
2481 if ( statement.
step() == SQLITE_ROW )
2483 QString myRecordCountString = statement.
columnAsText( 0 );
2484 myRecordCount = myRecordCountString.toLong();
2487 return myRecordCount;
2493 bool isGeographic =
false;
2497 if ( !horizontalCrs )
2501 if ( coordinateSystem )
2503 const int axisCount = proj_cs_get_axis_count( pjContext, coordinateSystem.get() );
2504 if ( axisCount > 0 )
2506 const char *outUnitAuthName =
nullptr;
2507 const char *outUnitAuthCode =
nullptr;
2509 proj_cs_get_axis_info( pjContext, coordinateSystem.get(), 0,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr, &outUnitAuthName, &outUnitAuthCode );
2511 if ( outUnitAuthName && outUnitAuthCode )
2513 const char *unitCategory =
nullptr;
2514 if ( proj_uom_get_info_from_database( pjContext, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
2516 isGeographic = QString( unitCategory ).compare(
"angular"_L1, Qt::CaseInsensitive ) == 0;
2521 return isGeographic;
2526 thread_local const QRegularExpression projRegExp( u
"\\+proj=(\\S+)"_s );
2527 const QRegularExpressionMatch projMatch = projRegExp.match( proj );
2528 if ( !projMatch.hasMatch() )
2533 operation = projMatch.captured( 1 );
2535 const QRegularExpressionMatch ellipseMatch = projRegExp.match( proj );
2536 if ( ellipseMatch.hasMatch() )
2538 ellipsoid = ellipseMatch.captured( 1 );
2552bool QgsCoordinateReferenceSystem::loadFromAuthCode(
const QString &auth,
const QString &code )
2558 d->mIsValid =
false;
2559 d->mWktPreferred.clear();
2562 QgsProjUtils::proj_pj_unique_ptr crs( proj_create_from_database( pjContext, auth.toUtf8().constData(), code.toUtf8().constData(), PJ_CATEGORY_CRS,
false,
nullptr ) );
2571 proj4.replace(
"+type=crs"_L1, QString() );
2572 proj4 = proj4.trimmed();
2576 d->mWktPreferred.clear();
2577 d->mDescription = QString( proj_get_name( crs.get() ) );
2578 d->mAuthId = u
"%1:%2"_s.arg( auth, code );
2580 d->mAxisInvertedDirty =
true;
2585 d->mEllipsoidAcronym.clear();
2586 d->setPj( std::move( crs ) );
2589 if ( !dbVals.isEmpty() )
2591 const QStringList parts = dbVals.split(
',' );
2592 d->mSrsId = parts.at( 0 ).toInt();
2593 d->mSRID = parts.at( 1 ).toInt();
2601QList<long> QgsCoordinateReferenceSystem::userSrsIds()
2603 QList<long> results;
2607 QFileInfo myInfo( db );
2608 if ( !myInfo.exists() )
2614 sqlite3_database_unique_ptr database;
2615 sqlite3_statement_unique_ptr statement;
2618 int result = openDatabase( db, database );
2619 if ( result != SQLITE_OK )
2621 QgsDebugError(
"failed : " + db +
" could not be opened!" );
2627 statement = database.
prepare( sql, rc );
2630 int ret = statement.
step();
2632 if ( ret == SQLITE_DONE )
2638 if ( ret == SQLITE_ROW )
2644 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
2652long QgsCoordinateReferenceSystem::matchToUserCrs()
const
2654 PJ *obj = d->threadLocalProjObject();
2658 const QList< long > ids = userSrsIds();
2659 for (
long id : ids )
2662 if ( candidate.
projObject() && proj_is_equivalent_to( obj, candidate.
projObject(), PJ_COMP_EQUIVALENT ) )
2670static void sync_db_proj_logger(
void * ,
int level,
const char *message )
2675 if ( level == PJ_LOG_ERROR )
2679 else if ( level == PJ_LOG_DEBUG )
2687 setlocale( LC_ALL,
"C" );
2690 int inserted = 0, updated = 0, deleted = 0, errors = 0;
2695 if ( database.
open( dbFilePath ) != SQLITE_OK )
2701 if ( sqlite3_exec( database.get(),
"BEGIN TRANSACTION",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2709 char *errMsg =
nullptr;
2711 bool createdTypeColumn =
false;
2712 if ( sqlite3_exec( database.get(),
"ALTER TABLE tbl_srs ADD COLUMN srs_type text",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2714 createdTypeColumn =
true;
2715 if ( sqlite3_exec( database.get(),
"CREATE INDEX srs_type ON tbl_srs(srs_type)",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2722 if ( sqlite3_exec( database.get(),
"create table tbl_info (proj_major INT, proj_minor INT, proj_patch INT)",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2724 QString sql = u
"INSERT INTO tbl_info(proj_major, proj_minor, proj_patch) VALUES (%1, %2,%3)"_s
2725 .arg( QString::number( PROJ_VERSION_MAJOR ), QString::number( PROJ_VERSION_MINOR ), QString::number( PROJ_VERSION_PATCH ) );
2726 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2728 QgsDebugError( u
"Could not execute: %1 [%2/%3]\n"_s.arg( sql, database.
errorMessage(), errMsg ? errMsg :
"(unknown error)" ) );
2730 sqlite3_free( errMsg );
2737 QString sql = u
"SELECT proj_major, proj_minor, proj_patch FROM tbl_info"_s;
2738 statement = database.
prepare( sql, result );
2739 if ( result != SQLITE_OK )
2744 if ( statement.
step() == SQLITE_ROW )
2749 if ( !createdTypeColumn && major == PROJ_VERSION_MAJOR && minor == PROJ_VERSION_MINOR && patch == PROJ_VERSION_PATCH )
2755 QgsDebugError( u
"Could not retrieve previous CRS sync PROJ version number"_s );
2762 proj_log_func( pjContext,
nullptr, sync_db_proj_logger );
2764 PROJ_STRING_LIST authorities = proj_get_authorities_from_database( pjContext );
2766 int nextSrsId = 67218;
2767 int nextSrId = 520007218;
2768 for (
auto authIter = authorities; authIter && *authIter; ++authIter )
2770 const QString authority( *authIter );
2772 PROJ_STRING_LIST codes = proj_get_codes_from_database( pjContext, *authIter, PJ_TYPE_CRS,
true );
2774 QStringList allCodes;
2776 for (
auto codesIter = codes; codesIter && *codesIter; ++codesIter )
2778 const QString code( *codesIter );
2784 QgsDebugError( u
"Could not load '%1:%2'"_s.arg( authority, code ) );
2788 const PJ_TYPE pjType = proj_get_type( crs.get() );
2790 QString srsTypeString;
2795 case PJ_TYPE_ELLIPSOID:
2796 case PJ_TYPE_PRIME_MERIDIAN:
2797 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
2798 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
2799 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
2800 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
2801 case PJ_TYPE_DATUM_ENSEMBLE:
2802 case PJ_TYPE_CONVERSION:
2803 case PJ_TYPE_TRANSFORMATION:
2804 case PJ_TYPE_CONCATENATED_OPERATION:
2805 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
2806 case PJ_TYPE_TEMPORAL_DATUM:
2807 case PJ_TYPE_ENGINEERING_DATUM:
2808 case PJ_TYPE_PARAMETRIC_DATUM:
2809 case PJ_TYPE_UNKNOWN:
2813 case PJ_TYPE_GEOGRAPHIC_CRS:
2816 case PJ_TYPE_GEODETIC_CRS:
2820 case PJ_TYPE_GEOCENTRIC_CRS:
2824 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
2828 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
2832 case PJ_TYPE_PROJECTED_CRS:
2836 case PJ_TYPE_COMPOUND_CRS:
2840 case PJ_TYPE_TEMPORAL_CRS:
2844 case PJ_TYPE_ENGINEERING_CRS:
2848 case PJ_TYPE_BOUND_CRS:
2852 case PJ_TYPE_VERTICAL_CRS:
2856#if PROJ_VERSION_MAJOR > 9 || ( PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 2 )
2857 case PJ_TYPE_DERIVED_PROJECTED_CRS:
2860 case PJ_TYPE_COORDINATE_METADATA:
2863 case PJ_TYPE_OTHER_CRS:
2872 proj4.replace(
"+type=crs"_L1, QString() );
2873 proj4 = proj4.trimmed();
2875 if ( proj4.isEmpty() )
2888 if ( translatedOperation.isEmpty() && !
operation.isEmpty() )
2890 std::cout << u
"Operation needs translation in QgsCoordinateReferenceSystemUtils::translateProjection: %1"_s.arg(
operation ).toLocal8Bit().constData() << std::endl;
2891 qFatal(
"aborted" );
2894 const bool deprecated = proj_is_deprecated( crs.get() );
2895 const QString name( proj_get_name( crs.get() ) );
2897 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 );
2898 statement = database.
prepare( sql, result );
2899 if ( result != SQLITE_OK )
2908 QString dbOperation;
2909 bool dbSrsDeprecated = deprecated;
2910 if ( statement.
step() == SQLITE_ROW )
2914 dbSrsDeprecated = statement.
columnAsText( 2 ).toInt() != 0;
2919 if ( !dbSrsProj4.isEmpty() || !dbSrsDesc.isEmpty() )
2921 if ( proj4 != dbSrsProj4 || name != dbSrsDesc || deprecated != dbSrsDeprecated || dbSrsType != srsTypeString || dbOperation !=
operation )
2924 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 ) )
2926 .arg( deprecated ? 1 : 0 )
2929 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2931 QgsDebugError( u
"Could not execute: %1 [%2/%3]\n"_s.arg( sql, database.
errorMessage(), errMsg ? errMsg :
"(unknown error)" ) );
2933 sqlite3_free( errMsg );
2950 if ( !dbVals.isEmpty() )
2952 const QStringList parts = dbVals.split(
',' );
2953 srsId = parts.at( 0 );
2954 srId = parts.at( 1 );
2956 if ( srId.isEmpty() )
2958 srId = QString::number( nextSrId );
2961 if ( srsId.isEmpty() )
2963 srsId = QString::number( nextSrsId );
2967 if ( !srsId.isEmpty() )
2969 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
2976 .arg( deprecated ? 1 : 0 )
2981 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
2987 .arg( deprecated ? 1 : 0 )
2992 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) == SQLITE_OK )
2998 qCritical(
"Could not execute: %s [%s/%s]\n", sql.toLocal8Bit().constData(), sqlite3_errmsg( database.get() ), errMsg ? errMsg :
"(unknown error)" );
3002 sqlite3_free( errMsg );
3007 proj_string_list_destroy( codes );
3009 const QString sql = u
"DELETE FROM tbl_srs WHERE auth_name='%1' AND NOT auth_id IN (%2)"_s.arg( authority, allCodes.join(
',' ) );
3010 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
3012 deleted = sqlite3_changes( database.get() );
3017 qCritical(
"Could not execute: %s [%s]\n", sql.toLocal8Bit().constData(), sqlite3_errmsg( database.get() ) );
3020 proj_string_list_destroy( authorities );
3022 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 ) );
3023 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
3025 QgsDebugError( u
"Could not execute: %1 [%2/%3]\n"_s.arg( sql, database.
errorMessage(), errMsg ? errMsg :
"(unknown error)" ) );
3027 sqlite3_free( errMsg );
3031 if ( sqlite3_exec( database.get(),
"COMMIT",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
3038 QgsDebugMsgLevel( u
"CRS update (inserted:%1 updated:%2 deleted:%3 errors:%4)"_s.arg( inserted ).arg( updated ).arg( deleted ).arg( errors ), 4 );
3046 return updated + inserted;
3049const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::stringCache()
3051 return *sStringCache();
3054const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::projCache()
3056 return *sProj4Cache();
3059const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::ogcCache()
3061 return *sOgcCache();
3064const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::wktCache()
3066 return *sWktCache();
3069const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srIdCache()
3071 return *sSrIdCache();
3074const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srsIdCache()
3076 return *sSrsIdCache();
3086 if (
PJ *obj = d->threadLocalProjObject() )
3093 const PJ_TYPE pjType = proj_get_type( geoCrs.get() );
3094 if ( pjType == PJ_TYPE_GEOCENTRIC_CRS )
3103 if ( !geoGraphicCrs )
3130 if (
PJ *obj = d->threadLocalProjObject() )
3184 if (
PJ *obj = d->threadLocalProjObject() )
3217 if (
PJ *obj = d->threadLocalProjObject() )
3228 if (
PJ *obj = d->threadLocalProjObject() )
3241 else if (
PJ *obj = d->threadLocalProjObject() )
3244 return geoCrs ? u
"%1:%2"_s.arg( proj_get_id_auth_name( geoCrs.get(), 0 ), proj_get_id_code( geoCrs.get(), 0 ) ) : QString();
3266 const int paramCount = proj_coordoperation_get_param_count( ctx, conversion.get() );
3267 bool hasLat =
false, hasLon =
false;
3269 for (
int i = 0; i < paramCount; i++ )
3271 const char *paramName =
nullptr, *authName =
nullptr, *code =
nullptr, *valueString =
nullptr, *unitName =
nullptr, *unitAuthName =
nullptr, *unitCode =
nullptr, *unitCategory =
nullptr;
3273 double unitConvFactor = 1.0;
3274 if ( !proj_coordoperation_get_param( ctx, conversion.get(), i, ¶mName, &authName, &code, &value, &valueString, &unitConvFactor, &unitName, &unitAuthName, &unitCode, &unitCategory ) )
3277 const int paramCode = QString( code ).toInt();
3279 if ( paramCode == EPSG_CODE_PARAMETER_LATITUDE_TOPOGRAPHIC_ORIGIN )
3281 latDeg = value * unitConvFactor * 180.0 / M_PI;
3284 else if ( paramCode == EPSG_CODE_PARAMETER_LONGITUDE_TOPOGRAPHIC_ORIGIN )
3286 lonDeg = value * unitConvFactor * 180.0 / M_PI;
3291 return hasLat && hasLon;
3307 double lat = 0.0, lon = 0.0;
3309 if ( isTopocentric )
3311 baseCrs.reset( proj_get_source_crs( ctx, pj ) );
3320 if ( proj_get_type( geodeticCrs.get() ) == PJ_TYPE_GEOGRAPHIC_2D_CRS )
3323 if ( geodetic3DCrs )
3324 geodeticCrs = std::move( geodetic3DCrs );
3327 if ( proj_get_type( geodeticCrs.get() ) == PJ_TYPE_GEOCENTRIC_CRS )
3334 geodeticCrs.reset( proj_create_geographic_crs_from_datum( ctx,
nullptr, datum ? datum.get() :
datumEnsemble.get(), cs.get() ) );
3339 const char *projWkt = proj_as_wkt( ctx, geodeticCrs.get(), PJ_WKT2_2019,
nullptr );
3343 QString baseWkt = QString::fromUtf8( projWkt );
3344 if ( baseWkt.startsWith(
"GEOGCRS["_L1 ) )
3345 baseWkt.replace( 0, 8,
"BASEGEOGCRS["_L1 );
3349 const QString wkt = u
"GEODCRS[\"Topocentric\","
3351 "DERIVINGCONVERSION[\"Geographic/topocentric\","
3352 " METHOD[\"Geographic/topocentric conversions\",ID[\"EPSG\",9837]],"
3353 " PARAMETER[\"Latitude of topocentric origin\",%2,"
3354 " ANGLEUNIT[\"degree\",0.0174532925199433],ID[\"EPSG\",8834]],"
3355 " PARAMETER[\"Longitude of topocentric origin\",%3,"
3356 " ANGLEUNIT[\"degree\",0.0174532925199433],ID[\"EPSG\",8835]],"
3357 " PARAMETER[\"Ellipsoidal height of topocentric origin\",0,"
3358 " LENGTHUNIT[\"metre\",1],ID[\"EPSG\",8836]]],"
3360 "AXIS[\"(X)\",east,ORDER[1],LENGTHUNIT[\"metre\",1]],"
3361 "AXIS[\"(Y)\",north,ORDER[2],LENGTHUNIT[\"metre\",1]],"
3362 "AXIS[\"(Z)\",up,ORDER[3],LENGTHUNIT[\"metre\",1]]]"_s.arg( baseWkt )
3371 if ( isTopocentric )
3372 crs.d->mTopocentricBaseCrs = std::make_unique<QgsCoordinateReferenceSystem>( *d->mTopocentricBaseCrs );
3374 crs.d->mTopocentricBaseCrs = std::make_unique<QgsCoordinateReferenceSystem>( *
this );
3386 return d->threadLocalProjObject();
3399 d->mIsValid =
false;
3401 d->mWktPreferred.clear();
3408 switch ( proj_get_type(
object ) )
3410 case PJ_TYPE_GEODETIC_CRS:
3411 case PJ_TYPE_GEOCENTRIC_CRS:
3412 case PJ_TYPE_GEOGRAPHIC_CRS:
3413 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
3414 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
3415 case PJ_TYPE_VERTICAL_CRS:
3416 case PJ_TYPE_PROJECTED_CRS:
3417 case PJ_TYPE_COMPOUND_CRS:
3418 case PJ_TYPE_TEMPORAL_CRS:
3419 case PJ_TYPE_ENGINEERING_CRS:
3420 case PJ_TYPE_BOUND_CRS:
3421 case PJ_TYPE_OTHER_CRS:
3437 const QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
3438 const QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
3439 if ( !authName.isEmpty() && !authCode.isEmpty() &&
createFromOgcWmsCrs( u
"%1:%2"_s.arg( authName, authCode ) ) )
3447 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
3458 QStringList projections;
3460 projections.reserve( res.size() );
3463 projections << QString::number( crs.srsid() );
3490 sSrIdCacheLock()->lockForWrite();
3491 if ( !sDisableSrIdCache )
3494 sDisableSrIdCache =
true;
3495 sSrIdCache()->clear();
3497 sSrIdCacheLock()->unlock();
3499 sOgcLock()->lockForWrite();
3500 if ( !sDisableOgcCache )
3503 sDisableOgcCache =
true;
3504 sOgcCache()->clear();
3506 sOgcLock()->unlock();
3508 sProj4CacheLock()->lockForWrite();
3509 if ( !sDisableProjCache )
3512 sDisableProjCache =
true;
3513 sProj4Cache()->clear();
3515 sProj4CacheLock()->unlock();
3517 sCRSWktLock()->lockForWrite();
3518 if ( !sDisableWktCache )
3521 sDisableWktCache =
true;
3522 sWktCache()->clear();
3524 sCRSWktLock()->unlock();
3526 sCRSSrsIdLock()->lockForWrite();
3527 if ( !sDisableSrsIdCache )
3530 sDisableSrsIdCache =
true;
3531 sSrsIdCache()->clear();
3533 sCRSSrsIdLock()->unlock();
3535 sCrsStringLock()->lockForWrite();
3536 if ( !sDisableStringCache )
3539 sDisableStringCache =
true;
3540 sStringCache()->clear();
3542 sCrsStringLock()->unlock();
3561 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3564 if ( !c1.d->mIsValid && c2.d->mIsValid )
3567 if ( c1.d->mIsValid && !c2.d->mIsValid )
3573 if ( c1IsUser && !c2IsUser )
3576 if ( !c1IsUser && c2IsUser )
3579 if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
3581 if ( c1.d->mAuthId != c2.d->mAuthId )
3582 return c1.d->mAuthId > c2.d->mAuthId;
3590 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3593 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3596 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3599 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3602 return c1.d->mCoordinateEpoch > c2.d->mCoordinateEpoch;
3610 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3613 if ( c1.d->mIsValid && !c2.d->mIsValid )
3616 if ( !c1.d->mIsValid && c2.d->mIsValid )
3622 if ( !c1IsUser && c2IsUser )
3625 if ( c1IsUser && !c2IsUser )
3628 if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
3630 if ( c1.d->mAuthId != c2.d->mAuthId )
3631 return c1.d->mAuthId < c2.d->mAuthId;
3639 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3642 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3645 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3648 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3651 return c1.d->mCoordinateEpoch < c2.d->mCoordinateEpoch;
3656 return !( c1 < c2 );
3660 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 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.
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)
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.