22#include <proj_experimental.h>
49#include <QRegularExpression>
53#include "moc_qgscoordinatereferencesystem.cpp"
55using namespace Qt::StringLiterals;
58#include <ogr_srs_api.h>
70bool QgsCoordinateReferenceSystem::sDisableSrIdCache =
false;
74bool QgsCoordinateReferenceSystem::sDisableOgcCache =
false;
78bool QgsCoordinateReferenceSystem::sDisableProjCache =
false;
82bool QgsCoordinateReferenceSystem::sDisableWktCache =
false;
86bool QgsCoordinateReferenceSystem::sDisableSrsIdCache =
false;
90bool QgsCoordinateReferenceSystem::sDisableStringCache =
false;
99 if (
const char *proj4src = proj_as_proj_string(
QgsProjContext::get(), boundCrs.get(), PJ_PROJ_4,
nullptr ) )
101 return QString( proj4src );
118 d =
new QgsCoordinateReferenceSystemPrivate();
124 d =
new QgsCoordinateReferenceSystemPrivate();
132 , mValidationHint( srs.mValidationHint )
133 , mNativeFormat( srs.mNativeFormat )
142 mValidationHint = srs.mValidationHint;
143 mNativeFormat = srs.mNativeFormat;
153 const auto constDbs = dbs;
154 for (
const QString &db : constDbs )
156 QFileInfo myInfo( db );
157 if ( !myInfo.exists() )
167 int result = openDatabase( db, database );
168 if ( result != SQLITE_OK )
174 QString sql = u
"select srs_id from tbl_srs"_s;
176 statement = database.
prepare( sql, rc );
180 int ret = statement.
step();
182 if ( ret == SQLITE_DONE )
188 if ( ret == SQLITE_ROW )
194 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
199 std::sort( results.begin(), results.end() );
255 if ( horizontalObj && verticalObj )
262 QStringList formattedErrorList;
263 for (
const QString &rawError : std::as_const( errors ) )
265 QString formattedError = rawError;
266 formattedError.replace(
"proj_create_compound_crs: "_L1, QString() );
267 formattedErrorList.append( formattedError );
269 error = formattedErrorList.join(
'\n' );
277 if ( !ellipsoidParams.
valid )
325 if ( definition.isEmpty() )
329 if ( !sDisableStringCache )
331 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sStringCache()->constFind( definition );
332 if ( crsIt != sStringCache()->constEnd() )
335 *
this = crsIt.value();
342 const thread_local QRegularExpression reCrsId( u
"^(epsg|esri|osgeo|ignf|ogc|nkg|zangi|iau_2015|iau2000|postgis|internal|user)\\:(\\w+)$"_s, QRegularExpression::CaseInsensitiveOption );
343 QRegularExpressionMatch match = reCrsId.match( definition );
344 if ( match.capturedStart() == 0 )
346 QString authName = match.captured( 1 ).toLower();
347 if ( authName ==
"epsg"_L1 )
351 else if ( authName ==
"postgis"_L1 )
353 const long id = match.captured( 2 ).toLong();
358 else if ( authName ==
"esri"_L1
359 || authName ==
"osgeo"_L1
360 || authName ==
"ignf"_L1
361 || authName ==
"zangi"_L1
362 || authName ==
"iau2000"_L1
363 || authName ==
"ogc"_L1
364 || authName ==
"nkg"_L1
365 || authName ==
"iau_2015"_L1 )
371 const long id = match.captured( 2 ).toLong();
379 const thread_local QRegularExpression reCrsStr( u
"^(?:(wkt|proj4|proj)\\:)?(.+)$"_s, QRegularExpression::CaseInsensitiveOption );
380 match = reCrsStr.match( definition );
381 if ( match.capturedStart() == 0 )
383 if ( match.captured( 1 ).startsWith(
"proj"_L1, Qt::CaseInsensitive ) )
395 if ( !sDisableStringCache )
396 sStringCache()->insert( definition, *
this );
402 if ( definition.isEmpty() )
406 OGRSpatialReferenceH crs = OSRNewSpatialReference(
nullptr );
408 if ( OSRSetFromUserInput( crs, definition.toLocal8Bit().constData() ) == OGRERR_NONE )
411 OSRDestroySpatialReference( crs );
421 const char *configOld = CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" );
422 const char *configNew =
"GEOGCS";
424 if ( strcmp( configOld,
"" ) == 0 )
426 CPLSetConfigOption(
"GDAL_FIX_ESRI_WKT", configNew );
427 if ( strcmp( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) != 0 )
428 QgsLogger::warning( u
"GDAL_FIX_ESRI_WKT could not be set to %1 : %2"_s.arg( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) );
433 QgsDebugMsgLevel( u
"GDAL_FIX_ESRI_WKT was already set : %1"_s.arg( configNew ), 4 );
443 if ( !sDisableOgcCache )
445 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sOgcCache()->constFind( crs );
446 if ( crsIt != sOgcCache()->constEnd() )
449 *
this = crsIt.value();
455 QString wmsCrs = crs;
460 const QString authorityLower = authority.toLower();
466 if ( !sDisableOgcCache )
467 sOgcCache()->insert( crs, *
this );
473 wmsCrs = authority +
':' + code;
477 const QString legacyKey = wmsCrs.toLower();
480 if ( it.key().compare( legacyKey, Qt::CaseInsensitive ) == 0 )
482 const QStringList parts = it.key().split(
':' );
483 const QString auth = parts.at( 0 );
484 const QString code = parts.at( 1 );
485 if ( loadFromAuthCode( auth, code ) )
488 if ( !sDisableOgcCache )
489 sOgcCache()->insert( crs, *
this );
498 if ( !sDisableOgcCache )
499 sOgcCache()->insert( crs, *
this );
504 if ( wmsCrs.compare(
"CRS:27"_L1, Qt::CaseInsensitive ) == 0 || wmsCrs.compare(
"OGC:CRS27"_L1, Qt::CaseInsensitive ) == 0 )
511 if ( wmsCrs.compare(
"CRS:83"_L1, Qt::CaseInsensitive ) == 0 || wmsCrs.compare(
"OGC:CRS83"_L1, Qt::CaseInsensitive ) == 0 )
518 if ( wmsCrs.compare(
"CRS:84"_L1, Qt::CaseInsensitive ) == 0 || wmsCrs.compare(
"OGC:CRS84"_L1, Qt::CaseInsensitive ) == 0 )
522 d->mAxisInverted =
false;
523 d->mAxisInvertedDirty =
false;
527 if ( !sDisableOgcCache )
528 sOgcCache()->insert( crs, *
this );
535 if ( !authority.isEmpty() && !code.isEmpty() && loadFromAuthCode( authority, code ) )
538 if ( !sDisableOgcCache )
539 sOgcCache()->insert( crs, *
this );
544 if ( !sDisableOgcCache )
554 if ( d->mIsValid || !sCustomSrsValidation )
558 if ( sCustomSrsValidation )
559 sCustomSrsValidation( *
this );
565 if ( !sDisableSrIdCache )
567 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrIdCache()->constFind(
id );
568 if ( crsIt != sSrIdCache()->constEnd() )
571 *
this = crsIt.value();
580 if ( it.value().endsWith( u
",%1"_s.arg(
id ) ) )
582 const QStringList parts = it.key().split(
':' );
583 const QString auth = parts.at( 0 );
584 const QString code = parts.at( 1 );
585 if ( loadFromAuthCode( auth, code ) )
588 if ( !sDisableSrIdCache )
589 sSrIdCache()->insert(
id, *
this );
599 if ( !sDisableSrIdCache )
600 sSrIdCache()->insert(
id, *
this );
608 if ( !sDisableSrsIdCache )
610 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrsIdCache()->constFind(
id );
611 if ( crsIt != sSrsIdCache()->constEnd() )
614 *
this = crsIt.value();
623 if ( it.value().startsWith( QString::number(
id ) +
',' ) )
625 const QStringList parts = it.key().split(
':' );
626 const QString auth = parts.at( 0 );
627 const QString code = parts.at( 1 );
628 if ( loadFromAuthCode( auth, code ) )
631 if ( !sDisableSrsIdCache )
632 sSrsIdCache()->insert(
id, *
this );
641 if ( !sDisableSrsIdCache )
642 sSrsIdCache()->insert(
id, *
this );
646bool QgsCoordinateReferenceSystem::loadFromDatabase(
const QString &db,
const QString &expression,
const QString &value )
650 QgsDebugMsgLevel(
"load CRS from " + db +
" where " + expression +
" is " + value, 3 );
652 d->mWktPreferred.clear();
654 QFileInfo myInfo( db );
655 if ( !myInfo.exists() )
661 sqlite3_database_unique_ptr database;
662 sqlite3_statement_unique_ptr statement;
665 myResult = openDatabase( db, database );
666 if ( myResult != SQLITE_OK )
683 QString mySql =
"select srs_id,description,projection_acronym,"
684 "ellipsoid_acronym,parameters,srid,auth_name||':'||auth_id,is_geo,wkt "
685 "from tbl_srs where "
689 +
" order by deprecated";
690 statement = database.
prepare( mySql, myResult );
693 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
698 d->mEllipsoidAcronym.clear();
700 d->mWktPreferred.clear();
703 d->mIsGeographic = statement.
columnAsText( 7 ).toInt() != 0;
705 d->mAxisInvertedDirty =
true;
709 d->mAuthId = u
"USER:%1"_s.arg( d->mSrsId );
711 else if ( !d->mAuthId.startsWith(
"USER:"_L1, Qt::CaseInsensitive ) )
713 QStringList parts = d->mAuthId.split(
':' );
714 QString auth = parts.at( 0 );
715 QString code = parts.at( 1 );
722 d->mIsValid = d->hasPj();
728 if ( !wkt.isEmpty() )
736 setProjString( d->mProj4 );
746void QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread(
PJ_CONTEXT *pj_context )
753 if ( !sDisableSrIdCache )
756 if ( !sDisableSrIdCache )
758 for (
auto it = sSrIdCache()->begin(); it != sSrIdCache()->end(); )
760 auto &v = it.value();
761 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
762 it = sSrIdCache()->erase( it );
768 if ( !sDisableOgcCache )
771 if ( !sDisableOgcCache )
773 for (
auto it = sOgcCache()->begin(); it != sOgcCache()->end(); )
775 auto &v = it.value();
776 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
777 it = sOgcCache()->erase( it );
783 if ( !sDisableProjCache )
786 if ( !sDisableProjCache )
788 for (
auto it = sProj4Cache()->begin(); it != sProj4Cache()->end(); )
790 auto &v = it.value();
791 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
792 it = sProj4Cache()->erase( it );
798 if ( !sDisableWktCache )
801 if ( !sDisableWktCache )
803 for (
auto it = sWktCache()->begin(); it != sWktCache()->end(); )
805 auto &v = it.value();
806 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
807 it = sWktCache()->erase( it );
813 if ( !sDisableSrsIdCache )
816 if ( !sDisableSrsIdCache )
818 for (
auto it = sSrsIdCache()->begin(); it != sSrsIdCache()->end(); )
820 auto &v = it.value();
821 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
822 it = sSrsIdCache()->erase( it );
828 if ( !sDisableStringCache )
831 if ( !sDisableStringCache )
833 for (
auto it = sStringCache()->begin(); it != sStringCache()->end(); )
835 auto &v = it.value();
836 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
837 it = sStringCache()->erase( it );
847 if ( d->mAxisInvertedDirty )
850 d->mAxisInvertedDirty =
false;
853 return d->mAxisInverted;
870 const thread_local QMap< Qgis::CrsAxisDirection, QString > mapping = {
912 QList< Qgis::CrsAxisDirection > res;
913 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
916 res.reserve( axisCount );
918 for (
int i = 0; i < axisCount; ++i )
920 const char *outDirection =
nullptr;
921 proj_cs_get_axis_info( context, pjCs.get(), i,
nullptr,
nullptr, &outDirection,
nullptr,
nullptr,
nullptr,
nullptr );
923 const thread_local QRegularExpression rx( u
"([^\\s]+).*"_s );
924 const QRegularExpressionMatch match = rx.match( QString( outDirection ) );
925 if ( !match.hasMatch() )
928 const QString direction = match.captured( 1 );
930 for (
auto it = mapping.constBegin(); it != mapping.constEnd(); ++it )
932 if ( it.value().compare( direction, Qt::CaseInsensitive ) == 0 )
947 return createFromWktInternal( wkt, QString() );
950bool QgsCoordinateReferenceSystem::createFromWktInternal(
const QString &wkt,
const QString &description )
958 if ( !sDisableWktCache )
960 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sWktCache()->constFind( wkt );
961 if ( crsIt != sWktCache()->constEnd() )
964 *
this = crsIt.value();
966 if ( !
description.isEmpty() && d->mDescription.isEmpty() )
971 sWktCache()->insert( wkt, *
this );
980 d->mWktPreferred.clear();
988 QgsCoordinateReferenceSystem::RecordMap record = getRecord(
"select * from tbl_srs where wkt=" +
QgsSqliteUtils::quotedString( wkt ) +
" order by deprecated" );
989 if ( !record.empty() )
991 long srsId = record[u
"srs_id"_s].toLong();
1004 if ( d->mSrsId == 0 )
1007 long id = matchToUserCrs();
1016 if ( !sDisableWktCache )
1017 sWktCache()->insert( wkt, *
this );
1035 if ( projString.isEmpty() )
1040 if ( projString.trimmed().isEmpty() )
1042 d->mIsValid =
false;
1044 d->mWktPreferred.clear();
1049 if ( !sDisableProjCache )
1051 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sProj4Cache()->constFind( projString );
1052 if ( crsIt != sProj4Cache()->constEnd() )
1055 *
this = crsIt.value();
1069 QString myProj4String = projString.trimmed();
1070 myProj4String.remove( u
"+type=crs"_s );
1071 myProj4String = myProj4String.trimmed();
1073 d->mIsValid =
false;
1074 d->mWktPreferred.clear();
1079 const QString projCrsString = myProj4String + ( myProj4String.contains( u
"+type=crs"_s ) ? QString() : u
" +type=crs"_s );
1087 const QString
authid = u
"%1:%2"_s.arg( authName, authCode );
1091 if ( !sDisableProjCache )
1092 sProj4Cache()->insert( projString, *
this );
1099 QgsCoordinateReferenceSystem::RecordMap myRecord = getRecord(
"select * from tbl_srs where parameters=" +
QgsSqliteUtils::quotedString( myProj4String ) +
" order by deprecated" );
1101 if ( !myRecord.empty() )
1103 id = myRecord[u
"srs_id"_s].toLong();
1112 setProjString( myProj4String );
1115 id = matchToUserCrs();
1124 setProjString( myProj4String );
1128 if ( !sDisableProjCache )
1129 sProj4Cache()->insert( projString, *
this );
1135QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord(
const QString &sql )
1137 QString myDatabaseFileName;
1138 QgsCoordinateReferenceSystem::RecordMap myMap;
1139 QString myFieldName;
1140 QString myFieldValue;
1147 QFileInfo myInfo( myDatabaseFileName );
1148 if ( !myInfo.exists() )
1150 QgsDebugError(
"failed : " + myDatabaseFileName +
" does not exist!" );
1155 myResult = openDatabase( myDatabaseFileName, database );
1156 if ( myResult != SQLITE_OK )
1161 statement = database.
prepare( sql, myResult );
1163 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1167 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1169 myFieldName = statement.
columnName( myColNo );
1171 myMap[myFieldName] = myFieldValue;
1173 if ( statement.
step() != SQLITE_DONE )
1184 if ( myMap.empty() )
1187 QFileInfo myFileInfo;
1188 myFileInfo.setFile( myDatabaseFileName );
1189 if ( !myFileInfo.exists() )
1196 myResult = openDatabase( myDatabaseFileName, database );
1197 if ( myResult != SQLITE_OK )
1202 statement = database.
prepare( sql, myResult );
1204 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1208 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1210 myFieldName = statement.
columnName( myColNo );
1212 myMap[myFieldName] = myFieldValue;
1215 if ( statement.
step() != SQLITE_DONE )
1248 if ( d->mDescription.isNull() )
1254 return d->mDescription;
1261 if ( !
authid().isEmpty() )
1271 id =
isValid() ? QObject::tr(
"Custom CRS" ) : QObject::tr(
"Unknown CRS" );
1273 id = QObject::tr(
"Custom CRS: %1" )
1275 else if ( !
toProj().isEmpty() )
1277 if ( !
id.isEmpty() && !std::isnan( d->mCoordinateEpoch ) )
1285 if ( d->mProjectionAcronym.isNull() )
1291 return d->mProjectionAcronym;
1297 if ( d->mEllipsoidAcronym.isNull() )
1299 if (
PJ *obj = d->threadLocalProjObject() )
1304 const QString ellipsoidAuthName( proj_get_id_auth_name( ellipsoid.get(), 0 ) );
1305 const QString ellipsoidAuthCode( proj_get_id_code( ellipsoid.get(), 0 ) );
1306 if ( !ellipsoidAuthName.isEmpty() && !ellipsoidAuthCode.isEmpty() )
1307 d->mEllipsoidAcronym = u
"%1:%2"_s.arg( ellipsoidAuthName, ellipsoidAuthCode );
1310 double semiMajor, semiMinor, invFlattening;
1311 int semiMinorComputed = 0;
1312 if ( proj_ellipsoid_get_parameters(
QgsProjContext::get(), ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
1318 d->mEllipsoidAcronym.clear();
1323 return d->mEllipsoidAcronym;
1327 return d->mEllipsoidAcronym;
1341 if ( d->mProj4.isEmpty() )
1343 if (
PJ *obj = d->threadLocalProjObject() )
1349 return d->mProj4.trimmed();
1357 if (
PJ *obj = d->threadLocalProjObject() )
1359 const QByteArray multiLineOption = u
"MULTILINE=%1"_s.arg( multiline ? u
"YES"_s : u
"NO"_s ).toLocal8Bit();
1360 const QByteArray indentatationWidthOption = u
"INDENTATION_WIDTH=%1"_s.arg( multiline ? QString::number( indentationWidth ) : u
"0"_s ).toLocal8Bit();
1361 const QByteArray schemaOption = u
"SCHEMA=%1"_s.arg( schema ).toLocal8Bit();
1362 const char *
const options[] = { multiLineOption.constData(), indentatationWidthOption.constData(), schemaOption.constData(),
nullptr };
1365 return json ? std::string { json } : std::string {};
1376 switch ( d->mProjType )
1378 case PJ_TYPE_UNKNOWN:
1381 case PJ_TYPE_ELLIPSOID:
1382 case PJ_TYPE_PRIME_MERIDIAN:
1383 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
1384 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
1385 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
1386 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
1387 case PJ_TYPE_DATUM_ENSEMBLE:
1388 case PJ_TYPE_CONVERSION:
1389 case PJ_TYPE_TRANSFORMATION:
1390 case PJ_TYPE_CONCATENATED_OPERATION:
1391 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
1392 case PJ_TYPE_TEMPORAL_DATUM:
1393 case PJ_TYPE_ENGINEERING_DATUM:
1394 case PJ_TYPE_PARAMETRIC_DATUM:
1398 case PJ_TYPE_GEOGRAPHIC_CRS:
1402 case PJ_TYPE_GEODETIC_CRS:
1404 case PJ_TYPE_GEOCENTRIC_CRS:
1406 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
1408 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
1410 case PJ_TYPE_VERTICAL_CRS:
1412 case PJ_TYPE_PROJECTED_CRS:
1414 case PJ_TYPE_COMPOUND_CRS:
1416 case PJ_TYPE_TEMPORAL_CRS:
1418 case PJ_TYPE_ENGINEERING_CRS:
1420 case PJ_TYPE_BOUND_CRS:
1422 case PJ_TYPE_OTHER_CRS:
1424#if PROJ_VERSION_MAJOR > 9 || ( PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 2 )
1425 case PJ_TYPE_DERIVED_PROJECTED_CRS:
1427 case PJ_TYPE_COORDINATE_METADATA:
1441 return proj_is_deprecated( pj );
1446 return d->mIsGeographic;
1466 return QString( proj_get_celestial_body_name( context, pj ) );
1471 if ( d->mCoordinateEpoch == epoch )
1477 d->mCoordinateEpoch = epoch;
1478 d->setPj( std::move( clone ) );
1483 return d->mCoordinateEpoch;
1502 res.mName = QString( proj_get_name( ensemble.get() ) );
1503 res.mAuthority = QString( proj_get_id_auth_name( ensemble.get(), 0 ) );
1504 res.mCode = QString( proj_get_id_code( ensemble.get(), 0 ) );
1505 res.mRemarks = QString( proj_get_remarks( ensemble.get() ) );
1506 res.mScope = QString( proj_get_scope( ensemble.get() ) );
1507 res.mAccuracy = proj_datum_ensemble_get_accuracy( context, ensemble.get() );
1509 const int memberCount = proj_datum_ensemble_get_member_count( context, ensemble.get() );
1510 for (
int i = 0; i < memberCount; ++i )
1517 details.mName = QString( proj_get_name( member.get() ) );
1518 details.mAuthority = QString( proj_get_id_auth_name( member.get(), 0 ) );
1519 details.mCode = QString( proj_get_id_code( member.get(), 0 ) );
1520 details.mRemarks = QString( proj_get_remarks( member.get() ) );
1521 details.mScope = QString( proj_get_scope( member.get() ) );
1523 res.mMembers << details;
1533 QString projString =
toProj();
1534 projString.replace(
"+type=crs"_L1, QString() );
1537 if ( !transformation )
1540 PJ_COORD coord = proj_coord( 0, 0, 0, HUGE_VAL );
1541 coord.uv.u = point.
x() * M_PI / 180.0;
1542 coord.uv.v = point.
y() * M_PI / 180.0;
1544 proj_errno_reset( transformation.get() );
1545 const PJ_FACTORS pjFactors = proj_factors( transformation.get(), coord );
1546 if ( proj_errno( transformation.get() ) )
1551 res.mIsValid =
true;
1552 res.mMeridionalScale = pjFactors.meridional_scale;
1553 res.mParallelScale = pjFactors.parallel_scale;
1554 res.mArealScale = pjFactors.areal_scale;
1555 res.mAngularDistortion = pjFactors.angular_distortion;
1556 res.mMeridianParallelAngle = pjFactors.meridian_parallel_angle * 180 / M_PI;
1557 res.mMeridianConvergence = pjFactors.meridian_convergence * 180 / M_PI;
1558 res.mTissotSemimajor = pjFactors.tissot_semimajor;
1559 res.mTissotSemiminor = pjFactors.tissot_semiminor;
1560 res.mDxDlam = pjFactors.dx_dlam;
1561 res.mDxDphi = pjFactors.dx_dphi;
1562 res.mDyDlam = pjFactors.dy_dlam;
1563 res.mDyDphi = pjFactors.dy_dphi;
1575 QString projString =
toProj();
1576 projString.replace(
"+type=crs"_L1, QString() );
1577 if ( projString.isEmpty() )
1581 if ( !transformation )
1584 PJ_PROJ_INFO info = proj_pj_info( transformation.get() );
1599 return d->mMapUnits;
1607 PJ *obj = d->threadLocalProjObject();
1612 double southLat = 0;
1614 double northLat = 0;
1616 if ( !proj_get_area_of_use(
QgsProjContext::get(), obj, &westLon, &southLat, &eastLon, &northLat,
nullptr ) )
1631 const auto parts {
authid().split(
':' ) };
1632 if ( parts.length() == 2 )
1634 if ( parts[0] ==
"EPSG"_L1 )
1635 return u
"http://www.opengis.net/def/crs/EPSG/0/%1"_s.arg( parts[1] );
1636 else if ( parts[0] ==
"OGC"_L1 )
1638 return u
"http://www.opengis.net/def/crs/OGC/1.3/%1"_s.arg( parts[1] );
1654 const auto parts {
authid().split(
':' ) };
1655 if ( parts.length() == 2 )
1657 if ( parts[0] ==
"EPSG"_L1 )
1658 return u
"urn:ogc:def:crs:EPSG::%1"_s.arg( parts[1] );
1659 else if ( parts[0] ==
"OGC"_L1 )
1661 return u
"urn:ogc:def:crs:OGC:1.3:%1"_s.arg( parts[1] );
1692void QgsCoordinateReferenceSystem::setProjString(
const QString &proj4String )
1695 d->mProj4 = proj4String;
1696 d->mWktPreferred.clear();
1699 QString trimmed = proj4String.trimmed();
1701 trimmed +=
" +type=crs"_L1;
1711 const int errNo = proj_context_errno( ctx );
1712 QgsDebugError( u
"proj string rejected: %1"_s.arg( proj_context_errno_string( ctx, errNo ) ) );
1714 d->mIsValid =
false;
1718 d->mEllipsoidAcronym.clear();
1725bool QgsCoordinateReferenceSystem::setWktString(
const QString &wkt )
1728 d->mIsValid =
false;
1729 d->mWktPreferred.clear();
1731 PROJ_STRING_LIST warnings =
nullptr;
1732 PROJ_STRING_LIST grammarErrors =
nullptr;
1740 QgsDebugMsgLevel( u
"\n---------------------------------------------------------------"_s, 2 );
1741 QgsDebugMsgLevel( u
"This CRS could *** NOT *** be set from the supplied Wkt "_s, 2 );
1743 for (
auto iter = warnings; iter && *iter; ++iter )
1747 for (
auto iter = grammarErrors; iter && *iter; ++iter )
1751 QgsDebugMsgLevel( u
"---------------------------------------------------------------\n"_s, 2 );
1753 proj_string_list_destroy( warnings );
1754 proj_string_list_destroy( grammarErrors );
1760 if ( !sDisableWktCache )
1761 sWktCache()->insert( wkt, *
this );
1769 QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
1770 QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
1772 if ( authName.isEmpty() || authCode.isEmpty() )
1778 if ( !authName.isEmpty() && !authCode.isEmpty() )
1781 if ( fromAuthCode.loadFromAuthCode( authName, authCode ) )
1783 *
this = fromAuthCode;
1785 if ( !sDisableWktCache )
1786 sWktCache()->insert( wkt, *
this );
1793 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
1800void QgsCoordinateReferenceSystem::setMapUnits()
1827 if ( !coordinateSystem )
1833 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
1834 if ( axisCount > 0 )
1836 const char *outUnitName =
nullptr;
1838 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
nullptr,
nullptr,
nullptr,
nullptr, &outUnitName,
nullptr,
nullptr );
1840 const QString unitName( outUnitName );
1844 if ( unitName.compare(
"degree"_L1, Qt::CaseInsensitive ) == 0
1845 || unitName.compare(
"degree minute second"_L1, Qt::CaseInsensitive ) == 0
1846 || unitName.compare(
"degree minute second hemisphere"_L1, Qt::CaseInsensitive ) == 0
1847 || unitName.compare(
"degree minute"_L1, Qt::CaseInsensitive ) == 0
1848 || unitName.compare(
"degree hemisphere"_L1, Qt::CaseInsensitive ) == 0
1849 || unitName.compare(
"degree minute hemisphere"_L1, Qt::CaseInsensitive ) == 0
1850 || unitName.compare(
"hemisphere degree"_L1, Qt::CaseInsensitive ) == 0
1851 || unitName.compare(
"hemisphere degree minute"_L1, Qt::CaseInsensitive ) == 0
1852 || unitName.compare(
"hemisphere degree minute second"_L1, Qt::CaseInsensitive ) == 0
1853 || unitName.compare(
"degree (supplier to define representation)"_L1, Qt::CaseInsensitive ) == 0 )
1855 else if ( unitName.compare(
"metre"_L1, Qt::CaseInsensitive ) == 0 || unitName.compare(
'm'_L1, Qt::CaseInsensitive ) == 0 || unitName.compare(
"meter"_L1, Qt::CaseInsensitive ) == 0 )
1857 else if ( unitName.compare(
"US survey foot"_L1, Qt::CaseInsensitive ) == 0 )
1859 else if ( unitName.compare(
"foot"_L1, Qt::CaseInsensitive ) == 0 )
1861 else if ( unitName.compare(
"British yard (Sears 1922)"_L1, Qt::CaseInsensitive ) == 0 )
1863 else if ( unitName.compare(
"British yard (Sears 1922 truncated)"_L1, Qt::CaseInsensitive ) == 0 )
1865 else if ( unitName.compare(
"British foot (Sears 1922)"_L1, Qt::CaseInsensitive ) == 0 )
1867 else if ( unitName.compare(
"British foot (Sears 1922 truncated)"_L1, Qt::CaseInsensitive ) == 0 )
1869 else if ( unitName.compare(
"British chain (Sears 1922)"_L1, Qt::CaseInsensitive ) == 0 )
1871 else if ( unitName.compare(
"British chain (Sears 1922 truncated)"_L1, Qt::CaseInsensitive ) == 0 )
1873 else if ( unitName.compare(
"British link (Sears 1922)"_L1, Qt::CaseInsensitive ) == 0 )
1875 else if ( unitName.compare(
"British link (Sears 1922 truncated)"_L1, Qt::CaseInsensitive ) == 0 )
1877 else if ( unitName.compare(
"British yard (Benoit 1895 A)"_L1, Qt::CaseInsensitive ) == 0 )
1879 else if ( unitName.compare(
"British foot (Benoit 1895 A)"_L1, Qt::CaseInsensitive ) == 0 )
1881 else if ( unitName.compare(
"British chain (Benoit 1895 A)"_L1, Qt::CaseInsensitive ) == 0 )
1883 else if ( unitName.compare(
"British link (Benoit 1895 A)"_L1, Qt::CaseInsensitive ) == 0 )
1885 else if ( unitName.compare(
"British yard (Benoit 1895 B)"_L1, Qt::CaseInsensitive ) == 0 )
1887 else if ( unitName.compare(
"British foot (Benoit 1895 B)"_L1, Qt::CaseInsensitive ) == 0 )
1889 else if ( unitName.compare(
"British chain (Benoit 1895 B)"_L1, Qt::CaseInsensitive ) == 0 )
1891 else if ( unitName.compare(
"British link (Benoit 1895 B)"_L1, Qt::CaseInsensitive ) == 0 )
1893 else if ( unitName.compare(
"British foot (1865)"_L1, Qt::CaseInsensitive ) == 0 )
1895 else if ( unitName.compare(
"British foot (1936)"_L1, Qt::CaseInsensitive ) == 0 )
1897 else if ( unitName.compare(
"Indian foot"_L1, Qt::CaseInsensitive ) == 0 )
1899 else if ( unitName.compare(
"Indian foot (1937)"_L1, Qt::CaseInsensitive ) == 0 )
1901 else if ( unitName.compare(
"Indian foot (1962)"_L1, Qt::CaseInsensitive ) == 0 )
1903 else if ( unitName.compare(
"Indian foot (1975)"_L1, Qt::CaseInsensitive ) == 0 )
1905 else if ( unitName.compare(
"Indian yard"_L1, Qt::CaseInsensitive ) == 0 )
1907 else if ( unitName.compare(
"Indian yard (1937)"_L1, Qt::CaseInsensitive ) == 0 )
1909 else if ( unitName.compare(
"Indian yard (1962)"_L1, Qt::CaseInsensitive ) == 0 )
1911 else if ( unitName.compare(
"Indian yard (1975)"_L1, Qt::CaseInsensitive ) == 0 )
1913 else if ( unitName.compare(
"Gold Coast foot"_L1, Qt::CaseInsensitive ) == 0 )
1915 else if ( unitName.compare(
"Clarke's foot"_L1, Qt::CaseInsensitive ) == 0 )
1917 else if ( unitName.compare(
"Clarke's yard"_L1, Qt::CaseInsensitive ) == 0 )
1919 else if ( unitName.compare(
"Clarke's chain"_L1, Qt::CaseInsensitive ) == 0 )
1921 else if ( unitName.compare(
"Clarke's link"_L1, Qt::CaseInsensitive ) == 0 )
1923 else if ( unitName.compare(
"kilometre"_L1, Qt::CaseInsensitive ) == 0 )
1925 else if ( unitName.compare(
"centimetre"_L1, Qt::CaseInsensitive ) == 0 )
1927 else if ( unitName.compare(
"millimetre"_L1, Qt::CaseInsensitive ) == 0 )
1929 else if ( unitName.compare(
"Statute mile"_L1, Qt::CaseInsensitive ) == 0 )
1931 else if ( unitName.compare(
"nautical mile"_L1, Qt::CaseInsensitive ) == 0 )
1933 else if ( unitName.compare(
"yard"_L1, Qt::CaseInsensitive ) == 0 )
1935 else if ( unitName.compare(
"fathom"_L1, Qt::CaseInsensitive ) == 0 )
1937 else if ( unitName.compare(
"US survey chain"_L1, Qt::CaseInsensitive ) == 0 )
1939 else if ( unitName.compare(
"chain"_L1, Qt::CaseInsensitive ) == 0 )
1941 else if ( unitName.compare(
"link"_L1, Qt::CaseInsensitive ) == 0 )
1943 else if ( unitName.compare(
"US survey link"_L1, Qt::CaseInsensitive ) == 0 )
1945 else if ( unitName.compare(
"US survey mile"_L1, Qt::CaseInsensitive ) == 0 )
1947 else if ( unitName.compare(
"German legal metre"_L1, Qt::CaseInsensitive ) == 0 )
1964 if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull() || !d->mIsValid )
1967 "QgsCoordinateReferenceSystem::findMatchingProj will only "
1968 "work if prj acr ellipsoid acr and proj4string are set"
1969 " and the current projection is valid!",
1981 QString mySql = QString(
1982 "select srs_id,parameters from tbl_srs where "
1983 "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated"
1990 myResult = openDatabase( myDatabaseFileName, database );
1991 if ( myResult != SQLITE_OK )
1996 statement = database.
prepare( mySql, myResult );
1997 if ( myResult == SQLITE_OK )
1999 while ( statement.
step() == SQLITE_ROW )
2003 if (
toProj() == myProj4String.trimmed() )
2005 return mySrsId.toLong();
2016 myResult = openDatabase( myDatabaseFileName, database );
2017 if ( myResult != SQLITE_OK )
2022 statement = database.
prepare( mySql, myResult );
2024 if ( myResult == SQLITE_OK )
2026 while ( statement.
step() == SQLITE_ROW )
2030 if (
toProj() == myProj4String.trimmed() )
2032 return mySrsId.toLong();
2046 if ( !d->mIsValid && !srs.d->mIsValid )
2049 if ( !d->mIsValid || !srs.d->mIsValid )
2057 if ( isUser != otherIsUser )
2061 if ( !isUser && ( !d->mAuthId.isEmpty() || !srs.d->mAuthId.isEmpty() ) )
2062 return d->mAuthId == srs.d->mAuthId;
2069 return !( *
this == srs );
2074 if (
PJ *obj = d->threadLocalProjObject() )
2077 if ( isDefaultPreferredFormat && !d->mWktPreferred.isEmpty() )
2080 return d->mWktPreferred;
2083 PJ_WKT_TYPE
type = PJ_WKT1_GDAL;
2087 type = PJ_WKT1_GDAL;
2090 type = PJ_WKT1_ESRI;
2093 type = PJ_WKT2_2015;
2096 type = PJ_WKT2_2015_SIMPLIFIED;
2099 type = PJ_WKT2_2019;
2102 type = PJ_WKT2_2019_SIMPLIFIED;
2106 const QByteArray multiLineOption = u
"MULTILINE=%1"_s.arg( multiline ? u
"YES"_s : u
"NO"_s ).toLocal8Bit();
2107 const QByteArray indentatationWidthOption = u
"INDENTATION_WIDTH=%1"_s.arg( multiline ? QString::number( indentationWidth ) : u
"0"_s ).toLocal8Bit();
2108 const char *
const options[] = { multiLineOption.constData(), indentatationWidthOption.constData(),
nullptr };
2111 if ( isDefaultPreferredFormat )
2114 d->mWktPreferred = res;
2126 QDomNode srsNode = node.namedItem( u
"spatialrefsys"_s );
2128 if ( !srsNode.isNull() )
2130 bool initialized =
false;
2133 long srsid = srsNode.namedItem( u
"srsid"_s ).toElement().text().toLong( &ok );
2139 node = srsNode.namedItem( u
"authid"_s );
2140 if ( !node.isNull() )
2151 node = srsNode.namedItem( u
"epsg"_s );
2152 if ( !node.isNull() )
2170 const QString
description = srsNode.namedItem( u
"description"_s ).toElement().text();
2172 const QString wkt = srsNode.namedItem( u
"wkt"_s ).toElement().text();
2173 initialized = createFromWktInternal( wkt,
description );
2178 node = srsNode.namedItem( u
"proj4"_s );
2179 const QString proj4 = node.toElement().text();
2186 node = srsNode.namedItem( u
"proj4"_s );
2187 const QString proj4 = node.toElement().text();
2188 if ( !proj4.trimmed().isEmpty() )
2189 setProjString( node.toElement().text() );
2191 node = srsNode.namedItem( u
"srsid"_s );
2192 d->mSrsId = node.toElement().text().toLong();
2194 node = srsNode.namedItem( u
"srid"_s );
2195 d->mSRID = node.toElement().text().toLong();
2197 node = srsNode.namedItem( u
"authid"_s );
2198 d->mAuthId = node.toElement().text();
2200 node = srsNode.namedItem( u
"description"_s );
2201 d->mDescription = node.toElement().text();
2203 node = srsNode.namedItem( u
"projectionacronym"_s );
2204 d->mProjectionAcronym = node.toElement().text();
2206 node = srsNode.namedItem( u
"ellipsoidacronym"_s );
2207 d->mEllipsoidAcronym = node.toElement().text();
2209 node = srsNode.namedItem( u
"geographicflag"_s );
2210 d->mIsGeographic = node.toElement().text() ==
"true"_L1;
2212 d->mWktPreferred.clear();
2218 const QString epoch = srsNode.toElement().attribute( u
"coordinateEpoch"_s );
2219 if ( !epoch.isEmpty() )
2221 bool epochOk =
false;
2222 d->mCoordinateEpoch = epoch.toDouble( &epochOk );
2224 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2228 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2236 d =
new QgsCoordinateReferenceSystemPrivate();
2244 QDomElement layerNode = node.toElement();
2245 QDomElement srsElement = doc.createElement( u
"spatialrefsys"_s );
2249 if ( std::isfinite( d->mCoordinateEpoch ) )
2251 srsElement.setAttribute( u
"coordinateEpoch"_s, d->mCoordinateEpoch );
2254 QDomElement wktElement = doc.createElement( u
"wkt"_s );
2256 srsElement.appendChild( wktElement );
2258 QDomElement proj4Element = doc.createElement( u
"proj4"_s );
2259 proj4Element.appendChild( doc.createTextNode(
toProj() ) );
2260 srsElement.appendChild( proj4Element );
2262 QDomElement srsIdElement = doc.createElement( u
"srsid"_s );
2263 srsIdElement.appendChild( doc.createTextNode( QString::number(
srsid() ) ) );
2264 srsElement.appendChild( srsIdElement );
2266 QDomElement sridElement = doc.createElement( u
"srid"_s );
2267 sridElement.appendChild( doc.createTextNode( QString::number(
postgisSrid() ) ) );
2268 srsElement.appendChild( sridElement );
2270 QDomElement authidElement = doc.createElement( u
"authid"_s );
2271 authidElement.appendChild( doc.createTextNode(
authid() ) );
2272 srsElement.appendChild( authidElement );
2274 QDomElement descriptionElement = doc.createElement( u
"description"_s );
2275 descriptionElement.appendChild( doc.createTextNode(
description() ) );
2276 srsElement.appendChild( descriptionElement );
2278 QDomElement projectionAcronymElement = doc.createElement( u
"projectionacronym"_s );
2279 projectionAcronymElement.appendChild( doc.createTextNode(
projectionAcronym() ) );
2280 srsElement.appendChild( projectionAcronymElement );
2282 QDomElement ellipsoidAcronymElement = doc.createElement( u
"ellipsoidacronym"_s );
2283 ellipsoidAcronymElement.appendChild( doc.createTextNode(
ellipsoidAcronym() ) );
2284 srsElement.appendChild( ellipsoidAcronymElement );
2286 QDomElement geographicFlagElement = doc.createElement( u
"geographicflag"_s );
2287 QString geoFlagText = u
"false"_s;
2290 geoFlagText = u
"true"_s;
2293 geographicFlagElement.appendChild( doc.createTextNode( geoFlagText ) );
2294 srsElement.appendChild( geographicFlagElement );
2296 layerNode.appendChild( srsElement );
2308QString QgsCoordinateReferenceSystem::projFromSrsId(
const int srsId )
2310 QString myDatabaseFileName;
2311 QString myProjString;
2312 QString mySql = u
"select parameters from tbl_srs where srs_id = %1 order by deprecated"_s.arg( srsId );
2321 QFileInfo myFileInfo;
2322 myFileInfo.setFile( myDatabaseFileName );
2323 if ( !myFileInfo.exists() )
2334 sqlite3_database_unique_ptr database;
2335 sqlite3_statement_unique_ptr statement;
2338 rc = openDatabase( myDatabaseFileName, database );
2344 statement = database.
prepare( mySql, rc );
2346 if ( rc == SQLITE_OK )
2348 if ( statement.
step() == SQLITE_ROW )
2354 return myProjString;
2361 myResult = database.
open_v2( path, SQLITE_OPEN_READONLY,
nullptr );
2363 myResult = database.
open( path );
2365 if ( myResult != SQLITE_OK )
2378 sCustomSrsValidation = f;
2383 return sCustomSrsValidation;
2386void QgsCoordinateReferenceSystem::debugPrint()
2389 QgsDebugMsgLevel(
"* Valid : " + ( d->mIsValid ? QString(
"true" ) : QString(
"false" ) ), 1 );
2410 mValidationHint = html;
2415 return mValidationHint;
2425 mNativeFormat = format;
2430 return mNativeFormat;
2433long QgsCoordinateReferenceSystem::getRecordCount()
2438 long myRecordCount = 0;
2441 if ( myResult != SQLITE_OK )
2447 QString mySql = u
"select count(*) from tbl_srs"_s;
2448 statement = database.
prepare( mySql, myResult );
2449 if ( myResult == SQLITE_OK )
2451 if ( statement.
step() == SQLITE_ROW )
2453 QString myRecordCountString = statement.
columnAsText( 0 );
2454 myRecordCount = myRecordCountString.toLong();
2457 return myRecordCount;
2463 bool isGeographic =
false;
2467 if ( !horizontalCrs )
2471 if ( coordinateSystem )
2473 const int axisCount = proj_cs_get_axis_count( pjContext, coordinateSystem.get() );
2474 if ( axisCount > 0 )
2476 const char *outUnitAuthName =
nullptr;
2477 const char *outUnitAuthCode =
nullptr;
2479 proj_cs_get_axis_info( pjContext, coordinateSystem.get(), 0,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr, &outUnitAuthName, &outUnitAuthCode );
2481 if ( outUnitAuthName && outUnitAuthCode )
2483 const char *unitCategory =
nullptr;
2484 if ( proj_uom_get_info_from_database( pjContext, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
2486 isGeographic = QString( unitCategory ).compare(
"angular"_L1, Qt::CaseInsensitive ) == 0;
2491 return isGeographic;
2496 thread_local const QRegularExpression projRegExp( u
"\\+proj=(\\S+)"_s );
2497 const QRegularExpressionMatch projMatch = projRegExp.match( proj );
2498 if ( !projMatch.hasMatch() )
2503 operation = projMatch.captured( 1 );
2505 const QRegularExpressionMatch ellipseMatch = projRegExp.match( proj );
2506 if ( ellipseMatch.hasMatch() )
2508 ellipsoid = ellipseMatch.captured( 1 );
2522bool QgsCoordinateReferenceSystem::loadFromAuthCode(
const QString &auth,
const QString &code )
2528 d->mIsValid =
false;
2529 d->mWktPreferred.clear();
2532 QgsProjUtils::proj_pj_unique_ptr crs( proj_create_from_database( pjContext, auth.toUtf8().constData(), code.toUtf8().constData(), PJ_CATEGORY_CRS,
false,
nullptr ) );
2541 proj4.replace(
"+type=crs"_L1, QString() );
2542 proj4 = proj4.trimmed();
2546 d->mWktPreferred.clear();
2547 d->mDescription = QString( proj_get_name( crs.get() ) );
2548 d->mAuthId = u
"%1:%2"_s.arg( auth, code );
2550 d->mAxisInvertedDirty =
true;
2555 d->mEllipsoidAcronym.clear();
2556 d->setPj( std::move( crs ) );
2559 if ( !dbVals.isEmpty() )
2561 const QStringList parts = dbVals.split(
',' );
2562 d->mSrsId = parts.at( 0 ).toInt();
2563 d->mSRID = parts.at( 1 ).toInt();
2571QList<long> QgsCoordinateReferenceSystem::userSrsIds()
2573 QList<long> results;
2577 QFileInfo myInfo( db );
2578 if ( !myInfo.exists() )
2584 sqlite3_database_unique_ptr database;
2585 sqlite3_statement_unique_ptr statement;
2588 int result = openDatabase( db, database );
2589 if ( result != SQLITE_OK )
2591 QgsDebugError(
"failed : " + db +
" could not be opened!" );
2597 statement = database.
prepare( sql, rc );
2600 int ret = statement.
step();
2602 if ( ret == SQLITE_DONE )
2608 if ( ret == SQLITE_ROW )
2614 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
2622long QgsCoordinateReferenceSystem::matchToUserCrs()
const
2624 PJ *obj = d->threadLocalProjObject();
2628 const QList< long > ids = userSrsIds();
2629 for (
long id : ids )
2632 if ( candidate.
projObject() && proj_is_equivalent_to( obj, candidate.
projObject(), PJ_COMP_EQUIVALENT ) )
2640static void sync_db_proj_logger(
void * ,
int level,
const char *message )
2645 if ( level == PJ_LOG_ERROR )
2649 else if ( level == PJ_LOG_DEBUG )
2657 setlocale( LC_ALL,
"C" );
2660 int inserted = 0, updated = 0, deleted = 0, errors = 0;
2665 if ( database.
open( dbFilePath ) != SQLITE_OK )
2671 if ( sqlite3_exec( database.get(),
"BEGIN TRANSACTION",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2679 char *errMsg =
nullptr;
2681 bool createdTypeColumn =
false;
2682 if ( sqlite3_exec( database.get(),
"ALTER TABLE tbl_srs ADD COLUMN srs_type text",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2684 createdTypeColumn =
true;
2685 if ( sqlite3_exec( database.get(),
"CREATE INDEX srs_type ON tbl_srs(srs_type)",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2692 if ( sqlite3_exec( database.get(),
"create table tbl_info (proj_major INT, proj_minor INT, proj_patch INT)",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2694 QString sql = u
"INSERT INTO tbl_info(proj_major, proj_minor, proj_patch) VALUES (%1, %2,%3)"_s
2695 .arg( QString::number( PROJ_VERSION_MAJOR ), QString::number( PROJ_VERSION_MINOR ), QString::number( PROJ_VERSION_PATCH ) );
2696 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2698 QgsDebugError( u
"Could not execute: %1 [%2/%3]\n"_s.arg( sql, database.
errorMessage(), errMsg ? errMsg :
"(unknown error)" ) );
2700 sqlite3_free( errMsg );
2707 QString sql = u
"SELECT proj_major, proj_minor, proj_patch FROM tbl_info"_s;
2708 statement = database.
prepare( sql, result );
2709 if ( result != SQLITE_OK )
2714 if ( statement.
step() == SQLITE_ROW )
2719 if ( !createdTypeColumn && major == PROJ_VERSION_MAJOR && minor == PROJ_VERSION_MINOR && patch == PROJ_VERSION_PATCH )
2725 QgsDebugError( u
"Could not retrieve previous CRS sync PROJ version number"_s );
2732 proj_log_func( pjContext,
nullptr, sync_db_proj_logger );
2734 PROJ_STRING_LIST authorities = proj_get_authorities_from_database( pjContext );
2736 int nextSrsId = 67218;
2737 int nextSrId = 520007218;
2738 for (
auto authIter = authorities; authIter && *authIter; ++authIter )
2740 const QString authority( *authIter );
2742 PROJ_STRING_LIST codes = proj_get_codes_from_database( pjContext, *authIter, PJ_TYPE_CRS,
true );
2744 QStringList allCodes;
2746 for (
auto codesIter = codes; codesIter && *codesIter; ++codesIter )
2748 const QString code( *codesIter );
2754 QgsDebugError( u
"Could not load '%1:%2'"_s.arg( authority, code ) );
2758 const PJ_TYPE pjType = proj_get_type( crs.get() );
2760 QString srsTypeString;
2765 case PJ_TYPE_ELLIPSOID:
2766 case PJ_TYPE_PRIME_MERIDIAN:
2767 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
2768 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
2769 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
2770 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
2771 case PJ_TYPE_DATUM_ENSEMBLE:
2772 case PJ_TYPE_CONVERSION:
2773 case PJ_TYPE_TRANSFORMATION:
2774 case PJ_TYPE_CONCATENATED_OPERATION:
2775 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
2776 case PJ_TYPE_TEMPORAL_DATUM:
2777 case PJ_TYPE_ENGINEERING_DATUM:
2778 case PJ_TYPE_PARAMETRIC_DATUM:
2779 case PJ_TYPE_UNKNOWN:
2783 case PJ_TYPE_GEOGRAPHIC_CRS:
2786 case PJ_TYPE_GEODETIC_CRS:
2790 case PJ_TYPE_GEOCENTRIC_CRS:
2794 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
2798 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
2802 case PJ_TYPE_PROJECTED_CRS:
2806 case PJ_TYPE_COMPOUND_CRS:
2810 case PJ_TYPE_TEMPORAL_CRS:
2814 case PJ_TYPE_ENGINEERING_CRS:
2818 case PJ_TYPE_BOUND_CRS:
2822 case PJ_TYPE_VERTICAL_CRS:
2826#if PROJ_VERSION_MAJOR > 9 || ( PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 2 )
2827 case PJ_TYPE_DERIVED_PROJECTED_CRS:
2830 case PJ_TYPE_COORDINATE_METADATA:
2833 case PJ_TYPE_OTHER_CRS:
2842 proj4.replace(
"+type=crs"_L1, QString() );
2843 proj4 = proj4.trimmed();
2845 if ( proj4.isEmpty() )
2858 if ( translatedOperation.isEmpty() && !
operation.isEmpty() )
2860 std::cout << u
"Operation needs translation in QgsCoordinateReferenceSystemUtils::translateProjection: %1"_s.arg(
operation ).toLocal8Bit().constData() << std::endl;
2861 qFatal(
"aborted" );
2864 const bool deprecated = proj_is_deprecated( crs.get() );
2865 const QString name( proj_get_name( crs.get() ) );
2867 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 );
2868 statement = database.
prepare( sql, result );
2869 if ( result != SQLITE_OK )
2878 QString dbOperation;
2879 bool dbSrsDeprecated = deprecated;
2880 if ( statement.
step() == SQLITE_ROW )
2884 dbSrsDeprecated = statement.
columnAsText( 2 ).toInt() != 0;
2889 if ( !dbSrsProj4.isEmpty() || !dbSrsDesc.isEmpty() )
2891 if ( proj4 != dbSrsProj4 || name != dbSrsDesc || deprecated != dbSrsDeprecated || dbSrsType != srsTypeString || dbOperation !=
operation )
2894 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 ) )
2896 .arg( deprecated ? 1 : 0 )
2899 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2901 QgsDebugError( u
"Could not execute: %1 [%2/%3]\n"_s.arg( sql, database.
errorMessage(), errMsg ? errMsg :
"(unknown error)" ) );
2903 sqlite3_free( errMsg );
2920 if ( !dbVals.isEmpty() )
2922 const QStringList parts = dbVals.split(
',' );
2923 srsId = parts.at( 0 );
2924 srId = parts.at( 1 );
2926 if ( srId.isEmpty() )
2928 srId = QString::number( nextSrId );
2931 if ( srsId.isEmpty() )
2933 srsId = QString::number( nextSrsId );
2937 if ( !srsId.isEmpty() )
2939 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
2946 .arg( deprecated ? 1 : 0 )
2951 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
2957 .arg( deprecated ? 1 : 0 )
2962 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) == SQLITE_OK )
2968 qCritical(
"Could not execute: %s [%s/%s]\n", sql.toLocal8Bit().constData(), sqlite3_errmsg( database.get() ), errMsg ? errMsg :
"(unknown error)" );
2972 sqlite3_free( errMsg );
2977 proj_string_list_destroy( codes );
2979 const QString sql = u
"DELETE FROM tbl_srs WHERE auth_name='%1' AND NOT auth_id IN (%2)"_s.arg( authority, allCodes.join(
',' ) );
2980 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2982 deleted = sqlite3_changes( database.get() );
2987 qCritical(
"Could not execute: %s [%s]\n", sql.toLocal8Bit().constData(), sqlite3_errmsg( database.get() ) );
2990 proj_string_list_destroy( authorities );
2992 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 ) );
2993 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2995 QgsDebugError( u
"Could not execute: %1 [%2/%3]\n"_s.arg( sql, database.
errorMessage(), errMsg ? errMsg :
"(unknown error)" ) );
2997 sqlite3_free( errMsg );
3001 if ( sqlite3_exec( database.get(),
"COMMIT",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
3008 QgsDebugMsgLevel( u
"CRS update (inserted:%1 updated:%2 deleted:%3 errors:%4)"_s.arg( inserted ).arg( updated ).arg( deleted ).arg( errors ), 4 );
3016 return updated + inserted;
3019const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::stringCache()
3021 return *sStringCache();
3024const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::projCache()
3026 return *sProj4Cache();
3029const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::ogcCache()
3031 return *sOgcCache();
3034const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::wktCache()
3036 return *sWktCache();
3039const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srIdCache()
3041 return *sSrIdCache();
3044const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srsIdCache()
3046 return *sSrsIdCache();
3056 if (
PJ *obj = d->threadLocalProjObject() )
3063 const PJ_TYPE pjType = proj_get_type( geoCrs.get() );
3064 if ( pjType == PJ_TYPE_GEOCENTRIC_CRS )
3073 if ( !geoGraphicCrs )
3100 if (
PJ *obj = d->threadLocalProjObject() )
3154 if (
PJ *obj = d->threadLocalProjObject() )
3187 if (
PJ *obj = d->threadLocalProjObject() )
3198 if (
PJ *obj = d->threadLocalProjObject() )
3211 else if (
PJ *obj = d->threadLocalProjObject() )
3214 return geoCrs ? u
"%1:%2"_s.arg( proj_get_id_auth_name( geoCrs.get(), 0 ), proj_get_id_code( geoCrs.get(), 0 ) ) : QString();
3224 return d->threadLocalProjObject();
3237 d->mIsValid =
false;
3239 d->mWktPreferred.clear();
3246 switch ( proj_get_type(
object ) )
3248 case PJ_TYPE_GEODETIC_CRS:
3249 case PJ_TYPE_GEOCENTRIC_CRS:
3250 case PJ_TYPE_GEOGRAPHIC_CRS:
3251 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
3252 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
3253 case PJ_TYPE_VERTICAL_CRS:
3254 case PJ_TYPE_PROJECTED_CRS:
3255 case PJ_TYPE_COMPOUND_CRS:
3256 case PJ_TYPE_TEMPORAL_CRS:
3257 case PJ_TYPE_ENGINEERING_CRS:
3258 case PJ_TYPE_BOUND_CRS:
3259 case PJ_TYPE_OTHER_CRS:
3275 const QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
3276 const QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
3277 if ( !authName.isEmpty() && !authCode.isEmpty() &&
createFromOgcWmsCrs( u
"%1:%2"_s.arg( authName, authCode ) ) )
3285 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
3296 QStringList projections;
3298 projections.reserve( res.size() );
3301 projections << QString::number( crs.srsid() );
3328 sSrIdCacheLock()->lockForWrite();
3329 if ( !sDisableSrIdCache )
3332 sDisableSrIdCache =
true;
3333 sSrIdCache()->clear();
3335 sSrIdCacheLock()->unlock();
3337 sOgcLock()->lockForWrite();
3338 if ( !sDisableOgcCache )
3341 sDisableOgcCache =
true;
3342 sOgcCache()->clear();
3344 sOgcLock()->unlock();
3346 sProj4CacheLock()->lockForWrite();
3347 if ( !sDisableProjCache )
3350 sDisableProjCache =
true;
3351 sProj4Cache()->clear();
3353 sProj4CacheLock()->unlock();
3355 sCRSWktLock()->lockForWrite();
3356 if ( !sDisableWktCache )
3359 sDisableWktCache =
true;
3360 sWktCache()->clear();
3362 sCRSWktLock()->unlock();
3364 sCRSSrsIdLock()->lockForWrite();
3365 if ( !sDisableSrsIdCache )
3368 sDisableSrsIdCache =
true;
3369 sSrsIdCache()->clear();
3371 sCRSSrsIdLock()->unlock();
3373 sCrsStringLock()->lockForWrite();
3374 if ( !sDisableStringCache )
3377 sDisableStringCache =
true;
3378 sStringCache()->clear();
3380 sCrsStringLock()->unlock();
3389 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3392 if ( !c1.d->mIsValid && c2.d->mIsValid )
3395 if ( c1.d->mIsValid && !c2.d->mIsValid )
3401 if ( c1IsUser && !c2IsUser )
3404 if ( !c1IsUser && c2IsUser )
3407 if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
3409 if ( c1.d->mAuthId != c2.d->mAuthId )
3410 return c1.d->mAuthId > c2.d->mAuthId;
3418 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3421 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3424 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3427 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3430 return c1.d->mCoordinateEpoch > c2.d->mCoordinateEpoch;
3438 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3441 if ( c1.d->mIsValid && !c2.d->mIsValid )
3444 if ( !c1.d->mIsValid && c2.d->mIsValid )
3450 if ( !c1IsUser && c2IsUser )
3453 if ( c1IsUser && !c2IsUser )
3456 if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
3458 if ( c1.d->mAuthId != c2.d->mAuthId )
3459 return c1.d->mAuthId < c2.d->mAuthId;
3467 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3470 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3473 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3476 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3479 return c1.d->mCoordinateEpoch < c2.d->mCoordinateEpoch;
3484 return !( c1 < c2 );
3488 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(), 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.