29 #include <QDomElement>
32 #include <QTextStream>
34 #include <QRegularExpression>
41 #include "qgssettings.h"
50 #include <proj_experimental.h>
53 #include <ogr_srs_api.h>
54 #include <cpl_error.h>
65 bool QgsCoordinateReferenceSystem::sDisableSrIdCache =
false;
69 bool QgsCoordinateReferenceSystem::sDisableOgcCache =
false;
73 bool QgsCoordinateReferenceSystem::sDisableProjCache =
false;
77 bool QgsCoordinateReferenceSystem::sDisableWktCache =
false;
81 bool QgsCoordinateReferenceSystem::sDisableSrsIdCache =
false;
85 bool QgsCoordinateReferenceSystem::sDisableStringCache =
false;
94 if (
const char *proj4src = proj_as_proj_string(
QgsProjContext::get(), boundCrs.get(), PJ_PROJ_4,
nullptr ) )
96 return QString( proj4src );
113 d =
new QgsCoordinateReferenceSystemPrivate();
119 d =
new QgsCoordinateReferenceSystemPrivate();
127 , mValidationHint( srs.mValidationHint )
134 mValidationHint = srs.mValidationHint;
144 const auto constDbs = dbs;
145 for (
const QString &db : constDbs )
147 QFileInfo myInfo( db );
148 if ( !myInfo.exists() )
150 QgsDebugMsg(
"failed : " + db +
" does not exist!" );
158 int result = openDatabase( db, database );
159 if ( result != SQLITE_OK )
161 QgsDebugMsg(
"failed : " + db +
" could not be opened!" );
165 QString sql = QStringLiteral(
"select srs_id from tbl_srs" );
167 statement = database.
prepare( sql, rc );
171 int ret = statement.
step();
173 if ( ret == SQLITE_DONE )
179 if ( ret == SQLITE_ROW )
185 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
190 std::sort( results.begin(), results.end() );
263 QgsDebugMsg( QStringLiteral(
"Unexpected case reached!" ) );
270 if ( definition.isEmpty() )
274 if ( !sDisableStringCache )
276 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sStringCache()->constFind( definition );
277 if ( crsIt != sStringCache()->constEnd() )
280 *
this = crsIt.value();
287 const thread_local QRegularExpression reCrsId( QStringLiteral(
"^(epsg|esri|osgeo|ignf|zangi|iau2000|postgis|internal|user)\\:(\\w+)$" ), QRegularExpression::CaseInsensitiveOption );
288 QRegularExpressionMatch match = reCrsId.match( definition );
289 if ( match.capturedStart() == 0 )
291 QString authName = match.captured( 1 ).toLower();
292 if ( authName == QLatin1String(
"epsg" ) )
296 else if ( authName == QLatin1String(
"postgis" ) )
298 const long id = match.captured( 2 ).toLong();
303 else if ( authName == QLatin1String(
"esri" ) || authName == QLatin1String(
"osgeo" ) || authName == QLatin1String(
"ignf" ) || authName == QLatin1String(
"zangi" ) || authName == QLatin1String(
"iau2000" ) )
309 const long id = match.captured( 2 ).toLong();
317 const thread_local QRegularExpression reCrsStr( QStringLiteral(
"^(?:(wkt|proj4|proj)\\:)?(.+)$" ), QRegularExpression::CaseInsensitiveOption );
318 match = reCrsStr.match( definition );
319 if ( match.capturedStart() == 0 )
321 if ( match.captured( 1 ).startsWith( QLatin1String(
"proj" ), Qt::CaseInsensitive ) )
333 if ( !sDisableStringCache )
334 sStringCache()->insert( definition, *
this );
340 if ( definition.isEmpty() )
346 if ( OSRSetFromUserInput(
crs, definition.toLocal8Bit().constData() ) == OGRERR_NONE )
349 OSRDestroySpatialReference(
crs );
359 const char *configOld = CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" );
360 const char *configNew =
"GEOGCS";
362 if ( strcmp( configOld,
"" ) == 0 )
364 CPLSetConfigOption(
"GDAL_FIX_ESRI_WKT", configNew );
365 if ( strcmp( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) != 0 )
367 .arg( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) );
368 QgsDebugMsgLevel( QStringLiteral(
"set GDAL_FIX_ESRI_WKT : %1" ).arg( configNew ), 4 );
372 QgsDebugMsgLevel( QStringLiteral(
"GDAL_FIX_ESRI_WKT was already set : %1" ).arg( configNew ), 4 );
382 if ( !sDisableOgcCache )
384 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sOgcCache()->constFind(
crs );
385 if ( crsIt != sOgcCache()->constEnd() )
388 *
this = crsIt.value();
394 QString wmsCrs =
crs;
396 thread_local
const QRegExp re_uri( QStringLiteral(
"http://www\\.opengis\\.net/def/crs/([^/]+).+/([^/]+)" ), Qt::CaseInsensitive );
397 thread_local
const QRegExp re_urn( QStringLiteral(
"urn:ogc:def:crs:([^:]+).+([^:]+)" ), Qt::CaseInsensitive );
398 if ( re_uri.exactMatch( wmsCrs ) )
400 wmsCrs = re_uri.cap( 1 ) +
':' + re_uri.cap( 2 );
402 else if ( re_urn.exactMatch( wmsCrs ) )
404 wmsCrs = re_urn.cap( 1 ) +
':' + re_urn.cap( 2 );
408 thread_local
const QRegExp re_urn_custom( QStringLiteral(
"(user|custom|qgis):(\\d+)" ), Qt::CaseInsensitive );
409 if ( re_urn_custom.exactMatch( wmsCrs ) &&
createFromSrsId( re_urn_custom.cap( 2 ).toInt() ) )
412 if ( !sDisableOgcCache )
413 sOgcCache()->insert(
crs, *
this );
419 const QString legacyKey = wmsCrs.toLower();
422 if ( it.key().compare( legacyKey, Qt::CaseInsensitive ) == 0 )
424 const QStringList parts = it.key().split(
':' );
425 const QString auth = parts.at( 0 );
426 const QString code = parts.at( 1 );
427 if ( loadFromAuthCode( auth, code ) )
430 if ( !sDisableOgcCache )
431 sOgcCache()->insert(
crs, *
this );
440 if ( !sDisableOgcCache )
441 sOgcCache()->insert(
crs, *
this );
446 if ( wmsCrs.compare( QLatin1String(
"CRS:27" ), Qt::CaseInsensitive ) == 0 ||
447 wmsCrs.compare( QLatin1String(
"OGC:CRS27" ), Qt::CaseInsensitive ) == 0 )
454 if ( wmsCrs.compare( QLatin1String(
"CRS:83" ), Qt::CaseInsensitive ) == 0 ||
455 wmsCrs.compare( QLatin1String(
"OGC:CRS83" ), Qt::CaseInsensitive ) == 0 )
462 if ( wmsCrs.compare( QLatin1String(
"CRS:84" ), Qt::CaseInsensitive ) == 0 ||
463 wmsCrs.compare( QLatin1String(
"OGC:CRS84" ), Qt::CaseInsensitive ) == 0 )
467 d->mAxisInverted =
false;
468 d->mAxisInvertedDirty =
false;
472 if ( !sDisableOgcCache )
473 sOgcCache()->insert(
crs, *
this );
479 if ( !sDisableOgcCache )
489 if ( d->mIsValid || !sCustomSrsValidation )
493 if ( sCustomSrsValidation )
494 sCustomSrsValidation( *
this );
500 if ( !sDisableSrIdCache )
502 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrIdCache()->constFind(
id );
503 if ( crsIt != sSrIdCache()->constEnd() )
506 *
this = crsIt.value();
515 if ( it.value().endsWith( QStringLiteral(
",%1" ).arg(
id ) ) )
517 const QStringList parts = it.key().split(
':' );
518 const QString auth = parts.at( 0 );
519 const QString code = parts.at( 1 );
520 if ( loadFromAuthCode( auth, code ) )
523 if ( !sDisableSrIdCache )
524 sSrIdCache()->insert(
id, *
this );
534 if ( !sDisableSrIdCache )
535 sSrIdCache()->insert(
id, *
this );
543 if ( !sDisableSrsIdCache )
545 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrsIdCache()->constFind(
id );
546 if ( crsIt != sSrsIdCache()->constEnd() )
549 *
this = crsIt.value();
558 if ( it.value().startsWith( QString::number(
id ) +
',' ) )
560 const QStringList parts = it.key().split(
':' );
561 const QString auth = parts.at( 0 );
562 const QString code = parts.at( 1 );
563 if ( loadFromAuthCode( auth, code ) )
566 if ( !sDisableSrsIdCache )
567 sSrsIdCache()->insert(
id, *
this );
575 QStringLiteral(
"srs_id" ), QString::number(
id ) );
578 if ( !sDisableSrsIdCache )
579 sSrsIdCache()->insert(
id, *
this );
583 bool QgsCoordinateReferenceSystem::loadFromDatabase(
const QString &db,
const QString &expression,
const QString &value )
587 QgsDebugMsgLevel(
"load CRS from " + db +
" where " + expression +
" is " + value, 3 );
589 d->mWktPreferred.clear();
591 QFileInfo myInfo( db );
592 if ( !myInfo.exists() )
594 QgsDebugMsg(
"failed : " + db +
" does not exist!" );
602 myResult = openDatabase( db, database );
603 if ( myResult != SQLITE_OK )
620 QString mySql =
"select srs_id,description,projection_acronym,"
621 "ellipsoid_acronym,parameters,srid,auth_name||':'||auth_id,is_geo,wkt "
623 statement = database.
prepare( mySql, myResult );
626 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
631 d->mEllipsoidAcronym.clear();
633 d->mWktPreferred.clear();
636 d->mIsGeographic = statement.
columnAsText( 7 ).toInt() != 0;
638 d->mAxisInvertedDirty =
true;
640 if ( d->mSrsId >=
USER_CRS_START_ID && ( d->mAuthId.isEmpty() || d->mAuthId == QChar(
':' ) ) )
642 d->mAuthId = QStringLiteral(
"USER:%1" ).arg( d->mSrsId );
644 else if ( !d->mAuthId.startsWith( QLatin1String(
"USER:" ), Qt::CaseInsensitive ) )
646 QStringList parts = d->mAuthId.split(
':' );
647 QString auth = parts.at( 0 );
648 QString code = parts.at( 1 );
655 d->mIsValid = d->hasPj();
661 if ( !wkt.isEmpty() )
669 setProjString( d->mProj4 );
679 void QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread(
PJ_CONTEXT *pj_context )
686 if ( !sDisableSrIdCache )
689 if ( !sDisableSrIdCache )
691 for (
auto it = sSrIdCache()->begin(); it != sSrIdCache()->end(); )
693 auto &v = it.value();
694 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
695 it = sSrIdCache()->erase( it );
701 if ( !sDisableOgcCache )
704 if ( !sDisableOgcCache )
706 for (
auto it = sOgcCache()->begin(); it != sOgcCache()->end(); )
708 auto &v = it.value();
709 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
710 it = sOgcCache()->erase( it );
716 if ( !sDisableProjCache )
719 if ( !sDisableProjCache )
721 for (
auto it = sProj4Cache()->begin(); it != sProj4Cache()->end(); )
723 auto &v = it.value();
724 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
725 it = sProj4Cache()->erase( it );
731 if ( !sDisableWktCache )
734 if ( !sDisableWktCache )
736 for (
auto it = sWktCache()->begin(); it != sWktCache()->end(); )
738 auto &v = it.value();
739 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
740 it = sWktCache()->erase( it );
746 if ( !sDisableSrsIdCache )
749 if ( !sDisableSrsIdCache )
751 for (
auto it = sSrsIdCache()->begin(); it != sSrsIdCache()->end(); )
753 auto &v = it.value();
754 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
755 it = sSrsIdCache()->erase( it );
761 if ( !sDisableStringCache )
764 if ( !sDisableStringCache )
766 for (
auto it = sStringCache()->begin(); it != sStringCache()->end(); )
768 auto &v = it.value();
769 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
770 it = sStringCache()->erase( it );
780 if ( d->mAxisInvertedDirty )
783 d->mAxisInvertedDirty =
false;
786 return d->mAxisInverted;
791 return createFromWktInternal( wkt, QString() );
794 bool QgsCoordinateReferenceSystem::createFromWktInternal(
const QString &wkt,
const QString &description )
802 if ( !sDisableWktCache )
804 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sWktCache()->constFind( wkt );
805 if ( crsIt != sWktCache()->constEnd() )
808 *
this = crsIt.value();
810 if ( !
description.isEmpty() && d->mDescription.isEmpty() )
815 sWktCache()->insert( wkt, *
this );
824 d->mWktPreferred.clear();
827 QgsDebugMsgLevel( QStringLiteral(
"theWkt is uninitialized, operation failed" ), 4 );
832 QgsCoordinateReferenceSystem::RecordMap record = getRecord(
"select * from tbl_srs where wkt=" +
QgsSqliteUtils::quotedString( wkt ) +
" order by deprecated" );
833 if ( !record.empty() )
835 long srsId = record[QStringLiteral(
"srs_id" )].toLong();
848 if ( d->mSrsId == 0 )
851 long id = matchToUserCrs();
860 if ( !sDisableWktCache )
861 sWktCache()->insert( wkt, *
this );
879 if ( projString.isEmpty() )
884 if ( projString.trimmed().isEmpty() )
888 d->mWktPreferred.clear();
893 if ( !sDisableProjCache )
895 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sProj4Cache()->constFind( projString );
896 if ( crsIt != sProj4Cache()->constEnd() )
899 *
this = crsIt.value();
913 QString myProj4String = projString.trimmed();
914 myProj4String.remove( QStringLiteral(
"+type=crs" ) );
915 myProj4String = myProj4String.trimmed();
918 d->mWktPreferred.clear();
923 const QString projCrsString = myProj4String + ( myProj4String.contains( QStringLiteral(
"+type=crs" ) ) ? QString() : QStringLiteral(
" +type=crs" ) );
931 const QString
authid = QStringLiteral(
"%1:%2" ).arg( authName, authCode );
935 if ( !sDisableProjCache )
936 sProj4Cache()->insert( projString, *
this );
943 QgsCoordinateReferenceSystem::RecordMap myRecord = getRecord(
"select * from tbl_srs where parameters=" +
QgsSqliteUtils::quotedString( myProj4String ) +
" order by deprecated" );
945 if ( !myRecord.empty() )
947 id = myRecord[QStringLiteral(
"srs_id" )].toLong();
956 setProjString( myProj4String );
959 id = matchToUserCrs();
968 setProjString( myProj4String );
972 if ( !sDisableProjCache )
973 sProj4Cache()->insert( projString, *
this );
979 QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord(
const QString &sql )
981 QString myDatabaseFileName;
982 QgsCoordinateReferenceSystem::RecordMap myMap;
984 QString myFieldValue;
991 QFileInfo myInfo( myDatabaseFileName );
992 if ( !myInfo.exists() )
994 QgsDebugMsg(
"failed : " + myDatabaseFileName +
" does not exist!" );
999 myResult = openDatabase( myDatabaseFileName, database );
1000 if ( myResult != SQLITE_OK )
1005 statement = database.
prepare( sql, myResult );
1007 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1011 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1013 myFieldName = statement.
columnName( myColNo );
1015 myMap[myFieldName] = myFieldValue;
1017 if ( statement.
step() != SQLITE_DONE )
1019 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1028 if ( myMap.empty() )
1031 QFileInfo myFileInfo;
1032 myFileInfo.setFile( myDatabaseFileName );
1033 if ( !myFileInfo.exists() )
1035 QgsDebugMsg( QStringLiteral(
"user qgis.db not found" ) );
1040 myResult = openDatabase( myDatabaseFileName, database );
1041 if ( myResult != SQLITE_OK )
1046 statement = database.
prepare( sql, myResult );
1048 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1052 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1054 myFieldName = statement.
columnName( myColNo );
1056 myMap[myFieldName] = myFieldValue;
1059 if ( statement.
step() != SQLITE_DONE )
1061 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1092 if ( d->mDescription.isNull() )
1098 return d->mDescription;
1105 if ( !
authid().isEmpty() )
1115 id =
isValid() ? QObject::tr(
"Custom CRS" ) : QObject::tr(
"Unknown CRS" );
1117 id = QObject::tr(
"Custom CRS: %1" ).arg(
1120 else if ( !
toProj().isEmpty() )
1121 id = QObject::tr(
"Custom CRS: %1" ).arg( type ==
MediumString ? (
toProj().left( 50 ) + QString( QChar( 0x2026 ) ) )
1123 if ( !
id.isEmpty() && !std::isnan( d->mCoordinateEpoch ) )
1124 id += QStringLiteral(
" @ %1" ).arg( d->mCoordinateEpoch );
1131 if ( d->mProjectionAcronym.isNull() )
1137 return d->mProjectionAcronym;
1143 if ( d->mEllipsoidAcronym.isNull() )
1145 if (
PJ *obj = d->threadLocalProjObject() )
1150 const QString ellipsoidAuthName( proj_get_id_auth_name( ellipsoid.get(), 0 ) );
1151 const QString ellipsoidAuthCode( proj_get_id_code( ellipsoid.get(), 0 ) );
1152 if ( !ellipsoidAuthName.isEmpty() && !ellipsoidAuthCode.isEmpty() )
1153 d->mEllipsoidAcronym = QStringLiteral(
"%1:%2" ).arg( ellipsoidAuthName, ellipsoidAuthCode );
1156 double semiMajor, semiMinor, invFlattening;
1157 int semiMinorComputed = 0;
1158 if ( proj_ellipsoid_get_parameters(
QgsProjContext::get(), ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
1160 d->mEllipsoidAcronym = QStringLiteral(
"PARAMETER:%1:%2" ).arg(
qgsDoubleToString( semiMajor ),
1165 d->mEllipsoidAcronym.clear();
1170 return d->mEllipsoidAcronym;
1174 return d->mEllipsoidAcronym;
1188 if ( d->mProj4.isEmpty() )
1190 if (
PJ *obj = d->threadLocalProjObject() )
1196 return d->mProj4.trimmed();
1201 return d->mIsGeographic;
1219 #if PROJ_VERSION_MAJOR>8 || (PROJ_VERSION_MAJOR==8 && PROJ_VERSION_MINOR>=1)
1222 return QString( proj_get_celestial_body_name( context, pj ) );
1224 throw QgsNotSupportedException( QStringLiteral(
"Retrieving celestial body requires a QGIS build based on PROJ 8.1 or later" ) );
1230 if ( d->mCoordinateEpoch == epoch )
1236 d->mCoordinateEpoch = epoch;
1237 d->setPj( std::move( clone ) );
1242 return d->mCoordinateEpoch;
1254 #if PROJ_VERSION_MAJOR>=8
1262 res.mName = QString( proj_get_name( ensemble.get() ) );
1263 res.mAuthority = QString( proj_get_id_auth_name( ensemble.get(), 0 ) );
1264 res.mCode = QString( proj_get_id_code( ensemble.get(), 0 ) );
1265 res.mRemarks = QString( proj_get_remarks( ensemble.get() ) );
1266 res.mScope = QString( proj_get_scope( ensemble.get() ) );
1267 res.mAccuracy = proj_datum_ensemble_get_accuracy( context, ensemble.get() );
1269 const int memberCount = proj_datum_ensemble_get_member_count( context, ensemble.get() );
1270 for (
int i = 0; i < memberCount; ++i )
1277 details.mName = QString( proj_get_name( member.get() ) );
1278 details.mAuthority = QString( proj_get_id_auth_name( member.get(), 0 ) );
1279 details.mCode = QString( proj_get_id_code( member.get(), 0 ) );
1280 details.mRemarks = QString( proj_get_remarks( member.get() ) );
1281 details.mScope = QString( proj_get_scope( member.get() ) );
1283 res.mMembers << details;
1287 throw QgsNotSupportedException( QStringLiteral(
"Calculating datum ensembles requires a QGIS build based on PROJ 8.0 or later" ) );
1296 QString projString =
toProj();
1297 projString.replace( QLatin1String(
"+type=crs" ), QString() );
1300 if ( !transformation )
1303 PJ_COORD coord = proj_coord( 0, 0, 0, HUGE_VAL );
1304 coord.uv.u = point.
x() * M_PI / 180.0;
1305 coord.uv.v = point.
y() * M_PI / 180.0;
1307 proj_errno_reset( transformation.get() );
1308 const PJ_FACTORS pjFactors = proj_factors( transformation.get(), coord );
1309 if ( proj_errno( transformation.get() ) )
1314 res.mIsValid =
true;
1315 res.mMeridionalScale = pjFactors.meridional_scale;
1316 res.mParallelScale = pjFactors.parallel_scale;
1317 res.mArealScale = pjFactors.areal_scale;
1318 res.mAngularDistortion = pjFactors.angular_distortion;
1319 res.mMeridianParallelAngle = pjFactors.meridian_parallel_angle * 180 / M_PI;
1320 res.mMeridianConvergence = pjFactors.meridian_convergence * 180 / M_PI;
1321 res.mTissotSemimajor = pjFactors.tissot_semimajor;
1322 res.mTissotSemiminor = pjFactors.tissot_semiminor;
1323 res.mDxDlam = pjFactors.dx_dlam;
1324 res.mDxDphi = pjFactors.dx_dphi;
1325 res.mDyDlam = pjFactors.dy_dlam;
1326 res.mDyDphi = pjFactors.dy_dphi;
1335 QString projString =
toProj();
1336 projString.replace( QLatin1String(
"+type=crs" ), QString() );
1339 if ( !transformation )
1342 PJ_PROJ_INFO info = proj_pj_info( transformation.get() );
1357 return d->mMapUnits;
1365 PJ *obj = d->threadLocalProjObject();
1370 double southLat = 0;
1372 double northLat = 0;
1375 &westLon, &southLat, &eastLon, &northLat,
nullptr ) )
1404 void QgsCoordinateReferenceSystem::setProjString(
const QString &proj4String )
1407 d->mProj4 = proj4String;
1408 d->mWktPreferred.clear();
1411 QString trimmed = proj4String.trimmed();
1413 trimmed += QLatin1String(
" +type=crs" );
1423 const int errNo = proj_context_errno( ctx );
1424 QgsDebugMsg( QStringLiteral(
"proj string rejected: %1" ).arg( proj_errno_string( errNo ) ) );
1426 d->mIsValid =
false;
1430 d->mEllipsoidAcronym.clear();
1437 bool QgsCoordinateReferenceSystem::setWktString(
const QString &wkt )
1440 d->mIsValid =
false;
1441 d->mWktPreferred.clear();
1443 PROJ_STRING_LIST warnings =
nullptr;
1444 PROJ_STRING_LIST grammerErrors =
nullptr;
1452 QgsDebugMsg( QStringLiteral(
"\n---------------------------------------------------------------" ) );
1453 QgsDebugMsg( QStringLiteral(
"This CRS could *** NOT *** be set from the supplied Wkt " ) );
1455 for (
auto iter = warnings; iter && *iter; ++iter )
1457 for (
auto iter = grammerErrors; iter && *iter; ++iter )
1459 QgsDebugMsg( QStringLiteral(
"---------------------------------------------------------------\n" ) );
1461 proj_string_list_destroy( warnings );
1462 proj_string_list_destroy( grammerErrors );
1468 if ( !sDisableWktCache )
1469 sWktCache()->insert( wkt, *
this );
1476 QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
1477 QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
1479 if ( authName.isEmpty() || authCode.isEmpty() )
1485 if ( !authName.isEmpty() && !authCode.isEmpty() )
1487 if ( loadFromAuthCode( authName, authCode ) )
1490 if ( !sDisableWktCache )
1491 sWktCache()->insert( wkt, *
this );
1499 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
1507 void QgsCoordinateReferenceSystem::setMapUnits()
1524 if ( !coordinateSystem )
1530 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
1531 if ( axisCount > 0 )
1533 const char *outUnitName =
nullptr;
1535 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
1544 const QString unitName( outUnitName );
1548 if ( unitName.compare( QLatin1String(
"degree" ), Qt::CaseInsensitive ) == 0 ||
1549 unitName.compare( QLatin1String(
"degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1550 unitName.compare( QLatin1String(
"degree minute second hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1551 unitName.compare( QLatin1String(
"degree minute" ), Qt::CaseInsensitive ) == 0 ||
1552 unitName.compare( QLatin1String(
"degree hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1553 unitName.compare( QLatin1String(
"degree minute hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1554 unitName.compare( QLatin1String(
"hemisphere degree" ), Qt::CaseInsensitive ) == 0 ||
1555 unitName.compare( QLatin1String(
"hemisphere degree minute" ), Qt::CaseInsensitive ) == 0 ||
1556 unitName.compare( QLatin1String(
"hemisphere degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1557 unitName.compare( QLatin1String(
"degree (supplier to define representation)" ), Qt::CaseInsensitive ) == 0 )
1559 else if ( unitName.compare( QLatin1String(
"metre" ), Qt::CaseInsensitive ) == 0
1560 || unitName.compare( QLatin1String(
"m" ), Qt::CaseInsensitive ) == 0
1561 || unitName.compare( QLatin1String(
"meter" ), Qt::CaseInsensitive ) == 0 )
1564 else if ( unitName.compare( QLatin1String(
"US survey foot" ), Qt::CaseInsensitive ) == 0 ||
1565 unitName.compare( QLatin1String(
"foot" ), Qt::CaseInsensitive ) == 0 )
1567 else if ( unitName.compare( QLatin1String(
"kilometre" ), Qt::CaseInsensitive ) == 0 )
1569 else if ( unitName.compare( QLatin1String(
"centimetre" ), Qt::CaseInsensitive ) == 0 )
1571 else if ( unitName.compare( QLatin1String(
"millimetre" ), Qt::CaseInsensitive ) == 0 )
1573 else if ( unitName.compare( QLatin1String(
"Statute mile" ), Qt::CaseInsensitive ) == 0 )
1575 else if ( unitName.compare( QLatin1String(
"nautical mile" ), Qt::CaseInsensitive ) == 0 )
1577 else if ( unitName.compare( QLatin1String(
"yard" ), Qt::CaseInsensitive ) == 0 )
1594 if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull()
1597 QgsDebugMsgLevel(
"QgsCoordinateReferenceSystem::findMatchingProj will only "
1598 "work if prj acr ellipsoid acr and proj4string are set"
1599 " and the current projection is valid!", 4 );
1609 QString mySql = QString(
"select srs_id,parameters from tbl_srs where "
1610 "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated" )
1617 myResult = openDatabase( myDatabaseFileName, database );
1618 if ( myResult != SQLITE_OK )
1623 statement = database.
prepare( mySql, myResult );
1624 if ( myResult == SQLITE_OK )
1627 while ( statement.
step() == SQLITE_ROW )
1631 if (
toProj() == myProj4String.trimmed() )
1633 return mySrsId.toLong();
1644 myResult = openDatabase( myDatabaseFileName, database );
1645 if ( myResult != SQLITE_OK )
1650 statement = database.
prepare( mySql, myResult );
1652 if ( myResult == SQLITE_OK )
1654 while ( statement.
step() == SQLITE_ROW )
1658 if (
toProj() == myProj4String.trimmed() )
1660 return mySrsId.toLong();
1674 if ( !d->mIsValid && !srs.d->mIsValid )
1677 if ( !d->mIsValid || !srs.d->mIsValid )
1685 if ( isUser != otherIsUser )
1689 if ( !isUser && ( !d->mAuthId.isEmpty() || !srs.d->mAuthId.isEmpty() ) )
1690 return d->mAuthId == srs.d->mAuthId;
1697 return !( *
this == srs );
1702 if (
PJ *obj = d->threadLocalProjObject() )
1704 const bool isDefaultPreferredFormat = variant ==
WKT_PREFERRED && !multiline;
1705 if ( isDefaultPreferredFormat && !d->mWktPreferred.isEmpty() )
1708 return d->mWktPreferred;
1711 PJ_WKT_TYPE type = PJ_WKT1_GDAL;
1715 type = PJ_WKT1_GDAL;
1718 type = PJ_WKT1_ESRI;
1721 type = PJ_WKT2_2015;
1724 type = PJ_WKT2_2015_SIMPLIFIED;
1727 type = PJ_WKT2_2019;
1730 type = PJ_WKT2_2019_SIMPLIFIED;
1734 const QByteArray multiLineOption = QStringLiteral(
"MULTILINE=%1" ).arg( multiline ? QStringLiteral(
"YES" ) : QStringLiteral(
"NO" ) ).toLocal8Bit();
1735 const QByteArray indentatationWidthOption = QStringLiteral(
"INDENTATION_WIDTH=%1" ).arg( multiline ? QString::number( indentationWidth ) : QStringLiteral(
"0" ) ).toLocal8Bit();
1736 const char *
const options[] = {multiLineOption.constData(), indentatationWidthOption.constData(),
nullptr};
1739 if ( isDefaultPreferredFormat )
1742 d->mWktPreferred = res;
1754 QDomNode srsNode = node.namedItem( QStringLiteral(
"spatialrefsys" ) );
1756 if ( ! srsNode.isNull() )
1758 bool initialized =
false;
1761 long srsid = srsNode.namedItem( QStringLiteral(
"srsid" ) ).toElement().text().toLong( &ok );
1767 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
1768 if ( !node.isNull() )
1779 node = srsNode.namedItem( QStringLiteral(
"epsg" ) );
1780 if ( !node.isNull() )
1798 const QString
description = srsNode.namedItem( QStringLiteral(
"description" ) ).toElement().text();
1800 const QString wkt = srsNode.namedItem( QStringLiteral(
"wkt" ) ).toElement().text();
1801 initialized = createFromWktInternal( wkt,
description );
1806 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
1807 const QString proj4 = node.toElement().text();
1814 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
1815 const QString proj4 = node.toElement().text();
1816 if ( !proj4.trimmed().isEmpty() )
1817 setProjString( node.toElement().text() );
1819 node = srsNode.namedItem( QStringLiteral(
"srsid" ) );
1820 d->mSrsId = node.toElement().text().toLong();
1822 node = srsNode.namedItem( QStringLiteral(
"srid" ) );
1823 d->mSRID = node.toElement().text().toLong();
1825 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
1826 d->mAuthId = node.toElement().text();
1828 node = srsNode.namedItem( QStringLiteral(
"description" ) );
1829 d->mDescription = node.toElement().text();
1831 node = srsNode.namedItem( QStringLiteral(
"projectionacronym" ) );
1832 d->mProjectionAcronym = node.toElement().text();
1834 node = srsNode.namedItem( QStringLiteral(
"ellipsoidacronym" ) );
1835 d->mEllipsoidAcronym = node.toElement().text();
1837 node = srsNode.namedItem( QStringLiteral(
"geographicflag" ) );
1838 d->mIsGeographic = node.toElement().text() == QLatin1String(
"true" );
1840 d->mWktPreferred.clear();
1846 const QString epoch = srsNode.toElement().attribute( QStringLiteral(
"coordinateEpoch" ) );
1847 if ( !epoch.isEmpty() )
1849 bool epochOk =
false;
1850 d->mCoordinateEpoch = epoch.toDouble( &epochOk );
1852 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
1856 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
1862 d =
new QgsCoordinateReferenceSystemPrivate();
1870 QDomElement layerNode = node.toElement();
1871 QDomElement srsElement = doc.createElement( QStringLiteral(
"spatialrefsys" ) );
1873 if ( std::isfinite( d->mCoordinateEpoch ) )
1875 srsElement.setAttribute( QStringLiteral(
"coordinateEpoch" ), d->mCoordinateEpoch );
1878 QDomElement wktElement = doc.createElement( QStringLiteral(
"wkt" ) );
1880 srsElement.appendChild( wktElement );
1882 QDomElement proj4Element = doc.createElement( QStringLiteral(
"proj4" ) );
1883 proj4Element.appendChild( doc.createTextNode(
toProj() ) );
1884 srsElement.appendChild( proj4Element );
1886 QDomElement srsIdElement = doc.createElement( QStringLiteral(
"srsid" ) );
1887 srsIdElement.appendChild( doc.createTextNode( QString::number(
srsid() ) ) );
1888 srsElement.appendChild( srsIdElement );
1890 QDomElement sridElement = doc.createElement( QStringLiteral(
"srid" ) );
1891 sridElement.appendChild( doc.createTextNode( QString::number(
postgisSrid() ) ) );
1892 srsElement.appendChild( sridElement );
1894 QDomElement authidElement = doc.createElement( QStringLiteral(
"authid" ) );
1895 authidElement.appendChild( doc.createTextNode(
authid() ) );
1896 srsElement.appendChild( authidElement );
1898 QDomElement descriptionElement = doc.createElement( QStringLiteral(
"description" ) );
1899 descriptionElement.appendChild( doc.createTextNode(
description() ) );
1900 srsElement.appendChild( descriptionElement );
1902 QDomElement projectionAcronymElement = doc.createElement( QStringLiteral(
"projectionacronym" ) );
1903 projectionAcronymElement.appendChild( doc.createTextNode(
projectionAcronym() ) );
1904 srsElement.appendChild( projectionAcronymElement );
1906 QDomElement ellipsoidAcronymElement = doc.createElement( QStringLiteral(
"ellipsoidacronym" ) );
1907 ellipsoidAcronymElement.appendChild( doc.createTextNode(
ellipsoidAcronym() ) );
1908 srsElement.appendChild( ellipsoidAcronymElement );
1910 QDomElement geographicFlagElement = doc.createElement( QStringLiteral(
"geographicflag" ) );
1911 QString geoFlagText = QStringLiteral(
"false" );
1914 geoFlagText = QStringLiteral(
"true" );
1917 geographicFlagElement.appendChild( doc.createTextNode( geoFlagText ) );
1918 srsElement.appendChild( geographicFlagElement );
1920 layerNode.appendChild( srsElement );
1932 QString QgsCoordinateReferenceSystem::projFromSrsId(
const int srsId )
1934 QString myDatabaseFileName;
1935 QString myProjString;
1936 QString mySql = QStringLiteral(
"select parameters from tbl_srs where srs_id = %1 order by deprecated" ).arg( srsId );
1945 QFileInfo myFileInfo;
1946 myFileInfo.setFile( myDatabaseFileName );
1947 if ( !myFileInfo.exists() )
1949 QgsDebugMsg( QStringLiteral(
"users qgis.db not found" ) );
1962 rc = openDatabase( myDatabaseFileName, database );
1968 statement = database.
prepare( mySql, rc );
1970 if ( rc == SQLITE_OK )
1972 if ( statement.
step() == SQLITE_ROW )
1978 return myProjString;
1985 myResult = database.
open_v2( path, SQLITE_OPEN_READONLY,
nullptr );
1987 myResult = database.
open( path );
1989 if ( myResult != SQLITE_OK )
1998 .arg( database.
errorMessage() ), QObject::tr(
"CRS" ) );
2005 sCustomSrsValidation = f;
2010 return sCustomSrsValidation;
2013 void QgsCoordinateReferenceSystem::debugPrint()
2015 QgsDebugMsg( QStringLiteral(
"***SpatialRefSystem***" ) );
2016 QgsDebugMsg(
"* Valid : " + ( d->mIsValid ? QString(
"true" ) : QString(
"false" ) ) );
2017 QgsDebugMsg(
"* SrsId : " + QString::number( d->mSrsId ) );
2023 QgsDebugMsg( QStringLiteral(
"* Units : meters" ) );
2027 QgsDebugMsg( QStringLiteral(
"* Units : feet" ) );
2031 QgsDebugMsg( QStringLiteral(
"* Units : degrees" ) );
2037 mValidationHint = html;
2042 return mValidationHint;
2050 long QgsCoordinateReferenceSystem::getRecordCount()
2055 long myRecordCount = 0;
2058 if ( myResult != SQLITE_OK )
2064 QString mySql = QStringLiteral(
"select count(*) from tbl_srs" );
2065 statement = database.
prepare( mySql, myResult );
2066 if ( myResult == SQLITE_OK )
2068 if ( statement.
step() == SQLITE_ROW )
2070 QString myRecordCountString = statement.
columnAsText( 0 );
2071 myRecordCount = myRecordCountString.toLong();
2074 return myRecordCount;
2080 bool isGeographic =
false;
2082 if ( coordinateSystem )
2084 const int axisCount = proj_cs_get_axis_count( pjContext, coordinateSystem.get() );
2085 if ( axisCount > 0 )
2087 const char *outUnitAuthName =
nullptr;
2088 const char *outUnitAuthCode =
nullptr;
2090 proj_cs_get_axis_info( pjContext, coordinateSystem.get(), 0,
2099 if ( outUnitAuthName && outUnitAuthCode )
2101 const char *unitCategory =
nullptr;
2102 if ( proj_uom_get_info_from_database( pjContext, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
2104 isGeographic = QString( unitCategory ).compare( QLatin1String(
"angular" ), Qt::CaseInsensitive ) == 0;
2109 return isGeographic;
2114 thread_local
const QRegularExpression projRegExp( QStringLiteral(
"\\+proj=(\\S+)" ) );
2115 const QRegularExpressionMatch projMatch = projRegExp.match( proj );
2116 if ( !projMatch.hasMatch() )
2118 QgsDebugMsgLevel( QStringLiteral(
"no +proj argument found [%2]" ).arg( proj ), 2 );
2121 operation = projMatch.captured( 1 );
2123 thread_local
const QRegularExpression ellipseRegExp( QStringLiteral(
"\\+(?:ellps|datum)=(\\S+)" ) );
2124 const QRegularExpressionMatch ellipseMatch = projRegExp.match( proj );
2125 if ( ellipseMatch.hasMatch() )
2127 ellipsoid = ellipseMatch.captured( 1 );
2141 bool QgsCoordinateReferenceSystem::loadFromAuthCode(
const QString &auth,
const QString &code )
2144 d->mIsValid =
false;
2145 d->mWktPreferred.clear();
2154 switch ( proj_get_type(
crs.get() ) )
2156 case PJ_TYPE_VERTICAL_CRS:
2166 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2167 proj4 = proj4.trimmed();
2171 d->mWktPreferred.clear();
2172 d->mDescription = QString( proj_get_name(
crs.get() ) );
2173 d->mAuthId = QStringLiteral(
"%1:%2" ).arg( auth, code );
2175 d->mAxisInvertedDirty =
true;
2180 d->mEllipsoidAcronym.clear();
2181 d->setPj( std::move(
crs ) );
2183 const QString dbVals =
sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( auth, code ).toUpper() );
2186 if ( !dbVals.isEmpty() )
2188 const QStringList parts = dbVals.split(
',' );
2189 d->mSrsId = parts.at( 0 ).toInt();
2190 d->mSRID = parts.at( 1 ).toInt();
2198 QList<long> QgsCoordinateReferenceSystem::userSrsIds()
2200 QList<long> results;
2204 QFileInfo myInfo( db );
2205 if ( !myInfo.exists() )
2207 QgsDebugMsg(
"failed : " + db +
" does not exist!" );
2215 int result = openDatabase( db, database );
2216 if ( result != SQLITE_OK )
2218 QgsDebugMsg(
"failed : " + db +
" could not be opened!" );
2222 QString sql = QStringLiteral(
"select srs_id from tbl_srs where srs_id >= %1" ).arg(
USER_CRS_START_ID );
2224 statement = database.
prepare( sql, rc );
2227 int ret = statement.
step();
2229 if ( ret == SQLITE_DONE )
2235 if ( ret == SQLITE_ROW )
2241 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
2249 long QgsCoordinateReferenceSystem::matchToUserCrs()
const
2251 PJ *obj = d->threadLocalProjObject();
2255 const QList< long > ids = userSrsIds();
2256 for (
long id : ids )
2259 if ( candidate.
projObject() && proj_is_equivalent_to( obj, candidate.
projObject(), PJ_COMP_EQUIVALENT ) )
2267 static void sync_db_proj_logger(
void * ,
int level,
const char *message )
2272 if ( level == PJ_LOG_ERROR )
2276 else if ( level == PJ_LOG_DEBUG )
2284 setlocale( LC_ALL,
"C" );
2287 int inserted = 0, updated = 0, deleted = 0, errors = 0;
2292 if ( database.
open( dbFilePath ) != SQLITE_OK )
2298 if ( sqlite3_exec( database.get(),
"BEGIN TRANSACTION",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2306 char *errMsg =
nullptr;
2308 if ( sqlite3_exec( database.get(),
"create table tbl_info (proj_major INT, proj_minor INT, proj_patch INT)",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2310 QString sql = QStringLiteral(
"INSERT INTO tbl_info(proj_major, proj_minor, proj_patch) VALUES (%1, %2,%3)" )
2311 .arg( QString::number( PROJ_VERSION_MAJOR ),
2312 QString::number( PROJ_VERSION_MINOR ),
2313 QString::number( PROJ_VERSION_PATCH ) );
2314 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2316 QgsDebugMsg( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2319 errMsg ? errMsg :
"(unknown error)" ) );
2321 sqlite3_free( errMsg );
2328 QString sql = QStringLiteral(
"SELECT proj_major, proj_minor, proj_patch FROM tbl_info" );
2329 statement = database.
prepare( sql, result );
2330 if ( result != SQLITE_OK )
2335 if ( statement.
step() == SQLITE_ROW )
2340 if ( major == PROJ_VERSION_MAJOR && minor == PROJ_VERSION_MINOR && patch == PROJ_VERSION_PATCH )
2346 QgsDebugMsg( QStringLiteral(
"Could not retrieve previous CRS sync PROJ version number" ) );
2353 proj_log_func( pjContext,
nullptr, sync_db_proj_logger );
2355 PROJ_STRING_LIST authorities = proj_get_authorities_from_database( pjContext );
2357 int nextSrsId = 63560;
2358 int nextSrId = 520003560;
2359 for (
auto authIter = authorities; authIter && *authIter; ++authIter )
2361 const QString authority( *authIter );
2362 QgsDebugMsgLevel( QStringLiteral(
"Loading authority '%1'" ).arg( authority ), 2 );
2363 PROJ_STRING_LIST codes = proj_get_codes_from_database( pjContext, *authIter, PJ_TYPE_CRS,
true );
2365 QStringList allCodes;
2367 for (
auto codesIter = codes; codesIter && *codesIter; ++codesIter )
2369 const QString code( *codesIter );
2375 QgsDebugMsg( QStringLiteral(
"Could not load '%1:%2'" ).arg( authority, code ) );
2379 switch ( proj_get_type(
crs.get() ) )
2381 case PJ_TYPE_VERTICAL_CRS:
2391 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2392 proj4 = proj4.trimmed();
2394 if ( proj4.isEmpty() )
2396 QgsDebugMsgLevel( QStringLiteral(
"No proj4 for '%1:%2'" ).arg( authority, code ), 2 );
2401 const bool deprecated = proj_is_deprecated(
crs.get() );
2402 const QString name( proj_get_name(
crs.get() ) );
2404 QString sql = QStringLiteral(
"SELECT parameters,description,deprecated FROM tbl_srs WHERE auth_name='%1' AND auth_id='%2'" ).arg( authority, code );
2405 statement = database.
prepare( sql, result );
2406 if ( result != SQLITE_OK )
2414 bool srsDeprecated = deprecated;
2415 if ( statement.
step() == SQLITE_ROW )
2419 srsDeprecated = statement.
columnAsText( 2 ).toInt() != 0;
2422 if ( !srsProj4.isEmpty() || !srsDesc.isEmpty() )
2424 if ( proj4 != srsProj4 || name != srsDesc || deprecated != srsDeprecated )
2427 sql = QStringLiteral(
"UPDATE tbl_srs SET parameters=%1,description=%2,deprecated=%3 WHERE auth_name=%4 AND auth_id=%5" )
2430 .arg( deprecated ? 1 : 0 )
2433 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2435 QgsDebugMsg( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2438 errMsg ? errMsg :
"(unknown error)" ) );
2440 sqlite3_free( errMsg );
2458 const QString dbVals =
sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( authority, code ) );
2461 if ( !dbVals.isEmpty() )
2463 const QStringList parts = dbVals.split(
',' );
2464 srsId = parts.at( 0 );
2465 srId = parts.at( 1 );
2467 if ( srId.isEmpty() )
2469 srId = QString::number( nextSrId );
2472 if ( srsId.isEmpty() )
2474 srsId = QString::number( nextSrsId );
2478 if ( !srsId.isEmpty() )
2480 sql = QStringLiteral(
"INSERT INTO tbl_srs(srs_id, description,projection_acronym,ellipsoid_acronym,parameters,srid,auth_name,auth_id,is_geo,deprecated) VALUES (%1, %2,%3,%4,%5,%6,%7,%8,%9,%10)" )
2490 .arg( deprecated ? 1 : 0 );
2494 sql = QStringLiteral(
"INSERT INTO tbl_srs(description,projection_acronym,ellipsoid_acronym,parameters,srid,auth_name,auth_id,is_geo,deprecated) VALUES (%2,%3,%4,%5,%6,%7,%8,%9,%10)" )
2503 .arg( deprecated ? 1 : 0 );
2507 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) == SQLITE_OK )
2513 qCritical(
"Could not execute: %s [%s/%s]\n",
2514 sql.toLocal8Bit().constData(),
2515 sqlite3_errmsg( database.get() ),
2516 errMsg ? errMsg :
"(unknown error)" );
2520 sqlite3_free( errMsg );
2525 proj_string_list_destroy( codes );
2527 const QString sql = QStringLiteral(
"DELETE FROM tbl_srs WHERE auth_name='%1' AND NOT auth_id IN (%2)" ).arg( authority, allCodes.join(
',' ) );
2528 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2530 deleted = sqlite3_changes( database.get() );
2535 qCritical(
"Could not execute: %s [%s]\n",
2536 sql.toLocal8Bit().constData(),
2537 sqlite3_errmsg( database.get() ) );
2541 proj_string_list_destroy( authorities );
2543 QString sql = QStringLiteral(
"UPDATE tbl_info set proj_major=%1,proj_minor=%2,proj_patch=%3" )
2544 .arg( QString::number( PROJ_VERSION_MAJOR ),
2545 QString::number( PROJ_VERSION_MINOR ),
2546 QString::number( PROJ_VERSION_PATCH ) );
2547 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2549 QgsDebugMsg( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2552 errMsg ? errMsg :
"(unknown error)" ) );
2554 sqlite3_free( errMsg );
2558 if ( sqlite3_exec( database.get(),
"COMMIT",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2560 QgsDebugMsg( QStringLiteral(
"Could not commit transaction: %1 [%2]\n" ).arg(
2562 sqlite3_errmsg( database.get() ) )
2568 QgsDebugMsgLevel( QStringLiteral(
"CRS update (inserted:%1 updated:%2 deleted:%3 errors:%4)" ).arg( inserted ).arg( updated ).arg( deleted ).arg( errors ), 4 );
2576 return updated + inserted;
2579 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::stringCache()
2581 return *sStringCache();
2584 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::projCache()
2586 return *sProj4Cache();
2589 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::ogcCache()
2591 return *sOgcCache();
2594 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::wktCache()
2596 return *sWktCache();
2599 const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srIdCache()
2601 return *sSrIdCache();
2604 const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srsIdCache()
2606 return *sSrsIdCache();
2615 else if (
PJ *obj = d->threadLocalProjObject() )
2618 return geoCrs ? QStringLiteral(
"%1:%2" ).arg( proj_get_id_auth_name( geoCrs.get(), 0 ), proj_get_id_code( geoCrs.get(), 0 ) ) : QString();
2628 return d->threadLocalProjObject();
2633 QStringList projections;
2635 projections.reserve( res.size() );
2638 projections << QString::number(
crs.
srsid() );
2645 QList<QgsCoordinateReferenceSystem> res;
2648 QgsSettings settings;
2649 QStringList projectionsProj4 = settings.value( QStringLiteral(
"UI/recentProjectionsProj4" ) ).toStringList();
2650 QStringList projectionsWkt = settings.value( QStringLiteral(
"UI/recentProjectionsWkt" ) ).toStringList();
2651 QStringList projectionsAuthId = settings.value( QStringLiteral(
"UI/recentProjectionsAuthId" ) ).toStringList();
2652 int max = std::max( projectionsAuthId.size(), std::max( projectionsProj4.size(), projectionsWkt.size() ) );
2654 for (
int i = 0; i < max; ++i )
2656 const QString proj = projectionsProj4.value( i );
2657 const QString wkt = projectionsWkt.value( i );
2658 const QString
authid = projectionsAuthId.value( i );
2681 recent.removeAll(
crs );
2682 recent.insert( 0,
crs );
2685 recent = recent.mid( 0, 30 );
2686 QStringList authids;
2687 authids.reserve( recent.size() );
2689 proj.reserve( recent.size() );
2691 wkt.reserve( recent.size() );
2694 authids <<
c.authid();
2699 QgsSettings settings;
2700 settings.setValue( QStringLiteral(
"UI/recentProjectionsAuthId" ), authids );
2701 settings.setValue( QStringLiteral(
"UI/recentProjectionsWkt" ), wkt );
2702 settings.setValue( QStringLiteral(
"UI/recentProjectionsProj4" ), proj );
2707 sSrIdCacheLock()->lockForWrite();
2708 if ( !sDisableSrIdCache )
2711 sDisableSrIdCache =
true;
2712 sSrIdCache()->clear();
2714 sSrIdCacheLock()->unlock();
2716 sOgcLock()->lockForWrite();
2717 if ( !sDisableOgcCache )
2720 sDisableOgcCache =
true;
2721 sOgcCache()->clear();
2723 sOgcLock()->unlock();
2725 sProj4CacheLock()->lockForWrite();
2726 if ( !sDisableProjCache )
2729 sDisableProjCache =
true;
2730 sProj4Cache()->clear();
2732 sProj4CacheLock()->unlock();
2734 sCRSWktLock()->lockForWrite();
2735 if ( !sDisableWktCache )
2738 sDisableWktCache =
true;
2739 sWktCache()->clear();
2741 sCRSWktLock()->unlock();
2743 sCRSSrsIdLock()->lockForWrite();
2744 if ( !sDisableSrsIdCache )
2747 sDisableSrsIdCache =
true;
2748 sSrsIdCache()->clear();
2750 sCRSSrsIdLock()->unlock();
2752 sCrsStringLock()->lockForWrite();
2753 if ( !sDisableStringCache )
2756 sDisableStringCache =
true;
2757 sStringCache()->clear();
2759 sCrsStringLock()->unlock();
2768 if ( !c1.d->mIsValid && !c2.d->mIsValid )
2771 if ( !c1.d->mIsValid && c2.d->mIsValid )
2774 if ( c1.d->mIsValid && !c2.d->mIsValid )
2780 if ( c1IsUser && !c2IsUser )
2783 if ( !c1IsUser && c2IsUser )
2786 if ( !c1IsUser && !c2IsUser )
2788 if ( c1.d->mAuthId != c2.d->mAuthId )
2789 return c1.d->mAuthId > c2.d->mAuthId;
2799 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
2802 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
2805 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
2808 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
2811 return c1.d->mCoordinateEpoch > c2.d->mCoordinateEpoch;
2819 if ( !c1.d->mIsValid && !c2.d->mIsValid )
2822 if ( c1.d->mIsValid && !c2.d->mIsValid )
2825 if ( !c1.d->mIsValid && c2.d->mIsValid )
2831 if ( !c1IsUser && c2IsUser )
2834 if ( c1IsUser && !c2IsUser )
2837 if ( !c1IsUser && !c2IsUser )
2839 if ( c1.d->mAuthId != c2.d->mAuthId )
2840 return c1.d->mAuthId < c2.d->mAuthId;
2850 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
2853 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
2856 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
2859 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
2862 return c1.d->mCoordinateEpoch < c2.d->mCoordinateEpoch;
2867 return !( c1 < c2 );
2871 return !( c1 > c2 );
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.
long addUserCrs(const QgsCoordinateReferenceSystem &crs, const QString &name, QgsCoordinateReferenceSystem::Format nativeFormat=QgsCoordinateReferenceSystem::FormatWkt)
Adds a new crs definition as a custom ("USER") CRS.
QMap< QString, QgsProjOperation > projOperations() const
Returns a map of all valid PROJ operations.
This class represents a coordinate reference system (CRS).
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.
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 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.
static void setCustomCrsValidation(CUSTOM_CRS_VALIDATION f)
Sets custom function to force valid CRS.
static void pushRecentCoordinateReferenceSystem(const QgsCoordinateReferenceSystem &crs)
Pushes a recently used CRS to the top of the recent CRS list.
QString description() const
Returns the descriptive name of the CRS, e.g., "WGS 84" or "GDA 94 / Vicgrid94".
long postgisSrid() const
Returns PostGIS SRID for the CRS.
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
Overloaded == operator used to compare to CRS's.
QString projectionAcronym() const
Returns the projection acronym for the projection used by the CRS.
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
Overloaded != operator used to compare to CRS's.
bool createFromProj(const QString &projString, bool identify=true)
Sets this CRS by passing it a PROJ style formatted string.
QgsDatumEnsemble datumEnsemble() const SIP_THROW(QgsNotSupportedException)
Attempts to retrieve datum ensemble details from the CRS.
IdentifierType
Type of identifier string to create.
@ MediumString
A medium-length string, recommended for general purpose use.
@ ShortString
A heavily abbreviated string, for use when a compact representation is required.
Format
Projection definition formats.
bool isDynamic() const
Returns true if the CRS is a dynamic CRS.
QString validationHint()
Gets user hint for validation.
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 void invalidateCache(bool disableCache=false)
Clears the internal cache used to initialize QgsCoordinateReferenceSystem objects.
QgsCoordinateReferenceSystem & operator=(const QgsCoordinateReferenceSystem &srs)
Assignment operator.
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 QList< QgsCoordinateReferenceSystem > recentCoordinateReferenceSystems()
Returns a list of recently used CRS.
PJ * projObject() const
Returns the underlying PROJ PJ object corresponding to the CRS, or nullptr if the CRS is invalid.
QString userFriendlyIdentifier(IdentifierType type=MediumString) const
Returns a user friendly identifier for the CRS.
void setCoordinateEpoch(double epoch)
Sets the coordinate epoch, as a decimal year.
QString authid() const
Returns the authority identifier for the CRS.
Q_DECL_DEPRECATED bool createFromSrid(long srid)
Sets this CRS by lookup of the given PostGIS SRID in the CRS database.
WktVariant
WKT formatting variants, only used for builds based on Proj >= 6.
@ WKT1_GDAL
WKT1 as traditionally output by GDAL, deriving from OGC 01-009. A notable departure from WKT1_GDAL wi...
@ WKT2_2019_SIMPLIFIED
WKT2_2019 with the simplification rule of WKT2_SIMPLIFIED.
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
@ WKT2_2015
Full WKT2 string, conforming to ISO 19162:2015(E) / OGC 12-063r5 with all possible nodes and new keyw...
@ WKT2_2019
Full WKT2 string, conforming to ISO 19162:2019 / OGC 18-010, with all possible nodes and new keyword ...
@ WKT1_ESRI
WKT1 as traditionally output by ESRI software, deriving from OGC 99-049.
@ WKT2_2015_SIMPLIFIED
Same as WKT2_2015 with the following exceptions: UNIT keyword used. ID node only on top element....
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.
long saveAsUserCrs(const QString &name, Format nativeFormat=FormatWkt)
Saves the CRS as a new custom ("USER") CRS.
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 toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
QgsProjOperation operation() const
Returns information about the PROJ operation associated with the coordinate reference system,...
Q_GADGET QgsUnitTypes::DistanceUnit mapUnits
long srsid() const
Returns the internal CRS ID, if available.
bool hasAxisInverted() const
Returns whether axis is inverted (e.g., for WMS 1.3) for the CRS.
QString celestialBodyName() const SIP_THROW(QgsNotSupportedException)
Attempts to retrieve the name of the celestial body associated with the CRS (e.g.
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 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)
Adds a message to the log instance (and creates it if necessary).
Custom exception class which is raised when an operation is not supported.
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.
@ FlagMatchBoundCrsToUnderlyingSourceCrs
Allow matching a BoundCRS object to its underlying SourceCRS.
static bool isDynamic(const PJ *crs)
Returns true if the given proj coordinate system is a dynamic CRS.
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 proj_pj_unique_ptr crsToSingleCrs(const PJ *crs)
Given a PROJ crs (which may be a compound or bound crs, or some other type), extract a single crs fro...
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...
The QgsReadWriteLocker class is a convenience class that simplifies locking and unlocking QReadWriteL...
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) SIP_HOLDGIL
Set the minimum y value.
void setXMaximum(double x) SIP_HOLDGIL
Set the maximum x value.
void setXMinimum(double x) SIP_HOLDGIL
Set the minimum x value.
void setYMaximum(double y) SIP_HOLDGIL
Set the maximum y value.
static QString quotedString(const QString &value)
Returns a quoted string value, surround by ' characters and with special characters correctly escaped...
DistanceUnit
Units of distance.
@ DistanceDegrees
Degrees, for planar geographic CRS distance measurements.
@ DistanceKilometers
Kilometers.
@ DistanceMiles
Terrestrial miles.
@ DistanceUnknownUnit
Unknown distance unit.
@ DistanceMillimeters
Millimeters.
@ DistanceYards
Imperial yards.
@ DistanceFeet
Imperial feet.
@ DistanceNauticalMiles
Nautical miles.
@ DistanceCentimeters
Centimeters.
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.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define Q_NOWARN_DEPRECATED_POP
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
#define Q_NOWARN_DEPRECATED_PUSH
bool qgsNanCompatibleEquals(double a, double b)
Compare two doubles, treating nan values as equal.
const int USER_CRS_START_ID
Magick number that determines whether a projection crsid is a system (srs.db) or user (~/....
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 * OGRSpatialReferenceH
struct projCtx_t PJ_CONTEXT
void(* CUSTOM_CRS_VALIDATION)(QgsCoordinateReferenceSystem &)
const QMap< QString, QString > sAuthIdToQgisSrsIdMap
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
#define QgsDebugMsgLevel(str, level)
const QgsCoordinateReferenceSystem & crs