22#include <proj_experimental.h>
49#include <QRegularExpression>
52#include "moc_qgscoordinatereferencesystem.cpp"
55#include <ogr_srs_api.h>
67bool QgsCoordinateReferenceSystem::sDisableSrIdCache =
false;
71bool QgsCoordinateReferenceSystem::sDisableOgcCache =
false;
75bool QgsCoordinateReferenceSystem::sDisableProjCache =
false;
79bool QgsCoordinateReferenceSystem::sDisableWktCache =
false;
83bool QgsCoordinateReferenceSystem::sDisableSrsIdCache =
false;
87bool QgsCoordinateReferenceSystem::sDisableStringCache =
false;
96 if (
const char *proj4src = proj_as_proj_string(
QgsProjContext::get(), boundCrs.get(), PJ_PROJ_4,
nullptr ) )
98 return QString( proj4src );
115 d =
new QgsCoordinateReferenceSystemPrivate();
121 d =
new QgsCoordinateReferenceSystemPrivate();
129 , mValidationHint( srs.mValidationHint )
130 , mNativeFormat( srs.mNativeFormat )
140 mValidationHint = srs.mValidationHint;
141 mNativeFormat = srs.mNativeFormat;
151 const auto constDbs = dbs;
152 for (
const QString &db : constDbs )
154 QFileInfo myInfo( db );
155 if ( !myInfo.exists() )
165 int result = openDatabase( db, database );
166 if ( result != SQLITE_OK )
172 QString sql = QStringLiteral(
"select srs_id from tbl_srs" );
174 statement = database.
prepare( sql, rc );
178 int ret = statement.
step();
180 if ( ret == SQLITE_DONE )
186 if ( ret == SQLITE_ROW )
192 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
197 std::sort( results.begin(), results.end() );
253 if ( horizontalObj && verticalObj )
260 QStringList formattedErrorList;
261 for (
const QString &rawError : std::as_const( errors ) )
263 QString formattedError = rawError;
264 formattedError.replace( QLatin1String(
"proj_create_compound_crs: " ), QString() );
265 formattedErrorList.append( formattedError );
267 error = formattedErrorList.join(
'\n' );
275 if ( !ellipsoidParams.
valid )
317 QgsDebugError( QStringLiteral(
"Unexpected case reached!" ) );
324 if ( definition.isEmpty() )
328 if ( !sDisableStringCache )
330 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sStringCache()->constFind( definition );
331 if ( crsIt != sStringCache()->constEnd() )
334 *
this = crsIt.value();
341 const thread_local QRegularExpression reCrsId( QStringLiteral(
"^(epsg|esri|osgeo|ignf|ogc|nkg|zangi|iau_2015|iau2000|postgis|internal|user)\\:(\\w+)$" ), QRegularExpression::CaseInsensitiveOption );
342 QRegularExpressionMatch match = reCrsId.match( definition );
343 if ( match.capturedStart() == 0 )
345 QString authName = match.captured( 1 ).toLower();
346 if ( authName == QLatin1String(
"epsg" ) )
350 else if ( authName == QLatin1String(
"postgis" ) )
352 const long id = match.captured( 2 ).toLong();
357 else if ( authName == QLatin1String(
"esri" )
358 || authName == QLatin1String(
"osgeo" )
359 || authName == QLatin1String(
"ignf" )
360 || authName == QLatin1String(
"zangi" )
361 || authName == QLatin1String(
"iau2000" )
362 || authName == QLatin1String(
"ogc" )
363 || authName == QLatin1String(
"nkg" )
364 || authName == QLatin1String(
"iau_2015" )
371 const long id = match.captured( 2 ).toLong();
379 const thread_local QRegularExpression reCrsStr( QStringLiteral(
"^(?:(wkt|proj4|proj)\\:)?(.+)$" ), QRegularExpression::CaseInsensitiveOption );
380 match = reCrsStr.match( definition );
381 if ( match.capturedStart() == 0 )
383 if ( match.captured( 1 ).startsWith( QLatin1String(
"proj" ), Qt::CaseInsensitive ) )
395 if ( !sDisableStringCache )
396 sStringCache()->insert( definition, *
this );
402 if ( definition.isEmpty() )
406 OGRSpatialReferenceH crs = OSRNewSpatialReference(
nullptr );
408 if ( OSRSetFromUserInput( crs, definition.toLocal8Bit().constData() ) == OGRERR_NONE )
411 OSRDestroySpatialReference( crs );
421 const char *configOld = CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" );
422 const char *configNew =
"GEOGCS";
424 if ( strcmp( configOld,
"" ) == 0 )
426 CPLSetConfigOption(
"GDAL_FIX_ESRI_WKT", configNew );
427 if ( strcmp( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) != 0 )
429 .arg( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) );
430 QgsDebugMsgLevel( QStringLiteral(
"set GDAL_FIX_ESRI_WKT : %1" ).arg( configNew ), 4 );
434 QgsDebugMsgLevel( QStringLiteral(
"GDAL_FIX_ESRI_WKT was already set : %1" ).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();
463 ( authorityLower == QLatin1String(
"user" ) ||
464 authorityLower == QLatin1String(
"custom" ) ||
465 authorityLower == QLatin1String(
"qgis" ) ) )
470 if ( !sDisableOgcCache )
471 sOgcCache()->insert( crs, *
this );
477 wmsCrs = authority +
':' + code;
481 const QString legacyKey = wmsCrs.toLower();
484 if ( it.key().compare( legacyKey, Qt::CaseInsensitive ) == 0 )
486 const QStringList parts = it.key().split(
':' );
487 const QString auth = parts.at( 0 );
488 const QString code = parts.at( 1 );
489 if ( loadFromAuthCode( auth, code ) )
492 if ( !sDisableOgcCache )
493 sOgcCache()->insert( crs, *
this );
502 if ( !sDisableOgcCache )
503 sOgcCache()->insert( crs, *
this );
508 if ( wmsCrs.compare( QLatin1String(
"CRS:27" ), Qt::CaseInsensitive ) == 0 ||
509 wmsCrs.compare( QLatin1String(
"OGC:CRS27" ), Qt::CaseInsensitive ) == 0 )
516 if ( wmsCrs.compare( QLatin1String(
"CRS:83" ), Qt::CaseInsensitive ) == 0 ||
517 wmsCrs.compare( QLatin1String(
"OGC:CRS83" ), Qt::CaseInsensitive ) == 0 )
524 if ( wmsCrs.compare( QLatin1String(
"CRS:84" ), Qt::CaseInsensitive ) == 0 ||
525 wmsCrs.compare( QLatin1String(
"OGC:CRS84" ), Qt::CaseInsensitive ) == 0 )
529 d->mAxisInverted =
false;
530 d->mAxisInvertedDirty =
false;
534 if ( !sDisableOgcCache )
535 sOgcCache()->insert( crs, *
this );
542 if ( !authority.isEmpty() && !code.isEmpty() && loadFromAuthCode( authority, code ) )
545 if ( !sDisableOgcCache )
546 sOgcCache()->insert( crs, *
this );
551 if ( !sDisableOgcCache )
561 if ( d->mIsValid || !sCustomSrsValidation )
565 if ( sCustomSrsValidation )
566 sCustomSrsValidation( *
this );
572 if ( !sDisableSrIdCache )
574 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrIdCache()->constFind(
id );
575 if ( crsIt != sSrIdCache()->constEnd() )
578 *
this = crsIt.value();
587 if ( it.value().endsWith( QStringLiteral(
",%1" ).arg(
id ) ) )
589 const QStringList parts = it.key().split(
':' );
590 const QString auth = parts.at( 0 );
591 const QString code = parts.at( 1 );
592 if ( loadFromAuthCode( auth, code ) )
595 if ( !sDisableSrIdCache )
596 sSrIdCache()->insert(
id, *
this );
606 if ( !sDisableSrIdCache )
607 sSrIdCache()->insert(
id, *
this );
615 if ( !sDisableSrsIdCache )
617 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrsIdCache()->constFind(
id );
618 if ( crsIt != sSrsIdCache()->constEnd() )
621 *
this = crsIt.value();
630 if ( it.value().startsWith( QString::number(
id ) +
',' ) )
632 const QStringList parts = it.key().split(
':' );
633 const QString auth = parts.at( 0 );
634 const QString code = parts.at( 1 );
635 if ( loadFromAuthCode( auth, code ) )
638 if ( !sDisableSrsIdCache )
639 sSrsIdCache()->insert(
id, *
this );
647 QStringLiteral(
"srs_id" ), QString::number(
id ) );
650 if ( !sDisableSrsIdCache )
651 sSrsIdCache()->insert(
id, *
this );
655bool QgsCoordinateReferenceSystem::loadFromDatabase(
const QString &db,
const QString &expression,
const QString &value )
659 QgsDebugMsgLevel(
"load CRS from " + db +
" where " + expression +
" is " + value, 3 );
661 d->mWktPreferred.clear();
663 QFileInfo myInfo( db );
664 if ( !myInfo.exists() )
670 sqlite3_database_unique_ptr database;
671 sqlite3_statement_unique_ptr statement;
674 myResult = openDatabase( db, database );
675 if ( myResult != SQLITE_OK )
692 QString mySql =
"select srs_id,description,projection_acronym,"
693 "ellipsoid_acronym,parameters,srid,auth_name||':'||auth_id,is_geo,wkt "
695 statement = database.
prepare( mySql, myResult );
698 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
703 d->mEllipsoidAcronym.clear();
705 d->mWktPreferred.clear();
708 d->mIsGeographic = statement.
columnAsText( 7 ).toInt() != 0;
710 d->mAxisInvertedDirty =
true;
714 d->mAuthId = QStringLiteral(
"USER:%1" ).arg( d->mSrsId );
716 else if ( !d->mAuthId.startsWith( QLatin1String(
"USER:" ), Qt::CaseInsensitive ) )
718 QStringList parts = d->mAuthId.split(
':' );
719 QString auth = parts.at( 0 );
720 QString code = parts.at( 1 );
727 d->mIsValid = d->hasPj();
733 if ( !wkt.isEmpty() )
741 setProjString( d->mProj4 );
751void QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread(
PJ_CONTEXT *pj_context )
758 if ( !sDisableSrIdCache )
761 if ( !sDisableSrIdCache )
763 for (
auto it = sSrIdCache()->begin(); it != sSrIdCache()->end(); )
765 auto &v = it.value();
766 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
767 it = sSrIdCache()->erase( it );
773 if ( !sDisableOgcCache )
776 if ( !sDisableOgcCache )
778 for (
auto it = sOgcCache()->begin(); it != sOgcCache()->end(); )
780 auto &v = it.value();
781 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
782 it = sOgcCache()->erase( it );
788 if ( !sDisableProjCache )
791 if ( !sDisableProjCache )
793 for (
auto it = sProj4Cache()->begin(); it != sProj4Cache()->end(); )
795 auto &v = it.value();
796 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
797 it = sProj4Cache()->erase( it );
803 if ( !sDisableWktCache )
806 if ( !sDisableWktCache )
808 for (
auto it = sWktCache()->begin(); it != sWktCache()->end(); )
810 auto &v = it.value();
811 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
812 it = sWktCache()->erase( it );
818 if ( !sDisableSrsIdCache )
821 if ( !sDisableSrsIdCache )
823 for (
auto it = sSrsIdCache()->begin(); it != sSrsIdCache()->end(); )
825 auto &v = it.value();
826 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
827 it = sSrsIdCache()->erase( it );
833 if ( !sDisableStringCache )
836 if ( !sDisableStringCache )
838 for (
auto it = sStringCache()->begin(); it != sStringCache()->end(); )
840 auto &v = it.value();
841 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
842 it = sStringCache()->erase( it );
852 if ( d->mAxisInvertedDirty )
855 d->mAxisInvertedDirty =
false;
858 return d->mAxisInverted;
875 const thread_local QMap< Qgis::CrsAxisDirection, QString > mapping =
918 QList< Qgis::CrsAxisDirection > res;
919 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
922 res.reserve( axisCount );
924 for (
int i = 0; i < axisCount; ++i )
926 const char *outDirection =
nullptr;
927 proj_cs_get_axis_info( context, pjCs.get(), i,
937 const thread_local QRegularExpression rx( QStringLiteral(
"([^\\s]+).*" ) );
938 const QRegularExpressionMatch match = rx.match( QString( outDirection ) );
939 if ( !match.hasMatch() )
942 const QString direction = match.captured( 1 );
944 for (
auto it = mapping.constBegin(); it != mapping.constEnd(); ++it )
946 if ( it.value().compare( direction, Qt::CaseInsensitive ) == 0 )
961 return createFromWktInternal( wkt, QString() );
964bool QgsCoordinateReferenceSystem::createFromWktInternal(
const QString &wkt,
const QString &description )
972 if ( !sDisableWktCache )
974 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sWktCache()->constFind( wkt );
975 if ( crsIt != sWktCache()->constEnd() )
978 *
this = crsIt.value();
980 if ( !
description.isEmpty() && d->mDescription.isEmpty() )
985 sWktCache()->insert( wkt, *
this );
994 d->mWktPreferred.clear();
997 QgsDebugMsgLevel( QStringLiteral(
"theWkt is uninitialized, operation failed" ), 4 );
1002 QgsCoordinateReferenceSystem::RecordMap record = getRecord(
"select * from tbl_srs where wkt=" +
QgsSqliteUtils::quotedString( wkt ) +
" order by deprecated" );
1003 if ( !record.empty() )
1005 long srsId = record[QStringLiteral(
"srs_id" )].toLong();
1013 setWktString( wkt );
1018 if ( d->mSrsId == 0 )
1021 long id = matchToUserCrs();
1030 if ( !sDisableWktCache )
1031 sWktCache()->insert( wkt, *
this );
1049 if ( projString.isEmpty() )
1054 if ( projString.trimmed().isEmpty() )
1056 d->mIsValid =
false;
1058 d->mWktPreferred.clear();
1063 if ( !sDisableProjCache )
1065 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sProj4Cache()->constFind( projString );
1066 if ( crsIt != sProj4Cache()->constEnd() )
1069 *
this = crsIt.value();
1083 QString myProj4String = projString.trimmed();
1084 myProj4String.remove( QStringLiteral(
"+type=crs" ) );
1085 myProj4String = myProj4String.trimmed();
1087 d->mIsValid =
false;
1088 d->mWktPreferred.clear();
1093 const QString projCrsString = myProj4String + ( myProj4String.contains( QStringLiteral(
"+type=crs" ) ) ? QString() : QStringLiteral(
" +type=crs" ) );
1101 const QString
authid = QStringLiteral(
"%1:%2" ).arg( authName, authCode );
1105 if ( !sDisableProjCache )
1106 sProj4Cache()->insert( projString, *
this );
1113 QgsCoordinateReferenceSystem::RecordMap myRecord = getRecord(
"select * from tbl_srs where parameters=" +
QgsSqliteUtils::quotedString( myProj4String ) +
" order by deprecated" );
1115 if ( !myRecord.empty() )
1117 id = myRecord[QStringLiteral(
"srs_id" )].toLong();
1126 setProjString( myProj4String );
1129 id = matchToUserCrs();
1138 setProjString( myProj4String );
1142 if ( !sDisableProjCache )
1143 sProj4Cache()->insert( projString, *
this );
1149QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord(
const QString &sql )
1151 QString myDatabaseFileName;
1152 QgsCoordinateReferenceSystem::RecordMap myMap;
1153 QString myFieldName;
1154 QString myFieldValue;
1161 QFileInfo myInfo( myDatabaseFileName );
1162 if ( !myInfo.exists() )
1164 QgsDebugError(
"failed : " + myDatabaseFileName +
" does not exist!" );
1169 myResult = openDatabase( myDatabaseFileName, database );
1170 if ( myResult != SQLITE_OK )
1175 statement = database.
prepare( sql, myResult );
1177 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1181 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1183 myFieldName = statement.
columnName( myColNo );
1185 myMap[myFieldName] = myFieldValue;
1187 if ( statement.
step() != SQLITE_DONE )
1189 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1198 if ( myMap.empty() )
1201 QFileInfo myFileInfo;
1202 myFileInfo.setFile( myDatabaseFileName );
1203 if ( !myFileInfo.exists() )
1205 QgsDebugError( QStringLiteral(
"user qgis.db not found" ) );
1210 myResult = openDatabase( myDatabaseFileName, database );
1211 if ( myResult != SQLITE_OK )
1216 statement = database.
prepare( sql, myResult );
1218 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1222 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1224 myFieldName = statement.
columnName( myColNo );
1226 myMap[myFieldName] = myFieldValue;
1229 if ( statement.
step() != SQLITE_DONE )
1231 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1262 if ( d->mDescription.isNull() )
1268 return d->mDescription;
1275 if ( !
authid().isEmpty() )
1285 id =
isValid() ? QObject::tr(
"Custom CRS" ) : QObject::tr(
"Unknown CRS" );
1287 id = QObject::tr(
"Custom CRS: %1" ).arg(
1290 else if ( !
toProj().isEmpty() )
1293 if ( !
id.isEmpty() && !std::isnan( d->mCoordinateEpoch ) )
1294 id += QStringLiteral(
" @ %1" ).arg(
qgsDoubleToString( d->mCoordinateEpoch, 3 ) );
1301 if ( d->mProjectionAcronym.isNull() )
1307 return d->mProjectionAcronym;
1313 if ( d->mEllipsoidAcronym.isNull() )
1315 if (
PJ *obj = d->threadLocalProjObject() )
1320 const QString ellipsoidAuthName( proj_get_id_auth_name( ellipsoid.get(), 0 ) );
1321 const QString ellipsoidAuthCode( proj_get_id_code( ellipsoid.get(), 0 ) );
1322 if ( !ellipsoidAuthName.isEmpty() && !ellipsoidAuthCode.isEmpty() )
1323 d->mEllipsoidAcronym = QStringLiteral(
"%1:%2" ).arg( ellipsoidAuthName, ellipsoidAuthCode );
1326 double semiMajor, semiMinor, invFlattening;
1327 int semiMinorComputed = 0;
1328 if ( proj_ellipsoid_get_parameters(
QgsProjContext::get(), ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
1330 d->mEllipsoidAcronym = QStringLiteral(
"PARAMETER:%1:%2" ).arg(
qgsDoubleToString( semiMajor ),
1335 d->mEllipsoidAcronym.clear();
1340 return d->mEllipsoidAcronym;
1344 return d->mEllipsoidAcronym;
1358 if ( d->mProj4.isEmpty() )
1360 if (
PJ *obj = d->threadLocalProjObject() )
1366 return d->mProj4.trimmed();
1374 if (
PJ *obj = d->threadLocalProjObject() )
1376 const QByteArray multiLineOption = QStringLiteral(
"MULTILINE=%1" ).arg( multiline ? QStringLiteral(
"YES" ) : QStringLiteral(
"NO" ) ).toLocal8Bit();
1377 const QByteArray indentatationWidthOption = QStringLiteral(
"INDENTATION_WIDTH=%1" ).arg( multiline ? QString::number( indentationWidth ) : QStringLiteral(
"0" ) ).toLocal8Bit();
1378 const QByteArray schemaOption = QStringLiteral(
"SCHEMA=%1" ).arg( schema ).toLocal8Bit();
1379 const char *
const options[] = {multiLineOption.constData(), indentatationWidthOption.constData(), schemaOption.constData(),
nullptr};
1382 return json ? std::string{ json } : std::string{};
1393 switch ( d->mProjType )
1395 case PJ_TYPE_UNKNOWN:
1398 case PJ_TYPE_ELLIPSOID:
1399 case PJ_TYPE_PRIME_MERIDIAN:
1400 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
1401 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
1402 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
1403 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
1404 case PJ_TYPE_DATUM_ENSEMBLE:
1405 case PJ_TYPE_CONVERSION:
1406 case PJ_TYPE_TRANSFORMATION:
1407 case PJ_TYPE_CONCATENATED_OPERATION:
1408 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
1409 case PJ_TYPE_TEMPORAL_DATUM:
1410 case PJ_TYPE_ENGINEERING_DATUM:
1411 case PJ_TYPE_PARAMETRIC_DATUM:
1415 case PJ_TYPE_GEOGRAPHIC_CRS:
1419 case PJ_TYPE_GEODETIC_CRS:
1421 case PJ_TYPE_GEOCENTRIC_CRS:
1423 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
1425 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
1427 case PJ_TYPE_VERTICAL_CRS:
1429 case PJ_TYPE_PROJECTED_CRS:
1431 case PJ_TYPE_COMPOUND_CRS:
1433 case PJ_TYPE_TEMPORAL_CRS:
1435 case PJ_TYPE_ENGINEERING_CRS:
1437 case PJ_TYPE_BOUND_CRS:
1439 case PJ_TYPE_OTHER_CRS:
1441#if PROJ_VERSION_MAJOR>9 || (PROJ_VERSION_MAJOR==9 && PROJ_VERSION_MINOR>=2)
1442 case PJ_TYPE_DERIVED_PROJECTED_CRS:
1444 case PJ_TYPE_COORDINATE_METADATA:
1458 return proj_is_deprecated( pj );
1463 return d->mIsGeographic;
1483 return QString( proj_get_celestial_body_name( context, pj ) );
1488 if ( d->mCoordinateEpoch == epoch )
1494 d->mCoordinateEpoch = epoch;
1495 d->setPj( std::move( clone ) );
1500 return d->mCoordinateEpoch;
1519 res.mName = QString( proj_get_name( ensemble.get() ) );
1520 res.mAuthority = QString( proj_get_id_auth_name( ensemble.get(), 0 ) );
1521 res.mCode = QString( proj_get_id_code( ensemble.get(), 0 ) );
1522 res.mRemarks = QString( proj_get_remarks( ensemble.get() ) );
1523 res.mScope = QString( proj_get_scope( ensemble.get() ) );
1524 res.mAccuracy = proj_datum_ensemble_get_accuracy( context, ensemble.get() );
1526 const int memberCount = proj_datum_ensemble_get_member_count( context, ensemble.get() );
1527 for (
int i = 0; i < memberCount; ++i )
1534 details.mName = QString( proj_get_name( member.get() ) );
1535 details.mAuthority = QString( proj_get_id_auth_name( member.get(), 0 ) );
1536 details.mCode = QString( proj_get_id_code( member.get(), 0 ) );
1537 details.mRemarks = QString( proj_get_remarks( member.get() ) );
1538 details.mScope = QString( proj_get_scope( member.get() ) );
1540 res.mMembers << details;
1550 QString projString =
toProj();
1551 projString.replace( QLatin1String(
"+type=crs" ), QString() );
1554 if ( !transformation )
1557 PJ_COORD coord = proj_coord( 0, 0, 0, HUGE_VAL );
1558 coord.uv.u = point.
x() * M_PI / 180.0;
1559 coord.uv.v = point.
y() * M_PI / 180.0;
1561 proj_errno_reset( transformation.get() );
1562 const PJ_FACTORS pjFactors = proj_factors( transformation.get(), coord );
1563 if ( proj_errno( transformation.get() ) )
1568 res.mIsValid =
true;
1569 res.mMeridionalScale = pjFactors.meridional_scale;
1570 res.mParallelScale = pjFactors.parallel_scale;
1571 res.mArealScale = pjFactors.areal_scale;
1572 res.mAngularDistortion = pjFactors.angular_distortion;
1573 res.mMeridianParallelAngle = pjFactors.meridian_parallel_angle * 180 / M_PI;
1574 res.mMeridianConvergence = pjFactors.meridian_convergence * 180 / M_PI;
1575 res.mTissotSemimajor = pjFactors.tissot_semimajor;
1576 res.mTissotSemiminor = pjFactors.tissot_semiminor;
1577 res.mDxDlam = pjFactors.dx_dlam;
1578 res.mDxDphi = pjFactors.dx_dphi;
1579 res.mDyDlam = pjFactors.dy_dlam;
1580 res.mDyDphi = pjFactors.dy_dphi;
1592 QString projString =
toProj();
1593 projString.replace( QLatin1String(
"+type=crs" ), QString() );
1594 if ( projString.isEmpty() )
1598 if ( !transformation )
1601 PJ_PROJ_INFO info = proj_pj_info( transformation.get() );
1616 return d->mMapUnits;
1624 PJ *obj = d->threadLocalProjObject();
1629 double southLat = 0;
1631 double northLat = 0;
1634 &westLon, &southLat, &eastLon, &northLat,
nullptr ) )
1649 const auto parts {
authid().split(
':' ) };
1650 if ( parts.length() == 2 )
1652 if ( parts[0] == QLatin1String(
"EPSG" ) )
1653 return QStringLiteral(
"http://www.opengis.net/def/crs/EPSG/0/%1" ).arg( parts[1] ) ;
1654 else if ( parts[0] == QLatin1String(
"OGC" ) )
1656 return QStringLiteral(
"http://www.opengis.net/def/crs/OGC/1.3/%1" ).arg( parts[1] ) ;
1672 const auto parts {
authid().split(
':' ) };
1673 if ( parts.length() == 2 )
1675 if ( parts[0] == QLatin1String(
"EPSG" ) )
1676 return QStringLiteral(
"urn:ogc:def:crs:EPSG::%1" ).arg( parts[1] );
1677 else if ( parts[0] == QLatin1String(
"OGC" ) )
1679 return QStringLiteral(
"urn:ogc:def:crs:OGC:1.3:%1" ).arg( parts[1] );
1713void QgsCoordinateReferenceSystem::setProjString(
const QString &proj4String )
1716 d->mProj4 = proj4String;
1717 d->mWktPreferred.clear();
1720 QString trimmed = proj4String.trimmed();
1722 trimmed += QLatin1String(
" +type=crs" );
1732 const int errNo = proj_context_errno( ctx );
1733 QgsDebugError( QStringLiteral(
"proj string rejected: %1" ).arg( proj_context_errno_string( ctx, errNo ) ) );
1735 d->mIsValid =
false;
1739 d->mEllipsoidAcronym.clear();
1746bool QgsCoordinateReferenceSystem::setWktString(
const QString &wkt )
1749 d->mIsValid =
false;
1750 d->mWktPreferred.clear();
1752 PROJ_STRING_LIST warnings =
nullptr;
1753 PROJ_STRING_LIST grammarErrors =
nullptr;
1761 QgsDebugMsgLevel( QStringLiteral(
"\n---------------------------------------------------------------" ), 2 );
1762 QgsDebugMsgLevel( QStringLiteral(
"This CRS could *** NOT *** be set from the supplied Wkt " ), 2 );
1764 for (
auto iter = warnings; iter && *iter; ++iter )
1768 for (
auto iter = grammarErrors; iter && *iter; ++iter )
1772 QgsDebugMsgLevel( QStringLiteral(
"---------------------------------------------------------------\n" ), 2 );
1774 proj_string_list_destroy( warnings );
1775 proj_string_list_destroy( grammarErrors );
1781 if ( !sDisableWktCache )
1782 sWktCache()->insert( wkt, *
this );
1790 QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
1791 QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
1793 if ( authName.isEmpty() || authCode.isEmpty() )
1799 if ( !authName.isEmpty() && !authCode.isEmpty() )
1802 if ( fromAuthCode.loadFromAuthCode( authName, authCode ) )
1804 *
this = fromAuthCode;
1806 if ( !sDisableWktCache )
1807 sWktCache()->insert( wkt, *
this );
1814 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
1821void QgsCoordinateReferenceSystem::setMapUnits()
1848 if ( !coordinateSystem )
1854 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
1855 if ( axisCount > 0 )
1857 const char *outUnitName =
nullptr;
1859 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
1868 const QString unitName( outUnitName );
1872 if ( unitName.compare( QLatin1String(
"degree" ), Qt::CaseInsensitive ) == 0 ||
1873 unitName.compare( QLatin1String(
"degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1874 unitName.compare( QLatin1String(
"degree minute second hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1875 unitName.compare( QLatin1String(
"degree minute" ), Qt::CaseInsensitive ) == 0 ||
1876 unitName.compare( QLatin1String(
"degree hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1877 unitName.compare( QLatin1String(
"degree minute hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1878 unitName.compare( QLatin1String(
"hemisphere degree" ), Qt::CaseInsensitive ) == 0 ||
1879 unitName.compare( QLatin1String(
"hemisphere degree minute" ), Qt::CaseInsensitive ) == 0 ||
1880 unitName.compare( QLatin1String(
"hemisphere degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1881 unitName.compare( QLatin1String(
"degree (supplier to define representation)" ), Qt::CaseInsensitive ) == 0 )
1883 else if ( unitName.compare( QLatin1String(
"metre" ), Qt::CaseInsensitive ) == 0
1884 || unitName.compare( QLatin1String(
"m" ), Qt::CaseInsensitive ) == 0
1885 || unitName.compare( QLatin1String(
"meter" ), Qt::CaseInsensitive ) == 0 )
1887 else if ( unitName.compare( QLatin1String(
"US survey foot" ), Qt::CaseInsensitive ) == 0 )
1889 else if ( unitName.compare( QLatin1String(
"foot" ), Qt::CaseInsensitive ) == 0 )
1891 else if ( unitName.compare( QLatin1String(
"British yard (Sears 1922)" ), Qt::CaseInsensitive ) == 0 )
1893 else if ( unitName.compare( QLatin1String(
"British yard (Sears 1922 truncated)" ), Qt::CaseInsensitive ) == 0 )
1895 else if ( unitName.compare( QLatin1String(
"British foot (Sears 1922)" ), Qt::CaseInsensitive ) == 0 )
1897 else if ( unitName.compare( QLatin1String(
"British foot (Sears 1922 truncated)" ), Qt::CaseInsensitive ) == 0 )
1899 else if ( unitName.compare( QLatin1String(
"British chain (Sears 1922)" ), Qt::CaseInsensitive ) == 0 )
1901 else if ( unitName.compare( QLatin1String(
"British chain (Sears 1922 truncated)" ), Qt::CaseInsensitive ) == 0 )
1903 else if ( unitName.compare( QLatin1String(
"British link (Sears 1922)" ), Qt::CaseInsensitive ) == 0 )
1905 else if ( unitName.compare( QLatin1String(
"British link (Sears 1922 truncated)" ), Qt::CaseInsensitive ) == 0 )
1907 else if ( unitName.compare( QLatin1String(
"British yard (Benoit 1895 A)" ), Qt::CaseInsensitive ) == 0 )
1909 else if ( unitName.compare( QLatin1String(
"British foot (Benoit 1895 A)" ), Qt::CaseInsensitive ) == 0 )
1911 else if ( unitName.compare( QLatin1String(
"British chain (Benoit 1895 A)" ), Qt::CaseInsensitive ) == 0 )
1913 else if ( unitName.compare( QLatin1String(
"British link (Benoit 1895 A)" ), Qt::CaseInsensitive ) == 0 )
1915 else if ( unitName.compare( QLatin1String(
"British yard (Benoit 1895 B)" ), Qt::CaseInsensitive ) == 0 )
1917 else if ( unitName.compare( QLatin1String(
"British foot (Benoit 1895 B)" ), Qt::CaseInsensitive ) == 0 )
1919 else if ( unitName.compare( QLatin1String(
"British chain (Benoit 1895 B)" ), Qt::CaseInsensitive ) == 0 )
1921 else if ( unitName.compare( QLatin1String(
"British link (Benoit 1895 B)" ), Qt::CaseInsensitive ) == 0 )
1923 else if ( unitName.compare( QLatin1String(
"British foot (1865)" ), Qt::CaseInsensitive ) == 0 )
1925 else if ( unitName.compare( QLatin1String(
"British foot (1936)" ), Qt::CaseInsensitive ) == 0 )
1927 else if ( unitName.compare( QLatin1String(
"Indian foot" ), Qt::CaseInsensitive ) == 0 )
1929 else if ( unitName.compare( QLatin1String(
"Indian foot (1937)" ), Qt::CaseInsensitive ) == 0 )
1931 else if ( unitName.compare( QLatin1String(
"Indian foot (1962)" ), Qt::CaseInsensitive ) == 0 )
1933 else if ( unitName.compare( QLatin1String(
"Indian foot (1975)" ), Qt::CaseInsensitive ) == 0 )
1935 else if ( unitName.compare( QLatin1String(
"Indian yard" ), Qt::CaseInsensitive ) == 0 )
1937 else if ( unitName.compare( QLatin1String(
"Indian yard (1937)" ), Qt::CaseInsensitive ) == 0 )
1939 else if ( unitName.compare( QLatin1String(
"Indian yard (1962)" ), Qt::CaseInsensitive ) == 0 )
1941 else if ( unitName.compare( QLatin1String(
"Indian yard (1975)" ), Qt::CaseInsensitive ) == 0 )
1943 else if ( unitName.compare( QLatin1String(
"Gold Coast foot" ), Qt::CaseInsensitive ) == 0 )
1945 else if ( unitName.compare( QLatin1String(
"Clarke's foot" ), Qt::CaseInsensitive ) == 0 )
1947 else if ( unitName.compare( QLatin1String(
"Clarke's yard" ), Qt::CaseInsensitive ) == 0 )
1949 else if ( unitName.compare( QLatin1String(
"Clarke's chain" ), Qt::CaseInsensitive ) == 0 )
1951 else if ( unitName.compare( QLatin1String(
"Clarke's link" ), Qt::CaseInsensitive ) == 0 )
1953 else if ( unitName.compare( QLatin1String(
"kilometre" ), Qt::CaseInsensitive ) == 0 )
1955 else if ( unitName.compare( QLatin1String(
"centimetre" ), Qt::CaseInsensitive ) == 0 )
1957 else if ( unitName.compare( QLatin1String(
"millimetre" ), Qt::CaseInsensitive ) == 0 )
1959 else if ( unitName.compare( QLatin1String(
"Statute mile" ), Qt::CaseInsensitive ) == 0 )
1961 else if ( unitName.compare( QLatin1String(
"nautical mile" ), Qt::CaseInsensitive ) == 0 )
1963 else if ( unitName.compare( QLatin1String(
"yard" ), Qt::CaseInsensitive ) == 0 )
1965 else if ( unitName.compare( QLatin1String(
"fathom" ), Qt::CaseInsensitive ) == 0 )
1967 else if ( unitName.compare( QLatin1String(
"US survey chain" ), Qt::CaseInsensitive ) == 0 )
1969 else if ( unitName.compare( QLatin1String(
"chain" ), Qt::CaseInsensitive ) == 0 )
1971 else if ( unitName.compare( QLatin1String(
"link" ), Qt::CaseInsensitive ) == 0 )
1973 else if ( unitName.compare( QLatin1String(
"US survey link" ), Qt::CaseInsensitive ) == 0 )
1975 else if ( unitName.compare( QLatin1String(
"US survey mile" ), Qt::CaseInsensitive ) == 0 )
1977 else if ( unitName.compare( QLatin1String(
"German legal metre" ), Qt::CaseInsensitive ) == 0 )
1994 if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull()
1997 QgsDebugMsgLevel(
"QgsCoordinateReferenceSystem::findMatchingProj will only "
1998 "work if prj acr ellipsoid acr and proj4string are set"
1999 " and the current projection is valid!", 4 );
2009 QString mySql = QString(
"select srs_id,parameters from tbl_srs where "
2010 "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated" )
2017 myResult = openDatabase( myDatabaseFileName, database );
2018 if ( myResult != SQLITE_OK )
2023 statement = database.
prepare( mySql, myResult );
2024 if ( myResult == SQLITE_OK )
2027 while ( statement.
step() == SQLITE_ROW )
2031 if (
toProj() == myProj4String.trimmed() )
2033 return mySrsId.toLong();
2044 myResult = openDatabase( myDatabaseFileName, database );
2045 if ( myResult != SQLITE_OK )
2050 statement = database.
prepare( mySql, myResult );
2052 if ( myResult == SQLITE_OK )
2054 while ( statement.
step() == SQLITE_ROW )
2058 if (
toProj() == myProj4String.trimmed() )
2060 return mySrsId.toLong();
2074 if ( !d->mIsValid && !srs.d->mIsValid )
2077 if ( !d->mIsValid || !srs.d->mIsValid )
2085 if ( isUser != otherIsUser )
2089 if ( !isUser && ( !d->mAuthId.isEmpty() || !srs.d->mAuthId.isEmpty() ) )
2090 return d->mAuthId == srs.d->mAuthId;
2097 return !( *
this == srs );
2102 if (
PJ *obj = d->threadLocalProjObject() )
2105 if ( isDefaultPreferredFormat && !d->mWktPreferred.isEmpty() )
2108 return d->mWktPreferred;
2111 PJ_WKT_TYPE
type = PJ_WKT1_GDAL;
2115 type = PJ_WKT1_GDAL;
2118 type = PJ_WKT1_ESRI;
2121 type = PJ_WKT2_2015;
2124 type = PJ_WKT2_2015_SIMPLIFIED;
2127 type = PJ_WKT2_2019;
2130 type = PJ_WKT2_2019_SIMPLIFIED;
2134 const QByteArray multiLineOption = QStringLiteral(
"MULTILINE=%1" ).arg( multiline ? QStringLiteral(
"YES" ) : QStringLiteral(
"NO" ) ).toLocal8Bit();
2135 const QByteArray indentatationWidthOption = QStringLiteral(
"INDENTATION_WIDTH=%1" ).arg( multiline ? QString::number( indentationWidth ) : QStringLiteral(
"0" ) ).toLocal8Bit();
2136 const char *
const options[] = {multiLineOption.constData(), indentatationWidthOption.constData(),
nullptr};
2139 if ( isDefaultPreferredFormat )
2142 d->mWktPreferred = res;
2154 QDomNode srsNode = node.namedItem( QStringLiteral(
"spatialrefsys" ) );
2156 if ( ! srsNode.isNull() )
2158 bool initialized =
false;
2161 long srsid = srsNode.namedItem( QStringLiteral(
"srsid" ) ).toElement().text().toLong( &ok );
2167 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
2168 if ( !node.isNull() )
2179 node = srsNode.namedItem( QStringLiteral(
"epsg" ) );
2180 if ( !node.isNull() )
2198 const QString
description = srsNode.namedItem( QStringLiteral(
"description" ) ).toElement().text();
2200 const QString wkt = srsNode.namedItem( QStringLiteral(
"wkt" ) ).toElement().text();
2201 initialized = createFromWktInternal( wkt,
description );
2206 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
2207 const QString proj4 = node.toElement().text();
2214 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
2215 const QString proj4 = node.toElement().text();
2216 if ( !proj4.trimmed().isEmpty() )
2217 setProjString( node.toElement().text() );
2219 node = srsNode.namedItem( QStringLiteral(
"srsid" ) );
2220 d->mSrsId = node.toElement().text().toLong();
2222 node = srsNode.namedItem( QStringLiteral(
"srid" ) );
2223 d->mSRID = node.toElement().text().toLong();
2225 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
2226 d->mAuthId = node.toElement().text();
2228 node = srsNode.namedItem( QStringLiteral(
"description" ) );
2229 d->mDescription = node.toElement().text();
2231 node = srsNode.namedItem( QStringLiteral(
"projectionacronym" ) );
2232 d->mProjectionAcronym = node.toElement().text();
2234 node = srsNode.namedItem( QStringLiteral(
"ellipsoidacronym" ) );
2235 d->mEllipsoidAcronym = node.toElement().text();
2237 node = srsNode.namedItem( QStringLiteral(
"geographicflag" ) );
2238 d->mIsGeographic = node.toElement().text() == QLatin1String(
"true" );
2240 d->mWktPreferred.clear();
2246 const QString epoch = srsNode.toElement().attribute( QStringLiteral(
"coordinateEpoch" ) );
2247 if ( !epoch.isEmpty() )
2249 bool epochOk =
false;
2250 d->mCoordinateEpoch = epoch.toDouble( &epochOk );
2252 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2256 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2264 d =
new QgsCoordinateReferenceSystemPrivate();
2272 QDomElement layerNode = node.toElement();
2273 QDomElement srsElement = doc.createElement( QStringLiteral(
"spatialrefsys" ) );
2277 if ( std::isfinite( d->mCoordinateEpoch ) )
2279 srsElement.setAttribute( QStringLiteral(
"coordinateEpoch" ), d->mCoordinateEpoch );
2282 QDomElement wktElement = doc.createElement( QStringLiteral(
"wkt" ) );
2284 srsElement.appendChild( wktElement );
2286 QDomElement proj4Element = doc.createElement( QStringLiteral(
"proj4" ) );
2287 proj4Element.appendChild( doc.createTextNode(
toProj() ) );
2288 srsElement.appendChild( proj4Element );
2290 QDomElement srsIdElement = doc.createElement( QStringLiteral(
"srsid" ) );
2291 srsIdElement.appendChild( doc.createTextNode( QString::number(
srsid() ) ) );
2292 srsElement.appendChild( srsIdElement );
2294 QDomElement sridElement = doc.createElement( QStringLiteral(
"srid" ) );
2295 sridElement.appendChild( doc.createTextNode( QString::number(
postgisSrid() ) ) );
2296 srsElement.appendChild( sridElement );
2298 QDomElement authidElement = doc.createElement( QStringLiteral(
"authid" ) );
2299 authidElement.appendChild( doc.createTextNode(
authid() ) );
2300 srsElement.appendChild( authidElement );
2302 QDomElement descriptionElement = doc.createElement( QStringLiteral(
"description" ) );
2303 descriptionElement.appendChild( doc.createTextNode(
description() ) );
2304 srsElement.appendChild( descriptionElement );
2306 QDomElement projectionAcronymElement = doc.createElement( QStringLiteral(
"projectionacronym" ) );
2307 projectionAcronymElement.appendChild( doc.createTextNode(
projectionAcronym() ) );
2308 srsElement.appendChild( projectionAcronymElement );
2310 QDomElement ellipsoidAcronymElement = doc.createElement( QStringLiteral(
"ellipsoidacronym" ) );
2311 ellipsoidAcronymElement.appendChild( doc.createTextNode(
ellipsoidAcronym() ) );
2312 srsElement.appendChild( ellipsoidAcronymElement );
2314 QDomElement geographicFlagElement = doc.createElement( QStringLiteral(
"geographicflag" ) );
2315 QString geoFlagText = QStringLiteral(
"false" );
2318 geoFlagText = QStringLiteral(
"true" );
2321 geographicFlagElement.appendChild( doc.createTextNode( geoFlagText ) );
2322 srsElement.appendChild( geographicFlagElement );
2324 layerNode.appendChild( srsElement );
2336QString QgsCoordinateReferenceSystem::projFromSrsId(
const int srsId )
2338 QString myDatabaseFileName;
2339 QString myProjString;
2340 QString mySql = QStringLiteral(
"select parameters from tbl_srs where srs_id = %1 order by deprecated" ).arg( srsId );
2349 QFileInfo myFileInfo;
2350 myFileInfo.setFile( myDatabaseFileName );
2351 if ( !myFileInfo.exists() )
2353 QgsDebugError( QStringLiteral(
"users qgis.db not found" ) );
2362 sqlite3_database_unique_ptr database;
2363 sqlite3_statement_unique_ptr statement;
2366 rc = openDatabase( myDatabaseFileName, database );
2372 statement = database.
prepare( mySql, rc );
2374 if ( rc == SQLITE_OK )
2376 if ( statement.
step() == SQLITE_ROW )
2382 return myProjString;
2389 myResult = database.
open_v2( path, SQLITE_OPEN_READONLY,
nullptr );
2391 myResult = database.
open( path );
2393 if ( myResult != SQLITE_OK )
2402 .arg( database.
errorMessage() ), QObject::tr(
"CRS" ) );
2409 sCustomSrsValidation = f;
2414 return sCustomSrsValidation;
2417void QgsCoordinateReferenceSystem::debugPrint()
2420 QgsDebugMsgLevel(
"* Valid : " + ( d->mIsValid ? QString(
"true" ) : QString(
"false" ) ), 1 );
2441 mValidationHint = html;
2446 return mValidationHint;
2456 mNativeFormat = format;
2461 return mNativeFormat;
2464long QgsCoordinateReferenceSystem::getRecordCount()
2469 long myRecordCount = 0;
2472 if ( myResult != SQLITE_OK )
2478 QString mySql = QStringLiteral(
"select count(*) from tbl_srs" );
2479 statement = database.
prepare( mySql, myResult );
2480 if ( myResult == SQLITE_OK )
2482 if ( statement.
step() == SQLITE_ROW )
2484 QString myRecordCountString = statement.
columnAsText( 0 );
2485 myRecordCount = myRecordCountString.toLong();
2488 return myRecordCount;
2494 bool isGeographic =
false;
2498 if ( !horizontalCrs )
2502 if ( coordinateSystem )
2504 const int axisCount = proj_cs_get_axis_count( pjContext, coordinateSystem.get() );
2505 if ( axisCount > 0 )
2507 const char *outUnitAuthName =
nullptr;
2508 const char *outUnitAuthCode =
nullptr;
2510 proj_cs_get_axis_info( pjContext, coordinateSystem.get(), 0,
2519 if ( outUnitAuthName && outUnitAuthCode )
2521 const char *unitCategory =
nullptr;
2522 if ( proj_uom_get_info_from_database( pjContext, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
2524 isGeographic = QString( unitCategory ).compare( QLatin1String(
"angular" ), Qt::CaseInsensitive ) == 0;
2529 return isGeographic;
2534 thread_local const QRegularExpression projRegExp( QStringLiteral(
"\\+proj=(\\S+)" ) );
2535 const QRegularExpressionMatch projMatch = projRegExp.match( proj );
2536 if ( !projMatch.hasMatch() )
2538 QgsDebugMsgLevel( QStringLiteral(
"no +proj argument found [%2]" ).arg( proj ), 2 );
2541 operation = projMatch.captured( 1 );
2543 const QRegularExpressionMatch ellipseMatch = projRegExp.match( proj );
2544 if ( ellipseMatch.hasMatch() )
2546 ellipsoid = ellipseMatch.captured( 1 );
2560bool QgsCoordinateReferenceSystem::loadFromAuthCode(
const QString &auth,
const QString &code )
2566 d->mIsValid =
false;
2567 d->mWktPreferred.clear();
2570 QgsProjUtils::proj_pj_unique_ptr crs( proj_create_from_database( pjContext, auth.toUtf8().constData(), code.toUtf8().constData(), PJ_CATEGORY_CRS,
false,
nullptr ) );
2579 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2580 proj4 = proj4.trimmed();
2584 d->mWktPreferred.clear();
2585 d->mDescription = QString( proj_get_name( crs.get() ) );
2586 d->mAuthId = QStringLiteral(
"%1:%2" ).arg( auth, code );
2588 d->mAxisInvertedDirty =
true;
2593 d->mEllipsoidAcronym.clear();
2594 d->setPj( std::move( crs ) );
2596 const QString dbVals =
sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( auth, code ).toUpper() );
2597 if ( !dbVals.isEmpty() )
2599 const QStringList parts = dbVals.split(
',' );
2600 d->mSrsId = parts.at( 0 ).toInt();
2601 d->mSRID = parts.at( 1 ).toInt();
2609QList<long> QgsCoordinateReferenceSystem::userSrsIds()
2611 QList<long> results;
2615 QFileInfo myInfo( db );
2616 if ( !myInfo.exists() )
2622 sqlite3_database_unique_ptr database;
2623 sqlite3_statement_unique_ptr statement;
2626 int result = openDatabase( db, database );
2627 if ( result != SQLITE_OK )
2629 QgsDebugError(
"failed : " + db +
" could not be opened!" );
2633 QString sql = QStringLiteral(
"select srs_id from tbl_srs where srs_id >= %1" ).arg(
Qgis::USER_CRS_START_ID );
2635 statement = database.
prepare( sql, rc );
2638 int ret = statement.
step();
2640 if ( ret == SQLITE_DONE )
2646 if ( ret == SQLITE_ROW )
2652 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
2660long QgsCoordinateReferenceSystem::matchToUserCrs()
const
2662 PJ *obj = d->threadLocalProjObject();
2666 const QList< long > ids = userSrsIds();
2667 for (
long id : ids )
2670 if ( candidate.
projObject() && proj_is_equivalent_to( obj, candidate.
projObject(), PJ_COMP_EQUIVALENT ) )
2678static void sync_db_proj_logger(
void * ,
int level,
const char *message )
2683 if ( level == PJ_LOG_ERROR )
2687 else if ( level == PJ_LOG_DEBUG )
2695 setlocale( LC_ALL,
"C" );
2698 int inserted = 0, updated = 0, deleted = 0, errors = 0;
2703 if ( database.
open( dbFilePath ) != SQLITE_OK )
2709 if ( sqlite3_exec( database.get(),
"BEGIN TRANSACTION",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2717 char *errMsg =
nullptr;
2719 bool createdTypeColumn =
false;
2720 if ( sqlite3_exec( database.get(),
"ALTER TABLE tbl_srs ADD COLUMN srs_type text",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2722 createdTypeColumn =
true;
2723 if ( sqlite3_exec( database.get(),
"CREATE INDEX srs_type ON tbl_srs(srs_type)",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2725 QgsDebugError( QStringLiteral(
"Could not create index for srs_type" ) );
2730 if ( sqlite3_exec( database.get(),
"create table tbl_info (proj_major INT, proj_minor INT, proj_patch INT)",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2732 QString sql = QStringLiteral(
"INSERT INTO tbl_info(proj_major, proj_minor, proj_patch) VALUES (%1, %2,%3)" )
2733 .arg( QString::number( PROJ_VERSION_MAJOR ),
2734 QString::number( PROJ_VERSION_MINOR ),
2735 QString::number( PROJ_VERSION_PATCH ) );
2736 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2738 QgsDebugError( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2741 errMsg ? errMsg :
"(unknown error)" ) );
2743 sqlite3_free( errMsg );
2750 QString sql = QStringLiteral(
"SELECT proj_major, proj_minor, proj_patch FROM tbl_info" );
2751 statement = database.
prepare( sql, result );
2752 if ( result != SQLITE_OK )
2757 if ( statement.
step() == SQLITE_ROW )
2762 if ( !createdTypeColumn && major == PROJ_VERSION_MAJOR && minor == PROJ_VERSION_MINOR && patch == PROJ_VERSION_PATCH )
2768 QgsDebugError( QStringLiteral(
"Could not retrieve previous CRS sync PROJ version number" ) );
2775 proj_log_func( pjContext,
nullptr, sync_db_proj_logger );
2777 PROJ_STRING_LIST authorities = proj_get_authorities_from_database( pjContext );
2779 int nextSrsId = 67218;
2780 int nextSrId = 520007218;
2781 for (
auto authIter = authorities; authIter && *authIter; ++authIter )
2783 const QString authority( *authIter );
2784 QgsDebugMsgLevel( QStringLiteral(
"Loading authority '%1'" ).arg( authority ), 2 );
2785 PROJ_STRING_LIST codes = proj_get_codes_from_database( pjContext, *authIter, PJ_TYPE_CRS,
true );
2787 QStringList allCodes;
2789 for (
auto codesIter = codes; codesIter && *codesIter; ++codesIter )
2791 const QString code( *codesIter );
2797 QgsDebugError( QStringLiteral(
"Could not load '%1:%2'" ).arg( authority, code ) );
2801 const PJ_TYPE pjType = proj_get_type( crs.get( ) );
2803 QString srsTypeString;
2808 case PJ_TYPE_ELLIPSOID:
2809 case PJ_TYPE_PRIME_MERIDIAN:
2810 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
2811 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
2812 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
2813 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
2814 case PJ_TYPE_DATUM_ENSEMBLE:
2815 case PJ_TYPE_CONVERSION:
2816 case PJ_TYPE_TRANSFORMATION:
2817 case PJ_TYPE_CONCATENATED_OPERATION:
2818 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
2819 case PJ_TYPE_TEMPORAL_DATUM:
2820 case PJ_TYPE_ENGINEERING_DATUM:
2821 case PJ_TYPE_PARAMETRIC_DATUM:
2822 case PJ_TYPE_UNKNOWN:
2826 case PJ_TYPE_GEOGRAPHIC_CRS:
2829 case PJ_TYPE_GEODETIC_CRS:
2833 case PJ_TYPE_GEOCENTRIC_CRS:
2837 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
2841 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
2845 case PJ_TYPE_PROJECTED_CRS:
2849 case PJ_TYPE_COMPOUND_CRS:
2853 case PJ_TYPE_TEMPORAL_CRS:
2857 case PJ_TYPE_ENGINEERING_CRS:
2861 case PJ_TYPE_BOUND_CRS:
2865 case PJ_TYPE_VERTICAL_CRS:
2869#if PROJ_VERSION_MAJOR>9 || (PROJ_VERSION_MAJOR==9 && PROJ_VERSION_MINOR>=2)
2870 case PJ_TYPE_DERIVED_PROJECTED_CRS:
2873 case PJ_TYPE_COORDINATE_METADATA:
2876 case PJ_TYPE_OTHER_CRS:
2885 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2886 proj4 = proj4.trimmed();
2888 if ( proj4.isEmpty() )
2890 QgsDebugMsgLevel( QStringLiteral(
"No proj4 for '%1:%2'" ).arg( authority, code ), 2 );
2901 if ( translatedOperation.isEmpty() && !
operation.isEmpty() )
2903 std::cout << QStringLiteral(
"Operation needs translation in QgsCoordinateReferenceSystemUtils::translateProjection: %1" ).arg(
operation ).toLocal8Bit().constData() << std::endl;
2904 qFatal(
"aborted" );
2907 const bool deprecated = proj_is_deprecated( crs.get() );
2908 const QString name( proj_get_name( crs.get() ) );
2910 QString sql = QStringLiteral(
"SELECT parameters,description,deprecated,srs_type,projection_acronym FROM tbl_srs WHERE auth_name='%1' AND auth_id='%2'" ).arg( authority, code );
2911 statement = database.
prepare( sql, result );
2912 if ( result != SQLITE_OK )
2921 QString dbOperation;
2922 bool dbSrsDeprecated = deprecated;
2923 if ( statement.
step() == SQLITE_ROW )
2927 dbSrsDeprecated = statement.
columnAsText( 2 ).toInt() != 0;
2932 if ( !dbSrsProj4.isEmpty() || !dbSrsDesc.isEmpty() )
2934 if ( proj4 != dbSrsProj4 || name != dbSrsDesc || deprecated != dbSrsDeprecated || dbSrsType != srsTypeString || dbOperation !=
operation )
2937 sql = QStringLiteral(
"UPDATE tbl_srs SET parameters=%1,description=%2,deprecated=%3, srs_type=%4,projection_acronym=%5 WHERE auth_name=%6 AND auth_id=%7" )
2940 .arg( deprecated ? 1 : 0 )
2945 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2947 QgsDebugError( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2950 errMsg ? errMsg :
"(unknown error)" ) );
2952 sqlite3_free( errMsg );
2966 const QString dbVals =
sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( authority, code ) );
2969 if ( !dbVals.isEmpty() )
2971 const QStringList parts = dbVals.split(
',' );
2972 srsId = parts.at( 0 );
2973 srId = parts.at( 1 );
2975 if ( srId.isEmpty() )
2977 srId = QString::number( nextSrId );
2980 if ( srsId.isEmpty() )
2982 srsId = QString::number( nextSrsId );
2986 if ( !srsId.isEmpty() )
2988 sql = QStringLiteral(
"INSERT INTO tbl_srs(srs_id, description,projection_acronym,ellipsoid_acronym,parameters,srid,auth_name,auth_id,is_geo,deprecated,srs_type) VALUES (%1, %2,%3,%4,%5,%6,%7,%8,%9,%10,%11)" )
2998 .arg( deprecated ? 1 : 0 )
3003 sql = QStringLiteral(
"INSERT INTO tbl_srs(description,projection_acronym,ellipsoid_acronym,parameters,srid,auth_name,auth_id,is_geo,deprecated,srs_type) VALUES (%1,%2,%3,%4,%5,%6,%7,%8,%9,%10)" )
3012 .arg( deprecated ? 1 : 0 )
3017 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) == SQLITE_OK )
3023 qCritical(
"Could not execute: %s [%s/%s]\n",
3024 sql.toLocal8Bit().constData(),
3025 sqlite3_errmsg( database.get() ),
3026 errMsg ? errMsg :
"(unknown error)" );
3030 sqlite3_free( errMsg );
3035 proj_string_list_destroy( codes );
3037 const QString sql = QStringLiteral(
"DELETE FROM tbl_srs WHERE auth_name='%1' AND NOT auth_id IN (%2)" ).arg( authority, allCodes.join(
',' ) );
3038 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
3040 deleted = sqlite3_changes( database.get() );
3045 qCritical(
"Could not execute: %s [%s]\n",
3046 sql.toLocal8Bit().constData(),
3047 sqlite3_errmsg( database.get() ) );
3051 proj_string_list_destroy( authorities );
3053 QString sql = QStringLiteral(
"UPDATE tbl_info set proj_major=%1,proj_minor=%2,proj_patch=%3" )
3054 .arg( QString::number( PROJ_VERSION_MAJOR ),
3055 QString::number( PROJ_VERSION_MINOR ),
3056 QString::number( PROJ_VERSION_PATCH ) );
3057 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
3059 QgsDebugError( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
3062 errMsg ? errMsg :
"(unknown error)" ) );
3064 sqlite3_free( errMsg );
3068 if ( sqlite3_exec( database.get(),
"COMMIT",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
3070 QgsDebugError( QStringLiteral(
"Could not commit transaction: %1 [%2]\n" ).arg(
3072 sqlite3_errmsg( database.get() ) )
3078 QgsDebugMsgLevel( QStringLiteral(
"CRS update (inserted:%1 updated:%2 deleted:%3 errors:%4)" ).arg( inserted ).arg( updated ).arg( deleted ).arg( errors ), 4 );
3086 return updated + inserted;
3089const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::stringCache()
3091 return *sStringCache();
3094const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::projCache()
3096 return *sProj4Cache();
3099const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::ogcCache()
3101 return *sOgcCache();
3104const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::wktCache()
3106 return *sWktCache();
3109const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srIdCache()
3111 return *sSrIdCache();
3114const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srsIdCache()
3116 return *sSrsIdCache();
3126 if (
PJ *obj = d->threadLocalProjObject() )
3133 const PJ_TYPE pjType = proj_get_type( geoCrs.get( ) );
3134 if ( pjType == PJ_TYPE_GEOCENTRIC_CRS )
3140 pjContext, PJ_ELLPS2D_LONGITUDE_LATITUDE,
"Degree", 1.0 ) );
3144 pjContext,
nullptr, datum ? datum.get() :
datumEnsemble.get(),
3147 if ( !geoGraphicCrs )
3174 if (
PJ *obj = d->threadLocalProjObject() )
3228 if (
PJ *obj = d->threadLocalProjObject() )
3261 if (
PJ *obj = d->threadLocalProjObject() )
3272 if (
PJ *obj = d->threadLocalProjObject() )
3285 else if (
PJ *obj = d->threadLocalProjObject() )
3288 return geoCrs ? QStringLiteral(
"%1:%2" ).arg( proj_get_id_auth_name( geoCrs.get(), 0 ), proj_get_id_code( geoCrs.get(), 0 ) ) : QString();
3298 return d->threadLocalProjObject();
3311 d->mIsValid =
false;
3313 d->mWktPreferred.clear();
3320 switch ( proj_get_type(
object ) )
3322 case PJ_TYPE_GEODETIC_CRS:
3323 case PJ_TYPE_GEOCENTRIC_CRS:
3324 case PJ_TYPE_GEOGRAPHIC_CRS:
3325 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
3326 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
3327 case PJ_TYPE_VERTICAL_CRS:
3328 case PJ_TYPE_PROJECTED_CRS:
3329 case PJ_TYPE_COMPOUND_CRS:
3330 case PJ_TYPE_TEMPORAL_CRS:
3331 case PJ_TYPE_ENGINEERING_CRS:
3332 case PJ_TYPE_BOUND_CRS:
3333 case PJ_TYPE_OTHER_CRS:
3349 const QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
3350 const QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
3351 if ( !authName.isEmpty() && !authCode.isEmpty() &&
createFromOgcWmsCrs( QStringLiteral(
"%1:%2" ).arg( authName, authCode ) ) )
3359 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
3370 QStringList projections;
3372 projections.reserve( res.size() );
3375 projections << QString::number( crs.srsid() );
3402 sSrIdCacheLock()->lockForWrite();
3403 if ( !sDisableSrIdCache )
3406 sDisableSrIdCache =
true;
3407 sSrIdCache()->clear();
3409 sSrIdCacheLock()->unlock();
3411 sOgcLock()->lockForWrite();
3412 if ( !sDisableOgcCache )
3415 sDisableOgcCache =
true;
3416 sOgcCache()->clear();
3418 sOgcLock()->unlock();
3420 sProj4CacheLock()->lockForWrite();
3421 if ( !sDisableProjCache )
3424 sDisableProjCache =
true;
3425 sProj4Cache()->clear();
3427 sProj4CacheLock()->unlock();
3429 sCRSWktLock()->lockForWrite();
3430 if ( !sDisableWktCache )
3433 sDisableWktCache =
true;
3434 sWktCache()->clear();
3436 sCRSWktLock()->unlock();
3438 sCRSSrsIdLock()->lockForWrite();
3439 if ( !sDisableSrsIdCache )
3442 sDisableSrsIdCache =
true;
3443 sSrsIdCache()->clear();
3445 sCRSSrsIdLock()->unlock();
3447 sCrsStringLock()->lockForWrite();
3448 if ( !sDisableStringCache )
3451 sDisableStringCache =
true;
3452 sStringCache()->clear();
3454 sCrsStringLock()->unlock();
3463 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3466 if ( !c1.d->mIsValid && c2.d->mIsValid )
3469 if ( c1.d->mIsValid && !c2.d->mIsValid )
3475 if ( c1IsUser && !c2IsUser )
3478 if ( !c1IsUser && c2IsUser )
3481 if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
3483 if ( c1.d->mAuthId != c2.d->mAuthId )
3484 return c1.d->mAuthId > c2.d->mAuthId;
3492 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3495 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3498 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3501 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3504 return c1.d->mCoordinateEpoch > c2.d->mCoordinateEpoch;
3512 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3515 if ( c1.d->mIsValid && !c2.d->mIsValid )
3518 if ( !c1.d->mIsValid && c2.d->mIsValid )
3524 if ( !c1IsUser && c2IsUser )
3527 if ( c1IsUser && !c2IsUser )
3530 if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
3532 if ( c1.d->mAuthId != c2.d->mAuthId )
3533 return c1.d->mAuthId < c2.d->mAuthId;
3541 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3544 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3547 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3550 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3553 return c1.d->mCoordinateEpoch < c2.d->mCoordinateEpoch;
3558 return !( c1 < c2 );
3562 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.
static Q_DECL_DEPRECATED void pushRecentCoordinateReferenceSystem(const QgsCoordinateReferenceSystem &crs)
Pushes a recently used CRS to the top of the recent CRS list.
long postgisSrid() const
Returns PostGIS SRID for the CRS.
QgsCoordinateReferenceSystem horizontalCrs() const
Returns the horizontal CRS associated with this CRS object.
Q_DECL_DEPRECATED long findMatchingProj()
Walks the CRS databases (both system and user database) trying to match stored PROJ string to a datab...
static QList< long > validSrsIds()
Returns a list of all valid SRS IDs present in the CRS database.
QgsProjectionFactors factors(const QgsPoint &point) const
Calculate various cartographic properties, such as scale factors, angular distortion and meridian con...
void setValidationHint(const QString &html)
Set user hint for validation.
Q_DECL_DEPRECATED QString toProj4() const
Returns a Proj string representation of this CRS.
bool operator==(const QgsCoordinateReferenceSystem &srs) const
QString projectionAcronym() const
Returns the projection acronym for the projection used by the CRS.
QString userFriendlyIdentifier(Qgis::CrsIdentifierType type=Qgis::CrsIdentifierType::MediumString) const
Returns a user friendly identifier for the CRS.
Qgis::CrsDefinitionFormat nativeFormat() const
Returns the native format for the CRS definition.
CrsType
Enumeration of types of IDs accepted in createFromId() method.
@ InternalCrsId
Internal ID used by QGIS in the local SQLite database.
@ PostgisCrsId
SRID used in PostGIS. DEPRECATED – DO NOT USE.
bool createFromUserInput(const QString &definition)
Set up this CRS from various text formats.
QgsCoordinateReferenceSystem()
Constructs an invalid CRS object.
static int syncDatabase()
Update proj.4 parameters in our database from proj.4.
bool operator!=(const QgsCoordinateReferenceSystem &srs) const
void setNativeFormat(Qgis::CrsDefinitionFormat format)
Sets the native format for the CRS definition.
bool createFromProj(const QString &projString, bool identify=true)
Sets this CRS by passing it a PROJ style formatted string.
QgsCoordinateReferenceSystem verticalCrs() const
Returns the vertical CRS associated with this CRS object.
static Q_DECL_DEPRECATED void removeRecentCoordinateReferenceSystem(const QgsCoordinateReferenceSystem &crs)
Removes a CRS from the list of recently used CRS.
QgsDatumEnsemble datumEnsemble() const
Attempts to retrieve datum ensemble details from the CRS.
QgsCoordinateReferenceSystem toGeographicCrs() const
Returns the geographic CRS associated with this CRS object.
bool isDynamic() const
Returns true if the CRS is a dynamic CRS.
bool createFromSrsId(long srsId)
Sets this CRS by lookup of internal QGIS CRS ID in the CRS database.
Q_DECL_DEPRECATED bool createFromId(long id, CrsType type=PostgisCrsId)
Sets this CRS by lookup of the given ID in the CRS database.
static QgsCoordinateReferenceSystem fromProjObject(PJ *object)
Constructs a QgsCoordinateReferenceSystem from a PROJ PJ object.
static void invalidateCache(bool disableCache=false)
Clears the internal cache used to initialize QgsCoordinateReferenceSystem objects.
QgsCoordinateReferenceSystem & operator=(const QgsCoordinateReferenceSystem &srs)
void updateDefinition()
Updates the definition and parameters of the coordinate reference system to their latest values.
static QgsCoordinateReferenceSystem fromProj(const QString &proj)
Creates a CRS from a proj style formatted string.
static Q_DECL_DEPRECATED void setupESRIWktFix()
Make sure that ESRI WKT import is done properly.
static Q_DECL_DEPRECATED QList< QgsCoordinateReferenceSystem > recentCoordinateReferenceSystems()
Returns a list of recently used CRS.
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
PJ * projObject() const
Returns the underlying PROJ PJ object corresponding to the CRS, or nullptr if the CRS is invalid.
void setCoordinateEpoch(double epoch)
Sets the coordinate epoch, as a decimal year.
Q_DECL_DEPRECATED bool createFromSrid(long srid)
Sets this CRS by lookup of the given PostGIS SRID in the CRS database.
long saveAsUserCrs(const QString &name, Qgis::CrsDefinitionFormat nativeFormat=Qgis::CrsDefinitionFormat::Wkt)
Saves the CRS as a new custom ("USER") CRS.
std::string toJsonString(bool multiline=false, int indentationWidth=4, const QString &schema=QString()) const
Returns a JSON string representation of this CRS.
static Q_DECL_DEPRECATED void clearRecentCoordinateReferenceSystems()
Cleans the list of recently used CRS.
QString celestialBodyName() const
Attempts to retrieve the name of the celestial body associated with the CRS (e.g.
static QgsCoordinateReferenceSystem fromWkt(const QString &wkt)
Creates a CRS from a WKT spatial ref sys definition string.
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
static QgsCoordinateReferenceSystem createGeocentricCrs(const QString &ellipsoid)
Creates a geocentric CRS given an ellipsoid definition.
static QgsCoordinateReferenceSystem fromSrsId(long srsId)
Creates a CRS from a specified QGIS SRS ID.
double coordinateEpoch() const
Returns the coordinate epoch, as a decimal year.
QString geographicCrsAuthId() const
Returns auth id of related geographic CRS.
QString validationHint() const
Gets user hint for validation.
QgsProjOperation operation() const
Returns information about the PROJ operation associated with the coordinate reference system,...
QgsCoordinateReferenceSystem toGeocentricCrs() const
Returns a new geocentric CRS based on this CRS object.
QList< Qgis::CrsAxisDirection > axisOrdering() const
Returns an ordered list of the axis directions reflecting the native axis order for the CRS.
long srsid() const
Returns the internal CRS ID, if available.
Qgis::CrsType type() const
Returns the type of the CRS.
Qgis::DistanceUnit mapUnits
bool hasAxisInverted() const
Returns whether the axis order is inverted for the CRS compared to the order east/north (longitude/la...
bool createFromProjObject(PJ *object)
Sets this CRS by passing it a PROJ PJ object, corresponding to a PROJ CRS object.
bool isDeprecated() const
Returns true if the CRS is considered deprecated.
static Q_DECL_DEPRECATED QgsCoordinateReferenceSystem fromProj4(const QString &proj4)
Creates a CRS from a proj style formatted string.
Contains information about a member of a datum ensemble.
Contains information about a datum ensemble.
static EllipsoidParameters ellipsoidParameters(const QString &ellipsoid)
Returns the parameters for the specified ellipsoid.
Sets the current locale to the c locale for the lifetime of the object.
static void warning(const QString &msg)
Goes to qWarning.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
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.