29 #include <QDomElement>
31 #include <QRegularExpression>
32 #include <QTextStream>
49 #include <proj_experimental.h>
52 #include <ogr_srs_api.h>
53 #include <cpl_error.h>
64 bool QgsCoordinateReferenceSystem::sDisableSrIdCache =
false;
68 bool QgsCoordinateReferenceSystem::sDisableOgcCache =
false;
72 bool QgsCoordinateReferenceSystem::sDisableProjCache =
false;
76 bool QgsCoordinateReferenceSystem::sDisableWktCache =
false;
80 bool QgsCoordinateReferenceSystem::sDisableSrsIdCache =
false;
84 bool QgsCoordinateReferenceSystem::sDisableStringCache =
false;
93 if (
const char *proj4src = proj_as_proj_string(
QgsProjContext::get(), boundCrs.get(), PJ_PROJ_4,
nullptr ) )
95 return QString( proj4src );
112 d =
new QgsCoordinateReferenceSystemPrivate();
118 d =
new QgsCoordinateReferenceSystemPrivate();
126 , mValidationHint( srs.mValidationHint )
127 , mNativeFormat( srs.mNativeFormat )
134 mValidationHint = srs.mValidationHint;
135 mNativeFormat = srs.mNativeFormat;
145 const auto constDbs = dbs;
146 for (
const QString &db : constDbs )
148 QFileInfo myInfo( db );
149 if ( !myInfo.exists() )
151 QgsDebugMsg(
"failed : " + db +
" does not exist!" );
159 int result = openDatabase( db, database );
160 if ( result != SQLITE_OK )
162 QgsDebugMsg(
"failed : " + db +
" could not be opened!" );
166 QString sql = QStringLiteral(
"select srs_id from tbl_srs" );
168 statement = database.
prepare( sql, rc );
172 int ret = statement.
step();
174 if ( ret == SQLITE_DONE )
180 if ( ret == SQLITE_ROW )
186 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
191 std::sort( results.begin(), results.end() );
264 QgsDebugMsg( QStringLiteral(
"Unexpected case reached!" ) );
271 if ( definition.isEmpty() )
275 if ( !sDisableStringCache )
277 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sStringCache()->constFind( definition );
278 if ( crsIt != sStringCache()->constEnd() )
281 *
this = crsIt.value();
288 const thread_local QRegularExpression reCrsId( QStringLiteral(
"^(epsg|esri|osgeo|ignf|zangi|iau2000|postgis|internal|user)\\:(\\w+)$" ), QRegularExpression::CaseInsensitiveOption );
289 QRegularExpressionMatch match = reCrsId.match( definition );
290 if ( match.capturedStart() == 0 )
292 QString authName = match.captured( 1 ).toLower();
293 if ( authName == QLatin1String(
"epsg" ) )
297 else if ( authName == QLatin1String(
"postgis" ) )
299 const long id = match.captured( 2 ).toLong();
304 else if ( authName == QLatin1String(
"esri" ) || authName == QLatin1String(
"osgeo" ) || authName == QLatin1String(
"ignf" ) || authName == QLatin1String(
"zangi" ) || authName == QLatin1String(
"iau2000" ) )
310 const long id = match.captured( 2 ).toLong();
318 const thread_local QRegularExpression reCrsStr( QStringLiteral(
"^(?:(wkt|proj4|proj)\\:)?(.+)$" ), QRegularExpression::CaseInsensitiveOption );
319 match = reCrsStr.match( definition );
320 if ( match.capturedStart() == 0 )
322 if ( match.captured( 1 ).startsWith( QLatin1String(
"proj" ), Qt::CaseInsensitive ) )
334 if ( !sDisableStringCache )
335 sStringCache()->insert( definition, *
this );
341 if ( definition.isEmpty() )
347 if ( OSRSetFromUserInput(
crs, definition.toLocal8Bit().constData() ) == OGRERR_NONE )
350 OSRDestroySpatialReference(
crs );
360 const char *configOld = CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" );
361 const char *configNew =
"GEOGCS";
363 if ( strcmp( configOld,
"" ) == 0 )
365 CPLSetConfigOption(
"GDAL_FIX_ESRI_WKT", configNew );
366 if ( strcmp( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) != 0 )
368 .arg( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) );
369 QgsDebugMsgLevel( QStringLiteral(
"set GDAL_FIX_ESRI_WKT : %1" ).arg( configNew ), 4 );
373 QgsDebugMsgLevel( QStringLiteral(
"GDAL_FIX_ESRI_WKT was already set : %1" ).arg( configNew ), 4 );
383 if ( !sDisableOgcCache )
385 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sOgcCache()->constFind(
crs );
386 if ( crsIt != sOgcCache()->constEnd() )
389 *
this = crsIt.value();
395 QString wmsCrs =
crs;
397 thread_local
const QRegularExpression re_uri( QRegularExpression::anchoredPattern( QStringLiteral(
"http://www\\.opengis\\.net/def/crs/([^/]+).+/([^/]+)" ) ), QRegularExpression::CaseInsensitiveOption );
398 QRegularExpressionMatch match = re_uri.match( wmsCrs );
399 if ( match.hasMatch() )
401 wmsCrs = match.captured( 1 ) +
':' + match.captured( 2 );
405 thread_local
const QRegularExpression re_urn( QRegularExpression::anchoredPattern( QStringLiteral(
"urn:ogc:def:crs:([^:]+).+(?<=:)([^:]+)" ) ), QRegularExpression::CaseInsensitiveOption );
406 match = re_urn.match( wmsCrs );
407 if ( match.hasMatch() )
409 wmsCrs = match.captured( 1 ) +
':' + match.captured( 2 );
413 thread_local
const QRegularExpression re_urn_custom( QRegularExpression::anchoredPattern( QStringLiteral(
"(user|custom|qgis):(\\d+)" ) ), QRegularExpression::CaseInsensitiveOption );
414 match = re_urn_custom.match( wmsCrs );
415 if ( match.hasMatch() &&
createFromSrsId( match.captured( 2 ).toInt() ) )
418 if ( !sDisableOgcCache )
419 sOgcCache()->insert(
crs, *
this );
426 const QString legacyKey = wmsCrs.toLower();
429 if ( it.key().compare( legacyKey, Qt::CaseInsensitive ) == 0 )
431 const QStringList parts = it.key().split(
':' );
432 const QString auth = parts.at( 0 );
433 const QString code = parts.at( 1 );
434 if ( loadFromAuthCode( auth, code ) )
437 if ( !sDisableOgcCache )
438 sOgcCache()->insert(
crs, *
this );
447 if ( !sDisableOgcCache )
448 sOgcCache()->insert(
crs, *
this );
453 if ( wmsCrs.compare( QLatin1String(
"CRS:27" ), Qt::CaseInsensitive ) == 0 ||
454 wmsCrs.compare( QLatin1String(
"OGC:CRS27" ), Qt::CaseInsensitive ) == 0 )
461 if ( wmsCrs.compare( QLatin1String(
"CRS:83" ), Qt::CaseInsensitive ) == 0 ||
462 wmsCrs.compare( QLatin1String(
"OGC:CRS83" ), Qt::CaseInsensitive ) == 0 )
469 if ( wmsCrs.compare( QLatin1String(
"CRS:84" ), Qt::CaseInsensitive ) == 0 ||
470 wmsCrs.compare( QLatin1String(
"OGC:CRS84" ), Qt::CaseInsensitive ) == 0 )
474 d->mAxisInverted =
false;
475 d->mAxisInvertedDirty =
false;
479 if ( !sDisableOgcCache )
480 sOgcCache()->insert(
crs, *
this );
486 if ( !sDisableOgcCache )
496 if ( d->mIsValid || !sCustomSrsValidation )
500 if ( sCustomSrsValidation )
501 sCustomSrsValidation( *
this );
507 if ( !sDisableSrIdCache )
509 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrIdCache()->constFind(
id );
510 if ( crsIt != sSrIdCache()->constEnd() )
513 *
this = crsIt.value();
522 if ( it.value().endsWith( QStringLiteral(
",%1" ).arg(
id ) ) )
524 const QStringList parts = it.key().split(
':' );
525 const QString auth = parts.at( 0 );
526 const QString code = parts.at( 1 );
527 if ( loadFromAuthCode( auth, code ) )
530 if ( !sDisableSrIdCache )
531 sSrIdCache()->insert(
id, *
this );
541 if ( !sDisableSrIdCache )
542 sSrIdCache()->insert(
id, *
this );
550 if ( !sDisableSrsIdCache )
552 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrsIdCache()->constFind(
id );
553 if ( crsIt != sSrsIdCache()->constEnd() )
556 *
this = crsIt.value();
565 if ( it.value().startsWith( QString::number(
id ) +
',' ) )
567 const QStringList parts = it.key().split(
':' );
568 const QString auth = parts.at( 0 );
569 const QString code = parts.at( 1 );
570 if ( loadFromAuthCode( auth, code ) )
573 if ( !sDisableSrsIdCache )
574 sSrsIdCache()->insert(
id, *
this );
582 QStringLiteral(
"srs_id" ), QString::number(
id ) );
585 if ( !sDisableSrsIdCache )
586 sSrsIdCache()->insert(
id, *
this );
590 bool QgsCoordinateReferenceSystem::loadFromDatabase(
const QString &db,
const QString &expression,
const QString &value )
594 QgsDebugMsgLevel(
"load CRS from " + db +
" where " + expression +
" is " + value, 3 );
596 d->mWktPreferred.clear();
598 QFileInfo myInfo( db );
599 if ( !myInfo.exists() )
601 QgsDebugMsg(
"failed : " + db +
" does not exist!" );
609 myResult = openDatabase( db, database );
610 if ( myResult != SQLITE_OK )
627 QString mySql =
"select srs_id,description,projection_acronym,"
628 "ellipsoid_acronym,parameters,srid,auth_name||':'||auth_id,is_geo,wkt "
630 statement = database.
prepare( mySql, myResult );
633 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
638 d->mEllipsoidAcronym.clear();
640 d->mWktPreferred.clear();
643 d->mIsGeographic = statement.
columnAsText( 7 ).toInt() != 0;
645 d->mAxisInvertedDirty =
true;
647 if ( d->mSrsId >=
USER_CRS_START_ID && ( d->mAuthId.isEmpty() || d->mAuthId == QChar(
':' ) ) )
649 d->mAuthId = QStringLiteral(
"USER:%1" ).arg( d->mSrsId );
651 else if ( !d->mAuthId.startsWith( QLatin1String(
"USER:" ), Qt::CaseInsensitive ) )
653 QStringList parts = d->mAuthId.split(
':' );
654 QString auth = parts.at( 0 );
655 QString code = parts.at( 1 );
662 d->mIsValid = d->hasPj();
668 if ( !wkt.isEmpty() )
676 setProjString( d->mProj4 );
686 void QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread(
PJ_CONTEXT *pj_context )
693 if ( !sDisableSrIdCache )
696 if ( !sDisableSrIdCache )
698 for (
auto it = sSrIdCache()->begin(); it != sSrIdCache()->end(); )
700 auto &v = it.value();
701 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
702 it = sSrIdCache()->erase( it );
708 if ( !sDisableOgcCache )
711 if ( !sDisableOgcCache )
713 for (
auto it = sOgcCache()->begin(); it != sOgcCache()->end(); )
715 auto &v = it.value();
716 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
717 it = sOgcCache()->erase( it );
723 if ( !sDisableProjCache )
726 if ( !sDisableProjCache )
728 for (
auto it = sProj4Cache()->begin(); it != sProj4Cache()->end(); )
730 auto &v = it.value();
731 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
732 it = sProj4Cache()->erase( it );
738 if ( !sDisableWktCache )
741 if ( !sDisableWktCache )
743 for (
auto it = sWktCache()->begin(); it != sWktCache()->end(); )
745 auto &v = it.value();
746 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
747 it = sWktCache()->erase( it );
753 if ( !sDisableSrsIdCache )
756 if ( !sDisableSrsIdCache )
758 for (
auto it = sSrsIdCache()->begin(); it != sSrsIdCache()->end(); )
760 auto &v = it.value();
761 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
762 it = sSrsIdCache()->erase( it );
768 if ( !sDisableStringCache )
771 if ( !sDisableStringCache )
773 for (
auto it = sStringCache()->begin(); it != sStringCache()->end(); )
775 auto &v = it.value();
776 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
777 it = sStringCache()->erase( it );
787 if ( d->mAxisInvertedDirty )
790 d->mAxisInvertedDirty =
false;
793 return d->mAxisInverted;
798 return createFromWktInternal( wkt, QString() );
801 bool QgsCoordinateReferenceSystem::createFromWktInternal(
const QString &wkt,
const QString &description )
809 if ( !sDisableWktCache )
811 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sWktCache()->constFind( wkt );
812 if ( crsIt != sWktCache()->constEnd() )
815 *
this = crsIt.value();
817 if ( !
description.isEmpty() && d->mDescription.isEmpty() )
822 sWktCache()->insert( wkt, *
this );
831 d->mWktPreferred.clear();
834 QgsDebugMsgLevel( QStringLiteral(
"theWkt is uninitialized, operation failed" ), 4 );
839 QgsCoordinateReferenceSystem::RecordMap record = getRecord(
"select * from tbl_srs where wkt=" +
QgsSqliteUtils::quotedString( wkt ) +
" order by deprecated" );
840 if ( !record.empty() )
842 long srsId = record[QStringLiteral(
"srs_id" )].toLong();
855 if ( d->mSrsId == 0 )
858 long id = matchToUserCrs();
867 if ( !sDisableWktCache )
868 sWktCache()->insert( wkt, *
this );
886 if ( projString.isEmpty() )
891 if ( projString.trimmed().isEmpty() )
895 d->mWktPreferred.clear();
900 if ( !sDisableProjCache )
902 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sProj4Cache()->constFind( projString );
903 if ( crsIt != sProj4Cache()->constEnd() )
906 *
this = crsIt.value();
920 QString myProj4String = projString.trimmed();
921 myProj4String.remove( QStringLiteral(
"+type=crs" ) );
922 myProj4String = myProj4String.trimmed();
925 d->mWktPreferred.clear();
930 const QString projCrsString = myProj4String + ( myProj4String.contains( QStringLiteral(
"+type=crs" ) ) ? QString() : QStringLiteral(
" +type=crs" ) );
938 const QString
authid = QStringLiteral(
"%1:%2" ).arg( authName, authCode );
942 if ( !sDisableProjCache )
943 sProj4Cache()->insert( projString, *
this );
950 QgsCoordinateReferenceSystem::RecordMap myRecord = getRecord(
"select * from tbl_srs where parameters=" +
QgsSqliteUtils::quotedString( myProj4String ) +
" order by deprecated" );
952 if ( !myRecord.empty() )
954 id = myRecord[QStringLiteral(
"srs_id" )].toLong();
963 setProjString( myProj4String );
966 id = matchToUserCrs();
975 setProjString( myProj4String );
979 if ( !sDisableProjCache )
980 sProj4Cache()->insert( projString, *
this );
986 QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord(
const QString &sql )
988 QString myDatabaseFileName;
989 QgsCoordinateReferenceSystem::RecordMap myMap;
991 QString myFieldValue;
998 QFileInfo myInfo( myDatabaseFileName );
999 if ( !myInfo.exists() )
1001 QgsDebugMsg(
"failed : " + myDatabaseFileName +
" does not exist!" );
1006 myResult = openDatabase( myDatabaseFileName, database );
1007 if ( myResult != SQLITE_OK )
1012 statement = database.
prepare( sql, myResult );
1014 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1018 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1020 myFieldName = statement.
columnName( myColNo );
1022 myMap[myFieldName] = myFieldValue;
1024 if ( statement.
step() != SQLITE_DONE )
1026 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1035 if ( myMap.empty() )
1038 QFileInfo myFileInfo;
1039 myFileInfo.setFile( myDatabaseFileName );
1040 if ( !myFileInfo.exists() )
1042 QgsDebugMsg( QStringLiteral(
"user qgis.db not found" ) );
1047 myResult = openDatabase( myDatabaseFileName, database );
1048 if ( myResult != SQLITE_OK )
1053 statement = database.
prepare( sql, myResult );
1055 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1059 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1061 myFieldName = statement.
columnName( myColNo );
1063 myMap[myFieldName] = myFieldValue;
1066 if ( statement.
step() != SQLITE_DONE )
1068 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1099 if ( d->mDescription.isNull() )
1105 return d->mDescription;
1112 if ( !
authid().isEmpty() )
1122 id =
isValid() ? QObject::tr(
"Custom CRS" ) : QObject::tr(
"Unknown CRS" );
1124 id = QObject::tr(
"Custom CRS: %1" ).arg(
1127 else if ( !
toProj().isEmpty() )
1128 id = QObject::tr(
"Custom CRS: %1" ).arg( type ==
MediumString ? (
toProj().left( 50 ) + QString( QChar( 0x2026 ) ) )
1130 if ( !
id.isEmpty() && !std::isnan( d->mCoordinateEpoch ) )
1131 id += QStringLiteral(
" @ %1" ).arg( d->mCoordinateEpoch );
1138 if ( d->mProjectionAcronym.isNull() )
1144 return d->mProjectionAcronym;
1150 if ( d->mEllipsoidAcronym.isNull() )
1152 if (
PJ *obj = d->threadLocalProjObject() )
1157 const QString ellipsoidAuthName( proj_get_id_auth_name( ellipsoid.get(), 0 ) );
1158 const QString ellipsoidAuthCode( proj_get_id_code( ellipsoid.get(), 0 ) );
1159 if ( !ellipsoidAuthName.isEmpty() && !ellipsoidAuthCode.isEmpty() )
1160 d->mEllipsoidAcronym = QStringLiteral(
"%1:%2" ).arg( ellipsoidAuthName, ellipsoidAuthCode );
1163 double semiMajor, semiMinor, invFlattening;
1164 int semiMinorComputed = 0;
1165 if ( proj_ellipsoid_get_parameters(
QgsProjContext::get(), ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
1167 d->mEllipsoidAcronym = QStringLiteral(
"PARAMETER:%1:%2" ).arg(
qgsDoubleToString( semiMajor ),
1172 d->mEllipsoidAcronym.clear();
1177 return d->mEllipsoidAcronym;
1181 return d->mEllipsoidAcronym;
1195 if ( d->mProj4.isEmpty() )
1197 if (
PJ *obj = d->threadLocalProjObject() )
1203 return d->mProj4.trimmed();
1208 return d->mIsGeographic;
1226 #if PROJ_VERSION_MAJOR>8 || (PROJ_VERSION_MAJOR==8 && PROJ_VERSION_MINOR>=1)
1229 return QString( proj_get_celestial_body_name( context, pj ) );
1231 throw QgsNotSupportedException( QStringLiteral(
"Retrieving celestial body requires a QGIS build based on PROJ 8.1 or later" ) );
1237 if ( d->mCoordinateEpoch == epoch )
1243 d->mCoordinateEpoch = epoch;
1244 d->setPj( std::move( clone ) );
1249 return d->mCoordinateEpoch;
1261 #if PROJ_VERSION_MAJOR>=8
1269 res.mName = QString( proj_get_name( ensemble.get() ) );
1270 res.mAuthority = QString( proj_get_id_auth_name( ensemble.get(), 0 ) );
1271 res.mCode = QString( proj_get_id_code( ensemble.get(), 0 ) );
1272 res.mRemarks = QString( proj_get_remarks( ensemble.get() ) );
1273 res.mScope = QString( proj_get_scope( ensemble.get() ) );
1274 res.mAccuracy = proj_datum_ensemble_get_accuracy( context, ensemble.get() );
1276 const int memberCount = proj_datum_ensemble_get_member_count( context, ensemble.get() );
1277 for (
int i = 0; i < memberCount; ++i )
1284 details.mName = QString( proj_get_name( member.get() ) );
1285 details.mAuthority = QString( proj_get_id_auth_name( member.get(), 0 ) );
1286 details.mCode = QString( proj_get_id_code( member.get(), 0 ) );
1287 details.mRemarks = QString( proj_get_remarks( member.get() ) );
1288 details.mScope = QString( proj_get_scope( member.get() ) );
1290 res.mMembers << details;
1294 throw QgsNotSupportedException( QStringLiteral(
"Calculating datum ensembles requires a QGIS build based on PROJ 8.0 or later" ) );
1303 QString projString =
toProj();
1304 projString.replace( QLatin1String(
"+type=crs" ), QString() );
1307 if ( !transformation )
1310 PJ_COORD coord = proj_coord( 0, 0, 0, HUGE_VAL );
1311 coord.uv.u = point.
x() * M_PI / 180.0;
1312 coord.uv.v = point.
y() * M_PI / 180.0;
1314 proj_errno_reset( transformation.get() );
1315 const PJ_FACTORS pjFactors = proj_factors( transformation.get(), coord );
1316 if ( proj_errno( transformation.get() ) )
1321 res.mIsValid =
true;
1322 res.mMeridionalScale = pjFactors.meridional_scale;
1323 res.mParallelScale = pjFactors.parallel_scale;
1324 res.mArealScale = pjFactors.areal_scale;
1325 res.mAngularDistortion = pjFactors.angular_distortion;
1326 res.mMeridianParallelAngle = pjFactors.meridian_parallel_angle * 180 / M_PI;
1327 res.mMeridianConvergence = pjFactors.meridian_convergence * 180 / M_PI;
1328 res.mTissotSemimajor = pjFactors.tissot_semimajor;
1329 res.mTissotSemiminor = pjFactors.tissot_semiminor;
1330 res.mDxDlam = pjFactors.dx_dlam;
1331 res.mDxDphi = pjFactors.dx_dphi;
1332 res.mDyDlam = pjFactors.dy_dlam;
1333 res.mDyDphi = pjFactors.dy_dphi;
1342 QString projString =
toProj();
1343 projString.replace( QLatin1String(
"+type=crs" ), QString() );
1346 if ( !transformation )
1349 PJ_PROJ_INFO info = proj_pj_info( transformation.get() );
1364 return d->mMapUnits;
1372 PJ *obj = d->threadLocalProjObject();
1377 double southLat = 0;
1379 double northLat = 0;
1382 &westLon, &southLat, &eastLon, &northLat,
nullptr ) )
1411 void QgsCoordinateReferenceSystem::setProjString(
const QString &proj4String )
1414 d->mProj4 = proj4String;
1415 d->mWktPreferred.clear();
1418 QString trimmed = proj4String.trimmed();
1420 trimmed += QLatin1String(
" +type=crs" );
1430 const int errNo = proj_context_errno( ctx );
1431 QgsDebugMsg( QStringLiteral(
"proj string rejected: %1" ).arg( proj_errno_string( errNo ) ) );
1433 d->mIsValid =
false;
1437 d->mEllipsoidAcronym.clear();
1444 bool QgsCoordinateReferenceSystem::setWktString(
const QString &wkt )
1447 d->mIsValid =
false;
1448 d->mWktPreferred.clear();
1450 PROJ_STRING_LIST warnings =
nullptr;
1451 PROJ_STRING_LIST grammerErrors =
nullptr;
1459 QgsDebugMsg( QStringLiteral(
"\n---------------------------------------------------------------" ) );
1460 QgsDebugMsg( QStringLiteral(
"This CRS could *** NOT *** be set from the supplied Wkt " ) );
1462 for (
auto iter = warnings; iter && *iter; ++iter )
1464 for (
auto iter = grammerErrors; iter && *iter; ++iter )
1466 QgsDebugMsg( QStringLiteral(
"---------------------------------------------------------------\n" ) );
1468 proj_string_list_destroy( warnings );
1469 proj_string_list_destroy( grammerErrors );
1475 if ( !sDisableWktCache )
1476 sWktCache()->insert( wkt, *
this );
1483 QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
1484 QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
1486 if ( authName.isEmpty() || authCode.isEmpty() )
1492 if ( !authName.isEmpty() && !authCode.isEmpty() )
1494 if ( loadFromAuthCode( authName, authCode ) )
1497 if ( !sDisableWktCache )
1498 sWktCache()->insert( wkt, *
this );
1506 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
1514 void QgsCoordinateReferenceSystem::setMapUnits()
1531 if ( !coordinateSystem )
1537 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
1538 if ( axisCount > 0 )
1540 const char *outUnitName =
nullptr;
1542 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
1551 const QString unitName( outUnitName );
1555 if ( unitName.compare( QLatin1String(
"degree" ), Qt::CaseInsensitive ) == 0 ||
1556 unitName.compare( QLatin1String(
"degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1557 unitName.compare( QLatin1String(
"degree minute second hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1558 unitName.compare( QLatin1String(
"degree minute" ), Qt::CaseInsensitive ) == 0 ||
1559 unitName.compare( QLatin1String(
"degree hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1560 unitName.compare( QLatin1String(
"degree minute hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1561 unitName.compare( QLatin1String(
"hemisphere degree" ), Qt::CaseInsensitive ) == 0 ||
1562 unitName.compare( QLatin1String(
"hemisphere degree minute" ), Qt::CaseInsensitive ) == 0 ||
1563 unitName.compare( QLatin1String(
"hemisphere degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1564 unitName.compare( QLatin1String(
"degree (supplier to define representation)" ), Qt::CaseInsensitive ) == 0 )
1566 else if ( unitName.compare( QLatin1String(
"metre" ), Qt::CaseInsensitive ) == 0
1567 || unitName.compare( QLatin1String(
"m" ), Qt::CaseInsensitive ) == 0
1568 || unitName.compare( QLatin1String(
"meter" ), Qt::CaseInsensitive ) == 0 )
1571 else if ( unitName.compare( QLatin1String(
"US survey foot" ), Qt::CaseInsensitive ) == 0 ||
1572 unitName.compare( QLatin1String(
"foot" ), Qt::CaseInsensitive ) == 0 )
1574 else if ( unitName.compare( QLatin1String(
"kilometre" ), Qt::CaseInsensitive ) == 0 )
1576 else if ( unitName.compare( QLatin1String(
"centimetre" ), Qt::CaseInsensitive ) == 0 )
1578 else if ( unitName.compare( QLatin1String(
"millimetre" ), Qt::CaseInsensitive ) == 0 )
1580 else if ( unitName.compare( QLatin1String(
"Statute mile" ), Qt::CaseInsensitive ) == 0 )
1582 else if ( unitName.compare( QLatin1String(
"nautical mile" ), Qt::CaseInsensitive ) == 0 )
1584 else if ( unitName.compare( QLatin1String(
"yard" ), Qt::CaseInsensitive ) == 0 )
1601 if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull()
1604 QgsDebugMsgLevel(
"QgsCoordinateReferenceSystem::findMatchingProj will only "
1605 "work if prj acr ellipsoid acr and proj4string are set"
1606 " and the current projection is valid!", 4 );
1616 QString mySql = QString(
"select srs_id,parameters from tbl_srs where "
1617 "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated" )
1624 myResult = openDatabase( myDatabaseFileName, database );
1625 if ( myResult != SQLITE_OK )
1630 statement = database.
prepare( mySql, myResult );
1631 if ( myResult == SQLITE_OK )
1634 while ( statement.
step() == SQLITE_ROW )
1638 if (
toProj() == myProj4String.trimmed() )
1640 return mySrsId.toLong();
1651 myResult = openDatabase( myDatabaseFileName, database );
1652 if ( myResult != SQLITE_OK )
1657 statement = database.
prepare( mySql, myResult );
1659 if ( myResult == SQLITE_OK )
1661 while ( statement.
step() == SQLITE_ROW )
1665 if (
toProj() == myProj4String.trimmed() )
1667 return mySrsId.toLong();
1681 if ( !d->mIsValid && !srs.d->mIsValid )
1684 if ( !d->mIsValid || !srs.d->mIsValid )
1692 if ( isUser != otherIsUser )
1696 if ( !isUser && ( !d->mAuthId.isEmpty() || !srs.d->mAuthId.isEmpty() ) )
1697 return d->mAuthId == srs.d->mAuthId;
1704 return !( *
this == srs );
1709 if (
PJ *obj = d->threadLocalProjObject() )
1711 const bool isDefaultPreferredFormat = variant ==
WKT_PREFERRED && !multiline;
1712 if ( isDefaultPreferredFormat && !d->mWktPreferred.isEmpty() )
1715 return d->mWktPreferred;
1718 PJ_WKT_TYPE type = PJ_WKT1_GDAL;
1722 type = PJ_WKT1_GDAL;
1725 type = PJ_WKT1_ESRI;
1728 type = PJ_WKT2_2015;
1731 type = PJ_WKT2_2015_SIMPLIFIED;
1734 type = PJ_WKT2_2019;
1737 type = PJ_WKT2_2019_SIMPLIFIED;
1741 const QByteArray multiLineOption = QStringLiteral(
"MULTILINE=%1" ).arg( multiline ? QStringLiteral(
"YES" ) : QStringLiteral(
"NO" ) ).toLocal8Bit();
1742 const QByteArray indentatationWidthOption = QStringLiteral(
"INDENTATION_WIDTH=%1" ).arg( multiline ? QString::number( indentationWidth ) : QStringLiteral(
"0" ) ).toLocal8Bit();
1743 const char *
const options[] = {multiLineOption.constData(), indentatationWidthOption.constData(),
nullptr};
1746 if ( isDefaultPreferredFormat )
1749 d->mWktPreferred = res;
1761 QDomNode srsNode = node.namedItem( QStringLiteral(
"spatialrefsys" ) );
1763 if ( ! srsNode.isNull() )
1765 bool initialized =
false;
1768 long srsid = srsNode.namedItem( QStringLiteral(
"srsid" ) ).toElement().text().toLong( &ok );
1774 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
1775 if ( !node.isNull() )
1786 node = srsNode.namedItem( QStringLiteral(
"epsg" ) );
1787 if ( !node.isNull() )
1805 const QString
description = srsNode.namedItem( QStringLiteral(
"description" ) ).toElement().text();
1807 const QString wkt = srsNode.namedItem( QStringLiteral(
"wkt" ) ).toElement().text();
1808 initialized = createFromWktInternal( wkt,
description );
1813 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
1814 const QString proj4 = node.toElement().text();
1821 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
1822 const QString proj4 = node.toElement().text();
1823 if ( !proj4.trimmed().isEmpty() )
1824 setProjString( node.toElement().text() );
1826 node = srsNode.namedItem( QStringLiteral(
"srsid" ) );
1827 d->mSrsId = node.toElement().text().toLong();
1829 node = srsNode.namedItem( QStringLiteral(
"srid" ) );
1830 d->mSRID = node.toElement().text().toLong();
1832 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
1833 d->mAuthId = node.toElement().text();
1835 node = srsNode.namedItem( QStringLiteral(
"description" ) );
1836 d->mDescription = node.toElement().text();
1838 node = srsNode.namedItem( QStringLiteral(
"projectionacronym" ) );
1839 d->mProjectionAcronym = node.toElement().text();
1841 node = srsNode.namedItem( QStringLiteral(
"ellipsoidacronym" ) );
1842 d->mEllipsoidAcronym = node.toElement().text();
1844 node = srsNode.namedItem( QStringLiteral(
"geographicflag" ) );
1845 d->mIsGeographic = node.toElement().text() == QLatin1String(
"true" );
1847 d->mWktPreferred.clear();
1853 const QString epoch = srsNode.toElement().attribute( QStringLiteral(
"coordinateEpoch" ) );
1854 if ( !epoch.isEmpty() )
1856 bool epochOk =
false;
1857 d->mCoordinateEpoch = epoch.toDouble( &epochOk );
1859 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
1863 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
1866 mNativeFormat = qgsEnumKeyToValue<Qgis::CrsDefinitionFormat>( srsNode.toElement().attribute( QStringLiteral(
"nativeFormat" ) ), Qgis::CrsDefinitionFormat::Wkt );
1871 d =
new QgsCoordinateReferenceSystemPrivate();
1879 QDomElement layerNode = node.toElement();
1880 QDomElement srsElement = doc.createElement( QStringLiteral(
"spatialrefsys" ) );
1882 srsElement.setAttribute( QStringLiteral(
"nativeFormat" ), qgsEnumValueToKey<Qgis::CrsDefinitionFormat>( mNativeFormat ) );
1884 if ( std::isfinite( d->mCoordinateEpoch ) )
1886 srsElement.setAttribute( QStringLiteral(
"coordinateEpoch" ), d->mCoordinateEpoch );
1889 QDomElement wktElement = doc.createElement( QStringLiteral(
"wkt" ) );
1891 srsElement.appendChild( wktElement );
1893 QDomElement proj4Element = doc.createElement( QStringLiteral(
"proj4" ) );
1894 proj4Element.appendChild( doc.createTextNode(
toProj() ) );
1895 srsElement.appendChild( proj4Element );
1897 QDomElement srsIdElement = doc.createElement( QStringLiteral(
"srsid" ) );
1898 srsIdElement.appendChild( doc.createTextNode( QString::number(
srsid() ) ) );
1899 srsElement.appendChild( srsIdElement );
1901 QDomElement sridElement = doc.createElement( QStringLiteral(
"srid" ) );
1902 sridElement.appendChild( doc.createTextNode( QString::number(
postgisSrid() ) ) );
1903 srsElement.appendChild( sridElement );
1905 QDomElement authidElement = doc.createElement( QStringLiteral(
"authid" ) );
1906 authidElement.appendChild( doc.createTextNode(
authid() ) );
1907 srsElement.appendChild( authidElement );
1909 QDomElement descriptionElement = doc.createElement( QStringLiteral(
"description" ) );
1910 descriptionElement.appendChild( doc.createTextNode(
description() ) );
1911 srsElement.appendChild( descriptionElement );
1913 QDomElement projectionAcronymElement = doc.createElement( QStringLiteral(
"projectionacronym" ) );
1914 projectionAcronymElement.appendChild( doc.createTextNode(
projectionAcronym() ) );
1915 srsElement.appendChild( projectionAcronymElement );
1917 QDomElement ellipsoidAcronymElement = doc.createElement( QStringLiteral(
"ellipsoidacronym" ) );
1918 ellipsoidAcronymElement.appendChild( doc.createTextNode(
ellipsoidAcronym() ) );
1919 srsElement.appendChild( ellipsoidAcronymElement );
1921 QDomElement geographicFlagElement = doc.createElement( QStringLiteral(
"geographicflag" ) );
1922 QString geoFlagText = QStringLiteral(
"false" );
1925 geoFlagText = QStringLiteral(
"true" );
1928 geographicFlagElement.appendChild( doc.createTextNode( geoFlagText ) );
1929 srsElement.appendChild( geographicFlagElement );
1931 layerNode.appendChild( srsElement );
1943 QString QgsCoordinateReferenceSystem::projFromSrsId(
const int srsId )
1945 QString myDatabaseFileName;
1946 QString myProjString;
1947 QString mySql = QStringLiteral(
"select parameters from tbl_srs where srs_id = %1 order by deprecated" ).arg( srsId );
1956 QFileInfo myFileInfo;
1957 myFileInfo.setFile( myDatabaseFileName );
1958 if ( !myFileInfo.exists() )
1960 QgsDebugMsg( QStringLiteral(
"users qgis.db not found" ) );
1973 rc = openDatabase( myDatabaseFileName, database );
1979 statement = database.
prepare( mySql, rc );
1981 if ( rc == SQLITE_OK )
1983 if ( statement.
step() == SQLITE_ROW )
1989 return myProjString;
1996 myResult = database.
open_v2( path, SQLITE_OPEN_READONLY,
nullptr );
1998 myResult = database.
open( path );
2000 if ( myResult != SQLITE_OK )
2009 .arg( database.
errorMessage() ), QObject::tr(
"CRS" ) );
2016 sCustomSrsValidation = f;
2021 return sCustomSrsValidation;
2024 void QgsCoordinateReferenceSystem::debugPrint()
2026 QgsDebugMsg( QStringLiteral(
"***SpatialRefSystem***" ) );
2027 QgsDebugMsg(
"* Valid : " + ( d->mIsValid ? QString(
"true" ) : QString(
"false" ) ) );
2028 QgsDebugMsg(
"* SrsId : " + QString::number( d->mSrsId ) );
2034 QgsDebugMsg( QStringLiteral(
"* Units : meters" ) );
2038 QgsDebugMsg( QStringLiteral(
"* Units : feet" ) );
2042 QgsDebugMsg( QStringLiteral(
"* Units : degrees" ) );
2048 mValidationHint = html;
2053 return mValidationHint;
2063 mNativeFormat = format;
2068 return mNativeFormat;
2071 long QgsCoordinateReferenceSystem::getRecordCount()
2076 long myRecordCount = 0;
2079 if ( myResult != SQLITE_OK )
2085 QString mySql = QStringLiteral(
"select count(*) from tbl_srs" );
2086 statement = database.
prepare( mySql, myResult );
2087 if ( myResult == SQLITE_OK )
2089 if ( statement.
step() == SQLITE_ROW )
2091 QString myRecordCountString = statement.
columnAsText( 0 );
2092 myRecordCount = myRecordCountString.toLong();
2095 return myRecordCount;
2101 bool isGeographic =
false;
2103 if ( coordinateSystem )
2105 const int axisCount = proj_cs_get_axis_count( pjContext, coordinateSystem.get() );
2106 if ( axisCount > 0 )
2108 const char *outUnitAuthName =
nullptr;
2109 const char *outUnitAuthCode =
nullptr;
2111 proj_cs_get_axis_info( pjContext, coordinateSystem.get(), 0,
2120 if ( outUnitAuthName && outUnitAuthCode )
2122 const char *unitCategory =
nullptr;
2123 if ( proj_uom_get_info_from_database( pjContext, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
2125 isGeographic = QString( unitCategory ).compare( QLatin1String(
"angular" ), Qt::CaseInsensitive ) == 0;
2130 return isGeographic;
2135 thread_local
const QRegularExpression projRegExp( QStringLiteral(
"\\+proj=(\\S+)" ) );
2136 const QRegularExpressionMatch projMatch = projRegExp.match( proj );
2137 if ( !projMatch.hasMatch() )
2139 QgsDebugMsgLevel( QStringLiteral(
"no +proj argument found [%2]" ).arg( proj ), 2 );
2142 operation = projMatch.captured( 1 );
2144 const QRegularExpressionMatch ellipseMatch = projRegExp.match( proj );
2145 if ( ellipseMatch.hasMatch() )
2147 ellipsoid = ellipseMatch.captured( 1 );
2161 bool QgsCoordinateReferenceSystem::loadFromAuthCode(
const QString &auth,
const QString &code )
2164 d->mIsValid =
false;
2165 d->mWktPreferred.clear();
2174 switch ( proj_get_type(
crs.get() ) )
2176 case PJ_TYPE_VERTICAL_CRS:
2186 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2187 proj4 = proj4.trimmed();
2191 d->mWktPreferred.clear();
2192 d->mDescription = QString( proj_get_name(
crs.get() ) );
2193 d->mAuthId = QStringLiteral(
"%1:%2" ).arg( auth, code );
2195 d->mAxisInvertedDirty =
true;
2200 d->mEllipsoidAcronym.clear();
2201 d->setPj( std::move(
crs ) );
2203 const QString dbVals =
sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( auth, code ).toUpper() );
2204 if ( !dbVals.isEmpty() )
2206 const QStringList parts = dbVals.split(
',' );
2207 d->mSrsId = parts.at( 0 ).toInt();
2208 d->mSRID = parts.at( 1 ).toInt();
2216 QList<long> QgsCoordinateReferenceSystem::userSrsIds()
2218 QList<long> results;
2222 QFileInfo myInfo( db );
2223 if ( !myInfo.exists() )
2225 QgsDebugMsg(
"failed : " + db +
" does not exist!" );
2233 int result = openDatabase( db, database );
2234 if ( result != SQLITE_OK )
2236 QgsDebugMsg(
"failed : " + db +
" could not be opened!" );
2240 QString sql = QStringLiteral(
"select srs_id from tbl_srs where srs_id >= %1" ).arg(
USER_CRS_START_ID );
2242 statement = database.
prepare( sql, rc );
2245 int ret = statement.
step();
2247 if ( ret == SQLITE_DONE )
2253 if ( ret == SQLITE_ROW )
2259 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
2267 long QgsCoordinateReferenceSystem::matchToUserCrs()
const
2269 PJ *obj = d->threadLocalProjObject();
2273 const QList< long > ids = userSrsIds();
2274 for (
long id : ids )
2277 if ( candidate.
projObject() && proj_is_equivalent_to( obj, candidate.
projObject(), PJ_COMP_EQUIVALENT ) )
2285 static void sync_db_proj_logger(
void * ,
int level,
const char *message )
2290 if ( level == PJ_LOG_ERROR )
2294 else if ( level == PJ_LOG_DEBUG )
2302 setlocale( LC_ALL,
"C" );
2305 int inserted = 0, updated = 0, deleted = 0, errors = 0;
2310 if ( database.
open( dbFilePath ) != SQLITE_OK )
2316 if ( sqlite3_exec( database.get(),
"BEGIN TRANSACTION",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2324 char *errMsg =
nullptr;
2326 if ( sqlite3_exec( database.get(),
"create table tbl_info (proj_major INT, proj_minor INT, proj_patch INT)",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2328 QString sql = QStringLiteral(
"INSERT INTO tbl_info(proj_major, proj_minor, proj_patch) VALUES (%1, %2,%3)" )
2329 .arg( QString::number( PROJ_VERSION_MAJOR ),
2330 QString::number( PROJ_VERSION_MINOR ),
2331 QString::number( PROJ_VERSION_PATCH ) );
2332 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2334 QgsDebugMsg( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2337 errMsg ? errMsg :
"(unknown error)" ) );
2339 sqlite3_free( errMsg );
2346 QString sql = QStringLiteral(
"SELECT proj_major, proj_minor, proj_patch FROM tbl_info" );
2347 statement = database.
prepare( sql, result );
2348 if ( result != SQLITE_OK )
2353 if ( statement.
step() == SQLITE_ROW )
2358 if ( major == PROJ_VERSION_MAJOR && minor == PROJ_VERSION_MINOR && patch == PROJ_VERSION_PATCH )
2364 QgsDebugMsg( QStringLiteral(
"Could not retrieve previous CRS sync PROJ version number" ) );
2371 proj_log_func( pjContext,
nullptr, sync_db_proj_logger );
2373 PROJ_STRING_LIST authorities = proj_get_authorities_from_database( pjContext );
2375 int nextSrsId = 63561;
2376 int nextSrId = 520003561;
2377 for (
auto authIter = authorities; authIter && *authIter; ++authIter )
2379 const QString authority( *authIter );
2380 QgsDebugMsgLevel( QStringLiteral(
"Loading authority '%1'" ).arg( authority ), 2 );
2381 PROJ_STRING_LIST codes = proj_get_codes_from_database( pjContext, *authIter, PJ_TYPE_CRS,
true );
2383 QStringList allCodes;
2385 for (
auto codesIter = codes; codesIter && *codesIter; ++codesIter )
2387 const QString code( *codesIter );
2393 QgsDebugMsg( QStringLiteral(
"Could not load '%1:%2'" ).arg( authority, code ) );
2397 switch ( proj_get_type(
crs.get() ) )
2399 case PJ_TYPE_VERTICAL_CRS:
2409 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2410 proj4 = proj4.trimmed();
2412 if ( proj4.isEmpty() )
2414 QgsDebugMsgLevel( QStringLiteral(
"No proj4 for '%1:%2'" ).arg( authority, code ), 2 );
2419 const bool deprecated = proj_is_deprecated(
crs.get() );
2420 const QString name( proj_get_name(
crs.get() ) );
2422 QString sql = QStringLiteral(
"SELECT parameters,description,deprecated FROM tbl_srs WHERE auth_name='%1' AND auth_id='%2'" ).arg( authority, code );
2423 statement = database.
prepare( sql, result );
2424 if ( result != SQLITE_OK )
2432 bool srsDeprecated = deprecated;
2433 if ( statement.
step() == SQLITE_ROW )
2437 srsDeprecated = statement.
columnAsText( 2 ).toInt() != 0;
2440 if ( !srsProj4.isEmpty() || !srsDesc.isEmpty() )
2442 if ( proj4 != srsProj4 || name != srsDesc || deprecated != srsDeprecated )
2445 sql = QStringLiteral(
"UPDATE tbl_srs SET parameters=%1,description=%2,deprecated=%3 WHERE auth_name=%4 AND auth_id=%5" )
2448 .arg( deprecated ? 1 : 0 )
2451 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2453 QgsDebugMsg( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2456 errMsg ? errMsg :
"(unknown error)" ) );
2458 sqlite3_free( errMsg );
2476 const QString dbVals =
sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( authority, code ) );
2479 if ( !dbVals.isEmpty() )
2481 const QStringList parts = dbVals.split(
',' );
2482 srsId = parts.at( 0 );
2483 srId = parts.at( 1 );
2485 if ( srId.isEmpty() )
2487 srId = QString::number( nextSrId );
2490 if ( srsId.isEmpty() )
2492 srsId = QString::number( nextSrsId );
2496 if ( !srsId.isEmpty() )
2498 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)" )
2508 .arg( deprecated ? 1 : 0 );
2512 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)" )
2521 .arg( deprecated ? 1 : 0 );
2525 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) == SQLITE_OK )
2531 qCritical(
"Could not execute: %s [%s/%s]\n",
2532 sql.toLocal8Bit().constData(),
2533 sqlite3_errmsg( database.get() ),
2534 errMsg ? errMsg :
"(unknown error)" );
2538 sqlite3_free( errMsg );
2543 proj_string_list_destroy( codes );
2545 const QString sql = QStringLiteral(
"DELETE FROM tbl_srs WHERE auth_name='%1' AND NOT auth_id IN (%2)" ).arg( authority, allCodes.join(
',' ) );
2546 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2548 deleted = sqlite3_changes( database.get() );
2553 qCritical(
"Could not execute: %s [%s]\n",
2554 sql.toLocal8Bit().constData(),
2555 sqlite3_errmsg( database.get() ) );
2559 proj_string_list_destroy( authorities );
2561 QString sql = QStringLiteral(
"UPDATE tbl_info set proj_major=%1,proj_minor=%2,proj_patch=%3" )
2562 .arg( QString::number( PROJ_VERSION_MAJOR ),
2563 QString::number( PROJ_VERSION_MINOR ),
2564 QString::number( PROJ_VERSION_PATCH ) );
2565 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2567 QgsDebugMsg( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2570 errMsg ? errMsg :
"(unknown error)" ) );
2572 sqlite3_free( errMsg );
2576 if ( sqlite3_exec( database.get(),
"COMMIT",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2578 QgsDebugMsg( QStringLiteral(
"Could not commit transaction: %1 [%2]\n" ).arg(
2580 sqlite3_errmsg( database.get() ) )
2586 QgsDebugMsgLevel( QStringLiteral(
"CRS update (inserted:%1 updated:%2 deleted:%3 errors:%4)" ).arg( inserted ).arg( updated ).arg( deleted ).arg( errors ), 4 );
2594 return updated + inserted;
2597 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::stringCache()
2599 return *sStringCache();
2602 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::projCache()
2604 return *sProj4Cache();
2607 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::ogcCache()
2609 return *sOgcCache();
2612 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::wktCache()
2614 return *sWktCache();
2617 const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srIdCache()
2619 return *sSrIdCache();
2622 const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srsIdCache()
2624 return *sSrsIdCache();
2634 if (
PJ *obj = d->threadLocalProjObject() )
2662 else if (
PJ *obj = d->threadLocalProjObject() )
2665 return geoCrs ? QStringLiteral(
"%1:%2" ).arg( proj_get_id_auth_name( geoCrs.get(), 0 ), proj_get_id_code( geoCrs.get(), 0 ) ) : QString();
2675 return d->threadLocalProjObject();
2688 d->mIsValid =
false;
2690 d->mWktPreferred.clear();
2697 switch ( proj_get_type(
object ) )
2699 case PJ_TYPE_GEODETIC_CRS:
2700 case PJ_TYPE_GEOCENTRIC_CRS:
2701 case PJ_TYPE_GEOGRAPHIC_CRS:
2702 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
2703 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
2704 case PJ_TYPE_VERTICAL_CRS:
2705 case PJ_TYPE_PROJECTED_CRS:
2706 case PJ_TYPE_COMPOUND_CRS:
2707 case PJ_TYPE_TEMPORAL_CRS:
2708 case PJ_TYPE_ENGINEERING_CRS:
2709 case PJ_TYPE_BOUND_CRS:
2710 case PJ_TYPE_OTHER_CRS:
2726 const QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
2727 const QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
2728 if ( !authName.isEmpty() && !authCode.isEmpty() && loadFromAuthCode( authName, authCode ) )
2736 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
2746 QStringList projections;
2748 projections.reserve( res.size() );
2751 projections << QString::number(
crs.
srsid() );
2758 QList<QgsCoordinateReferenceSystem> res;
2762 QStringList projectionsProj4 = settings.
value( QStringLiteral(
"UI/recentProjectionsProj4" ) ).toStringList();
2763 QStringList projectionsWkt = settings.
value( QStringLiteral(
"UI/recentProjectionsWkt" ) ).toStringList();
2764 QStringList projectionsAuthId = settings.
value( QStringLiteral(
"UI/recentProjectionsAuthId" ) ).toStringList();
2765 int max = std::max( projectionsAuthId.size(), std::max( projectionsProj4.size(), projectionsWkt.size() ) );
2767 for (
int i = 0; i < max; ++i )
2769 const QString proj = projectionsProj4.value( i );
2770 const QString wkt = projectionsWkt.value( i );
2771 const QString
authid = projectionsAuthId.value( i );
2794 recent.removeAll(
crs );
2795 recent.insert( 0,
crs );
2798 recent = recent.mid( 0, 30 );
2799 QStringList authids;
2800 authids.reserve( recent.size() );
2802 proj.reserve( recent.size() );
2804 wkt.reserve( recent.size() );
2807 authids <<
c.authid();
2813 settings.
setValue( QStringLiteral(
"UI/recentProjectionsAuthId" ), authids );
2814 settings.
setValue( QStringLiteral(
"UI/recentProjectionsWkt" ), wkt );
2815 settings.
setValue( QStringLiteral(
"UI/recentProjectionsProj4" ), proj );
2820 sSrIdCacheLock()->lockForWrite();
2821 if ( !sDisableSrIdCache )
2824 sDisableSrIdCache =
true;
2825 sSrIdCache()->clear();
2827 sSrIdCacheLock()->unlock();
2829 sOgcLock()->lockForWrite();
2830 if ( !sDisableOgcCache )
2833 sDisableOgcCache =
true;
2834 sOgcCache()->clear();
2836 sOgcLock()->unlock();
2838 sProj4CacheLock()->lockForWrite();
2839 if ( !sDisableProjCache )
2842 sDisableProjCache =
true;
2843 sProj4Cache()->clear();
2845 sProj4CacheLock()->unlock();
2847 sCRSWktLock()->lockForWrite();
2848 if ( !sDisableWktCache )
2851 sDisableWktCache =
true;
2852 sWktCache()->clear();
2854 sCRSWktLock()->unlock();
2856 sCRSSrsIdLock()->lockForWrite();
2857 if ( !sDisableSrsIdCache )
2860 sDisableSrsIdCache =
true;
2861 sSrsIdCache()->clear();
2863 sCRSSrsIdLock()->unlock();
2865 sCrsStringLock()->lockForWrite();
2866 if ( !sDisableStringCache )
2869 sDisableStringCache =
true;
2870 sStringCache()->clear();
2872 sCrsStringLock()->unlock();
2881 if ( !c1.d->mIsValid && !c2.d->mIsValid )
2884 if ( !c1.d->mIsValid && c2.d->mIsValid )
2887 if ( c1.d->mIsValid && !c2.d->mIsValid )
2893 if ( c1IsUser && !c2IsUser )
2896 if ( !c1IsUser && c2IsUser )
2899 if ( !c1IsUser && !c2IsUser )
2901 if ( c1.d->mAuthId != c2.d->mAuthId )
2902 return c1.d->mAuthId > c2.d->mAuthId;
2912 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
2915 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
2918 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
2921 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
2924 return c1.d->mCoordinateEpoch > c2.d->mCoordinateEpoch;
2932 if ( !c1.d->mIsValid && !c2.d->mIsValid )
2935 if ( c1.d->mIsValid && !c2.d->mIsValid )
2938 if ( !c1.d->mIsValid && c2.d->mIsValid )
2944 if ( !c1IsUser && c2IsUser )
2947 if ( c1IsUser && !c2IsUser )
2950 if ( !c1IsUser && !c2IsUser )
2952 if ( c1.d->mAuthId != c2.d->mAuthId )
2953 return c1.d->mAuthId < c2.d->mAuthId;
2963 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
2966 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
2969 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
2972 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
2975 return c1.d->mCoordinateEpoch < c2.d->mCoordinateEpoch;
2980 return !( c1 < c2 );
2984 return !( c1 > c2 );
CrsDefinitionFormat
CRS definition formats.
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, Qgis::CrsDefinitionFormat nativeFormat=Qgis::CrsDefinitionFormat::Wkt)
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.
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.
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
Overloaded != operator used to compare to CRS's.
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.
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.
QgsCoordinateReferenceSystem toGeographicCrs() const
Returns the geographic CRS associated with this CRS object.
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 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)
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.
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.
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.
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.
bool createFromProjObject(PJ *object)
Sets this CRS by passing it a PROJ PJ object, corresponding to a PROJ CRS object.
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.
This class is a composition of two QSettings instances:
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to 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