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 )
133 mValidationHint = srs.mValidationHint;
143 const auto constDbs = dbs;
144 for (
const QString &db : constDbs )
146 QFileInfo myInfo( db );
147 if ( !myInfo.exists() )
149 QgsDebugMsg(
"failed : " + db +
" does not exist!" );
157 int result = openDatabase( db, database );
158 if ( result != SQLITE_OK )
160 QgsDebugMsg(
"failed : " + db +
" could not be opened!" );
164 QString sql = QStringLiteral(
"select srs_id from tbl_srs" );
166 statement = database.
prepare( sql, rc );
170 int ret = statement.
step();
172 if ( ret == SQLITE_DONE )
178 if ( ret == SQLITE_ROW )
184 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
189 std::sort( results.begin(), results.end() );
262 QgsDebugMsg( QStringLiteral(
"Unexpected case reached!" ) );
269 if ( definition.isEmpty() )
273 if ( !sDisableStringCache )
275 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sStringCache()->constFind( definition );
276 if ( crsIt != sStringCache()->constEnd() )
279 *
this = crsIt.value();
286 const thread_local QRegularExpression reCrsId( QStringLiteral(
"^(epsg|esri|osgeo|ignf|zangi|iau2000|postgis|internal|user)\\:(\\w+)$" ), QRegularExpression::CaseInsensitiveOption );
287 QRegularExpressionMatch match = reCrsId.match( definition );
288 if ( match.capturedStart() == 0 )
290 QString authName = match.captured( 1 ).toLower();
291 if ( authName == QLatin1String(
"epsg" ) )
295 else if ( authName == QLatin1String(
"postgis" ) )
297 const long id = match.captured( 2 ).toLong();
302 else if ( authName == QLatin1String(
"esri" ) || authName == QLatin1String(
"osgeo" ) || authName == QLatin1String(
"ignf" ) || authName == QLatin1String(
"zangi" ) || authName == QLatin1String(
"iau2000" ) )
308 const long id = match.captured( 2 ).toLong();
316 const thread_local QRegularExpression reCrsStr( QStringLiteral(
"^(?:(wkt|proj4|proj)\\:)?(.+)$" ), QRegularExpression::CaseInsensitiveOption );
317 match = reCrsStr.match( definition );
318 if ( match.capturedStart() == 0 )
320 if ( match.captured( 1 ).startsWith( QLatin1String(
"proj" ), Qt::CaseInsensitive ) )
332 if ( !sDisableStringCache )
333 sStringCache()->insert( definition, *
this );
339 if ( definition.isEmpty() )
345 if ( OSRSetFromUserInput(
crs, definition.toLocal8Bit().constData() ) == OGRERR_NONE )
348 OSRDestroySpatialReference(
crs );
358 const char *configOld = CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" );
359 const char *configNew =
"GEOGCS";
361 if ( strcmp( configOld,
"" ) == 0 )
363 CPLSetConfigOption(
"GDAL_FIX_ESRI_WKT", configNew );
364 if ( strcmp( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) != 0 )
366 .arg( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) );
367 QgsDebugMsgLevel( QStringLiteral(
"set GDAL_FIX_ESRI_WKT : %1" ).arg( configNew ), 4 );
371 QgsDebugMsgLevel( QStringLiteral(
"GDAL_FIX_ESRI_WKT was already set : %1" ).arg( configNew ), 4 );
381 if ( !sDisableOgcCache )
383 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sOgcCache()->constFind(
crs );
384 if ( crsIt != sOgcCache()->constEnd() )
387 *
this = crsIt.value();
393 QString wmsCrs =
crs;
395 thread_local
const QRegularExpression re_uri( QRegularExpression::anchoredPattern( QStringLiteral(
"http://www\\.opengis\\.net/def/crs/([^/]+).+/([^/]+)" ) ), QRegularExpression::CaseInsensitiveOption );
396 QRegularExpressionMatch match = re_uri.match( wmsCrs );
397 if ( match.hasMatch() )
399 wmsCrs = match.captured( 1 ) +
':' + match.captured( 2 );
403 thread_local
const QRegularExpression re_urn( QRegularExpression::anchoredPattern( QStringLiteral(
"urn:ogc:def:crs:([^:]+).+(?<=:)([^:]+)" ) ), QRegularExpression::CaseInsensitiveOption );
404 match = re_urn.match( wmsCrs );
405 if ( match.hasMatch() )
407 wmsCrs = match.captured( 1 ) +
':' + match.captured( 2 );
411 thread_local
const QRegularExpression re_urn_custom( QRegularExpression::anchoredPattern( QStringLiteral(
"(user|custom|qgis):(\\d+)" ) ), QRegularExpression::CaseInsensitiveOption );
412 match = re_urn_custom.match( wmsCrs );
413 if ( match.hasMatch() &&
createFromSrsId( match.captured( 2 ).toInt() ) )
416 if ( !sDisableOgcCache )
417 sOgcCache()->insert(
crs, *
this );
424 const QString legacyKey = wmsCrs.toLower();
427 if ( it.key().compare( legacyKey, Qt::CaseInsensitive ) == 0 )
429 const QStringList parts = it.key().split(
':' );
430 const QString auth = parts.at( 0 );
431 const QString code = parts.at( 1 );
432 if ( loadFromAuthCode( auth, code ) )
435 if ( !sDisableOgcCache )
436 sOgcCache()->insert(
crs, *
this );
445 if ( !sDisableOgcCache )
446 sOgcCache()->insert(
crs, *
this );
451 if ( wmsCrs.compare( QLatin1String(
"CRS:27" ), Qt::CaseInsensitive ) == 0 ||
452 wmsCrs.compare( QLatin1String(
"OGC:CRS27" ), Qt::CaseInsensitive ) == 0 )
459 if ( wmsCrs.compare( QLatin1String(
"CRS:83" ), Qt::CaseInsensitive ) == 0 ||
460 wmsCrs.compare( QLatin1String(
"OGC:CRS83" ), Qt::CaseInsensitive ) == 0 )
467 if ( wmsCrs.compare( QLatin1String(
"CRS:84" ), Qt::CaseInsensitive ) == 0 ||
468 wmsCrs.compare( QLatin1String(
"OGC:CRS84" ), Qt::CaseInsensitive ) == 0 )
472 d->mAxisInverted =
false;
473 d->mAxisInvertedDirty =
false;
477 if ( !sDisableOgcCache )
478 sOgcCache()->insert(
crs, *
this );
484 if ( !sDisableOgcCache )
494 if ( d->mIsValid || !sCustomSrsValidation )
498 if ( sCustomSrsValidation )
499 sCustomSrsValidation( *
this );
505 if ( !sDisableSrIdCache )
507 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrIdCache()->constFind(
id );
508 if ( crsIt != sSrIdCache()->constEnd() )
511 *
this = crsIt.value();
520 if ( it.value().endsWith( QStringLiteral(
",%1" ).arg(
id ) ) )
522 const QStringList parts = it.key().split(
':' );
523 const QString auth = parts.at( 0 );
524 const QString code = parts.at( 1 );
525 if ( loadFromAuthCode( auth, code ) )
528 if ( !sDisableSrIdCache )
529 sSrIdCache()->insert(
id, *
this );
539 if ( !sDisableSrIdCache )
540 sSrIdCache()->insert(
id, *
this );
548 if ( !sDisableSrsIdCache )
550 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrsIdCache()->constFind(
id );
551 if ( crsIt != sSrsIdCache()->constEnd() )
554 *
this = crsIt.value();
563 if ( it.value().startsWith( QString::number(
id ) +
',' ) )
565 const QStringList parts = it.key().split(
':' );
566 const QString auth = parts.at( 0 );
567 const QString code = parts.at( 1 );
568 if ( loadFromAuthCode( auth, code ) )
571 if ( !sDisableSrsIdCache )
572 sSrsIdCache()->insert(
id, *
this );
580 QStringLiteral(
"srs_id" ), QString::number(
id ) );
583 if ( !sDisableSrsIdCache )
584 sSrsIdCache()->insert(
id, *
this );
588 bool QgsCoordinateReferenceSystem::loadFromDatabase(
const QString &db,
const QString &expression,
const QString &value )
592 QgsDebugMsgLevel(
"load CRS from " + db +
" where " + expression +
" is " + value, 3 );
594 d->mWktPreferred.clear();
596 QFileInfo myInfo( db );
597 if ( !myInfo.exists() )
599 QgsDebugMsg(
"failed : " + db +
" does not exist!" );
607 myResult = openDatabase( db, database );
608 if ( myResult != SQLITE_OK )
625 QString mySql =
"select srs_id,description,projection_acronym,"
626 "ellipsoid_acronym,parameters,srid,auth_name||':'||auth_id,is_geo,wkt "
628 statement = database.
prepare( mySql, myResult );
631 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
636 d->mEllipsoidAcronym.clear();
638 d->mWktPreferred.clear();
641 d->mIsGeographic = statement.
columnAsText( 7 ).toInt() != 0;
643 d->mAxisInvertedDirty =
true;
645 if ( d->mSrsId >=
USER_CRS_START_ID && ( d->mAuthId.isEmpty() || d->mAuthId == QChar(
':' ) ) )
647 d->mAuthId = QStringLiteral(
"USER:%1" ).arg( d->mSrsId );
649 else if ( !d->mAuthId.startsWith( QLatin1String(
"USER:" ), Qt::CaseInsensitive ) )
651 QStringList parts = d->mAuthId.split(
':' );
652 QString auth = parts.at( 0 );
653 QString code = parts.at( 1 );
660 d->mIsValid = d->hasPj();
666 if ( !wkt.isEmpty() )
674 setProjString( d->mProj4 );
684 void QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread(
PJ_CONTEXT *pj_context )
691 if ( !sDisableSrIdCache )
694 if ( !sDisableSrIdCache )
696 for (
auto it = sSrIdCache()->begin(); it != sSrIdCache()->end(); )
698 auto &v = it.value();
699 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
700 it = sSrIdCache()->erase( it );
706 if ( !sDisableOgcCache )
709 if ( !sDisableOgcCache )
711 for (
auto it = sOgcCache()->begin(); it != sOgcCache()->end(); )
713 auto &v = it.value();
714 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
715 it = sOgcCache()->erase( it );
721 if ( !sDisableProjCache )
724 if ( !sDisableProjCache )
726 for (
auto it = sProj4Cache()->begin(); it != sProj4Cache()->end(); )
728 auto &v = it.value();
729 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
730 it = sProj4Cache()->erase( it );
736 if ( !sDisableWktCache )
739 if ( !sDisableWktCache )
741 for (
auto it = sWktCache()->begin(); it != sWktCache()->end(); )
743 auto &v = it.value();
744 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
745 it = sWktCache()->erase( it );
751 if ( !sDisableSrsIdCache )
754 if ( !sDisableSrsIdCache )
756 for (
auto it = sSrsIdCache()->begin(); it != sSrsIdCache()->end(); )
758 auto &v = it.value();
759 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
760 it = sSrsIdCache()->erase( it );
766 if ( !sDisableStringCache )
769 if ( !sDisableStringCache )
771 for (
auto it = sStringCache()->begin(); it != sStringCache()->end(); )
773 auto &v = it.value();
774 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
775 it = sStringCache()->erase( it );
785 if ( d->mAxisInvertedDirty )
788 d->mAxisInvertedDirty =
false;
791 return d->mAxisInverted;
796 return createFromWktInternal( wkt, QString() );
799 bool QgsCoordinateReferenceSystem::createFromWktInternal(
const QString &wkt,
const QString &description )
807 if ( !sDisableWktCache )
809 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sWktCache()->constFind( wkt );
810 if ( crsIt != sWktCache()->constEnd() )
813 *
this = crsIt.value();
815 if ( !
description.isEmpty() && d->mDescription.isEmpty() )
820 sWktCache()->insert( wkt, *
this );
829 d->mWktPreferred.clear();
832 QgsDebugMsgLevel( QStringLiteral(
"theWkt is uninitialized, operation failed" ), 4 );
837 QgsCoordinateReferenceSystem::RecordMap record = getRecord(
"select * from tbl_srs where wkt=" +
QgsSqliteUtils::quotedString( wkt ) +
" order by deprecated" );
838 if ( !record.empty() )
840 long srsId = record[QStringLiteral(
"srs_id" )].toLong();
853 if ( d->mSrsId == 0 )
856 long id = matchToUserCrs();
865 if ( !sDisableWktCache )
866 sWktCache()->insert( wkt, *
this );
884 if ( projString.isEmpty() )
889 if ( projString.trimmed().isEmpty() )
893 d->mWktPreferred.clear();
898 if ( !sDisableProjCache )
900 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sProj4Cache()->constFind( projString );
901 if ( crsIt != sProj4Cache()->constEnd() )
904 *
this = crsIt.value();
918 QString myProj4String = projString.trimmed();
919 myProj4String.remove( QStringLiteral(
"+type=crs" ) );
920 myProj4String = myProj4String.trimmed();
923 d->mWktPreferred.clear();
928 const QString projCrsString = myProj4String + ( myProj4String.contains( QStringLiteral(
"+type=crs" ) ) ? QString() : QStringLiteral(
" +type=crs" ) );
936 const QString
authid = QStringLiteral(
"%1:%2" ).arg( authName, authCode );
940 if ( !sDisableProjCache )
941 sProj4Cache()->insert( projString, *
this );
948 QgsCoordinateReferenceSystem::RecordMap myRecord = getRecord(
"select * from tbl_srs where parameters=" +
QgsSqliteUtils::quotedString( myProj4String ) +
" order by deprecated" );
950 if ( !myRecord.empty() )
952 id = myRecord[QStringLiteral(
"srs_id" )].toLong();
961 setProjString( myProj4String );
964 id = matchToUserCrs();
973 setProjString( myProj4String );
977 if ( !sDisableProjCache )
978 sProj4Cache()->insert( projString, *
this );
984 QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord(
const QString &sql )
986 QString myDatabaseFileName;
987 QgsCoordinateReferenceSystem::RecordMap myMap;
989 QString myFieldValue;
996 QFileInfo myInfo( myDatabaseFileName );
997 if ( !myInfo.exists() )
999 QgsDebugMsg(
"failed : " + myDatabaseFileName +
" does not exist!" );
1004 myResult = openDatabase( myDatabaseFileName, database );
1005 if ( myResult != SQLITE_OK )
1010 statement = database.
prepare( sql, myResult );
1012 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1016 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1018 myFieldName = statement.
columnName( myColNo );
1020 myMap[myFieldName] = myFieldValue;
1022 if ( statement.
step() != SQLITE_DONE )
1024 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1033 if ( myMap.empty() )
1036 QFileInfo myFileInfo;
1037 myFileInfo.setFile( myDatabaseFileName );
1038 if ( !myFileInfo.exists() )
1040 QgsDebugMsg( QStringLiteral(
"user qgis.db not found" ) );
1045 myResult = openDatabase( myDatabaseFileName, database );
1046 if ( myResult != SQLITE_OK )
1051 statement = database.
prepare( sql, myResult );
1053 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1057 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1059 myFieldName = statement.
columnName( myColNo );
1061 myMap[myFieldName] = myFieldValue;
1064 if ( statement.
step() != SQLITE_DONE )
1066 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1097 if ( d->mDescription.isNull() )
1103 return d->mDescription;
1110 if ( !
authid().isEmpty() )
1120 id =
isValid() ? QObject::tr(
"Custom CRS" ) : QObject::tr(
"Unknown CRS" );
1122 id = QObject::tr(
"Custom CRS: %1" ).arg(
1125 else if ( !
toProj().isEmpty() )
1126 id = QObject::tr(
"Custom CRS: %1" ).arg( type ==
MediumString ? (
toProj().left( 50 ) + QString( QChar( 0x2026 ) ) )
1128 if ( !
id.isEmpty() && !std::isnan( d->mCoordinateEpoch ) )
1129 id += QStringLiteral(
" @ %1" ).arg( d->mCoordinateEpoch );
1136 if ( d->mProjectionAcronym.isNull() )
1142 return d->mProjectionAcronym;
1148 if ( d->mEllipsoidAcronym.isNull() )
1150 if (
PJ *obj = d->threadLocalProjObject() )
1155 const QString ellipsoidAuthName( proj_get_id_auth_name( ellipsoid.get(), 0 ) );
1156 const QString ellipsoidAuthCode( proj_get_id_code( ellipsoid.get(), 0 ) );
1157 if ( !ellipsoidAuthName.isEmpty() && !ellipsoidAuthCode.isEmpty() )
1158 d->mEllipsoidAcronym = QStringLiteral(
"%1:%2" ).arg( ellipsoidAuthName, ellipsoidAuthCode );
1161 double semiMajor, semiMinor, invFlattening;
1162 int semiMinorComputed = 0;
1163 if ( proj_ellipsoid_get_parameters(
QgsProjContext::get(), ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
1165 d->mEllipsoidAcronym = QStringLiteral(
"PARAMETER:%1:%2" ).arg(
qgsDoubleToString( semiMajor ),
1170 d->mEllipsoidAcronym.clear();
1175 return d->mEllipsoidAcronym;
1179 return d->mEllipsoidAcronym;
1193 if ( d->mProj4.isEmpty() )
1195 if (
PJ *obj = d->threadLocalProjObject() )
1201 return d->mProj4.trimmed();
1206 return d->mIsGeographic;
1224 #if PROJ_VERSION_MAJOR>8 || (PROJ_VERSION_MAJOR==8 && PROJ_VERSION_MINOR>=1)
1227 return QString( proj_get_celestial_body_name( context, pj ) );
1229 throw QgsNotSupportedException( QStringLiteral(
"Retrieving celestial body requires a QGIS build based on PROJ 8.1 or later" ) );
1235 if ( d->mCoordinateEpoch == epoch )
1241 d->mCoordinateEpoch = epoch;
1242 d->setPj( std::move( clone ) );
1247 return d->mCoordinateEpoch;
1259 #if PROJ_VERSION_MAJOR>=8
1267 res.mName = QString( proj_get_name( ensemble.get() ) );
1268 res.mAuthority = QString( proj_get_id_auth_name( ensemble.get(), 0 ) );
1269 res.mCode = QString( proj_get_id_code( ensemble.get(), 0 ) );
1270 res.mRemarks = QString( proj_get_remarks( ensemble.get() ) );
1271 res.mScope = QString( proj_get_scope( ensemble.get() ) );
1272 res.mAccuracy = proj_datum_ensemble_get_accuracy( context, ensemble.get() );
1274 const int memberCount = proj_datum_ensemble_get_member_count( context, ensemble.get() );
1275 for (
int i = 0; i < memberCount; ++i )
1282 details.mName = QString( proj_get_name( member.get() ) );
1283 details.mAuthority = QString( proj_get_id_auth_name( member.get(), 0 ) );
1284 details.mCode = QString( proj_get_id_code( member.get(), 0 ) );
1285 details.mRemarks = QString( proj_get_remarks( member.get() ) );
1286 details.mScope = QString( proj_get_scope( member.get() ) );
1288 res.mMembers << details;
1292 throw QgsNotSupportedException( QStringLiteral(
"Calculating datum ensembles requires a QGIS build based on PROJ 8.0 or later" ) );
1301 QString projString =
toProj();
1302 projString.replace( QLatin1String(
"+type=crs" ), QString() );
1305 if ( !transformation )
1308 PJ_COORD coord = proj_coord( 0, 0, 0, HUGE_VAL );
1309 coord.uv.u = point.
x() * M_PI / 180.0;
1310 coord.uv.v = point.
y() * M_PI / 180.0;
1312 proj_errno_reset( transformation.get() );
1313 const PJ_FACTORS pjFactors = proj_factors( transformation.get(), coord );
1314 if ( proj_errno( transformation.get() ) )
1319 res.mIsValid =
true;
1320 res.mMeridionalScale = pjFactors.meridional_scale;
1321 res.mParallelScale = pjFactors.parallel_scale;
1322 res.mArealScale = pjFactors.areal_scale;
1323 res.mAngularDistortion = pjFactors.angular_distortion;
1324 res.mMeridianParallelAngle = pjFactors.meridian_parallel_angle * 180 / M_PI;
1325 res.mMeridianConvergence = pjFactors.meridian_convergence * 180 / M_PI;
1326 res.mTissotSemimajor = pjFactors.tissot_semimajor;
1327 res.mTissotSemiminor = pjFactors.tissot_semiminor;
1328 res.mDxDlam = pjFactors.dx_dlam;
1329 res.mDxDphi = pjFactors.dx_dphi;
1330 res.mDyDlam = pjFactors.dy_dlam;
1331 res.mDyDphi = pjFactors.dy_dphi;
1340 QString projString =
toProj();
1341 projString.replace( QLatin1String(
"+type=crs" ), QString() );
1344 if ( !transformation )
1347 PJ_PROJ_INFO info = proj_pj_info( transformation.get() );
1362 return d->mMapUnits;
1370 PJ *obj = d->threadLocalProjObject();
1375 double southLat = 0;
1377 double northLat = 0;
1380 &westLon, &southLat, &eastLon, &northLat,
nullptr ) )
1409 void QgsCoordinateReferenceSystem::setProjString(
const QString &proj4String )
1412 d->mProj4 = proj4String;
1413 d->mWktPreferred.clear();
1416 QString trimmed = proj4String.trimmed();
1418 trimmed += QLatin1String(
" +type=crs" );
1428 const int errNo = proj_context_errno( ctx );
1429 QgsDebugMsg( QStringLiteral(
"proj string rejected: %1" ).arg( proj_errno_string( errNo ) ) );
1431 d->mIsValid =
false;
1435 d->mEllipsoidAcronym.clear();
1442 bool QgsCoordinateReferenceSystem::setWktString(
const QString &wkt )
1445 d->mIsValid =
false;
1446 d->mWktPreferred.clear();
1448 PROJ_STRING_LIST warnings =
nullptr;
1449 PROJ_STRING_LIST grammerErrors =
nullptr;
1457 QgsDebugMsg( QStringLiteral(
"\n---------------------------------------------------------------" ) );
1458 QgsDebugMsg( QStringLiteral(
"This CRS could *** NOT *** be set from the supplied Wkt " ) );
1460 for (
auto iter = warnings; iter && *iter; ++iter )
1462 for (
auto iter = grammerErrors; iter && *iter; ++iter )
1464 QgsDebugMsg( QStringLiteral(
"---------------------------------------------------------------\n" ) );
1466 proj_string_list_destroy( warnings );
1467 proj_string_list_destroy( grammerErrors );
1473 if ( !sDisableWktCache )
1474 sWktCache()->insert( wkt, *
this );
1481 QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
1482 QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
1484 if ( authName.isEmpty() || authCode.isEmpty() )
1490 if ( !authName.isEmpty() && !authCode.isEmpty() )
1492 if ( loadFromAuthCode( authName, authCode ) )
1495 if ( !sDisableWktCache )
1496 sWktCache()->insert( wkt, *
this );
1504 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
1512 void QgsCoordinateReferenceSystem::setMapUnits()
1529 if ( !coordinateSystem )
1535 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
1536 if ( axisCount > 0 )
1538 const char *outUnitName =
nullptr;
1540 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
1549 const QString unitName( outUnitName );
1553 if ( unitName.compare( QLatin1String(
"degree" ), Qt::CaseInsensitive ) == 0 ||
1554 unitName.compare( QLatin1String(
"degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1555 unitName.compare( QLatin1String(
"degree minute second hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1556 unitName.compare( QLatin1String(
"degree minute" ), Qt::CaseInsensitive ) == 0 ||
1557 unitName.compare( QLatin1String(
"degree hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1558 unitName.compare( QLatin1String(
"degree minute hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1559 unitName.compare( QLatin1String(
"hemisphere degree" ), Qt::CaseInsensitive ) == 0 ||
1560 unitName.compare( QLatin1String(
"hemisphere degree minute" ), Qt::CaseInsensitive ) == 0 ||
1561 unitName.compare( QLatin1String(
"hemisphere degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1562 unitName.compare( QLatin1String(
"degree (supplier to define representation)" ), Qt::CaseInsensitive ) == 0 )
1564 else if ( unitName.compare( QLatin1String(
"metre" ), Qt::CaseInsensitive ) == 0
1565 || unitName.compare( QLatin1String(
"m" ), Qt::CaseInsensitive ) == 0
1566 || unitName.compare( QLatin1String(
"meter" ), Qt::CaseInsensitive ) == 0 )
1569 else if ( unitName.compare( QLatin1String(
"US survey foot" ), Qt::CaseInsensitive ) == 0 ||
1570 unitName.compare( QLatin1String(
"foot" ), Qt::CaseInsensitive ) == 0 )
1572 else if ( unitName.compare( QLatin1String(
"kilometre" ), Qt::CaseInsensitive ) == 0 )
1574 else if ( unitName.compare( QLatin1String(
"centimetre" ), Qt::CaseInsensitive ) == 0 )
1576 else if ( unitName.compare( QLatin1String(
"millimetre" ), Qt::CaseInsensitive ) == 0 )
1578 else if ( unitName.compare( QLatin1String(
"Statute mile" ), Qt::CaseInsensitive ) == 0 )
1580 else if ( unitName.compare( QLatin1String(
"nautical mile" ), Qt::CaseInsensitive ) == 0 )
1582 else if ( unitName.compare( QLatin1String(
"yard" ), Qt::CaseInsensitive ) == 0 )
1599 if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull()
1602 QgsDebugMsgLevel(
"QgsCoordinateReferenceSystem::findMatchingProj will only "
1603 "work if prj acr ellipsoid acr and proj4string are set"
1604 " and the current projection is valid!", 4 );
1614 QString mySql = QString(
"select srs_id,parameters from tbl_srs where "
1615 "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated" )
1622 myResult = openDatabase( myDatabaseFileName, database );
1623 if ( myResult != SQLITE_OK )
1628 statement = database.
prepare( mySql, myResult );
1629 if ( myResult == SQLITE_OK )
1632 while ( statement.
step() == SQLITE_ROW )
1636 if (
toProj() == myProj4String.trimmed() )
1638 return mySrsId.toLong();
1649 myResult = openDatabase( myDatabaseFileName, database );
1650 if ( myResult != SQLITE_OK )
1655 statement = database.
prepare( mySql, myResult );
1657 if ( myResult == SQLITE_OK )
1659 while ( statement.
step() == SQLITE_ROW )
1663 if (
toProj() == myProj4String.trimmed() )
1665 return mySrsId.toLong();
1679 if ( !d->mIsValid && !srs.d->mIsValid )
1682 if ( !d->mIsValid || !srs.d->mIsValid )
1690 if ( isUser != otherIsUser )
1694 if ( !isUser && ( !d->mAuthId.isEmpty() || !srs.d->mAuthId.isEmpty() ) )
1695 return d->mAuthId == srs.d->mAuthId;
1702 return !( *
this == srs );
1707 if (
PJ *obj = d->threadLocalProjObject() )
1709 const bool isDefaultPreferredFormat = variant ==
WKT_PREFERRED && !multiline;
1710 if ( isDefaultPreferredFormat && !d->mWktPreferred.isEmpty() )
1713 return d->mWktPreferred;
1716 PJ_WKT_TYPE type = PJ_WKT1_GDAL;
1720 type = PJ_WKT1_GDAL;
1723 type = PJ_WKT1_ESRI;
1726 type = PJ_WKT2_2015;
1729 type = PJ_WKT2_2015_SIMPLIFIED;
1732 type = PJ_WKT2_2019;
1735 type = PJ_WKT2_2019_SIMPLIFIED;
1739 const QByteArray multiLineOption = QStringLiteral(
"MULTILINE=%1" ).arg( multiline ? QStringLiteral(
"YES" ) : QStringLiteral(
"NO" ) ).toLocal8Bit();
1740 const QByteArray indentatationWidthOption = QStringLiteral(
"INDENTATION_WIDTH=%1" ).arg( multiline ? QString::number( indentationWidth ) : QStringLiteral(
"0" ) ).toLocal8Bit();
1741 const char *
const options[] = {multiLineOption.constData(), indentatationWidthOption.constData(),
nullptr};
1744 if ( isDefaultPreferredFormat )
1747 d->mWktPreferred = res;
1759 QDomNode srsNode = node.namedItem( QStringLiteral(
"spatialrefsys" ) );
1761 if ( ! srsNode.isNull() )
1763 bool initialized =
false;
1766 long srsid = srsNode.namedItem( QStringLiteral(
"srsid" ) ).toElement().text().toLong( &ok );
1772 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
1773 if ( !node.isNull() )
1784 node = srsNode.namedItem( QStringLiteral(
"epsg" ) );
1785 if ( !node.isNull() )
1803 const QString
description = srsNode.namedItem( QStringLiteral(
"description" ) ).toElement().text();
1805 const QString wkt = srsNode.namedItem( QStringLiteral(
"wkt" ) ).toElement().text();
1806 initialized = createFromWktInternal( wkt,
description );
1811 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
1812 const QString proj4 = node.toElement().text();
1819 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
1820 const QString proj4 = node.toElement().text();
1821 if ( !proj4.trimmed().isEmpty() )
1822 setProjString( node.toElement().text() );
1824 node = srsNode.namedItem( QStringLiteral(
"srsid" ) );
1825 d->mSrsId = node.toElement().text().toLong();
1827 node = srsNode.namedItem( QStringLiteral(
"srid" ) );
1828 d->mSRID = node.toElement().text().toLong();
1830 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
1831 d->mAuthId = node.toElement().text();
1833 node = srsNode.namedItem( QStringLiteral(
"description" ) );
1834 d->mDescription = node.toElement().text();
1836 node = srsNode.namedItem( QStringLiteral(
"projectionacronym" ) );
1837 d->mProjectionAcronym = node.toElement().text();
1839 node = srsNode.namedItem( QStringLiteral(
"ellipsoidacronym" ) );
1840 d->mEllipsoidAcronym = node.toElement().text();
1842 node = srsNode.namedItem( QStringLiteral(
"geographicflag" ) );
1843 d->mIsGeographic = node.toElement().text() == QLatin1String(
"true" );
1845 d->mWktPreferred.clear();
1851 const QString epoch = srsNode.toElement().attribute( QStringLiteral(
"coordinateEpoch" ) );
1852 if ( !epoch.isEmpty() )
1854 bool epochOk =
false;
1855 d->mCoordinateEpoch = epoch.toDouble( &epochOk );
1857 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
1861 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
1867 d =
new QgsCoordinateReferenceSystemPrivate();
1875 QDomElement layerNode = node.toElement();
1876 QDomElement srsElement = doc.createElement( QStringLiteral(
"spatialrefsys" ) );
1878 if ( std::isfinite( d->mCoordinateEpoch ) )
1880 srsElement.setAttribute( QStringLiteral(
"coordinateEpoch" ), d->mCoordinateEpoch );
1883 QDomElement wktElement = doc.createElement( QStringLiteral(
"wkt" ) );
1885 srsElement.appendChild( wktElement );
1887 QDomElement proj4Element = doc.createElement( QStringLiteral(
"proj4" ) );
1888 proj4Element.appendChild( doc.createTextNode(
toProj() ) );
1889 srsElement.appendChild( proj4Element );
1891 QDomElement srsIdElement = doc.createElement( QStringLiteral(
"srsid" ) );
1892 srsIdElement.appendChild( doc.createTextNode( QString::number(
srsid() ) ) );
1893 srsElement.appendChild( srsIdElement );
1895 QDomElement sridElement = doc.createElement( QStringLiteral(
"srid" ) );
1896 sridElement.appendChild( doc.createTextNode( QString::number(
postgisSrid() ) ) );
1897 srsElement.appendChild( sridElement );
1899 QDomElement authidElement = doc.createElement( QStringLiteral(
"authid" ) );
1900 authidElement.appendChild( doc.createTextNode(
authid() ) );
1901 srsElement.appendChild( authidElement );
1903 QDomElement descriptionElement = doc.createElement( QStringLiteral(
"description" ) );
1904 descriptionElement.appendChild( doc.createTextNode(
description() ) );
1905 srsElement.appendChild( descriptionElement );
1907 QDomElement projectionAcronymElement = doc.createElement( QStringLiteral(
"projectionacronym" ) );
1908 projectionAcronymElement.appendChild( doc.createTextNode(
projectionAcronym() ) );
1909 srsElement.appendChild( projectionAcronymElement );
1911 QDomElement ellipsoidAcronymElement = doc.createElement( QStringLiteral(
"ellipsoidacronym" ) );
1912 ellipsoidAcronymElement.appendChild( doc.createTextNode(
ellipsoidAcronym() ) );
1913 srsElement.appendChild( ellipsoidAcronymElement );
1915 QDomElement geographicFlagElement = doc.createElement( QStringLiteral(
"geographicflag" ) );
1916 QString geoFlagText = QStringLiteral(
"false" );
1919 geoFlagText = QStringLiteral(
"true" );
1922 geographicFlagElement.appendChild( doc.createTextNode( geoFlagText ) );
1923 srsElement.appendChild( geographicFlagElement );
1925 layerNode.appendChild( srsElement );
1937 QString QgsCoordinateReferenceSystem::projFromSrsId(
const int srsId )
1939 QString myDatabaseFileName;
1940 QString myProjString;
1941 QString mySql = QStringLiteral(
"select parameters from tbl_srs where srs_id = %1 order by deprecated" ).arg( srsId );
1950 QFileInfo myFileInfo;
1951 myFileInfo.setFile( myDatabaseFileName );
1952 if ( !myFileInfo.exists() )
1954 QgsDebugMsg( QStringLiteral(
"users qgis.db not found" ) );
1967 rc = openDatabase( myDatabaseFileName, database );
1973 statement = database.
prepare( mySql, rc );
1975 if ( rc == SQLITE_OK )
1977 if ( statement.
step() == SQLITE_ROW )
1983 return myProjString;
1990 myResult = database.
open_v2( path, SQLITE_OPEN_READONLY,
nullptr );
1992 myResult = database.
open( path );
1994 if ( myResult != SQLITE_OK )
2003 .arg( database.
errorMessage() ), QObject::tr(
"CRS" ) );
2010 sCustomSrsValidation = f;
2015 return sCustomSrsValidation;
2018 void QgsCoordinateReferenceSystem::debugPrint()
2020 QgsDebugMsg( QStringLiteral(
"***SpatialRefSystem***" ) );
2021 QgsDebugMsg(
"* Valid : " + ( d->mIsValid ? QString(
"true" ) : QString(
"false" ) ) );
2022 QgsDebugMsg(
"* SrsId : " + QString::number( d->mSrsId ) );
2028 QgsDebugMsg( QStringLiteral(
"* Units : meters" ) );
2032 QgsDebugMsg( QStringLiteral(
"* Units : feet" ) );
2036 QgsDebugMsg( QStringLiteral(
"* Units : degrees" ) );
2042 mValidationHint = html;
2047 return mValidationHint;
2055 long QgsCoordinateReferenceSystem::getRecordCount()
2060 long myRecordCount = 0;
2063 if ( myResult != SQLITE_OK )
2069 QString mySql = QStringLiteral(
"select count(*) from tbl_srs" );
2070 statement = database.
prepare( mySql, myResult );
2071 if ( myResult == SQLITE_OK )
2073 if ( statement.
step() == SQLITE_ROW )
2075 QString myRecordCountString = statement.
columnAsText( 0 );
2076 myRecordCount = myRecordCountString.toLong();
2079 return myRecordCount;
2085 bool isGeographic =
false;
2087 if ( coordinateSystem )
2089 const int axisCount = proj_cs_get_axis_count( pjContext, coordinateSystem.get() );
2090 if ( axisCount > 0 )
2092 const char *outUnitAuthName =
nullptr;
2093 const char *outUnitAuthCode =
nullptr;
2095 proj_cs_get_axis_info( pjContext, coordinateSystem.get(), 0,
2104 if ( outUnitAuthName && outUnitAuthCode )
2106 const char *unitCategory =
nullptr;
2107 if ( proj_uom_get_info_from_database( pjContext, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
2109 isGeographic = QString( unitCategory ).compare( QLatin1String(
"angular" ), Qt::CaseInsensitive ) == 0;
2114 return isGeographic;
2119 thread_local
const QRegularExpression projRegExp( QStringLiteral(
"\\+proj=(\\S+)" ) );
2120 const QRegularExpressionMatch projMatch = projRegExp.match( proj );
2121 if ( !projMatch.hasMatch() )
2123 QgsDebugMsgLevel( QStringLiteral(
"no +proj argument found [%2]" ).arg( proj ), 2 );
2126 operation = projMatch.captured( 1 );
2128 thread_local
const QRegularExpression ellipseRegExp( QStringLiteral(
"\\+(?:ellps|datum)=(\\S+)" ) );
2129 const QRegularExpressionMatch ellipseMatch = projRegExp.match( proj );
2130 if ( ellipseMatch.hasMatch() )
2132 ellipsoid = ellipseMatch.captured( 1 );
2146 bool QgsCoordinateReferenceSystem::loadFromAuthCode(
const QString &auth,
const QString &code )
2149 d->mIsValid =
false;
2150 d->mWktPreferred.clear();
2159 switch ( proj_get_type(
crs.get() ) )
2161 case PJ_TYPE_VERTICAL_CRS:
2171 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2172 proj4 = proj4.trimmed();
2176 d->mWktPreferred.clear();
2177 d->mDescription = QString( proj_get_name(
crs.get() ) );
2178 d->mAuthId = QStringLiteral(
"%1:%2" ).arg( auth, code );
2180 d->mAxisInvertedDirty =
true;
2185 d->mEllipsoidAcronym.clear();
2186 d->setPj( std::move(
crs ) );
2188 const QString dbVals =
sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( auth, code ).toUpper() );
2191 if ( !dbVals.isEmpty() )
2193 const QStringList parts = dbVals.split(
',' );
2194 d->mSrsId = parts.at( 0 ).toInt();
2195 d->mSRID = parts.at( 1 ).toInt();
2203 QList<long> QgsCoordinateReferenceSystem::userSrsIds()
2205 QList<long> results;
2209 QFileInfo myInfo( db );
2210 if ( !myInfo.exists() )
2212 QgsDebugMsg(
"failed : " + db +
" does not exist!" );
2220 int result = openDatabase( db, database );
2221 if ( result != SQLITE_OK )
2223 QgsDebugMsg(
"failed : " + db +
" could not be opened!" );
2227 QString sql = QStringLiteral(
"select srs_id from tbl_srs where srs_id >= %1" ).arg(
USER_CRS_START_ID );
2229 statement = database.
prepare( sql, rc );
2232 int ret = statement.
step();
2234 if ( ret == SQLITE_DONE )
2240 if ( ret == SQLITE_ROW )
2246 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
2254 long QgsCoordinateReferenceSystem::matchToUserCrs()
const
2256 PJ *obj = d->threadLocalProjObject();
2260 const QList< long > ids = userSrsIds();
2261 for (
long id : ids )
2264 if ( candidate.
projObject() && proj_is_equivalent_to( obj, candidate.
projObject(), PJ_COMP_EQUIVALENT ) )
2272 static void sync_db_proj_logger(
void * ,
int level,
const char *message )
2277 if ( level == PJ_LOG_ERROR )
2281 else if ( level == PJ_LOG_DEBUG )
2289 setlocale( LC_ALL,
"C" );
2292 int inserted = 0, updated = 0, deleted = 0, errors = 0;
2297 if ( database.
open( dbFilePath ) != SQLITE_OK )
2303 if ( sqlite3_exec( database.get(),
"BEGIN TRANSACTION",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2311 char *errMsg =
nullptr;
2313 if ( sqlite3_exec( database.get(),
"create table tbl_info (proj_major INT, proj_minor INT, proj_patch INT)",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2315 QString sql = QStringLiteral(
"INSERT INTO tbl_info(proj_major, proj_minor, proj_patch) VALUES (%1, %2,%3)" )
2316 .arg( QString::number( PROJ_VERSION_MAJOR ),
2317 QString::number( PROJ_VERSION_MINOR ),
2318 QString::number( PROJ_VERSION_PATCH ) );
2319 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2321 QgsDebugMsg( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2324 errMsg ? errMsg :
"(unknown error)" ) );
2326 sqlite3_free( errMsg );
2333 QString sql = QStringLiteral(
"SELECT proj_major, proj_minor, proj_patch FROM tbl_info" );
2334 statement = database.
prepare( sql, result );
2335 if ( result != SQLITE_OK )
2340 if ( statement.
step() == SQLITE_ROW )
2345 if ( major == PROJ_VERSION_MAJOR && minor == PROJ_VERSION_MINOR && patch == PROJ_VERSION_PATCH )
2351 QgsDebugMsg( QStringLiteral(
"Could not retrieve previous CRS sync PROJ version number" ) );
2358 proj_log_func( pjContext,
nullptr, sync_db_proj_logger );
2360 PROJ_STRING_LIST authorities = proj_get_authorities_from_database( pjContext );
2362 int nextSrsId = 63561;
2363 int nextSrId = 520003561;
2364 for (
auto authIter = authorities; authIter && *authIter; ++authIter )
2366 const QString authority( *authIter );
2367 QgsDebugMsgLevel( QStringLiteral(
"Loading authority '%1'" ).arg( authority ), 2 );
2368 PROJ_STRING_LIST codes = proj_get_codes_from_database( pjContext, *authIter, PJ_TYPE_CRS,
true );
2370 QStringList allCodes;
2372 for (
auto codesIter = codes; codesIter && *codesIter; ++codesIter )
2374 const QString code( *codesIter );
2380 QgsDebugMsg( QStringLiteral(
"Could not load '%1:%2'" ).arg( authority, code ) );
2384 switch ( proj_get_type(
crs.get() ) )
2386 case PJ_TYPE_VERTICAL_CRS:
2396 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2397 proj4 = proj4.trimmed();
2399 if ( proj4.isEmpty() )
2401 QgsDebugMsgLevel( QStringLiteral(
"No proj4 for '%1:%2'" ).arg( authority, code ), 2 );
2406 const bool deprecated = proj_is_deprecated(
crs.get() );
2407 const QString name( proj_get_name(
crs.get() ) );
2409 QString sql = QStringLiteral(
"SELECT parameters,description,deprecated FROM tbl_srs WHERE auth_name='%1' AND auth_id='%2'" ).arg( authority, code );
2410 statement = database.
prepare( sql, result );
2411 if ( result != SQLITE_OK )
2419 bool srsDeprecated = deprecated;
2420 if ( statement.
step() == SQLITE_ROW )
2424 srsDeprecated = statement.
columnAsText( 2 ).toInt() != 0;
2427 if ( !srsProj4.isEmpty() || !srsDesc.isEmpty() )
2429 if ( proj4 != srsProj4 || name != srsDesc || deprecated != srsDeprecated )
2432 sql = QStringLiteral(
"UPDATE tbl_srs SET parameters=%1,description=%2,deprecated=%3 WHERE auth_name=%4 AND auth_id=%5" )
2435 .arg( deprecated ? 1 : 0 )
2438 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2440 QgsDebugMsg( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2443 errMsg ? errMsg :
"(unknown error)" ) );
2445 sqlite3_free( errMsg );
2463 const QString dbVals =
sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( authority, code ) );
2466 if ( !dbVals.isEmpty() )
2468 const QStringList parts = dbVals.split(
',' );
2469 srsId = parts.at( 0 );
2470 srId = parts.at( 1 );
2472 if ( srId.isEmpty() )
2474 srId = QString::number( nextSrId );
2477 if ( srsId.isEmpty() )
2479 srsId = QString::number( nextSrsId );
2483 if ( !srsId.isEmpty() )
2485 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)" )
2495 .arg( deprecated ? 1 : 0 );
2499 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)" )
2508 .arg( deprecated ? 1 : 0 );
2512 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) == SQLITE_OK )
2518 qCritical(
"Could not execute: %s [%s/%s]\n",
2519 sql.toLocal8Bit().constData(),
2520 sqlite3_errmsg( database.get() ),
2521 errMsg ? errMsg :
"(unknown error)" );
2525 sqlite3_free( errMsg );
2530 proj_string_list_destroy( codes );
2532 const QString sql = QStringLiteral(
"DELETE FROM tbl_srs WHERE auth_name='%1' AND NOT auth_id IN (%2)" ).arg( authority, allCodes.join(
',' ) );
2533 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2535 deleted = sqlite3_changes( database.get() );
2540 qCritical(
"Could not execute: %s [%s]\n",
2541 sql.toLocal8Bit().constData(),
2542 sqlite3_errmsg( database.get() ) );
2546 proj_string_list_destroy( authorities );
2548 QString sql = QStringLiteral(
"UPDATE tbl_info set proj_major=%1,proj_minor=%2,proj_patch=%3" )
2549 .arg( QString::number( PROJ_VERSION_MAJOR ),
2550 QString::number( PROJ_VERSION_MINOR ),
2551 QString::number( PROJ_VERSION_PATCH ) );
2552 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2554 QgsDebugMsg( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2557 errMsg ? errMsg :
"(unknown error)" ) );
2559 sqlite3_free( errMsg );
2563 if ( sqlite3_exec( database.get(),
"COMMIT",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2565 QgsDebugMsg( QStringLiteral(
"Could not commit transaction: %1 [%2]\n" ).arg(
2567 sqlite3_errmsg( database.get() ) )
2573 QgsDebugMsgLevel( QStringLiteral(
"CRS update (inserted:%1 updated:%2 deleted:%3 errors:%4)" ).arg( inserted ).arg( updated ).arg( deleted ).arg( errors ), 4 );
2581 return updated + inserted;
2584 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::stringCache()
2586 return *sStringCache();
2589 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::projCache()
2591 return *sProj4Cache();
2594 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::ogcCache()
2596 return *sOgcCache();
2599 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::wktCache()
2601 return *sWktCache();
2604 const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srIdCache()
2606 return *sSrIdCache();
2609 const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srsIdCache()
2611 return *sSrsIdCache();
2620 else if (
PJ *obj = d->threadLocalProjObject() )
2623 return geoCrs ? QStringLiteral(
"%1:%2" ).arg( proj_get_id_auth_name( geoCrs.get(), 0 ), proj_get_id_code( geoCrs.get(), 0 ) ) : QString();
2633 return d->threadLocalProjObject();
2638 QStringList projections;
2640 projections.reserve( res.size() );
2643 projections << QString::number(
crs.
srsid() );
2650 QList<QgsCoordinateReferenceSystem> res;
2654 QStringList projectionsProj4 = settings.
value( QStringLiteral(
"UI/recentProjectionsProj4" ) ).toStringList();
2655 QStringList projectionsWkt = settings.
value( QStringLiteral(
"UI/recentProjectionsWkt" ) ).toStringList();
2656 QStringList projectionsAuthId = settings.
value( QStringLiteral(
"UI/recentProjectionsAuthId" ) ).toStringList();
2657 int max = std::max( projectionsAuthId.size(), std::max( projectionsProj4.size(), projectionsWkt.size() ) );
2659 for (
int i = 0; i < max; ++i )
2661 const QString proj = projectionsProj4.value( i );
2662 const QString wkt = projectionsWkt.value( i );
2663 const QString
authid = projectionsAuthId.value( i );
2686 recent.removeAll(
crs );
2687 recent.insert( 0,
crs );
2690 recent = recent.mid( 0, 30 );
2691 QStringList authids;
2692 authids.reserve( recent.size() );
2694 proj.reserve( recent.size() );
2696 wkt.reserve( recent.size() );
2699 authids <<
c.authid();
2705 settings.
setValue( QStringLiteral(
"UI/recentProjectionsAuthId" ), authids );
2706 settings.
setValue( QStringLiteral(
"UI/recentProjectionsWkt" ), wkt );
2707 settings.
setValue( QStringLiteral(
"UI/recentProjectionsProj4" ), proj );
2712 sSrIdCacheLock()->lockForWrite();
2713 if ( !sDisableSrIdCache )
2716 sDisableSrIdCache =
true;
2717 sSrIdCache()->clear();
2719 sSrIdCacheLock()->unlock();
2721 sOgcLock()->lockForWrite();
2722 if ( !sDisableOgcCache )
2725 sDisableOgcCache =
true;
2726 sOgcCache()->clear();
2728 sOgcLock()->unlock();
2730 sProj4CacheLock()->lockForWrite();
2731 if ( !sDisableProjCache )
2734 sDisableProjCache =
true;
2735 sProj4Cache()->clear();
2737 sProj4CacheLock()->unlock();
2739 sCRSWktLock()->lockForWrite();
2740 if ( !sDisableWktCache )
2743 sDisableWktCache =
true;
2744 sWktCache()->clear();
2746 sCRSWktLock()->unlock();
2748 sCRSSrsIdLock()->lockForWrite();
2749 if ( !sDisableSrsIdCache )
2752 sDisableSrsIdCache =
true;
2753 sSrsIdCache()->clear();
2755 sCRSSrsIdLock()->unlock();
2757 sCrsStringLock()->lockForWrite();
2758 if ( !sDisableStringCache )
2761 sDisableStringCache =
true;
2762 sStringCache()->clear();
2764 sCrsStringLock()->unlock();
2773 if ( !c1.d->mIsValid && !c2.d->mIsValid )
2776 if ( !c1.d->mIsValid && c2.d->mIsValid )
2779 if ( c1.d->mIsValid && !c2.d->mIsValid )
2785 if ( c1IsUser && !c2IsUser )
2788 if ( !c1IsUser && c2IsUser )
2791 if ( !c1IsUser && !c2IsUser )
2793 if ( c1.d->mAuthId != c2.d->mAuthId )
2794 return c1.d->mAuthId > c2.d->mAuthId;
2804 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
2807 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
2810 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
2813 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
2816 return c1.d->mCoordinateEpoch > c2.d->mCoordinateEpoch;
2824 if ( !c1.d->mIsValid && !c2.d->mIsValid )
2827 if ( c1.d->mIsValid && !c2.d->mIsValid )
2830 if ( !c1.d->mIsValid && c2.d->mIsValid )
2836 if ( !c1IsUser && c2IsUser )
2839 if ( c1IsUser && !c2IsUser )
2842 if ( !c1IsUser && !c2IsUser )
2844 if ( c1.d->mAuthId != c2.d->mAuthId )
2845 return c1.d->mAuthId < c2.d->mAuthId;
2855 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
2858 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
2861 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
2864 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
2867 return c1.d->mCoordinateEpoch < c2.d->mCoordinateEpoch;
2872 return !( c1 < c2 );
2876 return !( c1 > c2 );
static QgsCoordinateReferenceSystemRegistry * coordinateReferenceSystemRegistry()
Returns the application's coordinate reference system (CRS) registry, which handles known CRS definit...
static QString qgisUserDatabaseFilePath()
Returns the path to the user qgis.db file.
static QString srsDatabaseFilePath()
Returns the path to the srs.db file.
long addUserCrs(const QgsCoordinateReferenceSystem &crs, const QString &name, QgsCoordinateReferenceSystem::Format nativeFormat=QgsCoordinateReferenceSystem::FormatWkt)
Adds a new crs definition as a custom ("USER") CRS.
QMap< QString, QgsProjOperation > projOperations() const
Returns a map of all valid PROJ operations.
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
bool createFromOgcWmsCrs(const QString &crs)
Sets this CRS to the given OGC WMS-format Coordinate Reference Systems.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
bool createFromWkt(const QString &wkt)
Sets this CRS using a WKT definition.
bool createFromString(const QString &definition)
Set up this CRS from a string definition.
void validate()
Perform some validation on this CRS.
~QgsCoordinateReferenceSystem()
QgsRectangle bounds() const
Returns the approximate bounds for the region the CRS is usable within.
QString toProj() const
Returns a Proj string representation of this CRS.
static CUSTOM_CRS_VALIDATION customCrsValidation()
Gets custom function.
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
static Q_INVOKABLE QgsCoordinateReferenceSystem fromEpsgId(long epsg)
Creates a CRS from a given EPSG ID.
Q_DECL_DEPRECATED bool createFromProj4(const QString &projString)
Sets this CRS by passing it a PROJ style formatted string.
static Q_DECL_DEPRECATED QStringList recentProjections()
Returns a list of recently used projections.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
static void setCustomCrsValidation(CUSTOM_CRS_VALIDATION f)
Sets custom function to force valid CRS.
static void pushRecentCoordinateReferenceSystem(const QgsCoordinateReferenceSystem &crs)
Pushes a recently used CRS to the top of the recent CRS list.
QString description() const
Returns the descriptive name of the CRS, e.g., "WGS 84" or "GDA 94 / Vicgrid94".
long postgisSrid() const
Returns PostGIS SRID for the CRS.
Q_DECL_DEPRECATED long findMatchingProj()
Walks the CRS databases (both system and user database) trying to match stored PROJ string to a datab...
static QList< long > validSrsIds()
Returns a list of all valid SRS IDs present in the CRS database.
QgsProjectionFactors factors(const QgsPoint &point) const
Calculate various cartographic properties, such as scale factors, angular distortion and meridian con...
void setValidationHint(const QString &html)
Set user hint for validation.
Q_DECL_DEPRECATED QString toProj4() const
Returns a Proj string representation of this CRS.
bool operator==(const QgsCoordinateReferenceSystem &srs) const
Overloaded == operator used to compare to CRS's.
QString projectionAcronym() const
Returns the projection acronym for the projection used by the CRS.
CrsType
Enumeration of types of IDs accepted in createFromId() method.
@ InternalCrsId
Internal ID used by QGIS in the local SQLite database.
@ PostgisCrsId
SRID used in PostGIS. DEPRECATED – DO NOT USE.
bool createFromUserInput(const QString &definition)
Set up this CRS from various text formats.
QgsCoordinateReferenceSystem()
Constructs an invalid CRS object.
static int syncDatabase()
Update proj.4 parameters in our database from proj.4.
bool operator!=(const QgsCoordinateReferenceSystem &srs) const
Overloaded != operator used to compare to CRS's.
bool createFromProj(const QString &projString, bool identify=true)
Sets this CRS by passing it a PROJ style formatted string.
QgsDatumEnsemble datumEnsemble() const SIP_THROW(QgsNotSupportedException)
Attempts to retrieve datum ensemble details from the CRS.
IdentifierType
Type of identifier string to create.
@ MediumString
A medium-length string, recommended for general purpose use.
@ ShortString
A heavily abbreviated string, for use when a compact representation is required.
Format
Projection definition formats.
bool isDynamic() const
Returns true if the CRS is a dynamic CRS.
QString validationHint()
Gets user hint for validation.
bool createFromSrsId(long srsId)
Sets this CRS by lookup of internal QGIS CRS ID in the CRS database.
Q_DECL_DEPRECATED bool createFromId(long id, CrsType type=PostgisCrsId)
Sets this CRS by lookup of the given ID in the CRS database.
static void invalidateCache(bool disableCache=false)
Clears the internal cache used to initialize QgsCoordinateReferenceSystem objects.
QgsCoordinateReferenceSystem & operator=(const QgsCoordinateReferenceSystem &srs)
Assignment operator.
void updateDefinition()
Updates the definition and parameters of the coordinate reference system to their latest values.
static QgsCoordinateReferenceSystem fromProj(const QString &proj)
Creates a CRS from a proj style formatted string.
static Q_DECL_DEPRECATED void setupESRIWktFix()
Make sure that ESRI WKT import is done properly.
static QList< QgsCoordinateReferenceSystem > recentCoordinateReferenceSystems()
Returns a list of recently used CRS.
PJ * projObject() const
Returns the underlying PROJ PJ object corresponding to the CRS, or nullptr if the CRS is invalid.
QString userFriendlyIdentifier(IdentifierType type=MediumString) const
Returns a user friendly identifier for the CRS.
void setCoordinateEpoch(double epoch)
Sets the coordinate epoch, as a decimal year.
QString authid() const
Returns the authority identifier for the CRS.
Q_DECL_DEPRECATED bool createFromSrid(long srid)
Sets this CRS by lookup of the given PostGIS SRID in the CRS database.
WktVariant
WKT formatting variants, only used for builds based on Proj >= 6.
@ WKT1_GDAL
WKT1 as traditionally output by GDAL, deriving from OGC 01-009. A notable departure from WKT1_GDAL wi...
@ WKT2_2019_SIMPLIFIED
WKT2_2019 with the simplification rule of WKT2_SIMPLIFIED.
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
@ WKT2_2015
Full WKT2 string, conforming to ISO 19162:2015(E) / OGC 12-063r5 with all possible nodes and new keyw...
@ WKT2_2019
Full WKT2 string, conforming to ISO 19162:2019 / OGC 18-010, with all possible nodes and new keyword ...
@ WKT1_ESRI
WKT1 as traditionally output by ESRI software, deriving from OGC 99-049.
@ WKT2_2015_SIMPLIFIED
Same as WKT2_2015 with the following exceptions: UNIT keyword used. ID node only on top element....
static QgsCoordinateReferenceSystem fromWkt(const QString &wkt)
Creates a CRS from a WKT spatial ref sys definition string.
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
long saveAsUserCrs(const QString &name, Format nativeFormat=FormatWkt)
Saves the CRS as a new custom ("USER") CRS.
static QgsCoordinateReferenceSystem fromSrsId(long srsId)
Creates a CRS from a specified QGIS SRS ID.
double coordinateEpoch() const
Returns the coordinate epoch, as a decimal year.
QString geographicCrsAuthId() const
Returns auth id of related geographic CRS.
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
QgsProjOperation operation() const
Returns information about the PROJ operation associated with the coordinate reference system,...
Q_GADGET QgsUnitTypes::DistanceUnit mapUnits
long srsid() const
Returns the internal CRS ID, if available.
bool hasAxisInverted() const
Returns whether axis is inverted (e.g., for WMS 1.3) for the CRS.
QString celestialBodyName() const SIP_THROW(QgsNotSupportedException)
Attempts to retrieve the name of the celestial body associated with the CRS (e.g.
static Q_DECL_DEPRECATED QgsCoordinateReferenceSystem fromProj4(const QString &proj4)
Creates a CRS from a proj style formatted string.
Contains information about a member of a datum ensemble.
Contains information about a datum ensemble.
static void warning(const QString &msg)
Goes to qWarning.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Custom exception class which is raised when an operation is not supported.
static QString OGRSpatialReferenceToWkt(OGRSpatialReferenceH srs)
Returns a WKT string corresponding to the specified OGR srs object.
Point geometry type, with support for z-dimension and m-values.
static PJ_CONTEXT * get()
Returns a thread local instance of a proj context, safe for use in the current thread.
Contains information about a PROJ operation.
@ FlagMatchBoundCrsToUnderlyingSourceCrs
Allow matching a BoundCRS object to its underlying SourceCRS.
static bool isDynamic(const PJ *crs)
Returns true if the given proj coordinate system is a dynamic CRS.
static bool identifyCrs(const PJ *crs, QString &authName, QString &authCode, IdentifyFlags flags=IdentifyFlags())
Attempts to identify a crs, matching it to a known authority and code within an acceptable level of t...
static proj_pj_unique_ptr crsToSingleCrs(const PJ *crs)
Given a PROJ crs (which may be a compound or bound crs, or some other type), extract a single crs fro...
static proj_pj_unique_ptr crsToDatumEnsemble(const PJ *crs)
Given a PROJ crs, attempt to retrieve the datum ensemble from it.
std::unique_ptr< PJ, ProjPJDeleter > proj_pj_unique_ptr
Scoped Proj PJ object.
static bool axisOrderIsSwapped(const PJ *crs)
Returns true if the given proj coordinate system uses requires y/x coordinate order instead of x/y.
contains various cartographic properties, such as scale factors, angular distortion and meridian conv...
The QgsReadWriteLocker class is a convenience class that simplifies locking and unlocking QReadWriteL...
void unlock()
Unlocks the lock.
void changeMode(Mode mode)
Change the mode of the lock to mode.
A rectangle specified with double values.
void setYMinimum(double y) SIP_HOLDGIL
Set the minimum y value.
void setXMaximum(double x) SIP_HOLDGIL
Set the maximum x value.
void setXMinimum(double x) SIP_HOLDGIL
Set the minimum x value.
void setYMaximum(double y) SIP_HOLDGIL
Set the maximum y value.
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