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 )
143 mValidationHint = srs.mValidationHint;
144 mNativeFormat = srs.mNativeFormat;
154 const auto constDbs = dbs;
155 for (
const QString &db : constDbs )
157 QFileInfo myInfo( db );
158 if ( !myInfo.exists() )
168 int result = openDatabase( db, database );
169 if ( result != SQLITE_OK )
175 QString sql = u
"select srs_id from tbl_srs"_s;
177 statement = database.
prepare( sql, rc );
181 int ret = statement.
step();
183 if ( ret == SQLITE_DONE )
189 if ( ret == SQLITE_ROW )
195 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
200 std::sort( results.begin(), results.end() );
256 if ( horizontalObj && verticalObj )
263 QStringList formattedErrorList;
264 for (
const QString &rawError : std::as_const( errors ) )
266 QString formattedError = rawError;
267 formattedError.replace(
"proj_create_compound_crs: "_L1, QString() );
268 formattedErrorList.append( formattedError );
270 error = formattedErrorList.join(
'\n' );
278 if ( !ellipsoidParams.
valid )
327 if ( definition.isEmpty() )
331 if ( !sDisableStringCache )
333 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sStringCache()->constFind( definition );
334 if ( crsIt != sStringCache()->constEnd() )
337 *
this = crsIt.value();
344 const thread_local QRegularExpression reCrsId( u
"^(epsg|esri|osgeo|ignf|ogc|nkg|zangi|iau_2015|iau2000|postgis|internal|user)\\:(\\w+)$"_s, QRegularExpression::CaseInsensitiveOption );
345 QRegularExpressionMatch match = reCrsId.match( definition );
346 if ( match.capturedStart() == 0 )
348 QString authName = match.captured( 1 ).toLower();
349 if ( authName ==
"epsg"_L1 )
353 else if ( authName ==
"postgis"_L1 )
355 const long id = match.captured( 2 ).toLong();
360 else if ( authName ==
"esri"_L1
361 || authName ==
"osgeo"_L1
362 || authName ==
"ignf"_L1
363 || authName ==
"zangi"_L1
364 || authName ==
"iau2000"_L1
365 || authName ==
"ogc"_L1
366 || authName ==
"nkg"_L1
367 || authName ==
"iau_2015"_L1
374 const long id = match.captured( 2 ).toLong();
382 const thread_local QRegularExpression reCrsStr( u
"^(?:(wkt|proj4|proj)\\:)?(.+)$"_s, QRegularExpression::CaseInsensitiveOption );
383 match = reCrsStr.match( definition );
384 if ( match.capturedStart() == 0 )
386 if ( match.captured( 1 ).startsWith(
"proj"_L1, Qt::CaseInsensitive ) )
398 if ( !sDisableStringCache )
399 sStringCache()->insert( definition, *
this );
405 if ( definition.isEmpty() )
409 OGRSpatialReferenceH crs = OSRNewSpatialReference(
nullptr );
411 if ( OSRSetFromUserInput( crs, definition.toLocal8Bit().constData() ) == OGRERR_NONE )
414 OSRDestroySpatialReference( crs );
424 const char *configOld = CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" );
425 const char *configNew =
"GEOGCS";
427 if ( strcmp( configOld,
"" ) == 0 )
429 CPLSetConfigOption(
"GDAL_FIX_ESRI_WKT", configNew );
430 if ( strcmp( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) != 0 )
432 .arg( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) );
437 QgsDebugMsgLevel( u
"GDAL_FIX_ESRI_WKT was already set : %1"_s.arg( configNew ), 4 );
447 if ( !sDisableOgcCache )
449 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sOgcCache()->constFind( crs );
450 if ( crsIt != sOgcCache()->constEnd() )
453 *
this = crsIt.value();
459 QString wmsCrs = crs;
464 const QString authorityLower = authority.toLower();
466 ( authorityLower ==
"user"_L1 ||
467 authorityLower ==
"custom"_L1 ||
468 authorityLower ==
"qgis"_L1 ) )
473 if ( !sDisableOgcCache )
474 sOgcCache()->insert( crs, *
this );
480 wmsCrs = authority +
':' + code;
484 const QString legacyKey = wmsCrs.toLower();
487 if ( it.key().compare( legacyKey, Qt::CaseInsensitive ) == 0 )
489 const QStringList parts = it.key().split(
':' );
490 const QString auth = parts.at( 0 );
491 const QString code = parts.at( 1 );
492 if ( loadFromAuthCode( auth, code ) )
495 if ( !sDisableOgcCache )
496 sOgcCache()->insert( crs, *
this );
505 if ( !sDisableOgcCache )
506 sOgcCache()->insert( crs, *
this );
511 if ( wmsCrs.compare(
"CRS:27"_L1, Qt::CaseInsensitive ) == 0 ||
512 wmsCrs.compare(
"OGC:CRS27"_L1, Qt::CaseInsensitive ) == 0 )
519 if ( wmsCrs.compare(
"CRS:83"_L1, Qt::CaseInsensitive ) == 0 ||
520 wmsCrs.compare(
"OGC:CRS83"_L1, Qt::CaseInsensitive ) == 0 )
527 if ( wmsCrs.compare(
"CRS:84"_L1, Qt::CaseInsensitive ) == 0 ||
528 wmsCrs.compare(
"OGC:CRS84"_L1, Qt::CaseInsensitive ) == 0 )
532 d->mAxisInverted =
false;
533 d->mAxisInvertedDirty =
false;
537 if ( !sDisableOgcCache )
538 sOgcCache()->insert( crs, *
this );
545 if ( !authority.isEmpty() && !code.isEmpty() && loadFromAuthCode( authority, code ) )
548 if ( !sDisableOgcCache )
549 sOgcCache()->insert( crs, *
this );
554 if ( !sDisableOgcCache )
564 if ( d->mIsValid || !sCustomSrsValidation )
568 if ( sCustomSrsValidation )
569 sCustomSrsValidation( *
this );
575 if ( !sDisableSrIdCache )
577 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrIdCache()->constFind(
id );
578 if ( crsIt != sSrIdCache()->constEnd() )
581 *
this = crsIt.value();
590 if ( it.value().endsWith( u
",%1"_s.arg(
id ) ) )
592 const QStringList parts = it.key().split(
':' );
593 const QString auth = parts.at( 0 );
594 const QString code = parts.at( 1 );
595 if ( loadFromAuthCode( auth, code ) )
598 if ( !sDisableSrIdCache )
599 sSrIdCache()->insert(
id, *
this );
609 if ( !sDisableSrIdCache )
610 sSrIdCache()->insert(
id, *
this );
618 if ( !sDisableSrsIdCache )
620 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrsIdCache()->constFind(
id );
621 if ( crsIt != sSrsIdCache()->constEnd() )
624 *
this = crsIt.value();
633 if ( it.value().startsWith( QString::number(
id ) +
',' ) )
635 const QStringList parts = it.key().split(
':' );
636 const QString auth = parts.at( 0 );
637 const QString code = parts.at( 1 );
638 if ( loadFromAuthCode( auth, code ) )
641 if ( !sDisableSrsIdCache )
642 sSrsIdCache()->insert(
id, *
this );
650 u
"srs_id"_s, QString::number(
id ) );
653 if ( !sDisableSrsIdCache )
654 sSrsIdCache()->insert(
id, *
this );
658bool QgsCoordinateReferenceSystem::loadFromDatabase(
const QString &db,
const QString &expression,
const QString &value )
662 QgsDebugMsgLevel(
"load CRS from " + db +
" where " + expression +
" is " + value, 3 );
664 d->mWktPreferred.clear();
666 QFileInfo myInfo( db );
667 if ( !myInfo.exists() )
673 sqlite3_database_unique_ptr database;
674 sqlite3_statement_unique_ptr statement;
677 myResult = openDatabase( db, database );
678 if ( myResult != SQLITE_OK )
695 QString mySql =
"select srs_id,description,projection_acronym,"
696 "ellipsoid_acronym,parameters,srid,auth_name||':'||auth_id,is_geo,wkt "
698 statement = database.
prepare( mySql, myResult );
701 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
706 d->mEllipsoidAcronym.clear();
708 d->mWktPreferred.clear();
711 d->mIsGeographic = statement.
columnAsText( 7 ).toInt() != 0;
713 d->mAxisInvertedDirty =
true;
717 d->mAuthId = u
"USER:%1"_s.arg( d->mSrsId );
719 else if ( !d->mAuthId.startsWith(
"USER:"_L1, Qt::CaseInsensitive ) )
721 QStringList parts = d->mAuthId.split(
':' );
722 QString auth = parts.at( 0 );
723 QString code = parts.at( 1 );
730 d->mIsValid = d->hasPj();
736 if ( !wkt.isEmpty() )
744 setProjString( d->mProj4 );
754void QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread(
PJ_CONTEXT *pj_context )
761 if ( !sDisableSrIdCache )
764 if ( !sDisableSrIdCache )
766 for (
auto it = sSrIdCache()->begin(); it != sSrIdCache()->end(); )
768 auto &v = it.value();
769 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
770 it = sSrIdCache()->erase( it );
776 if ( !sDisableOgcCache )
779 if ( !sDisableOgcCache )
781 for (
auto it = sOgcCache()->begin(); it != sOgcCache()->end(); )
783 auto &v = it.value();
784 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
785 it = sOgcCache()->erase( it );
791 if ( !sDisableProjCache )
794 if ( !sDisableProjCache )
796 for (
auto it = sProj4Cache()->begin(); it != sProj4Cache()->end(); )
798 auto &v = it.value();
799 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
800 it = sProj4Cache()->erase( it );
806 if ( !sDisableWktCache )
809 if ( !sDisableWktCache )
811 for (
auto it = sWktCache()->begin(); it != sWktCache()->end(); )
813 auto &v = it.value();
814 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
815 it = sWktCache()->erase( it );
821 if ( !sDisableSrsIdCache )
824 if ( !sDisableSrsIdCache )
826 for (
auto it = sSrsIdCache()->begin(); it != sSrsIdCache()->end(); )
828 auto &v = it.value();
829 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
830 it = sSrsIdCache()->erase( it );
836 if ( !sDisableStringCache )
839 if ( !sDisableStringCache )
841 for (
auto it = sStringCache()->begin(); it != sStringCache()->end(); )
843 auto &v = it.value();
844 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
845 it = sStringCache()->erase( it );
855 if ( d->mAxisInvertedDirty )
858 d->mAxisInvertedDirty =
false;
861 return d->mAxisInverted;
878 const thread_local QMap< Qgis::CrsAxisDirection, QString > mapping =
921 QList< Qgis::CrsAxisDirection > res;
922 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
925 res.reserve( axisCount );
927 for (
int i = 0; i < axisCount; ++i )
929 const char *outDirection =
nullptr;
930 proj_cs_get_axis_info( context, pjCs.get(), i,
940 const thread_local QRegularExpression rx( u
"([^\\s]+).*"_s );
941 const QRegularExpressionMatch match = rx.match( QString( outDirection ) );
942 if ( !match.hasMatch() )
945 const QString direction = match.captured( 1 );
947 for (
auto it = mapping.constBegin(); it != mapping.constEnd(); ++it )
949 if ( it.value().compare( direction, Qt::CaseInsensitive ) == 0 )
964 return createFromWktInternal( wkt, QString() );
967bool QgsCoordinateReferenceSystem::createFromWktInternal(
const QString &wkt,
const QString &description )
975 if ( !sDisableWktCache )
977 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sWktCache()->constFind( wkt );
978 if ( crsIt != sWktCache()->constEnd() )
981 *
this = crsIt.value();
983 if ( !
description.isEmpty() && d->mDescription.isEmpty() )
988 sWktCache()->insert( wkt, *
this );
997 d->mWktPreferred.clear();
1005 QgsCoordinateReferenceSystem::RecordMap record = getRecord(
"select * from tbl_srs where wkt=" +
QgsSqliteUtils::quotedString( wkt ) +
" order by deprecated" );
1006 if ( !record.empty() )
1008 long srsId = record[u
"srs_id"_s].toLong();
1016 setWktString( wkt );
1021 if ( d->mSrsId == 0 )
1024 long id = matchToUserCrs();
1033 if ( !sDisableWktCache )
1034 sWktCache()->insert( wkt, *
this );
1052 if ( projString.isEmpty() )
1057 if ( projString.trimmed().isEmpty() )
1059 d->mIsValid =
false;
1061 d->mWktPreferred.clear();
1066 if ( !sDisableProjCache )
1068 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sProj4Cache()->constFind( projString );
1069 if ( crsIt != sProj4Cache()->constEnd() )
1072 *
this = crsIt.value();
1086 QString myProj4String = projString.trimmed();
1087 myProj4String.remove( u
"+type=crs"_s );
1088 myProj4String = myProj4String.trimmed();
1090 d->mIsValid =
false;
1091 d->mWktPreferred.clear();
1096 const QString projCrsString = myProj4String + ( myProj4String.contains( u
"+type=crs"_s ) ? QString() : u
" +type=crs"_s );
1104 const QString
authid = u
"%1:%2"_s.arg( authName, authCode );
1108 if ( !sDisableProjCache )
1109 sProj4Cache()->insert( projString, *
this );
1116 QgsCoordinateReferenceSystem::RecordMap myRecord = getRecord(
"select * from tbl_srs where parameters=" +
QgsSqliteUtils::quotedString( myProj4String ) +
" order by deprecated" );
1118 if ( !myRecord.empty() )
1120 id = myRecord[u
"srs_id"_s].toLong();
1129 setProjString( myProj4String );
1132 id = matchToUserCrs();
1141 setProjString( myProj4String );
1145 if ( !sDisableProjCache )
1146 sProj4Cache()->insert( projString, *
this );
1152QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord(
const QString &sql )
1154 QString myDatabaseFileName;
1155 QgsCoordinateReferenceSystem::RecordMap myMap;
1156 QString myFieldName;
1157 QString myFieldValue;
1164 QFileInfo myInfo( myDatabaseFileName );
1165 if ( !myInfo.exists() )
1167 QgsDebugError(
"failed : " + myDatabaseFileName +
" does not exist!" );
1172 myResult = openDatabase( myDatabaseFileName, database );
1173 if ( myResult != SQLITE_OK )
1178 statement = database.
prepare( sql, myResult );
1180 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1184 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1186 myFieldName = statement.
columnName( myColNo );
1188 myMap[myFieldName] = myFieldValue;
1190 if ( statement.
step() != SQLITE_DONE )
1201 if ( myMap.empty() )
1204 QFileInfo myFileInfo;
1205 myFileInfo.setFile( myDatabaseFileName );
1206 if ( !myFileInfo.exists() )
1213 myResult = openDatabase( myDatabaseFileName, database );
1214 if ( myResult != SQLITE_OK )
1219 statement = database.
prepare( sql, myResult );
1221 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1225 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1227 myFieldName = statement.
columnName( myColNo );
1229 myMap[myFieldName] = myFieldValue;
1232 if ( statement.
step() != SQLITE_DONE )
1265 if ( d->mDescription.isNull() )
1271 return d->mDescription;
1278 if ( !
authid().isEmpty() )
1288 id =
isValid() ? QObject::tr(
"Custom CRS" ) : QObject::tr(
"Unknown CRS" );
1290 id = QObject::tr(
"Custom CRS: %1" ).arg(
1293 else if ( !
toProj().isEmpty() )
1296 if ( !
id.isEmpty() && !std::isnan( d->mCoordinateEpoch ) )
1304 if ( d->mProjectionAcronym.isNull() )
1310 return d->mProjectionAcronym;
1316 if ( d->mEllipsoidAcronym.isNull() )
1318 if (
PJ *obj = d->threadLocalProjObject() )
1323 const QString ellipsoidAuthName( proj_get_id_auth_name( ellipsoid.get(), 0 ) );
1324 const QString ellipsoidAuthCode( proj_get_id_code( ellipsoid.get(), 0 ) );
1325 if ( !ellipsoidAuthName.isEmpty() && !ellipsoidAuthCode.isEmpty() )
1326 d->mEllipsoidAcronym = u
"%1:%2"_s.arg( ellipsoidAuthName, ellipsoidAuthCode );
1329 double semiMajor, semiMinor, invFlattening;
1330 int semiMinorComputed = 0;
1331 if ( proj_ellipsoid_get_parameters(
QgsProjContext::get(), ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
1333 d->mEllipsoidAcronym = u
"PARAMETER:%1:%2"_s.arg(
qgsDoubleToString( semiMajor ),
1338 d->mEllipsoidAcronym.clear();
1343 return d->mEllipsoidAcronym;
1347 return d->mEllipsoidAcronym;
1361 if ( d->mProj4.isEmpty() )
1363 if (
PJ *obj = d->threadLocalProjObject() )
1369 return d->mProj4.trimmed();
1377 if (
PJ *obj = d->threadLocalProjObject() )
1379 const QByteArray multiLineOption = u
"MULTILINE=%1"_s.arg( multiline ? u
"YES"_s : u
"NO"_s ).toLocal8Bit();
1380 const QByteArray indentatationWidthOption = u
"INDENTATION_WIDTH=%1"_s.arg( multiline ? QString::number( indentationWidth ) : u
"0"_s ).toLocal8Bit();
1381 const QByteArray schemaOption = u
"SCHEMA=%1"_s.arg( schema ).toLocal8Bit();
1382 const char *
const options[] = {multiLineOption.constData(), indentatationWidthOption.constData(), schemaOption.constData(),
nullptr};
1385 return json ? std::string{ json } : std::string{};
1396 switch ( d->mProjType )
1398 case PJ_TYPE_UNKNOWN:
1401 case PJ_TYPE_ELLIPSOID:
1402 case PJ_TYPE_PRIME_MERIDIAN:
1403 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
1404 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
1405 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
1406 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
1407 case PJ_TYPE_DATUM_ENSEMBLE:
1408 case PJ_TYPE_CONVERSION:
1409 case PJ_TYPE_TRANSFORMATION:
1410 case PJ_TYPE_CONCATENATED_OPERATION:
1411 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
1412 case PJ_TYPE_TEMPORAL_DATUM:
1413 case PJ_TYPE_ENGINEERING_DATUM:
1414 case PJ_TYPE_PARAMETRIC_DATUM:
1418 case PJ_TYPE_GEOGRAPHIC_CRS:
1422 case PJ_TYPE_GEODETIC_CRS:
1424 case PJ_TYPE_GEOCENTRIC_CRS:
1426 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
1428 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
1430 case PJ_TYPE_VERTICAL_CRS:
1432 case PJ_TYPE_PROJECTED_CRS:
1434 case PJ_TYPE_COMPOUND_CRS:
1436 case PJ_TYPE_TEMPORAL_CRS:
1438 case PJ_TYPE_ENGINEERING_CRS:
1440 case PJ_TYPE_BOUND_CRS:
1442 case PJ_TYPE_OTHER_CRS:
1444#if PROJ_VERSION_MAJOR>9 || (PROJ_VERSION_MAJOR==9 && PROJ_VERSION_MINOR>=2)
1445 case PJ_TYPE_DERIVED_PROJECTED_CRS:
1447 case PJ_TYPE_COORDINATE_METADATA:
1461 return proj_is_deprecated( pj );
1466 return d->mIsGeographic;
1486 return QString( proj_get_celestial_body_name( context, pj ) );
1491 if ( d->mCoordinateEpoch == epoch )
1497 d->mCoordinateEpoch = epoch;
1498 d->setPj( std::move( clone ) );
1503 return d->mCoordinateEpoch;
1522 res.mName = QString( proj_get_name( ensemble.get() ) );
1523 res.mAuthority = QString( proj_get_id_auth_name( ensemble.get(), 0 ) );
1524 res.mCode = QString( proj_get_id_code( ensemble.get(), 0 ) );
1525 res.mRemarks = QString( proj_get_remarks( ensemble.get() ) );
1526 res.mScope = QString( proj_get_scope( ensemble.get() ) );
1527 res.mAccuracy = proj_datum_ensemble_get_accuracy( context, ensemble.get() );
1529 const int memberCount = proj_datum_ensemble_get_member_count( context, ensemble.get() );
1530 for (
int i = 0; i < memberCount; ++i )
1537 details.mName = QString( proj_get_name( member.get() ) );
1538 details.mAuthority = QString( proj_get_id_auth_name( member.get(), 0 ) );
1539 details.mCode = QString( proj_get_id_code( member.get(), 0 ) );
1540 details.mRemarks = QString( proj_get_remarks( member.get() ) );
1541 details.mScope = QString( proj_get_scope( member.get() ) );
1543 res.mMembers << details;
1553 QString projString =
toProj();
1554 projString.replace(
"+type=crs"_L1, QString() );
1557 if ( !transformation )
1560 PJ_COORD coord = proj_coord( 0, 0, 0, HUGE_VAL );
1561 coord.uv.u = point.
x() * M_PI / 180.0;
1562 coord.uv.v = point.
y() * M_PI / 180.0;
1564 proj_errno_reset( transformation.get() );
1565 const PJ_FACTORS pjFactors = proj_factors( transformation.get(), coord );
1566 if ( proj_errno( transformation.get() ) )
1571 res.mIsValid =
true;
1572 res.mMeridionalScale = pjFactors.meridional_scale;
1573 res.mParallelScale = pjFactors.parallel_scale;
1574 res.mArealScale = pjFactors.areal_scale;
1575 res.mAngularDistortion = pjFactors.angular_distortion;
1576 res.mMeridianParallelAngle = pjFactors.meridian_parallel_angle * 180 / M_PI;
1577 res.mMeridianConvergence = pjFactors.meridian_convergence * 180 / M_PI;
1578 res.mTissotSemimajor = pjFactors.tissot_semimajor;
1579 res.mTissotSemiminor = pjFactors.tissot_semiminor;
1580 res.mDxDlam = pjFactors.dx_dlam;
1581 res.mDxDphi = pjFactors.dx_dphi;
1582 res.mDyDlam = pjFactors.dy_dlam;
1583 res.mDyDphi = pjFactors.dy_dphi;
1595 QString projString =
toProj();
1596 projString.replace(
"+type=crs"_L1, QString() );
1597 if ( projString.isEmpty() )
1601 if ( !transformation )
1604 PJ_PROJ_INFO info = proj_pj_info( transformation.get() );
1619 return d->mMapUnits;
1627 PJ *obj = d->threadLocalProjObject();
1632 double southLat = 0;
1634 double northLat = 0;
1637 &westLon, &southLat, &eastLon, &northLat,
nullptr ) )
1652 const auto parts {
authid().split(
':' ) };
1653 if ( parts.length() == 2 )
1655 if ( parts[0] ==
"EPSG"_L1 )
1656 return u
"http://www.opengis.net/def/crs/EPSG/0/%1"_s.arg( parts[1] ) ;
1657 else if ( parts[0] ==
"OGC"_L1 )
1659 return u
"http://www.opengis.net/def/crs/OGC/1.3/%1"_s.arg( parts[1] ) ;
1675 const auto parts {
authid().split(
':' ) };
1676 if ( parts.length() == 2 )
1678 if ( parts[0] ==
"EPSG"_L1 )
1679 return u
"urn:ogc:def:crs:EPSG::%1"_s.arg( parts[1] );
1680 else if ( parts[0] ==
"OGC"_L1 )
1682 return u
"urn:ogc:def:crs:OGC:1.3:%1"_s.arg( parts[1] );
1716void QgsCoordinateReferenceSystem::setProjString(
const QString &proj4String )
1719 d->mProj4 = proj4String;
1720 d->mWktPreferred.clear();
1723 QString trimmed = proj4String.trimmed();
1725 trimmed +=
" +type=crs"_L1;
1735 const int errNo = proj_context_errno( ctx );
1736 QgsDebugError( u
"proj string rejected: %1"_s.arg( proj_context_errno_string( ctx, errNo ) ) );
1738 d->mIsValid =
false;
1742 d->mEllipsoidAcronym.clear();
1749bool QgsCoordinateReferenceSystem::setWktString(
const QString &wkt )
1752 d->mIsValid =
false;
1753 d->mWktPreferred.clear();
1755 PROJ_STRING_LIST warnings =
nullptr;
1756 PROJ_STRING_LIST grammarErrors =
nullptr;
1764 QgsDebugMsgLevel( u
"\n---------------------------------------------------------------"_s, 2 );
1765 QgsDebugMsgLevel( u
"This CRS could *** NOT *** be set from the supplied Wkt "_s, 2 );
1767 for (
auto iter = warnings; iter && *iter; ++iter )
1771 for (
auto iter = grammarErrors; iter && *iter; ++iter )
1775 QgsDebugMsgLevel( u
"---------------------------------------------------------------\n"_s, 2 );
1777 proj_string_list_destroy( warnings );
1778 proj_string_list_destroy( grammarErrors );
1784 if ( !sDisableWktCache )
1785 sWktCache()->insert( wkt, *
this );
1793 QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
1794 QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
1796 if ( authName.isEmpty() || authCode.isEmpty() )
1802 if ( !authName.isEmpty() && !authCode.isEmpty() )
1805 if ( fromAuthCode.loadFromAuthCode( authName, authCode ) )
1807 *
this = fromAuthCode;
1809 if ( !sDisableWktCache )
1810 sWktCache()->insert( wkt, *
this );
1817 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
1824void QgsCoordinateReferenceSystem::setMapUnits()
1851 if ( !coordinateSystem )
1857 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
1858 if ( axisCount > 0 )
1860 const char *outUnitName =
nullptr;
1862 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
1871 const QString unitName( outUnitName );
1875 if ( unitName.compare(
"degree"_L1, Qt::CaseInsensitive ) == 0 ||
1876 unitName.compare(
"degree minute second"_L1, Qt::CaseInsensitive ) == 0 ||
1877 unitName.compare(
"degree minute second hemisphere"_L1, Qt::CaseInsensitive ) == 0 ||
1878 unitName.compare(
"degree minute"_L1, Qt::CaseInsensitive ) == 0 ||
1879 unitName.compare(
"degree hemisphere"_L1, Qt::CaseInsensitive ) == 0 ||
1880 unitName.compare(
"degree minute hemisphere"_L1, Qt::CaseInsensitive ) == 0 ||
1881 unitName.compare(
"hemisphere degree"_L1, Qt::CaseInsensitive ) == 0 ||
1882 unitName.compare(
"hemisphere degree minute"_L1, Qt::CaseInsensitive ) == 0 ||
1883 unitName.compare(
"hemisphere degree minute second"_L1, Qt::CaseInsensitive ) == 0 ||
1884 unitName.compare(
"degree (supplier to define representation)"_L1, Qt::CaseInsensitive ) == 0 )
1886 else if ( unitName.compare(
"metre"_L1, Qt::CaseInsensitive ) == 0
1887 || unitName.compare(
'm'_L1, Qt::CaseInsensitive ) == 0
1888 || unitName.compare(
"meter"_L1, Qt::CaseInsensitive ) == 0 )
1890 else if ( unitName.compare(
"US survey foot"_L1, Qt::CaseInsensitive ) == 0 )
1892 else if ( unitName.compare(
"foot"_L1, Qt::CaseInsensitive ) == 0 )
1894 else if ( unitName.compare(
"British yard (Sears 1922)"_L1, Qt::CaseInsensitive ) == 0 )
1896 else if ( unitName.compare(
"British yard (Sears 1922 truncated)"_L1, Qt::CaseInsensitive ) == 0 )
1898 else if ( unitName.compare(
"British foot (Sears 1922)"_L1, Qt::CaseInsensitive ) == 0 )
1900 else if ( unitName.compare(
"British foot (Sears 1922 truncated)"_L1, Qt::CaseInsensitive ) == 0 )
1902 else if ( unitName.compare(
"British chain (Sears 1922)"_L1, Qt::CaseInsensitive ) == 0 )
1904 else if ( unitName.compare(
"British chain (Sears 1922 truncated)"_L1, Qt::CaseInsensitive ) == 0 )
1906 else if ( unitName.compare(
"British link (Sears 1922)"_L1, Qt::CaseInsensitive ) == 0 )
1908 else if ( unitName.compare(
"British link (Sears 1922 truncated)"_L1, Qt::CaseInsensitive ) == 0 )
1910 else if ( unitName.compare(
"British yard (Benoit 1895 A)"_L1, Qt::CaseInsensitive ) == 0 )
1912 else if ( unitName.compare(
"British foot (Benoit 1895 A)"_L1, Qt::CaseInsensitive ) == 0 )
1914 else if ( unitName.compare(
"British chain (Benoit 1895 A)"_L1, Qt::CaseInsensitive ) == 0 )
1916 else if ( unitName.compare(
"British link (Benoit 1895 A)"_L1, Qt::CaseInsensitive ) == 0 )
1918 else if ( unitName.compare(
"British yard (Benoit 1895 B)"_L1, Qt::CaseInsensitive ) == 0 )
1920 else if ( unitName.compare(
"British foot (Benoit 1895 B)"_L1, Qt::CaseInsensitive ) == 0 )
1922 else if ( unitName.compare(
"British chain (Benoit 1895 B)"_L1, Qt::CaseInsensitive ) == 0 )
1924 else if ( unitName.compare(
"British link (Benoit 1895 B)"_L1, Qt::CaseInsensitive ) == 0 )
1926 else if ( unitName.compare(
"British foot (1865)"_L1, Qt::CaseInsensitive ) == 0 )
1928 else if ( unitName.compare(
"British foot (1936)"_L1, Qt::CaseInsensitive ) == 0 )
1930 else if ( unitName.compare(
"Indian foot"_L1, Qt::CaseInsensitive ) == 0 )
1932 else if ( unitName.compare(
"Indian foot (1937)"_L1, Qt::CaseInsensitive ) == 0 )
1934 else if ( unitName.compare(
"Indian foot (1962)"_L1, Qt::CaseInsensitive ) == 0 )
1936 else if ( unitName.compare(
"Indian foot (1975)"_L1, Qt::CaseInsensitive ) == 0 )
1938 else if ( unitName.compare(
"Indian yard"_L1, Qt::CaseInsensitive ) == 0 )
1940 else if ( unitName.compare(
"Indian yard (1937)"_L1, Qt::CaseInsensitive ) == 0 )
1942 else if ( unitName.compare(
"Indian yard (1962)"_L1, Qt::CaseInsensitive ) == 0 )
1944 else if ( unitName.compare(
"Indian yard (1975)"_L1, Qt::CaseInsensitive ) == 0 )
1946 else if ( unitName.compare(
"Gold Coast foot"_L1, Qt::CaseInsensitive ) == 0 )
1948 else if ( unitName.compare(
"Clarke's foot"_L1, Qt::CaseInsensitive ) == 0 )
1950 else if ( unitName.compare(
"Clarke's yard"_L1, Qt::CaseInsensitive ) == 0 )
1952 else if ( unitName.compare(
"Clarke's chain"_L1, Qt::CaseInsensitive ) == 0 )
1954 else if ( unitName.compare(
"Clarke's link"_L1, Qt::CaseInsensitive ) == 0 )
1956 else if ( unitName.compare(
"kilometre"_L1, Qt::CaseInsensitive ) == 0 )
1958 else if ( unitName.compare(
"centimetre"_L1, Qt::CaseInsensitive ) == 0 )
1960 else if ( unitName.compare(
"millimetre"_L1, Qt::CaseInsensitive ) == 0 )
1962 else if ( unitName.compare(
"Statute mile"_L1, Qt::CaseInsensitive ) == 0 )
1964 else if ( unitName.compare(
"nautical mile"_L1, Qt::CaseInsensitive ) == 0 )
1966 else if ( unitName.compare(
"yard"_L1, Qt::CaseInsensitive ) == 0 )
1968 else if ( unitName.compare(
"fathom"_L1, Qt::CaseInsensitive ) == 0 )
1970 else if ( unitName.compare(
"US survey chain"_L1, Qt::CaseInsensitive ) == 0 )
1972 else if ( unitName.compare(
"chain"_L1, Qt::CaseInsensitive ) == 0 )
1974 else if ( unitName.compare(
"link"_L1, Qt::CaseInsensitive ) == 0 )
1976 else if ( unitName.compare(
"US survey link"_L1, Qt::CaseInsensitive ) == 0 )
1978 else if ( unitName.compare(
"US survey mile"_L1, Qt::CaseInsensitive ) == 0 )
1980 else if ( unitName.compare(
"German legal metre"_L1, Qt::CaseInsensitive ) == 0 )
1997 if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull()
2000 QgsDebugMsgLevel(
"QgsCoordinateReferenceSystem::findMatchingProj will only "
2001 "work if prj acr ellipsoid acr and proj4string are set"
2002 " and the current projection is valid!", 4 );
2012 QString mySql = QString(
"select srs_id,parameters from tbl_srs where "
2013 "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated" )
2020 myResult = openDatabase( myDatabaseFileName, database );
2021 if ( myResult != SQLITE_OK )
2026 statement = database.
prepare( mySql, myResult );
2027 if ( myResult == SQLITE_OK )
2030 while ( statement.
step() == SQLITE_ROW )
2034 if (
toProj() == myProj4String.trimmed() )
2036 return mySrsId.toLong();
2047 myResult = openDatabase( myDatabaseFileName, database );
2048 if ( myResult != SQLITE_OK )
2053 statement = database.
prepare( mySql, myResult );
2055 if ( myResult == SQLITE_OK )
2057 while ( statement.
step() == SQLITE_ROW )
2061 if (
toProj() == myProj4String.trimmed() )
2063 return mySrsId.toLong();
2077 if ( !d->mIsValid && !srs.d->mIsValid )
2080 if ( !d->mIsValid || !srs.d->mIsValid )
2088 if ( isUser != otherIsUser )
2092 if ( !isUser && ( !d->mAuthId.isEmpty() || !srs.d->mAuthId.isEmpty() ) )
2093 return d->mAuthId == srs.d->mAuthId;
2100 return !( *
this == srs );
2105 if (
PJ *obj = d->threadLocalProjObject() )
2108 if ( isDefaultPreferredFormat && !d->mWktPreferred.isEmpty() )
2111 return d->mWktPreferred;
2114 PJ_WKT_TYPE
type = PJ_WKT1_GDAL;
2118 type = PJ_WKT1_GDAL;
2121 type = PJ_WKT1_ESRI;
2124 type = PJ_WKT2_2015;
2127 type = PJ_WKT2_2015_SIMPLIFIED;
2130 type = PJ_WKT2_2019;
2133 type = PJ_WKT2_2019_SIMPLIFIED;
2137 const QByteArray multiLineOption = u
"MULTILINE=%1"_s.arg( multiline ? u
"YES"_s : u
"NO"_s ).toLocal8Bit();
2138 const QByteArray indentatationWidthOption = u
"INDENTATION_WIDTH=%1"_s.arg( multiline ? QString::number( indentationWidth ) : u
"0"_s ).toLocal8Bit();
2139 const char *
const options[] = {multiLineOption.constData(), indentatationWidthOption.constData(),
nullptr};
2142 if ( isDefaultPreferredFormat )
2145 d->mWktPreferred = res;
2157 QDomNode srsNode = node.namedItem( u
"spatialrefsys"_s );
2159 if ( ! srsNode.isNull() )
2161 bool initialized =
false;
2164 long srsid = srsNode.namedItem( u
"srsid"_s ).toElement().text().toLong( &ok );
2170 node = srsNode.namedItem( u
"authid"_s );
2171 if ( !node.isNull() )
2182 node = srsNode.namedItem( u
"epsg"_s );
2183 if ( !node.isNull() )
2201 const QString
description = srsNode.namedItem( u
"description"_s ).toElement().text();
2203 const QString wkt = srsNode.namedItem( u
"wkt"_s ).toElement().text();
2204 initialized = createFromWktInternal( wkt,
description );
2209 node = srsNode.namedItem( u
"proj4"_s );
2210 const QString proj4 = node.toElement().text();
2217 node = srsNode.namedItem( u
"proj4"_s );
2218 const QString proj4 = node.toElement().text();
2219 if ( !proj4.trimmed().isEmpty() )
2220 setProjString( node.toElement().text() );
2222 node = srsNode.namedItem( u
"srsid"_s );
2223 d->mSrsId = node.toElement().text().toLong();
2225 node = srsNode.namedItem( u
"srid"_s );
2226 d->mSRID = node.toElement().text().toLong();
2228 node = srsNode.namedItem( u
"authid"_s );
2229 d->mAuthId = node.toElement().text();
2231 node = srsNode.namedItem( u
"description"_s );
2232 d->mDescription = node.toElement().text();
2234 node = srsNode.namedItem( u
"projectionacronym"_s );
2235 d->mProjectionAcronym = node.toElement().text();
2237 node = srsNode.namedItem( u
"ellipsoidacronym"_s );
2238 d->mEllipsoidAcronym = node.toElement().text();
2240 node = srsNode.namedItem( u
"geographicflag"_s );
2241 d->mIsGeographic = node.toElement().text() ==
"true"_L1;
2243 d->mWktPreferred.clear();
2249 const QString epoch = srsNode.toElement().attribute( u
"coordinateEpoch"_s );
2250 if ( !epoch.isEmpty() )
2252 bool epochOk =
false;
2253 d->mCoordinateEpoch = epoch.toDouble( &epochOk );
2255 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2259 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2267 d =
new QgsCoordinateReferenceSystemPrivate();
2275 QDomElement layerNode = node.toElement();
2276 QDomElement srsElement = doc.createElement( u
"spatialrefsys"_s );
2280 if ( std::isfinite( d->mCoordinateEpoch ) )
2282 srsElement.setAttribute( u
"coordinateEpoch"_s, d->mCoordinateEpoch );
2285 QDomElement wktElement = doc.createElement( u
"wkt"_s );
2287 srsElement.appendChild( wktElement );
2289 QDomElement proj4Element = doc.createElement( u
"proj4"_s );
2290 proj4Element.appendChild( doc.createTextNode(
toProj() ) );
2291 srsElement.appendChild( proj4Element );
2293 QDomElement srsIdElement = doc.createElement( u
"srsid"_s );
2294 srsIdElement.appendChild( doc.createTextNode( QString::number(
srsid() ) ) );
2295 srsElement.appendChild( srsIdElement );
2297 QDomElement sridElement = doc.createElement( u
"srid"_s );
2298 sridElement.appendChild( doc.createTextNode( QString::number(
postgisSrid() ) ) );
2299 srsElement.appendChild( sridElement );
2301 QDomElement authidElement = doc.createElement( u
"authid"_s );
2302 authidElement.appendChild( doc.createTextNode(
authid() ) );
2303 srsElement.appendChild( authidElement );
2305 QDomElement descriptionElement = doc.createElement( u
"description"_s );
2306 descriptionElement.appendChild( doc.createTextNode(
description() ) );
2307 srsElement.appendChild( descriptionElement );
2309 QDomElement projectionAcronymElement = doc.createElement( u
"projectionacronym"_s );
2310 projectionAcronymElement.appendChild( doc.createTextNode(
projectionAcronym() ) );
2311 srsElement.appendChild( projectionAcronymElement );
2313 QDomElement ellipsoidAcronymElement = doc.createElement( u
"ellipsoidacronym"_s );
2314 ellipsoidAcronymElement.appendChild( doc.createTextNode(
ellipsoidAcronym() ) );
2315 srsElement.appendChild( ellipsoidAcronymElement );
2317 QDomElement geographicFlagElement = doc.createElement( u
"geographicflag"_s );
2318 QString geoFlagText = u
"false"_s;
2321 geoFlagText = u
"true"_s;
2324 geographicFlagElement.appendChild( doc.createTextNode( geoFlagText ) );
2325 srsElement.appendChild( geographicFlagElement );
2327 layerNode.appendChild( srsElement );
2339QString QgsCoordinateReferenceSystem::projFromSrsId(
const int srsId )
2341 QString myDatabaseFileName;
2342 QString myProjString;
2343 QString mySql = u
"select parameters from tbl_srs where srs_id = %1 order by deprecated"_s.arg( srsId );
2352 QFileInfo myFileInfo;
2353 myFileInfo.setFile( myDatabaseFileName );
2354 if ( !myFileInfo.exists() )
2365 sqlite3_database_unique_ptr database;
2366 sqlite3_statement_unique_ptr statement;
2369 rc = openDatabase( myDatabaseFileName, database );
2375 statement = database.
prepare( mySql, rc );
2377 if ( rc == SQLITE_OK )
2379 if ( statement.
step() == SQLITE_ROW )
2385 return myProjString;
2392 myResult = database.
open_v2( path, SQLITE_OPEN_READONLY,
nullptr );
2394 myResult = database.
open( path );
2396 if ( myResult != SQLITE_OK )
2405 .arg( database.
errorMessage() ), QObject::tr(
"CRS" ) );
2412 sCustomSrsValidation = f;
2417 return sCustomSrsValidation;
2420void QgsCoordinateReferenceSystem::debugPrint()
2423 QgsDebugMsgLevel(
"* Valid : " + ( d->mIsValid ? QString(
"true" ) : QString(
"false" ) ), 1 );
2444 mValidationHint = html;
2449 return mValidationHint;
2459 mNativeFormat = format;
2464 return mNativeFormat;
2467long QgsCoordinateReferenceSystem::getRecordCount()
2472 long myRecordCount = 0;
2475 if ( myResult != SQLITE_OK )
2481 QString mySql = u
"select count(*) from tbl_srs"_s;
2482 statement = database.
prepare( mySql, myResult );
2483 if ( myResult == SQLITE_OK )
2485 if ( statement.
step() == SQLITE_ROW )
2487 QString myRecordCountString = statement.
columnAsText( 0 );
2488 myRecordCount = myRecordCountString.toLong();
2491 return myRecordCount;
2497 bool isGeographic =
false;
2501 if ( !horizontalCrs )
2505 if ( coordinateSystem )
2507 const int axisCount = proj_cs_get_axis_count( pjContext, coordinateSystem.get() );
2508 if ( axisCount > 0 )
2510 const char *outUnitAuthName =
nullptr;
2511 const char *outUnitAuthCode =
nullptr;
2513 proj_cs_get_axis_info( pjContext, coordinateSystem.get(), 0,
2522 if ( outUnitAuthName && outUnitAuthCode )
2524 const char *unitCategory =
nullptr;
2525 if ( proj_uom_get_info_from_database( pjContext, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
2527 isGeographic = QString( unitCategory ).compare(
"angular"_L1, Qt::CaseInsensitive ) == 0;
2532 return isGeographic;
2537 thread_local const QRegularExpression projRegExp( u
"\\+proj=(\\S+)"_s );
2538 const QRegularExpressionMatch projMatch = projRegExp.match( proj );
2539 if ( !projMatch.hasMatch() )
2544 operation = projMatch.captured( 1 );
2546 const QRegularExpressionMatch ellipseMatch = projRegExp.match( proj );
2547 if ( ellipseMatch.hasMatch() )
2549 ellipsoid = ellipseMatch.captured( 1 );
2563bool QgsCoordinateReferenceSystem::loadFromAuthCode(
const QString &auth,
const QString &code )
2569 d->mIsValid =
false;
2570 d->mWktPreferred.clear();
2573 QgsProjUtils::proj_pj_unique_ptr crs( proj_create_from_database( pjContext, auth.toUtf8().constData(), code.toUtf8().constData(), PJ_CATEGORY_CRS,
false,
nullptr ) );
2582 proj4.replace(
"+type=crs"_L1, QString() );
2583 proj4 = proj4.trimmed();
2587 d->mWktPreferred.clear();
2588 d->mDescription = QString( proj_get_name( crs.get() ) );
2589 d->mAuthId = u
"%1:%2"_s.arg( auth, code );
2591 d->mAxisInvertedDirty =
true;
2596 d->mEllipsoidAcronym.clear();
2597 d->setPj( std::move( crs ) );
2600 if ( !dbVals.isEmpty() )
2602 const QStringList parts = dbVals.split(
',' );
2603 d->mSrsId = parts.at( 0 ).toInt();
2604 d->mSRID = parts.at( 1 ).toInt();
2612QList<long> QgsCoordinateReferenceSystem::userSrsIds()
2614 QList<long> results;
2618 QFileInfo myInfo( db );
2619 if ( !myInfo.exists() )
2625 sqlite3_database_unique_ptr database;
2626 sqlite3_statement_unique_ptr statement;
2629 int result = openDatabase( db, database );
2630 if ( result != SQLITE_OK )
2632 QgsDebugError(
"failed : " + db +
" could not be opened!" );
2638 statement = database.
prepare( sql, rc );
2641 int ret = statement.
step();
2643 if ( ret == SQLITE_DONE )
2649 if ( ret == SQLITE_ROW )
2655 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
2663long QgsCoordinateReferenceSystem::matchToUserCrs()
const
2665 PJ *obj = d->threadLocalProjObject();
2669 const QList< long > ids = userSrsIds();
2670 for (
long id : ids )
2673 if ( candidate.
projObject() && proj_is_equivalent_to( obj, candidate.
projObject(), PJ_COMP_EQUIVALENT ) )
2681static void sync_db_proj_logger(
void * ,
int level,
const char *message )
2686 if ( level == PJ_LOG_ERROR )
2690 else if ( level == PJ_LOG_DEBUG )
2698 setlocale( LC_ALL,
"C" );
2701 int inserted = 0, updated = 0, deleted = 0, errors = 0;
2706 if ( database.
open( dbFilePath ) != SQLITE_OK )
2712 if ( sqlite3_exec( database.get(),
"BEGIN TRANSACTION",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2720 char *errMsg =
nullptr;
2722 bool createdTypeColumn =
false;
2723 if ( sqlite3_exec( database.get(),
"ALTER TABLE tbl_srs ADD COLUMN srs_type text",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2725 createdTypeColumn =
true;
2726 if ( sqlite3_exec( database.get(),
"CREATE INDEX srs_type ON tbl_srs(srs_type)",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2733 if ( sqlite3_exec( database.get(),
"create table tbl_info (proj_major INT, proj_minor INT, proj_patch INT)",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2735 QString sql = u
"INSERT INTO tbl_info(proj_major, proj_minor, proj_patch) VALUES (%1, %2,%3)"_s
2736 .arg( QString::number( PROJ_VERSION_MAJOR ),
2737 QString::number( PROJ_VERSION_MINOR ),
2738 QString::number( PROJ_VERSION_PATCH ) );
2739 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2744 errMsg ? errMsg :
"(unknown error)" ) );
2746 sqlite3_free( errMsg );
2753 QString sql = u
"SELECT proj_major, proj_minor, proj_patch FROM tbl_info"_s;
2754 statement = database.
prepare( sql, result );
2755 if ( result != SQLITE_OK )
2760 if ( statement.
step() == SQLITE_ROW )
2765 if ( !createdTypeColumn && major == PROJ_VERSION_MAJOR && minor == PROJ_VERSION_MINOR && patch == PROJ_VERSION_PATCH )
2771 QgsDebugError( u
"Could not retrieve previous CRS sync PROJ version number"_s );
2778 proj_log_func( pjContext,
nullptr, sync_db_proj_logger );
2780 PROJ_STRING_LIST authorities = proj_get_authorities_from_database( pjContext );
2782 int nextSrsId = 67218;
2783 int nextSrId = 520007218;
2784 for (
auto authIter = authorities; authIter && *authIter; ++authIter )
2786 const QString authority( *authIter );
2788 PROJ_STRING_LIST codes = proj_get_codes_from_database( pjContext, *authIter, PJ_TYPE_CRS,
true );
2790 QStringList allCodes;
2792 for (
auto codesIter = codes; codesIter && *codesIter; ++codesIter )
2794 const QString code( *codesIter );
2800 QgsDebugError( u
"Could not load '%1:%2'"_s.arg( authority, code ) );
2804 const PJ_TYPE pjType = proj_get_type( crs.get( ) );
2806 QString srsTypeString;
2811 case PJ_TYPE_ELLIPSOID:
2812 case PJ_TYPE_PRIME_MERIDIAN:
2813 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
2814 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
2815 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
2816 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
2817 case PJ_TYPE_DATUM_ENSEMBLE:
2818 case PJ_TYPE_CONVERSION:
2819 case PJ_TYPE_TRANSFORMATION:
2820 case PJ_TYPE_CONCATENATED_OPERATION:
2821 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
2822 case PJ_TYPE_TEMPORAL_DATUM:
2823 case PJ_TYPE_ENGINEERING_DATUM:
2824 case PJ_TYPE_PARAMETRIC_DATUM:
2825 case PJ_TYPE_UNKNOWN:
2829 case PJ_TYPE_GEOGRAPHIC_CRS:
2832 case PJ_TYPE_GEODETIC_CRS:
2836 case PJ_TYPE_GEOCENTRIC_CRS:
2840 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
2844 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
2848 case PJ_TYPE_PROJECTED_CRS:
2852 case PJ_TYPE_COMPOUND_CRS:
2856 case PJ_TYPE_TEMPORAL_CRS:
2860 case PJ_TYPE_ENGINEERING_CRS:
2864 case PJ_TYPE_BOUND_CRS:
2868 case PJ_TYPE_VERTICAL_CRS:
2872#if PROJ_VERSION_MAJOR>9 || (PROJ_VERSION_MAJOR==9 && PROJ_VERSION_MINOR>=2)
2873 case PJ_TYPE_DERIVED_PROJECTED_CRS:
2876 case PJ_TYPE_COORDINATE_METADATA:
2879 case PJ_TYPE_OTHER_CRS:
2888 proj4.replace(
"+type=crs"_L1, QString() );
2889 proj4 = proj4.trimmed();
2891 if ( proj4.isEmpty() )
2904 if ( translatedOperation.isEmpty() && !
operation.isEmpty() )
2906 std::cout << u
"Operation needs translation in QgsCoordinateReferenceSystemUtils::translateProjection: %1"_s.arg(
operation ).toLocal8Bit().constData() << std::endl;
2907 qFatal(
"aborted" );
2910 const bool deprecated = proj_is_deprecated( crs.get() );
2911 const QString name( proj_get_name( crs.get() ) );
2913 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 );
2914 statement = database.
prepare( sql, result );
2915 if ( result != SQLITE_OK )
2924 QString dbOperation;
2925 bool dbSrsDeprecated = deprecated;
2926 if ( statement.
step() == SQLITE_ROW )
2930 dbSrsDeprecated = statement.
columnAsText( 2 ).toInt() != 0;
2935 if ( !dbSrsProj4.isEmpty() || !dbSrsDesc.isEmpty() )
2937 if ( proj4 != dbSrsProj4 || name != dbSrsDesc || deprecated != dbSrsDeprecated || dbSrsType != srsTypeString || dbOperation !=
operation )
2940 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
2943 .arg( deprecated ? 1 : 0 )
2948 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2953 errMsg ? errMsg :
"(unknown error)" ) );
2955 sqlite3_free( errMsg );
2972 if ( !dbVals.isEmpty() )
2974 const QStringList parts = dbVals.split(
',' );
2975 srsId = parts.at( 0 );
2976 srId = parts.at( 1 );
2978 if ( srId.isEmpty() )
2980 srId = QString::number( nextSrId );
2983 if ( srsId.isEmpty() )
2985 srsId = QString::number( nextSrsId );
2989 if ( !srsId.isEmpty() )
2991 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
3001 .arg( deprecated ? 1 : 0 )
3006 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
3015 .arg( deprecated ? 1 : 0 )
3020 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) == SQLITE_OK )
3026 qCritical(
"Could not execute: %s [%s/%s]\n",
3027 sql.toLocal8Bit().constData(),
3028 sqlite3_errmsg( database.get() ),
3029 errMsg ? errMsg :
"(unknown error)" );
3033 sqlite3_free( errMsg );
3038 proj_string_list_destroy( codes );
3040 const QString sql = u
"DELETE FROM tbl_srs WHERE auth_name='%1' AND NOT auth_id IN (%2)"_s.arg( authority, allCodes.join(
',' ) );
3041 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
3043 deleted = sqlite3_changes( database.get() );
3048 qCritical(
"Could not execute: %s [%s]\n",
3049 sql.toLocal8Bit().constData(),
3050 sqlite3_errmsg( database.get() ) );
3054 proj_string_list_destroy( authorities );
3056 QString sql = u
"UPDATE tbl_info set proj_major=%1,proj_minor=%2,proj_patch=%3"_s
3057 .arg( QString::number( PROJ_VERSION_MAJOR ),
3058 QString::number( PROJ_VERSION_MINOR ),
3059 QString::number( PROJ_VERSION_PATCH ) );
3060 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
3065 errMsg ? errMsg :
"(unknown error)" ) );
3067 sqlite3_free( errMsg );
3071 if ( sqlite3_exec( database.get(),
"COMMIT",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
3073 QgsDebugError( u
"Could not commit transaction: %1 [%2]\n"_s.arg(
3075 sqlite3_errmsg( database.get() ) )
3081 QgsDebugMsgLevel( u
"CRS update (inserted:%1 updated:%2 deleted:%3 errors:%4)"_s.arg( inserted ).arg( updated ).arg( deleted ).arg( errors ), 4 );
3089 return updated + inserted;
3092const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::stringCache()
3094 return *sStringCache();
3097const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::projCache()
3099 return *sProj4Cache();
3102const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::ogcCache()
3104 return *sOgcCache();
3107const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::wktCache()
3109 return *sWktCache();
3112const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srIdCache()
3114 return *sSrIdCache();
3117const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srsIdCache()
3119 return *sSrsIdCache();
3129 if (
PJ *obj = d->threadLocalProjObject() )
3136 const PJ_TYPE pjType = proj_get_type( geoCrs.get( ) );
3137 if ( pjType == PJ_TYPE_GEOCENTRIC_CRS )
3143 pjContext, PJ_ELLPS2D_LONGITUDE_LATITUDE,
"Degree", 1.0 ) );
3147 pjContext,
nullptr, datum ? datum.get() :
datumEnsemble.get(),
3150 if ( !geoGraphicCrs )
3177 if (
PJ *obj = d->threadLocalProjObject() )
3231 if (
PJ *obj = d->threadLocalProjObject() )
3264 if (
PJ *obj = d->threadLocalProjObject() )
3275 if (
PJ *obj = d->threadLocalProjObject() )
3288 else if (
PJ *obj = d->threadLocalProjObject() )
3291 return geoCrs ? u
"%1:%2"_s.arg( proj_get_id_auth_name( geoCrs.get(), 0 ), proj_get_id_code( geoCrs.get(), 0 ) ) : QString();
3301 return d->threadLocalProjObject();
3314 d->mIsValid =
false;
3316 d->mWktPreferred.clear();
3323 switch ( proj_get_type(
object ) )
3325 case PJ_TYPE_GEODETIC_CRS:
3326 case PJ_TYPE_GEOCENTRIC_CRS:
3327 case PJ_TYPE_GEOGRAPHIC_CRS:
3328 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
3329 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
3330 case PJ_TYPE_VERTICAL_CRS:
3331 case PJ_TYPE_PROJECTED_CRS:
3332 case PJ_TYPE_COMPOUND_CRS:
3333 case PJ_TYPE_TEMPORAL_CRS:
3334 case PJ_TYPE_ENGINEERING_CRS:
3335 case PJ_TYPE_BOUND_CRS:
3336 case PJ_TYPE_OTHER_CRS:
3352 const QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
3353 const QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
3354 if ( !authName.isEmpty() && !authCode.isEmpty() &&
createFromOgcWmsCrs( u
"%1:%2"_s.arg( authName, authCode ) ) )
3362 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
3373 QStringList projections;
3375 projections.reserve( res.size() );
3378 projections << QString::number( crs.srsid() );
3405 sSrIdCacheLock()->lockForWrite();
3406 if ( !sDisableSrIdCache )
3409 sDisableSrIdCache =
true;
3410 sSrIdCache()->clear();
3412 sSrIdCacheLock()->unlock();
3414 sOgcLock()->lockForWrite();
3415 if ( !sDisableOgcCache )
3418 sDisableOgcCache =
true;
3419 sOgcCache()->clear();
3421 sOgcLock()->unlock();
3423 sProj4CacheLock()->lockForWrite();
3424 if ( !sDisableProjCache )
3427 sDisableProjCache =
true;
3428 sProj4Cache()->clear();
3430 sProj4CacheLock()->unlock();
3432 sCRSWktLock()->lockForWrite();
3433 if ( !sDisableWktCache )
3436 sDisableWktCache =
true;
3437 sWktCache()->clear();
3439 sCRSWktLock()->unlock();
3441 sCRSSrsIdLock()->lockForWrite();
3442 if ( !sDisableSrsIdCache )
3445 sDisableSrsIdCache =
true;
3446 sSrsIdCache()->clear();
3448 sCRSSrsIdLock()->unlock();
3450 sCrsStringLock()->lockForWrite();
3451 if ( !sDisableStringCache )
3454 sDisableStringCache =
true;
3455 sStringCache()->clear();
3457 sCrsStringLock()->unlock();
3466 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3469 if ( !c1.d->mIsValid && c2.d->mIsValid )
3472 if ( c1.d->mIsValid && !c2.d->mIsValid )
3478 if ( c1IsUser && !c2IsUser )
3481 if ( !c1IsUser && c2IsUser )
3484 if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
3486 if ( c1.d->mAuthId != c2.d->mAuthId )
3487 return c1.d->mAuthId > c2.d->mAuthId;
3495 if ( c1.d->mCoordinateEpoch == 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 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3507 return c1.d->mCoordinateEpoch > c2.d->mCoordinateEpoch;
3515 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3518 if ( c1.d->mIsValid && !c2.d->mIsValid )
3521 if ( !c1.d->mIsValid && c2.d->mIsValid )
3527 if ( !c1IsUser && c2IsUser )
3530 if ( c1IsUser && !c2IsUser )
3533 if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
3535 if ( c1.d->mAuthId != c2.d->mAuthId )
3536 return c1.d->mAuthId < c2.d->mAuthId;
3544 if ( c1.d->mCoordinateEpoch == 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 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3556 return c1.d->mCoordinateEpoch < c2.d->mCoordinateEpoch;
3561 return !( c1 < c2 );
3565 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.