19#include "moc_qgscoordinatereferencesystem.cpp"
33#include <QRegularExpression>
53#include <proj_experimental.h>
56#include <ogr_srs_api.h>
68bool QgsCoordinateReferenceSystem::sDisableSrIdCache =
false;
72bool QgsCoordinateReferenceSystem::sDisableOgcCache =
false;
76bool QgsCoordinateReferenceSystem::sDisableProjCache =
false;
80bool QgsCoordinateReferenceSystem::sDisableWktCache =
false;
84bool QgsCoordinateReferenceSystem::sDisableSrsIdCache =
false;
88bool QgsCoordinateReferenceSystem::sDisableStringCache =
false;
97 if (
const char *proj4src = proj_as_proj_string(
QgsProjContext::get(), boundCrs.get(), PJ_PROJ_4,
nullptr ) )
99 return QString( proj4src );
116 d =
new QgsCoordinateReferenceSystemPrivate();
122 d =
new QgsCoordinateReferenceSystemPrivate();
130 , mValidationHint( srs.mValidationHint )
131 , mNativeFormat( srs.mNativeFormat )
138 mValidationHint = srs.mValidationHint;
139 mNativeFormat = srs.mNativeFormat;
149 const auto constDbs = dbs;
150 for (
const QString &db : constDbs )
152 QFileInfo myInfo( db );
153 if ( !myInfo.exists() )
163 int result = openDatabase( db, database );
164 if ( result != SQLITE_OK )
170 QString sql = QStringLiteral(
"select srs_id from tbl_srs" );
172 statement = database.
prepare( sql, rc );
176 int ret = statement.
step();
178 if ( ret == SQLITE_DONE )
184 if ( ret == SQLITE_ROW )
190 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
195 std::sort( results.begin(), results.end() );
251 if ( horizontalObj && verticalObj )
258 QStringList formattedErrorList;
259 for (
const QString &rawError : std::as_const( errors ) )
261 QString formattedError = rawError;
262 formattedError.replace( QLatin1String(
"proj_create_compound_crs: " ), QString() );
263 formattedErrorList.append( formattedError );
265 error = formattedErrorList.join(
'\n' );
273 if ( !ellipsoidParams.
valid )
315 QgsDebugError( QStringLiteral(
"Unexpected case reached!" ) );
322 if ( definition.isEmpty() )
326 if ( !sDisableStringCache )
328 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sStringCache()->constFind( definition );
329 if ( crsIt != sStringCache()->constEnd() )
332 *
this = crsIt.value();
339 const thread_local QRegularExpression reCrsId( QStringLiteral(
"^(epsg|esri|osgeo|ignf|ogc|nkg|zangi|iau_2015|iau2000|postgis|internal|user)\\:(\\w+)$" ), QRegularExpression::CaseInsensitiveOption );
340 QRegularExpressionMatch match = reCrsId.match( definition );
341 if ( match.capturedStart() == 0 )
343 QString authName = match.captured( 1 ).toLower();
344 if ( authName == QLatin1String(
"epsg" ) )
348 else if ( authName == QLatin1String(
"postgis" ) )
350 const long id = match.captured( 2 ).toLong();
355 else if ( authName == QLatin1String(
"esri" )
356 || authName == QLatin1String(
"osgeo" )
357 || authName == QLatin1String(
"ignf" )
358 || authName == QLatin1String(
"zangi" )
359 || authName == QLatin1String(
"iau2000" )
360 || authName == QLatin1String(
"ogc" )
361 || authName == QLatin1String(
"nkg" )
362 || authName == QLatin1String(
"iau_2015" )
369 const long id = match.captured( 2 ).toLong();
377 const thread_local QRegularExpression reCrsStr( QStringLiteral(
"^(?:(wkt|proj4|proj)\\:)?(.+)$" ), QRegularExpression::CaseInsensitiveOption );
378 match = reCrsStr.match( definition );
379 if ( match.capturedStart() == 0 )
381 if ( match.captured( 1 ).startsWith( QLatin1String(
"proj" ), Qt::CaseInsensitive ) )
393 if ( !sDisableStringCache )
394 sStringCache()->insert( definition, *
this );
400 if ( definition.isEmpty() )
404 OGRSpatialReferenceH
crs = OSRNewSpatialReference(
nullptr );
406 if ( OSRSetFromUserInput(
crs, definition.toLocal8Bit().constData() ) == OGRERR_NONE )
409 OSRDestroySpatialReference(
crs );
419 const char *configOld = CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" );
420 const char *configNew =
"GEOGCS";
422 if ( strcmp( configOld,
"" ) == 0 )
424 CPLSetConfigOption(
"GDAL_FIX_ESRI_WKT", configNew );
425 if ( strcmp( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) != 0 )
427 .arg( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) );
428 QgsDebugMsgLevel( QStringLiteral(
"set GDAL_FIX_ESRI_WKT : %1" ).arg( configNew ), 4 );
432 QgsDebugMsgLevel( QStringLiteral(
"GDAL_FIX_ESRI_WKT was already set : %1" ).arg( configNew ), 4 );
442 if ( !sDisableOgcCache )
444 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sOgcCache()->constFind(
crs );
445 if ( crsIt != sOgcCache()->constEnd() )
448 *
this = crsIt.value();
454 QString wmsCrs =
crs;
459 const QString authorityLower = authority.toLower();
461 ( authorityLower == QLatin1String(
"user" ) ||
462 authorityLower == QLatin1String(
"custom" ) ||
463 authorityLower == QLatin1String(
"qgis" ) ) )
468 if ( !sDisableOgcCache )
469 sOgcCache()->insert(
crs, *
this );
475 wmsCrs = authority +
':' + code;
479 const QString legacyKey = wmsCrs.toLower();
482 if ( it.key().compare( legacyKey, Qt::CaseInsensitive ) == 0 )
484 const QStringList parts = it.key().split(
':' );
485 const QString auth = parts.at( 0 );
486 const QString code = parts.at( 1 );
487 if ( loadFromAuthCode( auth, code ) )
490 if ( !sDisableOgcCache )
491 sOgcCache()->insert(
crs, *
this );
500 if ( !sDisableOgcCache )
501 sOgcCache()->insert(
crs, *
this );
506 if ( wmsCrs.compare( QLatin1String(
"CRS:27" ), Qt::CaseInsensitive ) == 0 ||
507 wmsCrs.compare( QLatin1String(
"OGC:CRS27" ), Qt::CaseInsensitive ) == 0 )
514 if ( wmsCrs.compare( QLatin1String(
"CRS:83" ), Qt::CaseInsensitive ) == 0 ||
515 wmsCrs.compare( QLatin1String(
"OGC:CRS83" ), Qt::CaseInsensitive ) == 0 )
522 if ( wmsCrs.compare( QLatin1String(
"CRS:84" ), Qt::CaseInsensitive ) == 0 ||
523 wmsCrs.compare( QLatin1String(
"OGC:CRS84" ), Qt::CaseInsensitive ) == 0 )
527 d->mAxisInverted =
false;
528 d->mAxisInvertedDirty =
false;
532 if ( !sDisableOgcCache )
533 sOgcCache()->insert(
crs, *
this );
540 if ( !authority.isEmpty() && !code.isEmpty() && loadFromAuthCode( authority, code ) )
543 if ( !sDisableOgcCache )
544 sOgcCache()->insert(
crs, *
this );
549 if ( !sDisableOgcCache )
559 if ( d->mIsValid || !sCustomSrsValidation )
563 if ( sCustomSrsValidation )
564 sCustomSrsValidation( *
this );
570 if ( !sDisableSrIdCache )
572 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrIdCache()->constFind(
id );
573 if ( crsIt != sSrIdCache()->constEnd() )
576 *
this = crsIt.value();
585 if ( it.value().endsWith( QStringLiteral(
",%1" ).arg(
id ) ) )
587 const QStringList parts = it.key().split(
':' );
588 const QString auth = parts.at( 0 );
589 const QString code = parts.at( 1 );
590 if ( loadFromAuthCode( auth, code ) )
593 if ( !sDisableSrIdCache )
594 sSrIdCache()->insert(
id, *
this );
604 if ( !sDisableSrIdCache )
605 sSrIdCache()->insert(
id, *
this );
613 if ( !sDisableSrsIdCache )
615 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrsIdCache()->constFind(
id );
616 if ( crsIt != sSrsIdCache()->constEnd() )
619 *
this = crsIt.value();
628 if ( it.value().startsWith( QString::number(
id ) +
',' ) )
630 const QStringList parts = it.key().split(
':' );
631 const QString auth = parts.at( 0 );
632 const QString code = parts.at( 1 );
633 if ( loadFromAuthCode( auth, code ) )
636 if ( !sDisableSrsIdCache )
637 sSrsIdCache()->insert(
id, *
this );
645 QStringLiteral(
"srs_id" ), QString::number(
id ) );
648 if ( !sDisableSrsIdCache )
649 sSrsIdCache()->insert(
id, *
this );
653bool QgsCoordinateReferenceSystem::loadFromDatabase(
const QString &db,
const QString &expression,
const QString &value )
657 QgsDebugMsgLevel(
"load CRS from " + db +
" where " + expression +
" is " + value, 3 );
659 d->mWktPreferred.clear();
661 QFileInfo myInfo( db );
662 if ( !myInfo.exists() )
672 myResult = openDatabase( db, database );
673 if ( myResult != SQLITE_OK )
690 QString mySql =
"select srs_id,description,projection_acronym,"
691 "ellipsoid_acronym,parameters,srid,auth_name||':'||auth_id,is_geo,wkt "
693 statement = database.
prepare( mySql, myResult );
696 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
701 d->mEllipsoidAcronym.clear();
703 d->mWktPreferred.clear();
706 d->mIsGeographic = statement.
columnAsText( 7 ).toInt() != 0;
708 d->mAxisInvertedDirty =
true;
712 d->mAuthId = QStringLiteral(
"USER:%1" ).arg( d->mSrsId );
714 else if ( !d->mAuthId.startsWith( QLatin1String(
"USER:" ), Qt::CaseInsensitive ) )
716 QStringList parts = d->mAuthId.split(
':' );
717 QString auth = parts.at( 0 );
718 QString code = parts.at( 1 );
725 d->mIsValid = d->hasPj();
731 if ( !wkt.isEmpty() )
739 setProjString( d->mProj4 );
749void QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread(
PJ_CONTEXT *pj_context )
756 if ( !sDisableSrIdCache )
759 if ( !sDisableSrIdCache )
761 for (
auto it = sSrIdCache()->begin(); it != sSrIdCache()->end(); )
763 auto &v = it.value();
764 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
765 it = sSrIdCache()->erase( it );
771 if ( !sDisableOgcCache )
774 if ( !sDisableOgcCache )
776 for (
auto it = sOgcCache()->begin(); it != sOgcCache()->end(); )
778 auto &v = it.value();
779 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
780 it = sOgcCache()->erase( it );
786 if ( !sDisableProjCache )
789 if ( !sDisableProjCache )
791 for (
auto it = sProj4Cache()->begin(); it != sProj4Cache()->end(); )
793 auto &v = it.value();
794 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
795 it = sProj4Cache()->erase( it );
801 if ( !sDisableWktCache )
804 if ( !sDisableWktCache )
806 for (
auto it = sWktCache()->begin(); it != sWktCache()->end(); )
808 auto &v = it.value();
809 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
810 it = sWktCache()->erase( it );
816 if ( !sDisableSrsIdCache )
819 if ( !sDisableSrsIdCache )
821 for (
auto it = sSrsIdCache()->begin(); it != sSrsIdCache()->end(); )
823 auto &v = it.value();
824 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
825 it = sSrsIdCache()->erase( it );
831 if ( !sDisableStringCache )
834 if ( !sDisableStringCache )
836 for (
auto it = sStringCache()->begin(); it != sStringCache()->end(); )
838 auto &v = it.value();
839 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
840 it = sStringCache()->erase( it );
850 if ( d->mAxisInvertedDirty )
853 d->mAxisInvertedDirty =
false;
856 return d->mAxisInverted;
873 const thread_local QMap< Qgis::CrsAxisDirection, QString > mapping =
916 QList< Qgis::CrsAxisDirection > res;
917 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
920 res.reserve( axisCount );
922 for (
int i = 0; i < axisCount; ++i )
924 const char *outDirection =
nullptr;
925 proj_cs_get_axis_info( context, pjCs.get(), i,
935 const thread_local QRegularExpression rx( QStringLiteral(
"([^\\s]+).*" ) );
936 const QRegularExpressionMatch match = rx.match( QString( outDirection ) );
937 if ( !match.hasMatch() )
940 const QString direction = match.captured( 1 );
942 for (
auto it = mapping.constBegin(); it != mapping.constEnd(); ++it )
944 if ( it.value().compare( direction, Qt::CaseInsensitive ) == 0 )
959 return createFromWktInternal( wkt, QString() );
962bool QgsCoordinateReferenceSystem::createFromWktInternal(
const QString &wkt,
const QString &description )
970 if ( !sDisableWktCache )
972 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sWktCache()->constFind( wkt );
973 if ( crsIt != sWktCache()->constEnd() )
976 *
this = crsIt.value();
978 if ( !
description.isEmpty() && d->mDescription.isEmpty() )
983 sWktCache()->insert( wkt, *
this );
992 d->mWktPreferred.clear();
995 QgsDebugMsgLevel( QStringLiteral(
"theWkt is uninitialized, operation failed" ), 4 );
1000 QgsCoordinateReferenceSystem::RecordMap record = getRecord(
"select * from tbl_srs where wkt=" +
QgsSqliteUtils::quotedString( wkt ) +
" order by deprecated" );
1001 if ( !record.empty() )
1003 long srsId = record[QStringLiteral(
"srs_id" )].toLong();
1011 setWktString( wkt );
1016 if ( d->mSrsId == 0 )
1019 long id = matchToUserCrs();
1028 if ( !sDisableWktCache )
1029 sWktCache()->insert( wkt, *
this );
1047 if ( projString.isEmpty() )
1052 if ( projString.trimmed().isEmpty() )
1054 d->mIsValid =
false;
1056 d->mWktPreferred.clear();
1061 if ( !sDisableProjCache )
1063 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sProj4Cache()->constFind( projString );
1064 if ( crsIt != sProj4Cache()->constEnd() )
1067 *
this = crsIt.value();
1081 QString myProj4String = projString.trimmed();
1082 myProj4String.remove( QStringLiteral(
"+type=crs" ) );
1083 myProj4String = myProj4String.trimmed();
1085 d->mIsValid =
false;
1086 d->mWktPreferred.clear();
1091 const QString projCrsString = myProj4String + ( myProj4String.contains( QStringLiteral(
"+type=crs" ) ) ? QString() : QStringLiteral(
" +type=crs" ) );
1099 const QString
authid = QStringLiteral(
"%1:%2" ).arg( authName, authCode );
1103 if ( !sDisableProjCache )
1104 sProj4Cache()->insert( projString, *
this );
1111 QgsCoordinateReferenceSystem::RecordMap myRecord = getRecord(
"select * from tbl_srs where parameters=" +
QgsSqliteUtils::quotedString( myProj4String ) +
" order by deprecated" );
1113 if ( !myRecord.empty() )
1115 id = myRecord[QStringLiteral(
"srs_id" )].toLong();
1124 setProjString( myProj4String );
1127 id = matchToUserCrs();
1136 setProjString( myProj4String );
1140 if ( !sDisableProjCache )
1141 sProj4Cache()->insert( projString, *
this );
1147QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord(
const QString &sql )
1149 QString myDatabaseFileName;
1150 QgsCoordinateReferenceSystem::RecordMap myMap;
1151 QString myFieldName;
1152 QString myFieldValue;
1159 QFileInfo myInfo( myDatabaseFileName );
1160 if ( !myInfo.exists() )
1162 QgsDebugError(
"failed : " + myDatabaseFileName +
" does not exist!" );
1167 myResult = openDatabase( myDatabaseFileName, database );
1168 if ( myResult != SQLITE_OK )
1173 statement = database.
prepare( sql, myResult );
1175 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1179 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1181 myFieldName = statement.
columnName( myColNo );
1183 myMap[myFieldName] = myFieldValue;
1185 if ( statement.
step() != SQLITE_DONE )
1187 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1196 if ( myMap.empty() )
1199 QFileInfo myFileInfo;
1200 myFileInfo.setFile( myDatabaseFileName );
1201 if ( !myFileInfo.exists() )
1203 QgsDebugError( QStringLiteral(
"user qgis.db not found" ) );
1208 myResult = openDatabase( myDatabaseFileName, database );
1209 if ( myResult != SQLITE_OK )
1214 statement = database.
prepare( sql, myResult );
1216 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1220 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1222 myFieldName = statement.
columnName( myColNo );
1224 myMap[myFieldName] = myFieldValue;
1227 if ( statement.
step() != SQLITE_DONE )
1229 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1260 if ( d->mDescription.isNull() )
1266 return d->mDescription;
1273 if ( !
authid().isEmpty() )
1283 id =
isValid() ? QObject::tr(
"Custom CRS" ) : QObject::tr(
"Unknown CRS" );
1285 id = QObject::tr(
"Custom CRS: %1" ).arg(
1288 else if ( !
toProj().isEmpty() )
1291 if ( !
id.isEmpty() && !std::isnan( d->mCoordinateEpoch ) )
1292 id += QStringLiteral(
" @ %1" ).arg(
qgsDoubleToString( d->mCoordinateEpoch, 3 ) );
1299 if ( d->mProjectionAcronym.isNull() )
1305 return d->mProjectionAcronym;
1311 if ( d->mEllipsoidAcronym.isNull() )
1313 if (
PJ *obj = d->threadLocalProjObject() )
1318 const QString ellipsoidAuthName( proj_get_id_auth_name( ellipsoid.get(), 0 ) );
1319 const QString ellipsoidAuthCode( proj_get_id_code( ellipsoid.get(), 0 ) );
1320 if ( !ellipsoidAuthName.isEmpty() && !ellipsoidAuthCode.isEmpty() )
1321 d->mEllipsoidAcronym = QStringLiteral(
"%1:%2" ).arg( ellipsoidAuthName, ellipsoidAuthCode );
1324 double semiMajor, semiMinor, invFlattening;
1325 int semiMinorComputed = 0;
1326 if ( proj_ellipsoid_get_parameters(
QgsProjContext::get(), ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
1328 d->mEllipsoidAcronym = QStringLiteral(
"PARAMETER:%1:%2" ).arg(
qgsDoubleToString( semiMajor ),
1333 d->mEllipsoidAcronym.clear();
1338 return d->mEllipsoidAcronym;
1342 return d->mEllipsoidAcronym;
1356 if ( d->mProj4.isEmpty() )
1358 if (
PJ *obj = d->threadLocalProjObject() )
1364 return d->mProj4.trimmed();
1370 switch ( d->mProjType )
1372 case PJ_TYPE_UNKNOWN:
1375 case PJ_TYPE_ELLIPSOID:
1376 case PJ_TYPE_PRIME_MERIDIAN:
1377 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
1378 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
1379 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
1380 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
1381 case PJ_TYPE_DATUM_ENSEMBLE:
1382 case PJ_TYPE_CONVERSION:
1383 case PJ_TYPE_TRANSFORMATION:
1384 case PJ_TYPE_CONCATENATED_OPERATION:
1385 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
1386 case PJ_TYPE_TEMPORAL_DATUM:
1387 case PJ_TYPE_ENGINEERING_DATUM:
1388 case PJ_TYPE_PARAMETRIC_DATUM:
1392 case PJ_TYPE_GEOGRAPHIC_CRS:
1396 case PJ_TYPE_GEODETIC_CRS:
1398 case PJ_TYPE_GEOCENTRIC_CRS:
1400 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
1402 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
1404 case PJ_TYPE_VERTICAL_CRS:
1406 case PJ_TYPE_PROJECTED_CRS:
1408 case PJ_TYPE_COMPOUND_CRS:
1410 case PJ_TYPE_TEMPORAL_CRS:
1412 case PJ_TYPE_ENGINEERING_CRS:
1414 case PJ_TYPE_BOUND_CRS:
1416 case PJ_TYPE_OTHER_CRS:
1418#if PROJ_VERSION_MAJOR>9 || (PROJ_VERSION_MAJOR==9 && PROJ_VERSION_MINOR>=2)
1419 case PJ_TYPE_DERIVED_PROJECTED_CRS:
1421 case PJ_TYPE_COORDINATE_METADATA:
1435 return proj_is_deprecated( pj );
1440 return d->mIsGeographic;
1460 return QString( proj_get_celestial_body_name( context, pj ) );
1465 if ( d->mCoordinateEpoch == epoch )
1471 d->mCoordinateEpoch = epoch;
1472 d->setPj( std::move( clone ) );
1477 return d->mCoordinateEpoch;
1496 res.mName = QString( proj_get_name( ensemble.get() ) );
1497 res.mAuthority = QString( proj_get_id_auth_name( ensemble.get(), 0 ) );
1498 res.mCode = QString( proj_get_id_code( ensemble.get(), 0 ) );
1499 res.mRemarks = QString( proj_get_remarks( ensemble.get() ) );
1500 res.mScope = QString( proj_get_scope( ensemble.get() ) );
1501 res.mAccuracy = proj_datum_ensemble_get_accuracy( context, ensemble.get() );
1503 const int memberCount = proj_datum_ensemble_get_member_count( context, ensemble.get() );
1504 for (
int i = 0; i < memberCount; ++i )
1511 details.mName = QString( proj_get_name( member.get() ) );
1512 details.mAuthority = QString( proj_get_id_auth_name( member.get(), 0 ) );
1513 details.mCode = QString( proj_get_id_code( member.get(), 0 ) );
1514 details.mRemarks = QString( proj_get_remarks( member.get() ) );
1515 details.mScope = QString( proj_get_scope( member.get() ) );
1517 res.mMembers << details;
1527 QString projString =
toProj();
1528 projString.replace( QLatin1String(
"+type=crs" ), QString() );
1531 if ( !transformation )
1534 PJ_COORD coord = proj_coord( 0, 0, 0, HUGE_VAL );
1535 coord.uv.u = point.
x() * M_PI / 180.0;
1536 coord.uv.v = point.
y() * M_PI / 180.0;
1538 proj_errno_reset( transformation.get() );
1539 const PJ_FACTORS pjFactors = proj_factors( transformation.get(), coord );
1540 if ( proj_errno( transformation.get() ) )
1545 res.mIsValid =
true;
1546 res.mMeridionalScale = pjFactors.meridional_scale;
1547 res.mParallelScale = pjFactors.parallel_scale;
1548 res.mArealScale = pjFactors.areal_scale;
1549 res.mAngularDistortion = pjFactors.angular_distortion;
1550 res.mMeridianParallelAngle = pjFactors.meridian_parallel_angle * 180 / M_PI;
1551 res.mMeridianConvergence = pjFactors.meridian_convergence * 180 / M_PI;
1552 res.mTissotSemimajor = pjFactors.tissot_semimajor;
1553 res.mTissotSemiminor = pjFactors.tissot_semiminor;
1554 res.mDxDlam = pjFactors.dx_dlam;
1555 res.mDxDphi = pjFactors.dx_dphi;
1556 res.mDyDlam = pjFactors.dy_dlam;
1557 res.mDyDphi = pjFactors.dy_dphi;
1569 QString projString =
toProj();
1570 projString.replace( QLatin1String(
"+type=crs" ), QString() );
1571 if ( projString.isEmpty() )
1575 if ( !transformation )
1578 PJ_PROJ_INFO info = proj_pj_info( transformation.get() );
1593 return d->mMapUnits;
1601 PJ *obj = d->threadLocalProjObject();
1606 double southLat = 0;
1608 double northLat = 0;
1611 &westLon, &southLat, &eastLon, &northLat,
nullptr ) )
1626 const auto parts {
authid().split(
':' ) };
1627 if ( parts.length() == 2 )
1629 if ( parts[0] == QLatin1String(
"EPSG" ) )
1630 return QStringLiteral(
"http://www.opengis.net/def/crs/EPSG/0/%1" ).arg( parts[1] ) ;
1631 else if ( parts[0] == QLatin1String(
"OGC" ) )
1633 return QStringLiteral(
"http://www.opengis.net/def/crs/OGC/1.3/%1" ).arg( parts[1] ) ;
1649 const auto parts {
authid().split(
':' ) };
1650 if ( parts.length() == 2 )
1652 if ( parts[0] == QLatin1String(
"EPSG" ) )
1653 return QStringLiteral(
"urn:ogc:def:crs:EPSG::%1" ).arg( parts[1] );
1654 else if ( parts[0] == QLatin1String(
"OGC" ) )
1656 return QStringLiteral(
"urn:ogc:def:crs:OGC:1.3:%1" ).arg( parts[1] );
1690void QgsCoordinateReferenceSystem::setProjString(
const QString &proj4String )
1693 d->mProj4 = proj4String;
1694 d->mWktPreferred.clear();
1697 QString trimmed = proj4String.trimmed();
1699 trimmed += QLatin1String(
" +type=crs" );
1709 const int errNo = proj_context_errno( ctx );
1710 QgsDebugError( QStringLiteral(
"proj string rejected: %1" ).arg( proj_context_errno_string( ctx, errNo ) ) );
1712 d->mIsValid =
false;
1716 d->mEllipsoidAcronym.clear();
1723bool QgsCoordinateReferenceSystem::setWktString(
const QString &wkt )
1726 d->mIsValid =
false;
1727 d->mWktPreferred.clear();
1729 PROJ_STRING_LIST warnings =
nullptr;
1730 PROJ_STRING_LIST grammarErrors =
nullptr;
1738 QgsDebugMsgLevel( QStringLiteral(
"\n---------------------------------------------------------------" ), 2 );
1739 QgsDebugMsgLevel( QStringLiteral(
"This CRS could *** NOT *** be set from the supplied Wkt " ), 2 );
1741 for (
auto iter = warnings; iter && *iter; ++iter )
1745 for (
auto iter = grammarErrors; iter && *iter; ++iter )
1749 QgsDebugMsgLevel( QStringLiteral(
"---------------------------------------------------------------\n" ), 2 );
1751 proj_string_list_destroy( warnings );
1752 proj_string_list_destroy( grammarErrors );
1758 if ( !sDisableWktCache )
1759 sWktCache()->insert( wkt, *
this );
1767 QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
1768 QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
1770 if ( authName.isEmpty() || authCode.isEmpty() )
1776 if ( !authName.isEmpty() && !authCode.isEmpty() )
1779 if ( fromAuthCode.loadFromAuthCode( authName, authCode ) )
1781 *
this = fromAuthCode;
1783 if ( !sDisableWktCache )
1784 sWktCache()->insert( wkt, *
this );
1791 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
1798void QgsCoordinateReferenceSystem::setMapUnits()
1825 if ( !coordinateSystem )
1831 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
1832 if ( axisCount > 0 )
1834 const char *outUnitName =
nullptr;
1836 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
1845 const QString unitName( outUnitName );
1849 if ( unitName.compare( QLatin1String(
"degree" ), Qt::CaseInsensitive ) == 0 ||
1850 unitName.compare( QLatin1String(
"degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1851 unitName.compare( QLatin1String(
"degree minute second hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1852 unitName.compare( QLatin1String(
"degree minute" ), Qt::CaseInsensitive ) == 0 ||
1853 unitName.compare( QLatin1String(
"degree hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1854 unitName.compare( QLatin1String(
"degree minute hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1855 unitName.compare( QLatin1String(
"hemisphere degree" ), Qt::CaseInsensitive ) == 0 ||
1856 unitName.compare( QLatin1String(
"hemisphere degree minute" ), Qt::CaseInsensitive ) == 0 ||
1857 unitName.compare( QLatin1String(
"hemisphere degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1858 unitName.compare( QLatin1String(
"degree (supplier to define representation)" ), Qt::CaseInsensitive ) == 0 )
1860 else if ( unitName.compare( QLatin1String(
"metre" ), Qt::CaseInsensitive ) == 0
1861 || unitName.compare( QLatin1String(
"m" ), Qt::CaseInsensitive ) == 0
1862 || unitName.compare( QLatin1String(
"meter" ), Qt::CaseInsensitive ) == 0 )
1864 else if ( unitName.compare( QLatin1String(
"US survey foot" ), Qt::CaseInsensitive ) == 0 )
1866 else if ( unitName.compare( QLatin1String(
"foot" ), Qt::CaseInsensitive ) == 0 )
1868 else if ( unitName.compare( QLatin1String(
"British yard (Sears 1922)" ), Qt::CaseInsensitive ) == 0 )
1870 else if ( unitName.compare( QLatin1String(
"British yard (Sears 1922 truncated)" ), Qt::CaseInsensitive ) == 0 )
1872 else if ( unitName.compare( QLatin1String(
"British foot (Sears 1922)" ), Qt::CaseInsensitive ) == 0 )
1874 else if ( unitName.compare( QLatin1String(
"British foot (Sears 1922 truncated)" ), Qt::CaseInsensitive ) == 0 )
1876 else if ( unitName.compare( QLatin1String(
"British chain (Sears 1922)" ), Qt::CaseInsensitive ) == 0 )
1878 else if ( unitName.compare( QLatin1String(
"British chain (Sears 1922 truncated)" ), Qt::CaseInsensitive ) == 0 )
1880 else if ( unitName.compare( QLatin1String(
"British link (Sears 1922)" ), Qt::CaseInsensitive ) == 0 )
1882 else if ( unitName.compare( QLatin1String(
"British link (Sears 1922 truncated)" ), Qt::CaseInsensitive ) == 0 )
1884 else if ( unitName.compare( QLatin1String(
"British yard (Benoit 1895 A)" ), Qt::CaseInsensitive ) == 0 )
1886 else if ( unitName.compare( QLatin1String(
"British foot (Benoit 1895 A)" ), Qt::CaseInsensitive ) == 0 )
1888 else if ( unitName.compare( QLatin1String(
"British chain (Benoit 1895 A)" ), Qt::CaseInsensitive ) == 0 )
1890 else if ( unitName.compare( QLatin1String(
"British link (Benoit 1895 A)" ), Qt::CaseInsensitive ) == 0 )
1892 else if ( unitName.compare( QLatin1String(
"British yard (Benoit 1895 B)" ), Qt::CaseInsensitive ) == 0 )
1894 else if ( unitName.compare( QLatin1String(
"British foot (Benoit 1895 B)" ), Qt::CaseInsensitive ) == 0 )
1896 else if ( unitName.compare( QLatin1String(
"British chain (Benoit 1895 B)" ), Qt::CaseInsensitive ) == 0 )
1898 else if ( unitName.compare( QLatin1String(
"British link (Benoit 1895 B)" ), Qt::CaseInsensitive ) == 0 )
1900 else if ( unitName.compare( QLatin1String(
"British foot (1865)" ), Qt::CaseInsensitive ) == 0 )
1902 else if ( unitName.compare( QLatin1String(
"British foot (1936)" ), Qt::CaseInsensitive ) == 0 )
1904 else if ( unitName.compare( QLatin1String(
"Indian foot" ), Qt::CaseInsensitive ) == 0 )
1906 else if ( unitName.compare( QLatin1String(
"Indian foot (1937)" ), Qt::CaseInsensitive ) == 0 )
1908 else if ( unitName.compare( QLatin1String(
"Indian foot (1962)" ), Qt::CaseInsensitive ) == 0 )
1910 else if ( unitName.compare( QLatin1String(
"Indian foot (1975)" ), Qt::CaseInsensitive ) == 0 )
1912 else if ( unitName.compare( QLatin1String(
"Indian yard" ), Qt::CaseInsensitive ) == 0 )
1914 else if ( unitName.compare( QLatin1String(
"Indian yard (1937)" ), Qt::CaseInsensitive ) == 0 )
1916 else if ( unitName.compare( QLatin1String(
"Indian yard (1962)" ), Qt::CaseInsensitive ) == 0 )
1918 else if ( unitName.compare( QLatin1String(
"Indian yard (1975)" ), Qt::CaseInsensitive ) == 0 )
1920 else if ( unitName.compare( QLatin1String(
"Gold Coast foot" ), Qt::CaseInsensitive ) == 0 )
1922 else if ( unitName.compare( QLatin1String(
"Clarke's foot" ), Qt::CaseInsensitive ) == 0 )
1924 else if ( unitName.compare( QLatin1String(
"Clarke's yard" ), Qt::CaseInsensitive ) == 0 )
1926 else if ( unitName.compare( QLatin1String(
"Clarke's chain" ), Qt::CaseInsensitive ) == 0 )
1928 else if ( unitName.compare( QLatin1String(
"Clarke's link" ), Qt::CaseInsensitive ) == 0 )
1930 else if ( unitName.compare( QLatin1String(
"kilometre" ), Qt::CaseInsensitive ) == 0 )
1932 else if ( unitName.compare( QLatin1String(
"centimetre" ), Qt::CaseInsensitive ) == 0 )
1934 else if ( unitName.compare( QLatin1String(
"millimetre" ), Qt::CaseInsensitive ) == 0 )
1936 else if ( unitName.compare( QLatin1String(
"Statute mile" ), Qt::CaseInsensitive ) == 0 )
1938 else if ( unitName.compare( QLatin1String(
"nautical mile" ), Qt::CaseInsensitive ) == 0 )
1940 else if ( unitName.compare( QLatin1String(
"yard" ), Qt::CaseInsensitive ) == 0 )
1942 else if ( unitName.compare( QLatin1String(
"fathom" ), Qt::CaseInsensitive ) == 0 )
1944 else if ( unitName.compare( QLatin1String(
"US survey chain" ), Qt::CaseInsensitive ) == 0 )
1946 else if ( unitName.compare( QLatin1String(
"chain" ), Qt::CaseInsensitive ) == 0 )
1948 else if ( unitName.compare( QLatin1String(
"link" ), Qt::CaseInsensitive ) == 0 )
1950 else if ( unitName.compare( QLatin1String(
"US survey link" ), Qt::CaseInsensitive ) == 0 )
1952 else if ( unitName.compare( QLatin1String(
"US survey mile" ), Qt::CaseInsensitive ) == 0 )
1954 else if ( unitName.compare( QLatin1String(
"German legal metre" ), Qt::CaseInsensitive ) == 0 )
1971 if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull()
1974 QgsDebugMsgLevel(
"QgsCoordinateReferenceSystem::findMatchingProj will only "
1975 "work if prj acr ellipsoid acr and proj4string are set"
1976 " and the current projection is valid!", 4 );
1986 QString mySql = QString(
"select srs_id,parameters from tbl_srs where "
1987 "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated" )
1994 myResult = openDatabase( myDatabaseFileName, database );
1995 if ( myResult != SQLITE_OK )
2000 statement = database.
prepare( mySql, myResult );
2001 if ( myResult == SQLITE_OK )
2004 while ( statement.
step() == SQLITE_ROW )
2008 if (
toProj() == myProj4String.trimmed() )
2010 return mySrsId.toLong();
2021 myResult = openDatabase( myDatabaseFileName, database );
2022 if ( myResult != SQLITE_OK )
2027 statement = database.
prepare( mySql, myResult );
2029 if ( myResult == SQLITE_OK )
2031 while ( statement.
step() == SQLITE_ROW )
2035 if (
toProj() == myProj4String.trimmed() )
2037 return mySrsId.toLong();
2051 if ( !d->mIsValid && !srs.d->mIsValid )
2054 if ( !d->mIsValid || !srs.d->mIsValid )
2062 if ( isUser != otherIsUser )
2066 if ( !isUser && ( !d->mAuthId.isEmpty() || !srs.d->mAuthId.isEmpty() ) )
2067 return d->mAuthId == srs.d->mAuthId;
2074 return !( *
this == srs );
2079 if (
PJ *obj = d->threadLocalProjObject() )
2082 if ( isDefaultPreferredFormat && !d->mWktPreferred.isEmpty() )
2085 return d->mWktPreferred;
2088 PJ_WKT_TYPE
type = PJ_WKT1_GDAL;
2092 type = PJ_WKT1_GDAL;
2095 type = PJ_WKT1_ESRI;
2098 type = PJ_WKT2_2015;
2101 type = PJ_WKT2_2015_SIMPLIFIED;
2104 type = PJ_WKT2_2019;
2107 type = PJ_WKT2_2019_SIMPLIFIED;
2111 const QByteArray multiLineOption = QStringLiteral(
"MULTILINE=%1" ).arg( multiline ? QStringLiteral(
"YES" ) : QStringLiteral(
"NO" ) ).toLocal8Bit();
2112 const QByteArray indentatationWidthOption = QStringLiteral(
"INDENTATION_WIDTH=%1" ).arg( multiline ? QString::number( indentationWidth ) : QStringLiteral(
"0" ) ).toLocal8Bit();
2113 const char *
const options[] = {multiLineOption.constData(), indentatationWidthOption.constData(),
nullptr};
2116 if ( isDefaultPreferredFormat )
2119 d->mWktPreferred = res;
2131 QDomNode srsNode = node.namedItem( QStringLiteral(
"spatialrefsys" ) );
2133 if ( ! srsNode.isNull() )
2135 bool initialized =
false;
2138 long srsid = srsNode.namedItem( QStringLiteral(
"srsid" ) ).toElement().text().toLong( &ok );
2144 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
2145 if ( !node.isNull() )
2156 node = srsNode.namedItem( QStringLiteral(
"epsg" ) );
2157 if ( !node.isNull() )
2175 const QString
description = srsNode.namedItem( QStringLiteral(
"description" ) ).toElement().text();
2177 const QString wkt = srsNode.namedItem( QStringLiteral(
"wkt" ) ).toElement().text();
2178 initialized = createFromWktInternal( wkt,
description );
2183 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
2184 const QString proj4 = node.toElement().text();
2191 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
2192 const QString proj4 = node.toElement().text();
2193 if ( !proj4.trimmed().isEmpty() )
2194 setProjString( node.toElement().text() );
2196 node = srsNode.namedItem( QStringLiteral(
"srsid" ) );
2197 d->mSrsId = node.toElement().text().toLong();
2199 node = srsNode.namedItem( QStringLiteral(
"srid" ) );
2200 d->mSRID = node.toElement().text().toLong();
2202 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
2203 d->mAuthId = node.toElement().text();
2205 node = srsNode.namedItem( QStringLiteral(
"description" ) );
2206 d->mDescription = node.toElement().text();
2208 node = srsNode.namedItem( QStringLiteral(
"projectionacronym" ) );
2209 d->mProjectionAcronym = node.toElement().text();
2211 node = srsNode.namedItem( QStringLiteral(
"ellipsoidacronym" ) );
2212 d->mEllipsoidAcronym = node.toElement().text();
2214 node = srsNode.namedItem( QStringLiteral(
"geographicflag" ) );
2215 d->mIsGeographic = node.toElement().text() == QLatin1String(
"true" );
2217 d->mWktPreferred.clear();
2223 const QString epoch = srsNode.toElement().attribute( QStringLiteral(
"coordinateEpoch" ) );
2224 if ( !epoch.isEmpty() )
2226 bool epochOk =
false;
2227 d->mCoordinateEpoch = epoch.toDouble( &epochOk );
2229 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2233 d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
2236 mNativeFormat = qgsEnumKeyToValue<Qgis::CrsDefinitionFormat>( srsNode.toElement().attribute( QStringLiteral(
"nativeFormat" ) ),
Qgis::CrsDefinitionFormat::Wkt );
2241 d =
new QgsCoordinateReferenceSystemPrivate();
2249 QDomElement layerNode = node.toElement();
2250 QDomElement srsElement = doc.createElement( QStringLiteral(
"spatialrefsys" ) );
2252 srsElement.setAttribute( QStringLiteral(
"nativeFormat" ), qgsEnumValueToKey<Qgis::CrsDefinitionFormat>( mNativeFormat ) );
2254 if ( std::isfinite( d->mCoordinateEpoch ) )
2256 srsElement.setAttribute( QStringLiteral(
"coordinateEpoch" ), d->mCoordinateEpoch );
2259 QDomElement wktElement = doc.createElement( QStringLiteral(
"wkt" ) );
2261 srsElement.appendChild( wktElement );
2263 QDomElement proj4Element = doc.createElement( QStringLiteral(
"proj4" ) );
2264 proj4Element.appendChild( doc.createTextNode(
toProj() ) );
2265 srsElement.appendChild( proj4Element );
2267 QDomElement srsIdElement = doc.createElement( QStringLiteral(
"srsid" ) );
2268 srsIdElement.appendChild( doc.createTextNode( QString::number(
srsid() ) ) );
2269 srsElement.appendChild( srsIdElement );
2271 QDomElement sridElement = doc.createElement( QStringLiteral(
"srid" ) );
2272 sridElement.appendChild( doc.createTextNode( QString::number(
postgisSrid() ) ) );
2273 srsElement.appendChild( sridElement );
2275 QDomElement authidElement = doc.createElement( QStringLiteral(
"authid" ) );
2276 authidElement.appendChild( doc.createTextNode(
authid() ) );
2277 srsElement.appendChild( authidElement );
2279 QDomElement descriptionElement = doc.createElement( QStringLiteral(
"description" ) );
2280 descriptionElement.appendChild( doc.createTextNode(
description() ) );
2281 srsElement.appendChild( descriptionElement );
2283 QDomElement projectionAcronymElement = doc.createElement( QStringLiteral(
"projectionacronym" ) );
2284 projectionAcronymElement.appendChild( doc.createTextNode(
projectionAcronym() ) );
2285 srsElement.appendChild( projectionAcronymElement );
2287 QDomElement ellipsoidAcronymElement = doc.createElement( QStringLiteral(
"ellipsoidacronym" ) );
2288 ellipsoidAcronymElement.appendChild( doc.createTextNode(
ellipsoidAcronym() ) );
2289 srsElement.appendChild( ellipsoidAcronymElement );
2291 QDomElement geographicFlagElement = doc.createElement( QStringLiteral(
"geographicflag" ) );
2292 QString geoFlagText = QStringLiteral(
"false" );
2295 geoFlagText = QStringLiteral(
"true" );
2298 geographicFlagElement.appendChild( doc.createTextNode( geoFlagText ) );
2299 srsElement.appendChild( geographicFlagElement );
2301 layerNode.appendChild( srsElement );
2313QString QgsCoordinateReferenceSystem::projFromSrsId(
const int srsId )
2315 QString myDatabaseFileName;
2316 QString myProjString;
2317 QString mySql = QStringLiteral(
"select parameters from tbl_srs where srs_id = %1 order by deprecated" ).arg( srsId );
2326 QFileInfo myFileInfo;
2327 myFileInfo.setFile( myDatabaseFileName );
2328 if ( !myFileInfo.exists() )
2330 QgsDebugError( QStringLiteral(
"users qgis.db not found" ) );
2343 rc = openDatabase( myDatabaseFileName, database );
2349 statement = database.
prepare( mySql, rc );
2351 if ( rc == SQLITE_OK )
2353 if ( statement.
step() == SQLITE_ROW )
2359 return myProjString;
2366 myResult = database.
open_v2( path, SQLITE_OPEN_READONLY,
nullptr );
2368 myResult = database.
open( path );
2370 if ( myResult != SQLITE_OK )
2379 .arg( database.
errorMessage() ), QObject::tr(
"CRS" ) );
2386 sCustomSrsValidation = f;
2391 return sCustomSrsValidation;
2394void QgsCoordinateReferenceSystem::debugPrint()
2397 QgsDebugMsgLevel(
"* Valid : " + ( d->mIsValid ? QString(
"true" ) : QString(
"false" ) ), 1 );
2418 mValidationHint = html;
2423 return mValidationHint;
2433 mNativeFormat = format;
2438 return mNativeFormat;
2441long QgsCoordinateReferenceSystem::getRecordCount()
2446 long myRecordCount = 0;
2449 if ( myResult != SQLITE_OK )
2455 QString mySql = QStringLiteral(
"select count(*) from tbl_srs" );
2456 statement = database.
prepare( mySql, myResult );
2457 if ( myResult == SQLITE_OK )
2459 if ( statement.
step() == SQLITE_ROW )
2461 QString myRecordCountString = statement.
columnAsText( 0 );
2462 myRecordCount = myRecordCountString.toLong();
2465 return myRecordCount;
2471 bool isGeographic =
false;
2475 if ( !horizontalCrs )
2479 if ( coordinateSystem )
2481 const int axisCount = proj_cs_get_axis_count( pjContext, coordinateSystem.get() );
2482 if ( axisCount > 0 )
2484 const char *outUnitAuthName =
nullptr;
2485 const char *outUnitAuthCode =
nullptr;
2487 proj_cs_get_axis_info( pjContext, coordinateSystem.get(), 0,
2496 if ( outUnitAuthName && outUnitAuthCode )
2498 const char *unitCategory =
nullptr;
2499 if ( proj_uom_get_info_from_database( pjContext, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
2501 isGeographic = QString( unitCategory ).compare( QLatin1String(
"angular" ), Qt::CaseInsensitive ) == 0;
2506 return isGeographic;
2511 thread_local const QRegularExpression projRegExp( QStringLiteral(
"\\+proj=(\\S+)" ) );
2512 const QRegularExpressionMatch projMatch = projRegExp.match( proj );
2513 if ( !projMatch.hasMatch() )
2515 QgsDebugMsgLevel( QStringLiteral(
"no +proj argument found [%2]" ).arg( proj ), 2 );
2518 operation = projMatch.captured( 1 );
2520 const QRegularExpressionMatch ellipseMatch = projRegExp.match( proj );
2521 if ( ellipseMatch.hasMatch() )
2523 ellipsoid = ellipseMatch.captured( 1 );
2537bool QgsCoordinateReferenceSystem::loadFromAuthCode(
const QString &auth,
const QString &code )
2543 d->mIsValid =
false;
2544 d->mWktPreferred.clear();
2556 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2557 proj4 = proj4.trimmed();
2561 d->mWktPreferred.clear();
2562 d->mDescription = QString( proj_get_name(
crs.get() ) );
2563 d->mAuthId = QStringLiteral(
"%1:%2" ).arg( auth, code );
2565 d->mAxisInvertedDirty =
true;
2570 d->mEllipsoidAcronym.clear();
2571 d->setPj( std::move(
crs ) );
2573 const QString dbVals =
sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( auth, code ).toUpper() );
2574 if ( !dbVals.isEmpty() )
2576 const QStringList parts = dbVals.split(
',' );
2577 d->mSrsId = parts.at( 0 ).toInt();
2578 d->mSRID = parts.at( 1 ).toInt();
2586QList<long> QgsCoordinateReferenceSystem::userSrsIds()
2588 QList<long> results;
2592 QFileInfo myInfo( db );
2593 if ( !myInfo.exists() )
2603 int result = openDatabase( db, database );
2604 if ( result != SQLITE_OK )
2606 QgsDebugError(
"failed : " + db +
" could not be opened!" );
2610 QString sql = QStringLiteral(
"select srs_id from tbl_srs where srs_id >= %1" ).arg(
Qgis::USER_CRS_START_ID );
2612 statement = database.
prepare( sql, rc );
2615 int ret = statement.
step();
2617 if ( ret == SQLITE_DONE )
2623 if ( ret == SQLITE_ROW )
2629 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
2637long QgsCoordinateReferenceSystem::matchToUserCrs()
const
2639 PJ *obj = d->threadLocalProjObject();
2643 const QList< long > ids = userSrsIds();
2644 for (
long id : ids )
2647 if ( candidate.
projObject() && proj_is_equivalent_to( obj, candidate.
projObject(), PJ_COMP_EQUIVALENT ) )
2655static void sync_db_proj_logger(
void * ,
int level,
const char *message )
2660 if ( level == PJ_LOG_ERROR )
2664 else if ( level == PJ_LOG_DEBUG )
2672 setlocale( LC_ALL,
"C" );
2675 int inserted = 0, updated = 0, deleted = 0, errors = 0;
2680 if ( database.
open( dbFilePath ) != SQLITE_OK )
2686 if ( sqlite3_exec( database.get(),
"BEGIN TRANSACTION",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2694 char *errMsg =
nullptr;
2696 bool createdTypeColumn =
false;
2697 if ( sqlite3_exec( database.get(),
"ALTER TABLE tbl_srs ADD COLUMN srs_type text",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2699 createdTypeColumn =
true;
2700 if ( sqlite3_exec( database.get(),
"CREATE INDEX srs_type ON tbl_srs(srs_type)",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2702 QgsDebugError( QStringLiteral(
"Could not create index for srs_type" ) );
2707 if ( sqlite3_exec( database.get(),
"create table tbl_info (proj_major INT, proj_minor INT, proj_patch INT)",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2709 QString sql = QStringLiteral(
"INSERT INTO tbl_info(proj_major, proj_minor, proj_patch) VALUES (%1, %2,%3)" )
2710 .arg( QString::number( PROJ_VERSION_MAJOR ),
2711 QString::number( PROJ_VERSION_MINOR ),
2712 QString::number( PROJ_VERSION_PATCH ) );
2713 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2715 QgsDebugError( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2718 errMsg ? errMsg :
"(unknown error)" ) );
2720 sqlite3_free( errMsg );
2727 QString sql = QStringLiteral(
"SELECT proj_major, proj_minor, proj_patch FROM tbl_info" );
2728 statement = database.
prepare( sql, result );
2729 if ( result != SQLITE_OK )
2734 if ( statement.
step() == SQLITE_ROW )
2739 if ( !createdTypeColumn && major == PROJ_VERSION_MAJOR && minor == PROJ_VERSION_MINOR && patch == PROJ_VERSION_PATCH )
2745 QgsDebugError( QStringLiteral(
"Could not retrieve previous CRS sync PROJ version number" ) );
2752 proj_log_func( pjContext,
nullptr, sync_db_proj_logger );
2754 PROJ_STRING_LIST authorities = proj_get_authorities_from_database( pjContext );
2756 int nextSrsId = 67218;
2757 int nextSrId = 520007218;
2758 for (
auto authIter = authorities; authIter && *authIter; ++authIter )
2760 const QString authority( *authIter );
2761 QgsDebugMsgLevel( QStringLiteral(
"Loading authority '%1'" ).arg( authority ), 2 );
2762 PROJ_STRING_LIST codes = proj_get_codes_from_database( pjContext, *authIter, PJ_TYPE_CRS,
true );
2764 QStringList allCodes;
2766 for (
auto codesIter = codes; codesIter && *codesIter; ++codesIter )
2768 const QString code( *codesIter );
2774 QgsDebugError( QStringLiteral(
"Could not load '%1:%2'" ).arg( authority, code ) );
2778 const PJ_TYPE pjType = proj_get_type(
crs.get( ) );
2780 QString srsTypeString;
2785 case PJ_TYPE_ELLIPSOID:
2786 case PJ_TYPE_PRIME_MERIDIAN:
2787 case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
2788 case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
2789 case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
2790 case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
2791 case PJ_TYPE_DATUM_ENSEMBLE:
2792 case PJ_TYPE_CONVERSION:
2793 case PJ_TYPE_TRANSFORMATION:
2794 case PJ_TYPE_CONCATENATED_OPERATION:
2795 case PJ_TYPE_OTHER_COORDINATE_OPERATION:
2796 case PJ_TYPE_TEMPORAL_DATUM:
2797 case PJ_TYPE_ENGINEERING_DATUM:
2798 case PJ_TYPE_PARAMETRIC_DATUM:
2799 case PJ_TYPE_UNKNOWN:
2803 case PJ_TYPE_GEOGRAPHIC_CRS:
2806 case PJ_TYPE_GEODETIC_CRS:
2810 case PJ_TYPE_GEOCENTRIC_CRS:
2814 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
2818 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
2822 case PJ_TYPE_PROJECTED_CRS:
2826 case PJ_TYPE_COMPOUND_CRS:
2830 case PJ_TYPE_TEMPORAL_CRS:
2834 case PJ_TYPE_ENGINEERING_CRS:
2838 case PJ_TYPE_BOUND_CRS:
2842 case PJ_TYPE_VERTICAL_CRS:
2846#if PROJ_VERSION_MAJOR>9 || (PROJ_VERSION_MAJOR==9 && PROJ_VERSION_MINOR>=2)
2847 case PJ_TYPE_DERIVED_PROJECTED_CRS:
2850 case PJ_TYPE_COORDINATE_METADATA:
2853 case PJ_TYPE_OTHER_CRS:
2862 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2863 proj4 = proj4.trimmed();
2865 if ( proj4.isEmpty() )
2867 QgsDebugMsgLevel( QStringLiteral(
"No proj4 for '%1:%2'" ).arg( authority, code ), 2 );
2878 if ( translatedOperation.isEmpty() && !
operation.isEmpty() )
2880 std::cout << QStringLiteral(
"Operation needs translation in QgsCoordinateReferenceSystemUtils::translateProjection: %1" ).arg(
operation ).toLocal8Bit().constData() << std::endl;
2881 qFatal(
"aborted" );
2884 const bool deprecated = proj_is_deprecated(
crs.get() );
2885 const QString name( proj_get_name(
crs.get() ) );
2887 QString sql = QStringLiteral(
"SELECT parameters,description,deprecated,srs_type,projection_acronym FROM tbl_srs WHERE auth_name='%1' AND auth_id='%2'" ).arg( authority, code );
2888 statement = database.
prepare( sql, result );
2889 if ( result != SQLITE_OK )
2898 QString dbOperation;
2899 bool dbSrsDeprecated = deprecated;
2900 if ( statement.
step() == SQLITE_ROW )
2904 dbSrsDeprecated = statement.
columnAsText( 2 ).toInt() != 0;
2909 if ( !dbSrsProj4.isEmpty() || !dbSrsDesc.isEmpty() )
2911 if ( proj4 != dbSrsProj4 || name != dbSrsDesc || deprecated != dbSrsDeprecated || dbSrsType != srsTypeString || dbOperation !=
operation )
2914 sql = QStringLiteral(
"UPDATE tbl_srs SET parameters=%1,description=%2,deprecated=%3, srs_type=%4,projection_acronym=%5 WHERE auth_name=%6 AND auth_id=%7" )
2917 .arg( deprecated ? 1 : 0 )
2922 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2924 QgsDebugError( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2927 errMsg ? errMsg :
"(unknown error)" ) );
2929 sqlite3_free( errMsg );
2943 const QString dbVals =
sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( authority, code ) );
2946 if ( !dbVals.isEmpty() )
2948 const QStringList parts = dbVals.split(
',' );
2949 srsId = parts.at( 0 );
2950 srId = parts.at( 1 );
2952 if ( srId.isEmpty() )
2954 srId = QString::number( nextSrId );
2957 if ( srsId.isEmpty() )
2959 srsId = QString::number( nextSrsId );
2963 if ( !srsId.isEmpty() )
2965 sql = QStringLiteral(
"INSERT INTO tbl_srs(srs_id, description,projection_acronym,ellipsoid_acronym,parameters,srid,auth_name,auth_id,is_geo,deprecated,srs_type) VALUES (%1, %2,%3,%4,%5,%6,%7,%8,%9,%10,%11)" )
2975 .arg( deprecated ? 1 : 0 )
2980 sql = QStringLiteral(
"INSERT INTO tbl_srs(description,projection_acronym,ellipsoid_acronym,parameters,srid,auth_name,auth_id,is_geo,deprecated,srs_type) VALUES (%1,%2,%3,%4,%5,%6,%7,%8,%9,%10)" )
2989 .arg( deprecated ? 1 : 0 )
2994 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) == SQLITE_OK )
3000 qCritical(
"Could not execute: %s [%s/%s]\n",
3001 sql.toLocal8Bit().constData(),
3002 sqlite3_errmsg( database.get() ),
3003 errMsg ? errMsg :
"(unknown error)" );
3007 sqlite3_free( errMsg );
3012 proj_string_list_destroy( codes );
3014 const QString sql = QStringLiteral(
"DELETE FROM tbl_srs WHERE auth_name='%1' AND NOT auth_id IN (%2)" ).arg( authority, allCodes.join(
',' ) );
3015 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
3017 deleted = sqlite3_changes( database.get() );
3022 qCritical(
"Could not execute: %s [%s]\n",
3023 sql.toLocal8Bit().constData(),
3024 sqlite3_errmsg( database.get() ) );
3028 proj_string_list_destroy( authorities );
3030 QString sql = QStringLiteral(
"UPDATE tbl_info set proj_major=%1,proj_minor=%2,proj_patch=%3" )
3031 .arg( QString::number( PROJ_VERSION_MAJOR ),
3032 QString::number( PROJ_VERSION_MINOR ),
3033 QString::number( PROJ_VERSION_PATCH ) );
3034 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
3036 QgsDebugError( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
3039 errMsg ? errMsg :
"(unknown error)" ) );
3041 sqlite3_free( errMsg );
3045 if ( sqlite3_exec( database.get(),
"COMMIT",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
3047 QgsDebugError( QStringLiteral(
"Could not commit transaction: %1 [%2]\n" ).arg(
3049 sqlite3_errmsg( database.get() ) )
3055 QgsDebugMsgLevel( QStringLiteral(
"CRS update (inserted:%1 updated:%2 deleted:%3 errors:%4)" ).arg( inserted ).arg( updated ).arg( deleted ).arg( errors ), 4 );
3063 return updated + inserted;
3066const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::stringCache()
3068 return *sStringCache();
3071const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::projCache()
3073 return *sProj4Cache();
3076const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::ogcCache()
3078 return *sOgcCache();
3081const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::wktCache()
3083 return *sWktCache();
3086const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srIdCache()
3088 return *sSrIdCache();
3091const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srsIdCache()
3093 return *sSrsIdCache();
3103 if (
PJ *obj = d->threadLocalProjObject() )
3110 const PJ_TYPE pjType = proj_get_type( geoCrs.get( ) );
3111 if ( pjType == PJ_TYPE_GEOCENTRIC_CRS )
3117 pjContext, PJ_ELLPS2D_LONGITUDE_LATITUDE,
"Degree", 1.0 ) );
3121 pjContext,
nullptr, datum ? datum.get() :
datumEnsemble.get(),
3124 if ( !geoGraphicCrs )
3151 if (
PJ *obj = d->threadLocalProjObject() )
3205 if (
PJ *obj = d->threadLocalProjObject() )
3238 if (
PJ *obj = d->threadLocalProjObject() )
3249 if (
PJ *obj = d->threadLocalProjObject() )
3262 else if (
PJ *obj = d->threadLocalProjObject() )
3265 return geoCrs ? QStringLiteral(
"%1:%2" ).arg( proj_get_id_auth_name( geoCrs.get(), 0 ), proj_get_id_code( geoCrs.get(), 0 ) ) : QString();
3275 return d->threadLocalProjObject();
3288 d->mIsValid =
false;
3290 d->mWktPreferred.clear();
3297 switch ( proj_get_type(
object ) )
3299 case PJ_TYPE_GEODETIC_CRS:
3300 case PJ_TYPE_GEOCENTRIC_CRS:
3301 case PJ_TYPE_GEOGRAPHIC_CRS:
3302 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
3303 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
3304 case PJ_TYPE_VERTICAL_CRS:
3305 case PJ_TYPE_PROJECTED_CRS:
3306 case PJ_TYPE_COMPOUND_CRS:
3307 case PJ_TYPE_TEMPORAL_CRS:
3308 case PJ_TYPE_ENGINEERING_CRS:
3309 case PJ_TYPE_BOUND_CRS:
3310 case PJ_TYPE_OTHER_CRS:
3326 const QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
3327 const QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
3328 if ( !authName.isEmpty() && !authCode.isEmpty() &&
createFromOgcWmsCrs( QStringLiteral(
"%1:%2" ).arg( authName, authCode ) ) )
3336 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
3347 QStringList projections;
3349 projections.reserve( res.size() );
3352 projections << QString::number(
crs.
srsid() );
3379 sSrIdCacheLock()->lockForWrite();
3380 if ( !sDisableSrIdCache )
3383 sDisableSrIdCache =
true;
3384 sSrIdCache()->clear();
3386 sSrIdCacheLock()->unlock();
3388 sOgcLock()->lockForWrite();
3389 if ( !sDisableOgcCache )
3392 sDisableOgcCache =
true;
3393 sOgcCache()->clear();
3395 sOgcLock()->unlock();
3397 sProj4CacheLock()->lockForWrite();
3398 if ( !sDisableProjCache )
3401 sDisableProjCache =
true;
3402 sProj4Cache()->clear();
3404 sProj4CacheLock()->unlock();
3406 sCRSWktLock()->lockForWrite();
3407 if ( !sDisableWktCache )
3410 sDisableWktCache =
true;
3411 sWktCache()->clear();
3413 sCRSWktLock()->unlock();
3415 sCRSSrsIdLock()->lockForWrite();
3416 if ( !sDisableSrsIdCache )
3419 sDisableSrsIdCache =
true;
3420 sSrsIdCache()->clear();
3422 sCRSSrsIdLock()->unlock();
3424 sCrsStringLock()->lockForWrite();
3425 if ( !sDisableStringCache )
3428 sDisableStringCache =
true;
3429 sStringCache()->clear();
3431 sCrsStringLock()->unlock();
3440 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3443 if ( !c1.d->mIsValid && c2.d->mIsValid )
3446 if ( c1.d->mIsValid && !c2.d->mIsValid )
3452 if ( c1IsUser && !c2IsUser )
3455 if ( !c1IsUser && c2IsUser )
3458 if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
3460 if ( c1.d->mAuthId != c2.d->mAuthId )
3461 return c1.d->mAuthId > c2.d->mAuthId;
3469 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3472 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3475 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3478 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3481 return c1.d->mCoordinateEpoch > c2.d->mCoordinateEpoch;
3489 if ( !c1.d->mIsValid && !c2.d->mIsValid )
3492 if ( c1.d->mIsValid && !c2.d->mIsValid )
3495 if ( !c1.d->mIsValid && c2.d->mIsValid )
3501 if ( !c1IsUser && c2IsUser )
3504 if ( c1IsUser && !c2IsUser )
3507 if ( !c1IsUser && !c2IsUser && !c1.d->mAuthId.isEmpty() && !c2.d->mAuthId.isEmpty() )
3509 if ( c1.d->mAuthId != c2.d->mAuthId )
3510 return c1.d->mAuthId < c2.d->mAuthId;
3518 if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3521 if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3524 if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3527 if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3530 return c1.d->mCoordinateEpoch < c2.d->mCoordinateEpoch;
3535 return !( c1 < c2 );
3539 return !( c1 > c2 );
1311 if ( d->mEllipsoidAcronym.isNull() ) {
…}
CrsIdentifierType
Available identifier string types for representing coordinate reference systems.
@ ShortString
A heavily abbreviated string, for use when a compact representation is required.
@ MediumString
A medium-length string, recommended for general purpose use.
DistanceUnit
Units of distance.
@ YardsBritishSears1922Truncated
British yards (Sears 1922 truncated)
@ MilesUSSurvey
US Survey miles.
@ LinksBritishSears1922
British links (Sears 1922)
@ YardsBritishBenoit1895A
British yards (Benoit 1895 A)
@ LinksBritishBenoit1895A
British links (Benoit 1895 A)
@ Centimeters
Centimeters.
@ YardsIndian1975
Indian yards (1975)
@ FeetUSSurvey
US Survey feet.
@ Millimeters
Millimeters.
@ FeetBritishSears1922
British feet (Sears 1922)
@ YardsClarkes
Clarke's yards.
@ YardsIndian
Indian yards.
@ FeetBritishBenoit1895B
British feet (Benoit 1895 B)
@ Miles
Terrestrial miles.
@ LinksUSSurvey
US Survey links.
@ ChainsUSSurvey
US Survey chains.
@ FeetClarkes
Clarke's feet.
@ Unknown
Unknown distance unit.
@ FeetBritish1936
British feet (1936)
@ FeetIndian1962
Indian feet (1962)
@ YardsBritishSears1922
British yards (Sears 1922)
@ FeetIndian1937
Indian feet (1937)
@ YardsIndian1937
Indian yards (1937)
@ Degrees
Degrees, for planar geographic CRS distance measurements.
@ ChainsBritishBenoit1895B
British chains (Benoit 1895 B)
@ LinksBritishSears1922Truncated
British links (Sears 1922 truncated)
@ ChainsBritishBenoit1895A
British chains (Benoit 1895 A)
@ YardsBritishBenoit1895B
British yards (Benoit 1895 B)
@ FeetBritish1865
British feet (1865)
@ YardsIndian1962
Indian yards (1962)
@ FeetBritishSears1922Truncated
British feet (Sears 1922 truncated)
@ MetersGermanLegal
German legal meter.
@ LinksBritishBenoit1895B
British links (Benoit 1895 B)
@ ChainsInternational
International chains.
@ LinksInternational
International links.
@ ChainsBritishSears1922Truncated
British chains (Sears 1922 truncated)
@ FeetIndian
Indian (geodetic) feet.
@ NauticalMiles
Nautical miles.
@ ChainsClarkes
Clarke's chains.
@ LinksClarkes
Clarke's links.
@ ChainsBritishSears1922
British chains (Sears 1922)
@ FeetIndian1975
Indian feet (1975)
@ FeetGoldCoast
Gold Coast feet.
@ FeetBritishBenoit1895A
British feet (Benoit 1895 A)
@ Critical
Critical/error message.
CrsType
Coordinate reference system types.
@ Compound
Compound (horizontal + vertical) CRS.
@ Projected
Projected CRS.
@ DerivedProjected
Derived projected CRS.
@ Engineering
Engineering CRS.
@ Geographic3d
3D geopraphic CRS
@ Geographic2d
2D geographic CRS
@ Geocentric
Geocentric CRS.
CrsDefinitionFormat
CRS definition formats.
@ Wkt
WKT format (always recommended over proj string format)
static const int USER_CRS_START_ID
Minimum ID number for a user-defined projection.
CrsAxisDirection
Coordinate reference system axis directions.
@ ColumnPositive
Column positive.
@ SouthSouthEast
South South East.
@ ColumnNegative
Column negative.
@ RowPositive
Row positive.
@ DisplayDown
Display down.
@ GeocentricZ
Geocentric (Z)
@ DisplayRight
Display right.
@ WestSouthWest
West South West.
@ RowNegative
Row negative.
@ NorthNorthEast
North North East.
@ EastNorthEast
East North East.
@ Unspecified
Unspecified.
@ NorthNorthWest
North North West.
@ GeocentricY
Geocentric (Y)
@ CounterClockwise
Counter clockwise.
@ SouthSouthWest
South South West.
@ DisplayLeft
Display left.
@ WestNorthWest
West North West.
@ EastSouthEast
East South East.
@ GeocentricX
Geocentric (X)
CrsWktVariant
Coordinate reference system WKT formatting variants.
@ Wkt2_2019Simplified
WKT2_2019 with the simplification rule of WKT2_SIMPLIFIED.
@ Wkt2_2015Simplified
Same as WKT2_2015 with the following exceptions: UNIT keyword used. ID node only on top element....
@ Wkt1Esri
WKT1 as traditionally output by ESRI software, deriving from OGC 99-049.
@ Preferred
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
@ Wkt2_2019
Full WKT2 string, conforming to ISO 19162:2019 / OGC 18-010, with all possible nodes and new keyword ...
@ Wkt2_2015
Full WKT2 string, conforming to ISO 19162:2015(E) / OGC 12-063r5 with all possible nodes and new keyw...
@ Wkt1Gdal
WKT1 as traditionally output by GDAL, deriving from OGC 01-009. A notable departure from WKT1_GDAL wi...
static QgsCoordinateReferenceSystemRegistry * coordinateReferenceSystemRegistry()
Returns the application's coordinate reference system (CRS) registry, which handles known CRS definit...
static QString qgisUserDatabaseFilePath()
Returns the path to the user qgis.db file.
static QString srsDatabaseFilePath()
Returns the path to the srs.db file.
void removeRecent(const QgsCoordinateReferenceSystem &crs)
Removes a CRS from the list of recently used CRS.
long addUserCrs(const QgsCoordinateReferenceSystem &crs, const QString &name, Qgis::CrsDefinitionFormat nativeFormat=Qgis::CrsDefinitionFormat::Wkt)
Adds a new crs definition as a custom ("USER") CRS.
void clearRecent()
Cleans the list of recently used CRS.
QList< QgsCoordinateReferenceSystem > recentCrs()
Returns a list of recently used CRS.
void pushRecent(const QgsCoordinateReferenceSystem &crs)
Pushes a recently used CRS to the top of the recent CRS list.
QMap< QString, QgsProjOperation > projOperations() const
Returns a map of all valid PROJ operations.
static QString translateProjection(const QString &projection)
Returns a translated string for a projection method.
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.
bool hasVerticalAxis() const
Returns true if the CRS has a vertical axis.
void validate()
Perform some validation on this CRS.
~QgsCoordinateReferenceSystem()
QgsRectangle bounds() const
Returns the approximate bounds for the region the CRS is usable within.
QString toProj() const
Returns a Proj string representation of this CRS.
static CUSTOM_CRS_VALIDATION customCrsValidation()
Gets custom function.
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
static QgsCoordinateReferenceSystem createCompoundCrs(const QgsCoordinateReferenceSystem &horizontalCrs, const QgsCoordinateReferenceSystem &verticalCrs, QString &error)
Given a horizontal and vertical CRS, attempts to create a compound CRS from them.
static Q_INVOKABLE QgsCoordinateReferenceSystem fromEpsgId(long epsg)
Creates a CRS from a given EPSG ID.
Q_DECL_DEPRECATED bool createFromProj4(const QString &projString)
Sets this CRS by passing it a PROJ style formatted string.
static Q_DECL_DEPRECATED QStringList recentProjections()
Returns a list of recently used projections.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
QString toOgcUri() const
Returns the crs as OGC URI (format: http://www.opengis.net/def/crs/OGC/1.3/CRS84) Returns an empty st...
QString toOgcUrn() const
Returns the crs as OGC URN (format: urn:ogc:def:crs:OGC:1.3:CRS84) Returns an empty string on failure...
static void setCustomCrsValidation(CUSTOM_CRS_VALIDATION f)
Sets custom function to force valid CRS.
static Q_DECL_DEPRECATED void pushRecentCoordinateReferenceSystem(const QgsCoordinateReferenceSystem &crs)
Pushes a recently used CRS to the top of the recent CRS list.
long postgisSrid() const
Returns PostGIS SRID for the CRS.
QgsCoordinateReferenceSystem horizontalCrs() const
Returns the horizontal CRS associated with this CRS object.
Q_DECL_DEPRECATED long findMatchingProj()
Walks the CRS databases (both system and user database) trying to match stored PROJ string to a datab...
static QList< long > validSrsIds()
Returns a list of all valid SRS IDs present in the CRS database.
QgsProjectionFactors factors(const QgsPoint &point) const
Calculate various cartographic properties, such as scale factors, angular distortion and meridian con...
void setValidationHint(const QString &html)
Set user hint for validation.
Q_DECL_DEPRECATED QString toProj4() const
Returns a Proj string representation of this CRS.
bool operator==(const QgsCoordinateReferenceSystem &srs) const
QString projectionAcronym() const
Returns the projection acronym for the projection used by the CRS.
QString userFriendlyIdentifier(Qgis::CrsIdentifierType type=Qgis::CrsIdentifierType::MediumString) const
Returns a user friendly identifier for the CRS.
Qgis::CrsDefinitionFormat nativeFormat() const
Returns the native format for the CRS definition.
CrsType
Enumeration of types of IDs accepted in createFromId() method.
@ InternalCrsId
Internal ID used by QGIS in the local SQLite database.
@ PostgisCrsId
SRID used in PostGIS. DEPRECATED – DO NOT USE.
bool createFromUserInput(const QString &definition)
Set up this CRS from various text formats.
QgsCoordinateReferenceSystem()
Constructs an invalid CRS object.
static int syncDatabase()
Update proj.4 parameters in our database from proj.4.
bool operator!=(const QgsCoordinateReferenceSystem &srs) const
void setNativeFormat(Qgis::CrsDefinitionFormat format)
Sets the native format for the CRS definition.
bool createFromProj(const QString &projString, bool identify=true)
Sets this CRS by passing it a PROJ style formatted string.
QgsCoordinateReferenceSystem verticalCrs() const
Returns the vertical CRS associated with this CRS object.
static Q_DECL_DEPRECATED void removeRecentCoordinateReferenceSystem(const QgsCoordinateReferenceSystem &crs)
Removes a CRS from the list of recently used CRS.
QgsDatumEnsemble datumEnsemble() const
Attempts to retrieve datum ensemble details from the CRS.
QgsCoordinateReferenceSystem toGeographicCrs() const
Returns the geographic CRS associated with this CRS object.
bool isDynamic() const
Returns true if the CRS is a dynamic CRS.
bool createFromSrsId(long srsId)
Sets this CRS by lookup of internal QGIS CRS ID in the CRS database.
Q_DECL_DEPRECATED bool createFromId(long id, CrsType type=PostgisCrsId)
Sets this CRS by lookup of the given ID in the CRS database.
static QgsCoordinateReferenceSystem fromProjObject(PJ *object)
Constructs a QgsCoordinateReferenceSystem from a PROJ PJ object.
static void invalidateCache(bool disableCache=false)
Clears the internal cache used to initialize QgsCoordinateReferenceSystem objects.
QgsCoordinateReferenceSystem & operator=(const QgsCoordinateReferenceSystem &srs)
void updateDefinition()
Updates the definition and parameters of the coordinate reference system to their latest values.
static QgsCoordinateReferenceSystem fromProj(const QString &proj)
Creates a CRS from a proj style formatted string.
static Q_DECL_DEPRECATED void setupESRIWktFix()
Make sure that ESRI WKT import is done properly.
static Q_DECL_DEPRECATED QList< QgsCoordinateReferenceSystem > recentCoordinateReferenceSystems()
Returns a list of recently used CRS.
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
PJ * projObject() const
Returns the underlying PROJ PJ object corresponding to the CRS, or nullptr if the CRS is invalid.
void setCoordinateEpoch(double epoch)
Sets the coordinate epoch, as a decimal year.
Q_DECL_DEPRECATED bool createFromSrid(long srid)
Sets this CRS by lookup of the given PostGIS SRID in the CRS database.
long saveAsUserCrs(const QString &name, Qgis::CrsDefinitionFormat nativeFormat=Qgis::CrsDefinitionFormat::Wkt)
Saves the CRS as a new custom ("USER") CRS.
static Q_DECL_DEPRECATED void clearRecentCoordinateReferenceSystems()
Cleans the list of recently used CRS.
QString celestialBodyName() const
Attempts to retrieve the name of the celestial body associated with the CRS (e.g.
static QgsCoordinateReferenceSystem fromWkt(const QString &wkt)
Creates a CRS from a WKT spatial ref sys definition string.
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
static QgsCoordinateReferenceSystem createGeocentricCrs(const QString &ellipsoid)
Creates a geocentric CRS given an ellipsoid definition.
static QgsCoordinateReferenceSystem fromSrsId(long srsId)
Creates a CRS from a specified QGIS SRS ID.
double coordinateEpoch() const
Returns the coordinate epoch, as a decimal year.
QString geographicCrsAuthId() const
Returns auth id of related geographic CRS.
QString validationHint() const
Gets user hint for validation.
QgsProjOperation operation() const
Returns information about the PROJ operation associated with the coordinate reference system,...
QgsCoordinateReferenceSystem toGeocentricCrs() const
Returns a new geocentric CRS based on this CRS object.
QList< Qgis::CrsAxisDirection > axisOrdering() const
Returns an ordered list of the axis directions reflecting the native axis order for the CRS.
long srsid() const
Returns the internal CRS ID, if available.
Qgis::CrsType type() const
Returns the type of the CRS.
Qgis::DistanceUnit mapUnits
bool hasAxisInverted() const
Returns whether the axis order is inverted for the CRS compared to the order east/north (longitude/la...
bool createFromProjObject(PJ *object)
Sets this CRS by passing it a PROJ PJ object, corresponding to a PROJ CRS object.
bool isDeprecated() const
Returns true if the CRS is considered deprecated.
static Q_DECL_DEPRECATED QgsCoordinateReferenceSystem fromProj4(const QString &proj4)
Creates a CRS from a proj style formatted string.
Contains information about a member of a datum ensemble.
Contains information about a datum ensemble.
static EllipsoidParameters ellipsoidParameters(const QString &ellipsoid)
Returns the parameters for the specified ellipsoid.
Sets the current locale to the c locale for the lifetime of the object.
static void warning(const QString &msg)
Goes to qWarning.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
@ UNKNOWN
Unknown/unhandled flavor.
@ AUTH_CODE
E.g EPSG:4326.
static CRSFlavor parseCrsName(const QString &crsName, QString &authority, QString &code)
Parse a CRS name in one of the flavors of OGC services, and decompose it as authority and code.
static QString OGRSpatialReferenceToWkt(OGRSpatialReferenceH srs)
Returns a WKT string corresponding to the specified OGR srs object.
Point geometry type, with support for z-dimension and m-values.
static PJ_CONTEXT * get()
Returns a thread local instance of a proj context, safe for use in the current thread.
Contains information about a PROJ operation.
static proj_pj_unique_ptr crsToHorizontalCrs(const PJ *crs)
Given a PROJ crs (which may be a compound or bound crs, or some other type), extract the horizontal c...
@ FlagMatchBoundCrsToUnderlyingSourceCrs
Allow matching a BoundCRS object to its underlying SourceCRS.
static proj_pj_unique_ptr createCompoundCrs(const PJ *horizontalCrs, const PJ *verticalCrs, QStringList *errors=nullptr)
Given a PROJ horizontal and vertical CRS, attempt to create a compound CRS from them.
static bool isDynamic(const PJ *crs)
Returns true if the given proj coordinate system is a dynamic CRS.
static proj_pj_unique_ptr unboundCrs(const PJ *crs)
Given a PROJ crs (which may be a compound or bound crs, or some other type), ensure that it is not a ...
static bool identifyCrs(const PJ *crs, QString &authName, QString &authCode, IdentifyFlags flags=IdentifyFlags())
Attempts to identify a crs, matching it to a known authority and code within an acceptable level of t...
static bool hasVerticalAxis(const PJ *crs)
Returns true if a PROJ crs has a vertical axis.
static proj_pj_unique_ptr crsToVerticalCrs(const PJ *crs)
Given a PROJ crs (which may be a compound crs, or some other type), extract the vertical crs from it.
static proj_pj_unique_ptr crsToDatumEnsemble(const PJ *crs)
Given a PROJ crs, attempt to retrieve the datum ensemble from it.
std::unique_ptr< PJ, ProjPJDeleter > proj_pj_unique_ptr
Scoped Proj PJ object.
static bool axisOrderIsSwapped(const PJ *crs)
Returns true if the given proj coordinate system uses requires y/x coordinate order instead of x/y.
Contains various cartographic properties, such as scale factors, angular distortion and meridian conv...
A convenience class that simplifies locking and unlocking QReadWriteLocks.
void unlock()
Unlocks the lock.
void changeMode(Mode mode)
Change the mode of the lock to mode.
A rectangle specified with double values.
void setYMinimum(double y)
Set the minimum y value.
void setXMinimum(double x)
Set the minimum x value.
void setYMaximum(double y)
Set the maximum y value.
void setXMaximum(double x)
Set the maximum x value.
static QString quotedString(const QString &value)
Returns a quoted string value, surround by ' characters and with special characters correctly escaped...
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
int open(const QString &path)
Opens the database at the specified file path.
QString errorMessage() const
Returns the most recent error message encountered by the database.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
QString columnAsText(int column) const
Returns the column value from the current statement row as a string.
QString columnName(int column) const
Returns the name of column.
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
qlonglong columnAsInt64(int column) const
Gets column value from the current statement row as a long long integer (64 bits).
int columnCount() const
Gets the number of columns that this statement returns.
#define Q_NOWARN_DEPRECATED_POP
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
#define Q_NOWARN_DEPRECATED_PUSH
bool qgsNanCompatibleEquals(double a, double b)
Compare two doubles, treating nan values as equal.
QString getFullProjString(PJ *obj)
bool operator>=(const QgsCoordinateReferenceSystem &c1, const QgsCoordinateReferenceSystem &c2)
bool operator<(const QgsCoordinateReferenceSystem &c1, const QgsCoordinateReferenceSystem &c2)
bool testIsGeographic(PJ *crs)
void getOperationAndEllipsoidFromProjString(const QString &proj, QString &operation, QString &ellipsoid)
QHash< QString, QgsCoordinateReferenceSystem > StringCrsCacheHash
bool operator<=(const QgsCoordinateReferenceSystem &c1, const QgsCoordinateReferenceSystem &c2)
QHash< long, QgsCoordinateReferenceSystem > SrIdCrsCacheHash
bool operator>(const QgsCoordinateReferenceSystem &c1, const QgsCoordinateReferenceSystem &c2)
void(* CUSTOM_CRS_VALIDATION)(QgsCoordinateReferenceSystem &)
const QMap< QString, QString > sAuthIdToQgisSrsIdMap
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
const QgsCoordinateReferenceSystem & crs
Contains parameters for an ellipsoid.
double semiMajor
Semi-major axis, in meters.
bool valid
Whether ellipsoid parameters are valid.
double inverseFlattening
Inverse flattening.