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|ogc|nkg|zangi|iau_2015|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" )
305 || authName == QLatin1String(
"osgeo" )
306 || authName == QLatin1String(
"ignf" )
307 || authName == QLatin1String(
"zangi" )
308 || authName == QLatin1String(
"iau2000" )
309 || authName == QLatin1String(
"ogc" )
310 || authName == QLatin1String(
"nkg" )
311 || authName == QLatin1String(
"iau_2015" )
318 const long id = match.captured( 2 ).toLong();
326 const thread_local QRegularExpression reCrsStr( QStringLiteral(
"^(?:(wkt|proj4|proj)\\:)?(.+)$" ), QRegularExpression::CaseInsensitiveOption );
327 match = reCrsStr.match( definition );
328 if ( match.capturedStart() == 0 )
330 if ( match.captured( 1 ).startsWith( QLatin1String(
"proj" ), Qt::CaseInsensitive ) )
342 if ( !sDisableStringCache )
343 sStringCache()->insert( definition, *
this );
349 if ( definition.isEmpty() )
355 if ( OSRSetFromUserInput(
crs, definition.toLocal8Bit().constData() ) == OGRERR_NONE )
358 OSRDestroySpatialReference(
crs );
368 const char *configOld = CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" );
369 const char *configNew =
"GEOGCS";
371 if ( strcmp( configOld,
"" ) == 0 )
373 CPLSetConfigOption(
"GDAL_FIX_ESRI_WKT", configNew );
374 if ( strcmp( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) != 0 )
376 .arg( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) );
377 QgsDebugMsgLevel( QStringLiteral(
"set GDAL_FIX_ESRI_WKT : %1" ).arg( configNew ), 4 );
381 QgsDebugMsgLevel( QStringLiteral(
"GDAL_FIX_ESRI_WKT was already set : %1" ).arg( configNew ), 4 );
391 if ( !sDisableOgcCache )
393 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sOgcCache()->constFind(
crs );
394 if ( crsIt != sOgcCache()->constEnd() )
397 *
this = crsIt.value();
403 QString wmsCrs =
crs;
405 thread_local
const QRegularExpression re_uri( QRegularExpression::anchoredPattern( QStringLiteral(
"http://www\\.opengis\\.net/def/crs/([^/]+).+/([^/]+)" ) ), QRegularExpression::CaseInsensitiveOption );
406 QRegularExpressionMatch match = re_uri.match( wmsCrs );
407 if ( match.hasMatch() )
409 wmsCrs = match.captured( 1 ) +
':' + match.captured( 2 );
413 thread_local
const QRegularExpression re_urn( QRegularExpression::anchoredPattern( QStringLiteral(
"urn:ogc:def:crs:([^:]+).+(?<=:)([^:]+)" ) ), QRegularExpression::CaseInsensitiveOption );
414 match = re_urn.match( wmsCrs );
415 if ( match.hasMatch() )
417 wmsCrs = match.captured( 1 ) +
':' + match.captured( 2 );
421 thread_local
const QRegularExpression re_urn_custom( QRegularExpression::anchoredPattern( QStringLiteral(
"(user|custom|qgis):(\\d+)" ) ), QRegularExpression::CaseInsensitiveOption );
422 match = re_urn_custom.match( wmsCrs );
423 if ( match.hasMatch() &&
createFromSrsId( match.captured( 2 ).toInt() ) )
426 if ( !sDisableOgcCache )
427 sOgcCache()->insert(
crs, *
this );
434 const QString legacyKey = wmsCrs.toLower();
437 if ( it.key().compare( legacyKey, Qt::CaseInsensitive ) == 0 )
439 const QStringList parts = it.key().split(
':' );
440 const QString auth = parts.at( 0 );
441 const QString code = parts.at( 1 );
442 if ( loadFromAuthCode( auth, code ) )
445 if ( !sDisableOgcCache )
446 sOgcCache()->insert(
crs, *
this );
455 if ( !sDisableOgcCache )
456 sOgcCache()->insert(
crs, *
this );
461 if ( wmsCrs.compare( QLatin1String(
"CRS:27" ), Qt::CaseInsensitive ) == 0 ||
462 wmsCrs.compare( QLatin1String(
"OGC:CRS27" ), Qt::CaseInsensitive ) == 0 )
469 if ( wmsCrs.compare( QLatin1String(
"CRS:83" ), Qt::CaseInsensitive ) == 0 ||
470 wmsCrs.compare( QLatin1String(
"OGC:CRS83" ), Qt::CaseInsensitive ) == 0 )
477 if ( wmsCrs.compare( QLatin1String(
"CRS:84" ), Qt::CaseInsensitive ) == 0 ||
478 wmsCrs.compare( QLatin1String(
"OGC:CRS84" ), Qt::CaseInsensitive ) == 0 )
482 d->mAxisInverted =
false;
483 d->mAxisInvertedDirty =
false;
487 if ( !sDisableOgcCache )
488 sOgcCache()->insert(
crs, *
this );
494 if ( !sDisableOgcCache )
504 if ( d->mIsValid || !sCustomSrsValidation )
508 if ( sCustomSrsValidation )
509 sCustomSrsValidation( *
this );
515 if ( !sDisableSrIdCache )
517 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrIdCache()->constFind(
id );
518 if ( crsIt != sSrIdCache()->constEnd() )
521 *
this = crsIt.value();
530 if ( it.value().endsWith( QStringLiteral(
",%1" ).arg(
id ) ) )
532 const QStringList parts = it.key().split(
':' );
533 const QString auth = parts.at( 0 );
534 const QString code = parts.at( 1 );
535 if ( loadFromAuthCode( auth, code ) )
538 if ( !sDisableSrIdCache )
539 sSrIdCache()->insert(
id, *
this );
549 if ( !sDisableSrIdCache )
550 sSrIdCache()->insert(
id, *
this );
558 if ( !sDisableSrsIdCache )
560 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrsIdCache()->constFind(
id );
561 if ( crsIt != sSrsIdCache()->constEnd() )
564 *
this = crsIt.value();
573 if ( it.value().startsWith( QString::number(
id ) +
',' ) )
575 const QStringList parts = it.key().split(
':' );
576 const QString auth = parts.at( 0 );
577 const QString code = parts.at( 1 );
578 if ( loadFromAuthCode( auth, code ) )
581 if ( !sDisableSrsIdCache )
582 sSrsIdCache()->insert(
id, *
this );
590 QStringLiteral(
"srs_id" ), QString::number(
id ) );
593 if ( !sDisableSrsIdCache )
594 sSrsIdCache()->insert(
id, *
this );
598 bool QgsCoordinateReferenceSystem::loadFromDatabase(
const QString &db,
const QString &expression,
const QString &value )
602 QgsDebugMsgLevel(
"load CRS from " + db +
" where " + expression +
" is " + value, 3 );
604 d->mWktPreferred.clear();
606 QFileInfo myInfo( db );
607 if ( !myInfo.exists() )
609 QgsDebugMsg(
"failed : " + db +
" does not exist!" );
617 myResult = openDatabase( db, database );
618 if ( myResult != SQLITE_OK )
635 QString mySql =
"select srs_id,description,projection_acronym,"
636 "ellipsoid_acronym,parameters,srid,auth_name||':'||auth_id,is_geo,wkt "
638 statement = database.
prepare( mySql, myResult );
641 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
646 d->mEllipsoidAcronym.clear();
648 d->mWktPreferred.clear();
651 d->mIsGeographic = statement.
columnAsText( 7 ).toInt() != 0;
653 d->mAxisInvertedDirty =
true;
655 if ( d->mSrsId >=
USER_CRS_START_ID && ( d->mAuthId.isEmpty() || d->mAuthId == QChar(
':' ) ) )
657 d->mAuthId = QStringLiteral(
"USER:%1" ).arg( d->mSrsId );
659 else if ( !d->mAuthId.startsWith( QLatin1String(
"USER:" ), Qt::CaseInsensitive ) )
661 QStringList parts = d->mAuthId.split(
':' );
662 QString auth = parts.at( 0 );
663 QString code = parts.at( 1 );
670 d->mIsValid = d->hasPj();
676 if ( !wkt.isEmpty() )
684 setProjString( d->mProj4 );
694 void QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread(
PJ_CONTEXT *pj_context )
701 if ( !sDisableSrIdCache )
704 if ( !sDisableSrIdCache )
706 for (
auto it = sSrIdCache()->begin(); it != sSrIdCache()->end(); )
708 auto &v = it.value();
709 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
710 it = sSrIdCache()->erase( it );
716 if ( !sDisableOgcCache )
719 if ( !sDisableOgcCache )
721 for (
auto it = sOgcCache()->begin(); it != sOgcCache()->end(); )
723 auto &v = it.value();
724 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
725 it = sOgcCache()->erase( it );
731 if ( !sDisableProjCache )
734 if ( !sDisableProjCache )
736 for (
auto it = sProj4Cache()->begin(); it != sProj4Cache()->end(); )
738 auto &v = it.value();
739 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
740 it = sProj4Cache()->erase( it );
746 if ( !sDisableWktCache )
749 if ( !sDisableWktCache )
751 for (
auto it = sWktCache()->begin(); it != sWktCache()->end(); )
753 auto &v = it.value();
754 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
755 it = sWktCache()->erase( it );
761 if ( !sDisableSrsIdCache )
764 if ( !sDisableSrsIdCache )
766 for (
auto it = sSrsIdCache()->begin(); it != sSrsIdCache()->end(); )
768 auto &v = it.value();
769 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
770 it = sSrsIdCache()->erase( it );
776 if ( !sDisableStringCache )
779 if ( !sDisableStringCache )
781 for (
auto it = sStringCache()->begin(); it != sStringCache()->end(); )
783 auto &v = it.value();
784 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
785 it = sStringCache()->erase( it );
795 if ( d->mAxisInvertedDirty )
798 d->mAxisInvertedDirty =
false;
801 return d->mAxisInverted;
815 const thread_local QMap< Qgis::CrsAxisDirection, QString > mapping =
858 QList< Qgis::CrsAxisDirection > res;
859 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
862 res.reserve( axisCount );
864 for (
int i = 0; i < axisCount; ++i )
866 const char *outDirection =
nullptr;
867 proj_cs_get_axis_info( context, pjCs.get(), i,
877 const thread_local QRegularExpression rx( QStringLiteral(
"([^\\s]+).*" ) );
878 const QRegularExpressionMatch match = rx.match( QString( outDirection ) );
879 if ( !match.hasMatch() )
882 const QString direction = match.captured( 1 );
884 for (
auto it = mapping.constBegin(); it != mapping.constEnd(); ++it )
886 if ( it.value().compare( direction, Qt::CaseInsensitive ) == 0 )
901 return createFromWktInternal( wkt, QString() );
904 bool QgsCoordinateReferenceSystem::createFromWktInternal(
const QString &wkt,
const QString &description )
912 if ( !sDisableWktCache )
914 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sWktCache()->constFind( wkt );
915 if ( crsIt != sWktCache()->constEnd() )
918 *
this = crsIt.value();
920 if ( !
description.isEmpty() && d->mDescription.isEmpty() )
925 sWktCache()->insert( wkt, *
this );
934 d->mWktPreferred.clear();
937 QgsDebugMsgLevel( QStringLiteral(
"theWkt is uninitialized, operation failed" ), 4 );
942 QgsCoordinateReferenceSystem::RecordMap record = getRecord(
"select * from tbl_srs where wkt=" +
QgsSqliteUtils::quotedString( wkt ) +
" order by deprecated" );
943 if ( !record.empty() )
945 long srsId = record[QStringLiteral(
"srs_id" )].toLong();
958 if ( d->mSrsId == 0 )
961 long id = matchToUserCrs();
970 if ( !sDisableWktCache )
971 sWktCache()->insert( wkt, *
this );
989 if ( projString.isEmpty() )
994 if ( projString.trimmed().isEmpty() )
998 d->mWktPreferred.clear();
1003 if ( !sDisableProjCache )
1005 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sProj4Cache()->constFind( projString );
1006 if ( crsIt != sProj4Cache()->constEnd() )
1009 *
this = crsIt.value();
1023 QString myProj4String = projString.trimmed();
1024 myProj4String.remove( QStringLiteral(
"+type=crs" ) );
1025 myProj4String = myProj4String.trimmed();
1027 d->mIsValid =
false;
1028 d->mWktPreferred.clear();
1033 const QString projCrsString = myProj4String + ( myProj4String.contains( QStringLiteral(
"+type=crs" ) ) ? QString() : QStringLiteral(
" +type=crs" ) );
1041 const QString
authid = QStringLiteral(
"%1:%2" ).arg( authName, authCode );
1045 if ( !sDisableProjCache )
1046 sProj4Cache()->insert( projString, *
this );
1053 QgsCoordinateReferenceSystem::RecordMap myRecord = getRecord(
"select * from tbl_srs where parameters=" +
QgsSqliteUtils::quotedString( myProj4String ) +
" order by deprecated" );
1055 if ( !myRecord.empty() )
1057 id = myRecord[QStringLiteral(
"srs_id" )].toLong();
1066 setProjString( myProj4String );
1069 id = matchToUserCrs();
1078 setProjString( myProj4String );
1082 if ( !sDisableProjCache )
1083 sProj4Cache()->insert( projString, *
this );
1089 QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord(
const QString &sql )
1091 QString myDatabaseFileName;
1092 QgsCoordinateReferenceSystem::RecordMap myMap;
1093 QString myFieldName;
1094 QString myFieldValue;
1101 QFileInfo myInfo( myDatabaseFileName );
1102 if ( !myInfo.exists() )
1104 QgsDebugMsg(
"failed : " + myDatabaseFileName +
" does not exist!" );
1109 myResult = openDatabase( myDatabaseFileName, database );
1110 if ( myResult != SQLITE_OK )
1115 statement = database.
prepare( sql, myResult );
1117 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1121 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1123 myFieldName = statement.
columnName( myColNo );
1125 myMap[myFieldName] = myFieldValue;
1127 if ( statement.
step() != SQLITE_DONE )
1129 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1138 if ( myMap.empty() )
1141 QFileInfo myFileInfo;
1142 myFileInfo.setFile( myDatabaseFileName );
1143 if ( !myFileInfo.exists() )
1145 QgsDebugMsg( QStringLiteral(
"user qgis.db not found" ) );
1150 myResult = openDatabase( myDatabaseFileName, database );
1151 if ( myResult != SQLITE_OK )
1156 statement = database.
prepare( sql, myResult );
1158 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1162 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1164 myFieldName = statement.
columnName( myColNo );
1166 myMap[myFieldName] = myFieldValue;
1169 if ( statement.
step() != SQLITE_DONE )
1171 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1202 if ( d->mDescription.isNull() )
1208 return d->mDescription;
1215 if ( !
authid().isEmpty() )
1225 id =
isValid() ? QObject::tr(
"Custom CRS" ) : QObject::tr(
"Unknown CRS" );
1227 id = QObject::tr(
"Custom CRS: %1" ).arg(
1230 else if ( !
toProj().isEmpty() )
1231 id = QObject::tr(
"Custom CRS: %1" ).arg( type ==
MediumString ? (
toProj().left( 50 ) + QString( QChar( 0x2026 ) ) )
1233 if ( !
id.isEmpty() && !std::isnan( d->mCoordinateEpoch ) )
1234 id += QStringLiteral(
" @ %1" ).arg( d->mCoordinateEpoch );
1241 if ( d->mProjectionAcronym.isNull() )
1247 return d->mProjectionAcronym;
1253 if ( d->mEllipsoidAcronym.isNull() )
1255 if (
PJ *obj = d->threadLocalProjObject() )
1260 const QString ellipsoidAuthName( proj_get_id_auth_name( ellipsoid.get(), 0 ) );
1261 const QString ellipsoidAuthCode( proj_get_id_code( ellipsoid.get(), 0 ) );
1262 if ( !ellipsoidAuthName.isEmpty() && !ellipsoidAuthCode.isEmpty() )
1263 d->mEllipsoidAcronym = QStringLiteral(
"%1:%2" ).arg( ellipsoidAuthName, ellipsoidAuthCode );
1266 double semiMajor, semiMinor, invFlattening;
1267 int semiMinorComputed = 0;
1268 if ( proj_ellipsoid_get_parameters(
QgsProjContext::get(), ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
1270 d->mEllipsoidAcronym = QStringLiteral(
"PARAMETER:%1:%2" ).arg(
qgsDoubleToString( semiMajor ),
1275 d->mEllipsoidAcronym.clear();
1280 return d->mEllipsoidAcronym;
1284 return d->mEllipsoidAcronym;
1298 if ( d->mProj4.isEmpty() )
1300 if (
PJ *obj = d->threadLocalProjObject() )
1306 return d->mProj4.trimmed();
1311 return d->mIsGeographic;
1329 #if PROJ_VERSION_MAJOR>8 || (PROJ_VERSION_MAJOR==8 && PROJ_VERSION_MINOR>=1)
1332 return QString( proj_get_celestial_body_name( context, pj ) );
1334 throw QgsNotSupportedException( QStringLiteral(
"Retrieving celestial body requires a QGIS build based on PROJ 8.1 or later" ) );
1340 if ( d->mCoordinateEpoch == epoch )
1346 d->mCoordinateEpoch = epoch;
1347 d->setPj( std::move( clone ) );
1352 return d->mCoordinateEpoch;
1364 #if PROJ_VERSION_MAJOR>=8
1372 res.mName = QString( proj_get_name( ensemble.get() ) );
1373 res.mAuthority = QString( proj_get_id_auth_name( ensemble.get(), 0 ) );
1374 res.mCode = QString( proj_get_id_code( ensemble.get(), 0 ) );
1375 res.mRemarks = QString( proj_get_remarks( ensemble.get() ) );
1376 res.mScope = QString( proj_get_scope( ensemble.get() ) );
1377 res.mAccuracy = proj_datum_ensemble_get_accuracy( context, ensemble.get() );
1379 const int memberCount = proj_datum_ensemble_get_member_count( context, ensemble.get() );
1380 for (
int i = 0; i < memberCount; ++i )
1387 details.mName = QString( proj_get_name( member.get() ) );
1388 details.mAuthority = QString( proj_get_id_auth_name( member.get(), 0 ) );
1389 details.mCode = QString( proj_get_id_code( member.get(), 0 ) );
1390 details.mRemarks = QString( proj_get_remarks( member.get() ) );
1391 details.mScope = QString( proj_get_scope( member.get() ) );
1393 res.mMembers << details;
1397 throw QgsNotSupportedException( QStringLiteral(
"Calculating datum ensembles requires a QGIS build based on PROJ 8.0 or later" ) );
1406 QString projString =
toProj();
1407 projString.replace( QLatin1String(
"+type=crs" ), QString() );
1410 if ( !transformation )
1413 PJ_COORD coord = proj_coord( 0, 0, 0, HUGE_VAL );
1414 coord.uv.u = point.
x() * M_PI / 180.0;
1415 coord.uv.v = point.
y() * M_PI / 180.0;
1417 proj_errno_reset( transformation.get() );
1418 const PJ_FACTORS pjFactors = proj_factors( transformation.get(), coord );
1419 if ( proj_errno( transformation.get() ) )
1424 res.mIsValid =
true;
1425 res.mMeridionalScale = pjFactors.meridional_scale;
1426 res.mParallelScale = pjFactors.parallel_scale;
1427 res.mArealScale = pjFactors.areal_scale;
1428 res.mAngularDistortion = pjFactors.angular_distortion;
1429 res.mMeridianParallelAngle = pjFactors.meridian_parallel_angle * 180 / M_PI;
1430 res.mMeridianConvergence = pjFactors.meridian_convergence * 180 / M_PI;
1431 res.mTissotSemimajor = pjFactors.tissot_semimajor;
1432 res.mTissotSemiminor = pjFactors.tissot_semiminor;
1433 res.mDxDlam = pjFactors.dx_dlam;
1434 res.mDxDphi = pjFactors.dx_dphi;
1435 res.mDyDlam = pjFactors.dy_dlam;
1436 res.mDyDphi = pjFactors.dy_dphi;
1445 QString projString =
toProj();
1446 projString.replace( QLatin1String(
"+type=crs" ), QString() );
1449 if ( !transformation )
1452 PJ_PROJ_INFO info = proj_pj_info( transformation.get() );
1467 return d->mMapUnits;
1475 PJ *obj = d->threadLocalProjObject();
1480 double southLat = 0;
1482 double northLat = 0;
1485 &westLon, &southLat, &eastLon, &northLat,
nullptr ) )
1514 void QgsCoordinateReferenceSystem::setProjString(
const QString &proj4String )
1517 d->mProj4 = proj4String;
1518 d->mWktPreferred.clear();
1521 QString trimmed = proj4String.trimmed();
1523 trimmed += QLatin1String(
" +type=crs" );
1533 const int errNo = proj_context_errno( ctx );
1534 QgsDebugMsg( QStringLiteral(
"proj string rejected: %1" ).arg( proj_errno_string( errNo ) ) );
1536 d->mIsValid =
false;
1540 d->mEllipsoidAcronym.clear();
1547 bool QgsCoordinateReferenceSystem::setWktString(
const QString &wkt )
1550 d->mIsValid =
false;
1551 d->mWktPreferred.clear();
1553 PROJ_STRING_LIST warnings =
nullptr;
1554 PROJ_STRING_LIST grammerErrors =
nullptr;
1562 QgsDebugMsg( QStringLiteral(
"\n---------------------------------------------------------------" ) );
1563 QgsDebugMsg( QStringLiteral(
"This CRS could *** NOT *** be set from the supplied Wkt " ) );
1565 for (
auto iter = warnings; iter && *iter; ++iter )
1567 for (
auto iter = grammerErrors; iter && *iter; ++iter )
1569 QgsDebugMsg( QStringLiteral(
"---------------------------------------------------------------\n" ) );
1571 proj_string_list_destroy( warnings );
1572 proj_string_list_destroy( grammerErrors );
1578 if ( !sDisableWktCache )
1579 sWktCache()->insert( wkt, *
this );
1586 QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
1587 QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
1589 if ( authName.isEmpty() || authCode.isEmpty() )
1595 if ( !authName.isEmpty() && !authCode.isEmpty() )
1597 if ( loadFromAuthCode( authName, authCode ) )
1600 if ( !sDisableWktCache )
1601 sWktCache()->insert( wkt, *
this );
1609 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
1617 void QgsCoordinateReferenceSystem::setMapUnits()
1634 if ( !coordinateSystem )
1640 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
1641 if ( axisCount > 0 )
1643 const char *outUnitName =
nullptr;
1645 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
1654 const QString unitName( outUnitName );
1658 if ( unitName.compare( QLatin1String(
"degree" ), Qt::CaseInsensitive ) == 0 ||
1659 unitName.compare( QLatin1String(
"degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1660 unitName.compare( QLatin1String(
"degree minute second hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1661 unitName.compare( QLatin1String(
"degree minute" ), Qt::CaseInsensitive ) == 0 ||
1662 unitName.compare( QLatin1String(
"degree hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1663 unitName.compare( QLatin1String(
"degree minute hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1664 unitName.compare( QLatin1String(
"hemisphere degree" ), Qt::CaseInsensitive ) == 0 ||
1665 unitName.compare( QLatin1String(
"hemisphere degree minute" ), Qt::CaseInsensitive ) == 0 ||
1666 unitName.compare( QLatin1String(
"hemisphere degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1667 unitName.compare( QLatin1String(
"degree (supplier to define representation)" ), Qt::CaseInsensitive ) == 0 )
1669 else if ( unitName.compare( QLatin1String(
"metre" ), Qt::CaseInsensitive ) == 0
1670 || unitName.compare( QLatin1String(
"m" ), Qt::CaseInsensitive ) == 0
1671 || unitName.compare( QLatin1String(
"meter" ), Qt::CaseInsensitive ) == 0 )
1674 else if ( unitName.compare( QLatin1String(
"US survey foot" ), Qt::CaseInsensitive ) == 0 ||
1675 unitName.compare( QLatin1String(
"foot" ), Qt::CaseInsensitive ) == 0 )
1677 else if ( unitName.compare( QLatin1String(
"kilometre" ), Qt::CaseInsensitive ) == 0 )
1679 else if ( unitName.compare( QLatin1String(
"centimetre" ), Qt::CaseInsensitive ) == 0 )
1681 else if ( unitName.compare( QLatin1String(
"millimetre" ), Qt::CaseInsensitive ) == 0 )
1683 else if ( unitName.compare( QLatin1String(
"Statute mile" ), Qt::CaseInsensitive ) == 0 )
1685 else if ( unitName.compare( QLatin1String(
"nautical mile" ), Qt::CaseInsensitive ) == 0 )
1687 else if ( unitName.compare( QLatin1String(
"yard" ), Qt::CaseInsensitive ) == 0 )
1704 if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull()
1707 QgsDebugMsgLevel(
"QgsCoordinateReferenceSystem::findMatchingProj will only "
1708 "work if prj acr ellipsoid acr and proj4string are set"
1709 " and the current projection is valid!", 4 );
1719 QString mySql = QString(
"select srs_id,parameters from tbl_srs where "
1720 "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated" )
1727 myResult = openDatabase( myDatabaseFileName, database );
1728 if ( myResult != SQLITE_OK )
1733 statement = database.
prepare( mySql, myResult );
1734 if ( myResult == SQLITE_OK )
1737 while ( statement.
step() == SQLITE_ROW )
1741 if (
toProj() == myProj4String.trimmed() )
1743 return mySrsId.toLong();
1754 myResult = openDatabase( myDatabaseFileName, database );
1755 if ( myResult != SQLITE_OK )
1760 statement = database.
prepare( mySql, myResult );
1762 if ( myResult == SQLITE_OK )
1764 while ( statement.
step() == SQLITE_ROW )
1768 if (
toProj() == myProj4String.trimmed() )
1770 return mySrsId.toLong();
1784 if ( !d->mIsValid && !srs.d->mIsValid )
1787 if ( !d->mIsValid || !srs.d->mIsValid )
1795 if ( isUser != otherIsUser )
1799 if ( !isUser && ( !d->mAuthId.isEmpty() || !srs.d->mAuthId.isEmpty() ) )
1800 return d->mAuthId == srs.d->mAuthId;
1807 return !( *
this == srs );
1812 if (
PJ *obj = d->threadLocalProjObject() )
1814 const bool isDefaultPreferredFormat = variant ==
WKT_PREFERRED && !multiline;
1815 if ( isDefaultPreferredFormat && !d->mWktPreferred.isEmpty() )
1818 return d->mWktPreferred;
1821 PJ_WKT_TYPE type = PJ_WKT1_GDAL;
1825 type = PJ_WKT1_GDAL;
1828 type = PJ_WKT1_ESRI;
1831 type = PJ_WKT2_2015;
1834 type = PJ_WKT2_2015_SIMPLIFIED;
1837 type = PJ_WKT2_2019;
1840 type = PJ_WKT2_2019_SIMPLIFIED;
1844 const QByteArray multiLineOption = QStringLiteral(
"MULTILINE=%1" ).arg( multiline ? QStringLiteral(
"YES" ) : QStringLiteral(
"NO" ) ).toLocal8Bit();
1845 const QByteArray indentatationWidthOption = QStringLiteral(
"INDENTATION_WIDTH=%1" ).arg( multiline ? QString::number( indentationWidth ) : QStringLiteral(
"0" ) ).toLocal8Bit();
1846 const char *
const options[] = {multiLineOption.constData(), indentatationWidthOption.constData(),
nullptr};
1849 if ( isDefaultPreferredFormat )
1852 d->mWktPreferred = res;
1864 QDomNode srsNode = node.namedItem( QStringLiteral(
"spatialrefsys" ) );
1866 if ( ! srsNode.isNull() )
1868 bool initialized =
false;
1871 long srsid = srsNode.namedItem( QStringLiteral(
"srsid" ) ).toElement().text().toLong( &ok );
1877 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
1878 if ( !node.isNull() )
1889 node = srsNode.namedItem( QStringLiteral(
"epsg" ) );
1890 if ( !node.isNull() )
1908 const QString
description = srsNode.namedItem( QStringLiteral(
"description" ) ).toElement().text();
1910 const QString wkt = srsNode.namedItem( QStringLiteral(
"wkt" ) ).toElement().text();
1911 initialized = createFromWktInternal( wkt,
description );
1916 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
1917 const QString proj4 = node.toElement().text();
1924 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
1925 const QString proj4 = node.toElement().text();
1926 if ( !proj4.trimmed().isEmpty() )
1927 setProjString( node.toElement().text() );
1929 node = srsNode.namedItem( QStringLiteral(
"srsid" ) );
1930 d->mSrsId = node.toElement().text().toLong();
1932 node = srsNode.namedItem( QStringLiteral(
"srid" ) );
1933 d->mSRID = node.toElement().text().toLong();
1935 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
1936 d->mAuthId = node.toElement().text();
1938 node = srsNode.namedItem( QStringLiteral(
"description" ) );
1939 d->mDescription = node.toElement().text();
1941 node = srsNode.namedItem( QStringLiteral(
"projectionacronym" ) );
1942 d->mProjectionAcronym = node.toElement().text();
1944 node = srsNode.namedItem( QStringLiteral(
"ellipsoidacronym" ) );
1945 d->mEllipsoidAcronym = node.toElement().text();
1947 node = srsNode.namedItem( QStringLiteral(
"geographicflag" ) );
1948 d->mIsGeographic = node.toElement().text() == QLatin1String(
"true" );
1950 d->mWktPreferred.clear();
1956 const QString epoch = srsNode.toElement().attribute( QStringLiteral(
"coordinateEpoch" ) );
1957 if ( !epoch.isEmpty() )
1959 bool epochOk =
false;
1960 d->mCoordinateEpoch = epoch.toDouble( &epochOk );
1962 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
1966 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
1969 mNativeFormat = qgsEnumKeyToValue<Qgis::CrsDefinitionFormat>( srsNode.toElement().attribute( QStringLiteral(
"nativeFormat" ) ), Qgis::CrsDefinitionFormat::Wkt );
1974 d =
new QgsCoordinateReferenceSystemPrivate();
1982 QDomElement layerNode = node.toElement();
1983 QDomElement srsElement = doc.createElement( QStringLiteral(
"spatialrefsys" ) );
1985 srsElement.setAttribute( QStringLiteral(
"nativeFormat" ), qgsEnumValueToKey<Qgis::CrsDefinitionFormat>( mNativeFormat ) );
1987 if ( std::isfinite( d->mCoordinateEpoch ) )
1989 srsElement.setAttribute( QStringLiteral(
"coordinateEpoch" ), d->mCoordinateEpoch );
1992 QDomElement wktElement = doc.createElement( QStringLiteral(
"wkt" ) );
1994 srsElement.appendChild( wktElement );
1996 QDomElement proj4Element = doc.createElement( QStringLiteral(
"proj4" ) );
1997 proj4Element.appendChild( doc.createTextNode(
toProj() ) );
1998 srsElement.appendChild( proj4Element );
2000 QDomElement srsIdElement = doc.createElement( QStringLiteral(
"srsid" ) );
2001 srsIdElement.appendChild( doc.createTextNode( QString::number(
srsid() ) ) );
2002 srsElement.appendChild( srsIdElement );
2004 QDomElement sridElement = doc.createElement( QStringLiteral(
"srid" ) );
2005 sridElement.appendChild( doc.createTextNode( QString::number(
postgisSrid() ) ) );
2006 srsElement.appendChild( sridElement );
2008 QDomElement authidElement = doc.createElement( QStringLiteral(
"authid" ) );
2009 authidElement.appendChild( doc.createTextNode(
authid() ) );
2010 srsElement.appendChild( authidElement );
2012 QDomElement descriptionElement = doc.createElement( QStringLiteral(
"description" ) );
2013 descriptionElement.appendChild( doc.createTextNode(
description() ) );
2014 srsElement.appendChild( descriptionElement );
2016 QDomElement projectionAcronymElement = doc.createElement( QStringLiteral(
"projectionacronym" ) );
2017 projectionAcronymElement.appendChild( doc.createTextNode(
projectionAcronym() ) );
2018 srsElement.appendChild( projectionAcronymElement );
2020 QDomElement ellipsoidAcronymElement = doc.createElement( QStringLiteral(
"ellipsoidacronym" ) );
2021 ellipsoidAcronymElement.appendChild( doc.createTextNode(
ellipsoidAcronym() ) );
2022 srsElement.appendChild( ellipsoidAcronymElement );
2024 QDomElement geographicFlagElement = doc.createElement( QStringLiteral(
"geographicflag" ) );
2025 QString geoFlagText = QStringLiteral(
"false" );
2028 geoFlagText = QStringLiteral(
"true" );
2031 geographicFlagElement.appendChild( doc.createTextNode( geoFlagText ) );
2032 srsElement.appendChild( geographicFlagElement );
2034 layerNode.appendChild( srsElement );
2046 QString QgsCoordinateReferenceSystem::projFromSrsId(
const int srsId )
2048 QString myDatabaseFileName;
2049 QString myProjString;
2050 QString mySql = QStringLiteral(
"select parameters from tbl_srs where srs_id = %1 order by deprecated" ).arg( srsId );
2059 QFileInfo myFileInfo;
2060 myFileInfo.setFile( myDatabaseFileName );
2061 if ( !myFileInfo.exists() )
2063 QgsDebugMsg( QStringLiteral(
"users qgis.db not found" ) );
2076 rc = openDatabase( myDatabaseFileName, database );
2082 statement = database.
prepare( mySql, rc );
2084 if ( rc == SQLITE_OK )
2086 if ( statement.
step() == SQLITE_ROW )
2092 return myProjString;
2099 myResult = database.
open_v2( path, SQLITE_OPEN_READONLY,
nullptr );
2101 myResult = database.
open( path );
2103 if ( myResult != SQLITE_OK )
2112 .arg( database.
errorMessage() ), QObject::tr(
"CRS" ) );
2119 sCustomSrsValidation = f;
2124 return sCustomSrsValidation;
2127 void QgsCoordinateReferenceSystem::debugPrint()
2129 QgsDebugMsg( QStringLiteral(
"***SpatialRefSystem***" ) );
2130 QgsDebugMsg(
"* Valid : " + ( d->mIsValid ? QString(
"true" ) : QString(
"false" ) ) );
2131 QgsDebugMsg(
"* SrsId : " + QString::number( d->mSrsId ) );
2137 QgsDebugMsg( QStringLiteral(
"* Units : meters" ) );
2141 QgsDebugMsg( QStringLiteral(
"* Units : feet" ) );
2145 QgsDebugMsg( QStringLiteral(
"* Units : degrees" ) );
2151 mValidationHint = html;
2156 return mValidationHint;
2166 mNativeFormat = format;
2171 return mNativeFormat;
2174 long QgsCoordinateReferenceSystem::getRecordCount()
2179 long myRecordCount = 0;
2182 if ( myResult != SQLITE_OK )
2188 QString mySql = QStringLiteral(
"select count(*) from tbl_srs" );
2189 statement = database.
prepare( mySql, myResult );
2190 if ( myResult == SQLITE_OK )
2192 if ( statement.
step() == SQLITE_ROW )
2194 QString myRecordCountString = statement.
columnAsText( 0 );
2195 myRecordCount = myRecordCountString.toLong();
2198 return myRecordCount;
2204 bool isGeographic =
false;
2206 if ( coordinateSystem )
2208 const int axisCount = proj_cs_get_axis_count( pjContext, coordinateSystem.get() );
2209 if ( axisCount > 0 )
2211 const char *outUnitAuthName =
nullptr;
2212 const char *outUnitAuthCode =
nullptr;
2214 proj_cs_get_axis_info( pjContext, coordinateSystem.get(), 0,
2223 if ( outUnitAuthName && outUnitAuthCode )
2225 const char *unitCategory =
nullptr;
2226 if ( proj_uom_get_info_from_database( pjContext, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
2228 isGeographic = QString( unitCategory ).compare( QLatin1String(
"angular" ), Qt::CaseInsensitive ) == 0;
2233 return isGeographic;
2238 thread_local
const QRegularExpression projRegExp( QStringLiteral(
"\\+proj=(\\S+)" ) );
2239 const QRegularExpressionMatch projMatch = projRegExp.match( proj );
2240 if ( !projMatch.hasMatch() )
2242 QgsDebugMsgLevel( QStringLiteral(
"no +proj argument found [%2]" ).arg( proj ), 2 );
2245 operation = projMatch.captured( 1 );
2247 const QRegularExpressionMatch ellipseMatch = projRegExp.match( proj );
2248 if ( ellipseMatch.hasMatch() )
2250 ellipsoid = ellipseMatch.captured( 1 );
2264 bool QgsCoordinateReferenceSystem::loadFromAuthCode(
const QString &auth,
const QString &code )
2267 d->mIsValid =
false;
2268 d->mWktPreferred.clear();
2277 switch ( proj_get_type(
crs.get() ) )
2279 case PJ_TYPE_VERTICAL_CRS:
2289 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2290 proj4 = proj4.trimmed();
2294 d->mWktPreferred.clear();
2295 d->mDescription = QString( proj_get_name(
crs.get() ) );
2296 d->mAuthId = QStringLiteral(
"%1:%2" ).arg( auth, code );
2298 d->mAxisInvertedDirty =
true;
2303 d->mEllipsoidAcronym.clear();
2304 d->setPj( std::move(
crs ) );
2306 const QString dbVals =
sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( auth, code ).toUpper() );
2307 if ( !dbVals.isEmpty() )
2309 const QStringList parts = dbVals.split(
',' );
2310 d->mSrsId = parts.at( 0 ).toInt();
2311 d->mSRID = parts.at( 1 ).toInt();
2319 QList<long> QgsCoordinateReferenceSystem::userSrsIds()
2321 QList<long> results;
2325 QFileInfo myInfo( db );
2326 if ( !myInfo.exists() )
2328 QgsDebugMsg(
"failed : " + db +
" does not exist!" );
2336 int result = openDatabase( db, database );
2337 if ( result != SQLITE_OK )
2339 QgsDebugMsg(
"failed : " + db +
" could not be opened!" );
2343 QString sql = QStringLiteral(
"select srs_id from tbl_srs where srs_id >= %1" ).arg(
USER_CRS_START_ID );
2345 statement = database.
prepare( sql, rc );
2348 int ret = statement.
step();
2350 if ( ret == SQLITE_DONE )
2356 if ( ret == SQLITE_ROW )
2362 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
2370 long QgsCoordinateReferenceSystem::matchToUserCrs()
const
2372 PJ *obj = d->threadLocalProjObject();
2376 const QList< long > ids = userSrsIds();
2377 for (
long id : ids )
2380 if ( candidate.
projObject() && proj_is_equivalent_to( obj, candidate.
projObject(), PJ_COMP_EQUIVALENT ) )
2388 static void sync_db_proj_logger(
void * ,
int level,
const char *message )
2393 if ( level == PJ_LOG_ERROR )
2397 else if ( level == PJ_LOG_DEBUG )
2405 setlocale( LC_ALL,
"C" );
2408 int inserted = 0, updated = 0, deleted = 0, errors = 0;
2413 if ( database.
open( dbFilePath ) != SQLITE_OK )
2419 if ( sqlite3_exec( database.get(),
"BEGIN TRANSACTION",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2427 char *errMsg =
nullptr;
2429 if ( sqlite3_exec( database.get(),
"create table tbl_info (proj_major INT, proj_minor INT, proj_patch INT)",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2431 QString sql = QStringLiteral(
"INSERT INTO tbl_info(proj_major, proj_minor, proj_patch) VALUES (%1, %2,%3)" )
2432 .arg( QString::number( PROJ_VERSION_MAJOR ),
2433 QString::number( PROJ_VERSION_MINOR ),
2434 QString::number( PROJ_VERSION_PATCH ) );
2435 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2437 QgsDebugMsg( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2440 errMsg ? errMsg :
"(unknown error)" ) );
2442 sqlite3_free( errMsg );
2449 QString sql = QStringLiteral(
"SELECT proj_major, proj_minor, proj_patch FROM tbl_info" );
2450 statement = database.
prepare( sql, result );
2451 if ( result != SQLITE_OK )
2456 if ( statement.
step() == SQLITE_ROW )
2461 if ( major == PROJ_VERSION_MAJOR && minor == PROJ_VERSION_MINOR && patch == PROJ_VERSION_PATCH )
2467 QgsDebugMsg( QStringLiteral(
"Could not retrieve previous CRS sync PROJ version number" ) );
2474 proj_log_func( pjContext,
nullptr, sync_db_proj_logger );
2476 PROJ_STRING_LIST authorities = proj_get_authorities_from_database( pjContext );
2478 int nextSrsId = 63561;
2479 int nextSrId = 520003561;
2480 for (
auto authIter = authorities; authIter && *authIter; ++authIter )
2482 const QString authority( *authIter );
2483 QgsDebugMsgLevel( QStringLiteral(
"Loading authority '%1'" ).arg( authority ), 2 );
2484 PROJ_STRING_LIST codes = proj_get_codes_from_database( pjContext, *authIter, PJ_TYPE_CRS,
true );
2486 QStringList allCodes;
2488 for (
auto codesIter = codes; codesIter && *codesIter; ++codesIter )
2490 const QString code( *codesIter );
2496 QgsDebugMsg( QStringLiteral(
"Could not load '%1:%2'" ).arg( authority, code ) );
2500 switch ( proj_get_type(
crs.get() ) )
2502 case PJ_TYPE_VERTICAL_CRS:
2512 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2513 proj4 = proj4.trimmed();
2515 if ( proj4.isEmpty() )
2517 QgsDebugMsgLevel( QStringLiteral(
"No proj4 for '%1:%2'" ).arg( authority, code ), 2 );
2522 const bool deprecated = proj_is_deprecated(
crs.get() );
2523 const QString name( proj_get_name(
crs.get() ) );
2525 QString sql = QStringLiteral(
"SELECT parameters,description,deprecated FROM tbl_srs WHERE auth_name='%1' AND auth_id='%2'" ).arg( authority, code );
2526 statement = database.
prepare( sql, result );
2527 if ( result != SQLITE_OK )
2535 bool srsDeprecated = deprecated;
2536 if ( statement.
step() == SQLITE_ROW )
2540 srsDeprecated = statement.
columnAsText( 2 ).toInt() != 0;
2543 if ( !srsProj4.isEmpty() || !srsDesc.isEmpty() )
2545 if ( proj4 != srsProj4 || name != srsDesc || deprecated != srsDeprecated )
2548 sql = QStringLiteral(
"UPDATE tbl_srs SET parameters=%1,description=%2,deprecated=%3 WHERE auth_name=%4 AND auth_id=%5" )
2551 .arg( deprecated ? 1 : 0 )
2554 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2556 QgsDebugMsg( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2559 errMsg ? errMsg :
"(unknown error)" ) );
2561 sqlite3_free( errMsg );
2579 const QString dbVals =
sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( authority, code ) );
2582 if ( !dbVals.isEmpty() )
2584 const QStringList parts = dbVals.split(
',' );
2585 srsId = parts.at( 0 );
2586 srId = parts.at( 1 );
2588 if ( srId.isEmpty() )
2590 srId = QString::number( nextSrId );
2593 if ( srsId.isEmpty() )
2595 srsId = QString::number( nextSrsId );
2599 if ( !srsId.isEmpty() )
2601 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)" )
2611 .arg( deprecated ? 1 : 0 );
2615 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)" )
2624 .arg( deprecated ? 1 : 0 );
2628 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) == SQLITE_OK )
2634 qCritical(
"Could not execute: %s [%s/%s]\n",
2635 sql.toLocal8Bit().constData(),
2636 sqlite3_errmsg( database.get() ),
2637 errMsg ? errMsg :
"(unknown error)" );
2641 sqlite3_free( errMsg );
2646 proj_string_list_destroy( codes );
2648 const QString sql = QStringLiteral(
"DELETE FROM tbl_srs WHERE auth_name='%1' AND NOT auth_id IN (%2)" ).arg( authority, allCodes.join(
',' ) );
2649 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2651 deleted = sqlite3_changes( database.get() );
2656 qCritical(
"Could not execute: %s [%s]\n",
2657 sql.toLocal8Bit().constData(),
2658 sqlite3_errmsg( database.get() ) );
2662 proj_string_list_destroy( authorities );
2664 QString sql = QStringLiteral(
"UPDATE tbl_info set proj_major=%1,proj_minor=%2,proj_patch=%3" )
2665 .arg( QString::number( PROJ_VERSION_MAJOR ),
2666 QString::number( PROJ_VERSION_MINOR ),
2667 QString::number( PROJ_VERSION_PATCH ) );
2668 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2670 QgsDebugMsg( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2673 errMsg ? errMsg :
"(unknown error)" ) );
2675 sqlite3_free( errMsg );
2679 if ( sqlite3_exec( database.get(),
"COMMIT",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2681 QgsDebugMsg( QStringLiteral(
"Could not commit transaction: %1 [%2]\n" ).arg(
2683 sqlite3_errmsg( database.get() ) )
2689 QgsDebugMsgLevel( QStringLiteral(
"CRS update (inserted:%1 updated:%2 deleted:%3 errors:%4)" ).arg( inserted ).arg( updated ).arg( deleted ).arg( errors ), 4 );
2697 return updated + inserted;
2700 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::stringCache()
2702 return *sStringCache();
2705 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::projCache()
2707 return *sProj4Cache();
2710 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::ogcCache()
2712 return *sOgcCache();
2715 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::wktCache()
2717 return *sWktCache();
2720 const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srIdCache()
2722 return *sSrIdCache();
2725 const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srsIdCache()
2727 return *sSrsIdCache();
2737 if (
PJ *obj = d->threadLocalProjObject() )
2765 else if (
PJ *obj = d->threadLocalProjObject() )
2768 return geoCrs ? QStringLiteral(
"%1:%2" ).arg( proj_get_id_auth_name( geoCrs.get(), 0 ), proj_get_id_code( geoCrs.get(), 0 ) ) : QString();
2778 return d->threadLocalProjObject();
2791 d->mIsValid =
false;
2793 d->mWktPreferred.clear();
2800 switch ( proj_get_type(
object ) )
2802 case PJ_TYPE_GEODETIC_CRS:
2803 case PJ_TYPE_GEOCENTRIC_CRS:
2804 case PJ_TYPE_GEOGRAPHIC_CRS:
2805 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
2806 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
2807 case PJ_TYPE_VERTICAL_CRS:
2808 case PJ_TYPE_PROJECTED_CRS:
2809 case PJ_TYPE_COMPOUND_CRS:
2810 case PJ_TYPE_TEMPORAL_CRS:
2811 case PJ_TYPE_ENGINEERING_CRS:
2812 case PJ_TYPE_BOUND_CRS:
2813 case PJ_TYPE_OTHER_CRS:
2829 const QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
2830 const QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
2831 if ( !authName.isEmpty() && !authCode.isEmpty() && loadFromAuthCode( authName, authCode ) )
2839 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
2849 QStringList projections;
2851 projections.reserve( res.size() );
2854 projections << QString::number(
crs.
srsid() );
2861 QList<QgsCoordinateReferenceSystem> res;
2865 QStringList projectionsProj4 = settings.
value( QStringLiteral(
"UI/recentProjectionsProj4" ) ).toStringList();
2866 QStringList projectionsWkt = settings.
value( QStringLiteral(
"UI/recentProjectionsWkt" ) ).toStringList();
2867 QStringList projectionsAuthId = settings.
value( QStringLiteral(
"UI/recentProjectionsAuthId" ) ).toStringList();
2868 int max = std::max( projectionsAuthId.size(), std::max( projectionsProj4.size(), projectionsWkt.size() ) );
2870 for (
int i = 0; i < max; ++i )
2872 const QString proj = projectionsProj4.value( i );
2873 const QString wkt = projectionsWkt.value( i );
2874 const QString
authid = projectionsAuthId.value( i );
2897 recent.removeAll(
crs );
2898 recent.insert( 0,
crs );
2901 recent = recent.mid( 0, 30 );
2902 QStringList authids;
2903 authids.reserve( recent.size() );
2905 proj.reserve( recent.size() );
2907 wkt.reserve( recent.size() );
2910 authids <<
c.authid();
2916 settings.
setValue( QStringLiteral(
"UI/recentProjectionsAuthId" ), authids );
2917 settings.
setValue( QStringLiteral(
"UI/recentProjectionsWkt" ), wkt );
2918 settings.
setValue( QStringLiteral(
"UI/recentProjectionsProj4" ), proj );
2923 sSrIdCacheLock()->lockForWrite();
2924 if ( !sDisableSrIdCache )
2927 sDisableSrIdCache =
true;
2928 sSrIdCache()->clear();
2930 sSrIdCacheLock()->unlock();
2932 sOgcLock()->lockForWrite();
2933 if ( !sDisableOgcCache )
2936 sDisableOgcCache =
true;
2937 sOgcCache()->clear();
2939 sOgcLock()->unlock();
2941 sProj4CacheLock()->lockForWrite();
2942 if ( !sDisableProjCache )
2945 sDisableProjCache =
true;
2946 sProj4Cache()->clear();
2948 sProj4CacheLock()->unlock();
2950 sCRSWktLock()->lockForWrite();
2951 if ( !sDisableWktCache )
2954 sDisableWktCache =
true;
2955 sWktCache()->clear();
2957 sCRSWktLock()->unlock();
2959 sCRSSrsIdLock()->lockForWrite();
2960 if ( !sDisableSrsIdCache )
2963 sDisableSrsIdCache =
true;
2964 sSrsIdCache()->clear();
2966 sCRSSrsIdLock()->unlock();
2968 sCrsStringLock()->lockForWrite();
2969 if ( !sDisableStringCache )
2972 sDisableStringCache =
true;
2973 sStringCache()->clear();
2975 sCrsStringLock()->unlock();
2984 if ( !c1.d->mIsValid && !c2.d->mIsValid )
2987 if ( !c1.d->mIsValid && c2.d->mIsValid )
2990 if ( c1.d->mIsValid && !c2.d->mIsValid )
2996 if ( c1IsUser && !c2IsUser )
2999 if ( !c1IsUser && c2IsUser )
3002 if ( !c1IsUser && !c2IsUser )
3004 if ( c1.d->mAuthId != c2.d->mAuthId )
3005 return c1.d->mAuthId > c2.d->mAuthId;
3015 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3018 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3021 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3024 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3027 return c1.d->mCoordinateEpoch > c2.d->mCoordinateEpoch;
3035 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3038 if ( c1.d->mIsValid && !c2.d->mIsValid )
3041 if ( !c1.d->mIsValid && c2.d->mIsValid )
3047 if ( !c1IsUser && c2IsUser )
3050 if ( c1IsUser && !c2IsUser )
3053 if ( !c1IsUser && !c2IsUser )
3055 if ( c1.d->mAuthId != c2.d->mAuthId )
3056 return c1.d->mAuthId < c2.d->mAuthId;
3066 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3069 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3072 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3075 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3078 return c1.d->mCoordinateEpoch < c2.d->mCoordinateEpoch;
3083 return !( c1 < c2 );
3087 return !( c1 > c2 );