29 #include <QDomElement>
32 #include <QTextStream>
34 #include <QRegularExpression>
45 #if PROJ_VERSION_MAJOR>=6
48 #include <proj_experimental.h>
54 #include <ogr_srs_api.h>
55 #include <cpl_error.h>
60 #if PROJ_VERSION_MAJOR<6
72 bool QgsCoordinateReferenceSystem::sDisableSrIdCache =
false;
76 bool QgsCoordinateReferenceSystem::sDisableOgcCache =
false;
80 bool QgsCoordinateReferenceSystem::sDisableProjCache =
false;
84 bool QgsCoordinateReferenceSystem::sDisableWktCache =
false;
88 bool QgsCoordinateReferenceSystem::sDisableSrsIdCache =
false;
92 bool QgsCoordinateReferenceSystem::sDisableStringCache =
false;
94 #if PROJ_VERSION_MAJOR>=6
95 QString getFullProjString( PJ *obj )
99 QgsProjUtils::proj_pj_unique_ptr boundCrs( proj_crs_create_bound_crs_to_WGS84(
QgsProjContext::get(), obj,
nullptr ) );
102 if (
const char *proj4src = proj_as_proj_string(
QgsProjContext::get(), boundCrs.get(), PJ_PROJ_4,
nullptr ) )
104 return QString( proj4src );
122 d =
new QgsCoordinateReferenceSystemPrivate();
128 d =
new QgsCoordinateReferenceSystemPrivate();
136 , mValidationHint( srs.mValidationHint )
143 mValidationHint = srs.mValidationHint;
153 const auto constDbs = dbs;
154 for (
const QString &db : constDbs )
156 QFileInfo myInfo( db );
157 if ( !myInfo.exists() )
159 QgsDebugMsg(
"failed : " + db +
" does not exist!" );
167 int result = openDatabase( db, database );
168 if ( result != SQLITE_OK )
170 QgsDebugMsg(
"failed : " + db +
" could not be opened!" );
174 QString sql = QStringLiteral(
"select srs_id from tbl_srs" );
176 statement = database.
prepare( sql, rc );
180 int ret = statement.
step();
182 if ( ret == SQLITE_DONE )
188 if ( ret == SQLITE_ROW )
194 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
199 std::sort( results.begin(), results.end() );
263 result = createFromPostgisSrid(
id );
270 QgsDebugMsg( QStringLiteral(
"Unexpected case reached!" ) );
277 if ( definition.isEmpty() )
281 if ( !sDisableStringCache )
283 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sStringCache()->constFind( definition );
284 if ( crsIt != sStringCache()->constEnd() )
287 *
this = crsIt.value();
294 const thread_local QRegularExpression reCrsId( QStringLiteral(
"^(epsg|esri|osgeo|ignf|zangi|iau2000|postgis|internal|user)\\:(\\w+)$" ), QRegularExpression::CaseInsensitiveOption );
295 QRegularExpressionMatch match = reCrsId.match( definition );
296 if ( match.capturedStart() == 0 )
298 QString authName = match.captured( 1 ).toLower();
299 if ( authName == QLatin1String(
"epsg" ) )
303 else if ( authName == QLatin1String(
"postgis" ) )
305 const long id = match.captured( 2 ).toLong();
306 result = createFromPostgisSrid(
id );
308 else if ( authName == QLatin1String(
"esri" ) || authName == QLatin1String(
"osgeo" ) || authName == QLatin1String(
"ignf" ) || authName == QLatin1String(
"zangi" ) || authName == QLatin1String(
"iau2000" ) )
314 const long id = match.captured( 2 ).toLong();
322 const thread_local QRegularExpression reCrsStr( QStringLiteral(
"^(?:(wkt|proj4|proj)\\:)?(.+)$" ), QRegularExpression::CaseInsensitiveOption );
323 match = reCrsStr.match( definition );
324 if ( match.capturedStart() == 0 )
326 if ( match.captured( 1 ).startsWith( QLatin1String(
"proj" ), Qt::CaseInsensitive ) )
338 if ( !sDisableStringCache )
339 sStringCache()->insert( definition, *
this );
345 if ( definition.isEmpty() )
351 #if PROJ_VERSION_MAJOR<6
353 if ( definition.startsWith( QLatin1String(
"ESRI::" ) ) )
361 if ( OSRSetFromUserInput(
crs, definition.toLocal8Bit().constData() ) == OGRERR_NONE )
364 OSRDestroySpatialReference(
crs );
374 const char *configOld = CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" );
375 const char *configNew =
"GEOGCS";
377 if ( strcmp( configOld,
"" ) == 0 )
379 CPLSetConfigOption(
"GDAL_FIX_ESRI_WKT", configNew );
380 if ( strcmp( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) != 0 )
382 .arg( configNew, CPLGetConfigOption(
"GDAL_FIX_ESRI_WKT",
"" ) ) );
383 QgsDebugMsgLevel( QStringLiteral(
"set GDAL_FIX_ESRI_WKT : %1" ).arg( configNew ), 4 );
387 QgsDebugMsgLevel( QStringLiteral(
"GDAL_FIX_ESRI_WKT was already set : %1" ).arg( configNew ), 4 );
397 if ( !sDisableOgcCache )
399 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sOgcCache()->constFind(
crs );
400 if ( crsIt != sOgcCache()->constEnd() )
403 *
this = crsIt.value();
409 QString wmsCrs =
crs;
411 thread_local
const QRegExp re_uri( QStringLiteral(
"http://www\\.opengis\\.net/def/crs/([^/]+).+/([^/]+)" ), Qt::CaseInsensitive );
412 thread_local
const QRegExp re_urn( QStringLiteral(
"urn:ogc:def:crs:([^:]+).+([^:]+)" ), Qt::CaseInsensitive );
413 if ( re_uri.exactMatch( wmsCrs ) )
415 wmsCrs = re_uri.cap( 1 ) +
':' + re_uri.cap( 2 );
417 else if ( re_urn.exactMatch( wmsCrs ) )
419 wmsCrs = re_urn.cap( 1 ) +
':' + re_urn.cap( 2 );
423 thread_local
const QRegExp re_urn_custom( QStringLiteral(
"(user|custom|qgis):(\\d+)" ), Qt::CaseInsensitive );
424 if ( re_urn_custom.exactMatch( wmsCrs ) &&
createFromSrsId( re_urn_custom.cap( 2 ).toInt() ) )
427 if ( !sDisableOgcCache )
428 sOgcCache()->insert(
crs, *
this );
433 #if PROJ_VERSION_MAJOR>=6
435 const QString legacyKey = wmsCrs.toLower();
436 for (
auto it = sAuthIdToQgisSrsIdMap.constBegin(); it != sAuthIdToQgisSrsIdMap.constEnd(); ++it )
438 if ( it.key().compare( legacyKey, Qt::CaseInsensitive ) == 0 )
440 const QStringList parts = it.key().split(
':' );
441 const QString auth = parts.at( 0 );
442 const QString code = parts.at( 1 );
443 if ( loadFromAuthCode( auth, code ) )
446 if ( !sDisableOgcCache )
447 sOgcCache()->insert(
crs, *
this );
457 if ( !sDisableOgcCache )
458 sOgcCache()->insert(
crs, *
this );
463 if ( wmsCrs.compare( QLatin1String(
"CRS:27" ), Qt::CaseInsensitive ) == 0 ||
464 wmsCrs.compare( QLatin1String(
"OGC:CRS27" ), Qt::CaseInsensitive ) == 0 )
471 if ( wmsCrs.compare( QLatin1String(
"CRS:83" ), Qt::CaseInsensitive ) == 0 ||
472 wmsCrs.compare( QLatin1String(
"OGC:CRS83" ), Qt::CaseInsensitive ) == 0 )
479 if ( wmsCrs.compare( QLatin1String(
"CRS:84" ), Qt::CaseInsensitive ) == 0 ||
480 wmsCrs.compare( QLatin1String(
"OGC:CRS84" ), Qt::CaseInsensitive ) == 0 )
484 d->mAxisInverted =
false;
485 d->mAxisInvertedDirty =
false;
489 if ( !sDisableOgcCache )
490 sOgcCache()->insert(
crs, *
this );
496 if ( !sDisableOgcCache )
506 if ( d->mIsValid || !sCustomSrsValidation )
510 if ( sCustomSrsValidation )
511 sCustomSrsValidation( *
this );
516 return createFromPostgisSrid(
id );
519 bool QgsCoordinateReferenceSystem::createFromPostgisSrid(
const long id )
522 if ( !sDisableSrIdCache )
524 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrIdCache()->constFind(
id );
525 if ( crsIt != sSrIdCache()->constEnd() )
528 *
this = crsIt.value();
534 #if PROJ_VERSION_MAJOR>=6
536 for (
auto it = sAuthIdToQgisSrsIdMap.constBegin(); it != sAuthIdToQgisSrsIdMap.constEnd(); ++it )
538 if ( it.value().endsWith( QStringLiteral(
",%1" ).arg(
id ) ) )
540 const QStringList parts = it.key().split(
':' );
541 const QString auth = parts.at( 0 );
542 const QString code = parts.at( 1 );
543 if ( loadFromAuthCode( auth, code ) )
546 if ( !sDisableSrIdCache )
547 sSrIdCache()->insert(
id, *
this );
558 if ( !sDisableSrIdCache )
559 sSrIdCache()->insert(
id, *
this );
567 if ( !sDisableSrsIdCache )
569 QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrsIdCache()->constFind(
id );
570 if ( crsIt != sSrsIdCache()->constEnd() )
573 *
this = crsIt.value();
579 #if PROJ_VERSION_MAJOR>=6
581 for (
auto it = sAuthIdToQgisSrsIdMap.constBegin(); it != sAuthIdToQgisSrsIdMap.constEnd(); ++it )
583 if ( it.value().startsWith( QString::number(
id ) +
',' ) )
585 const QStringList parts = it.key().split(
':' );
586 const QString auth = parts.at( 0 );
587 const QString code = parts.at( 1 );
588 if ( loadFromAuthCode( auth, code ) )
591 if ( !sDisableSrsIdCache )
592 sSrsIdCache()->insert(
id, *
this );
601 QStringLiteral(
"srs_id" ), QString::number(
id ) );
604 if ( !sDisableSrsIdCache )
605 sSrsIdCache()->insert(
id, *
this );
609 bool QgsCoordinateReferenceSystem::loadFromDatabase(
const QString &db,
const QString &expression,
const QString &value )
613 QgsDebugMsgLevel(
"load CRS from " + db +
" where " + expression +
" is " + value, 3 );
615 d->mWktPreferred.clear();
617 QFileInfo myInfo( db );
618 if ( !myInfo.exists() )
620 QgsDebugMsg(
"failed : " + db +
" does not exist!" );
628 myResult = openDatabase( db, database );
629 if ( myResult != SQLITE_OK )
646 QString mySql =
"select srs_id,description,projection_acronym,"
647 "ellipsoid_acronym,parameters,srid,auth_name||':'||auth_id,is_geo,wkt "
649 statement = database.
prepare( mySql, myResult );
652 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
657 #if PROJ_VERSION_MAJOR>=6
658 d->mEllipsoidAcronym.clear();
663 d->mWktPreferred.clear();
666 d->mIsGeographic = statement.
columnAsText( 7 ).toInt() != 0;
668 d->mAxisInvertedDirty =
true;
670 if ( d->mSrsId >=
USER_CRS_START_ID && ( d->mAuthId.isEmpty() || d->mAuthId == QChar(
':' ) ) )
672 d->mAuthId = QStringLiteral(
"USER:%1" ).arg( d->mSrsId );
674 else if ( !d->mAuthId.startsWith( QLatin1String(
"USER:" ), Qt::CaseInsensitive ) )
676 #if PROJ_VERSION_MAJOR>=6
677 QStringList parts = d->mAuthId.split(
':' );
678 QString auth = parts.at( 0 );
679 QString code = parts.at( 1 );
682 QgsProjUtils::proj_pj_unique_ptr
crs( proj_create_from_database(
QgsProjContext::get(), auth.toLatin1(), code.toLatin1(), PJ_CATEGORY_CRS,
false,
nullptr ) );
683 d->setPj( QgsProjUtils::crsToSingleCrs(
crs.get() ) );
686 d->mIsValid = d->hasPj();
688 OSRDestroySpatialReference( d->mCRS );
689 d->mCRS = OSRNewSpatialReference(
nullptr );
690 d->mIsValid = OSRSetFromUserInput( d->mCRS, d->mAuthId.toLower().toLatin1() ) == OGRERR_NONE;
697 if ( !wkt.isEmpty() )
699 setWktString( wkt,
false );
705 setProjString( d->mProj4 );
715 #if PROJ_VERSION_MAJOR>=6
716 void QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread(
PJ_CONTEXT *pj_context )
723 if ( !sDisableSrIdCache )
726 if ( !sDisableSrIdCache )
728 for (
auto it = sSrIdCache()->begin(); it != sSrIdCache()->end(); )
730 auto &v = it.value();
731 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
732 it = sSrIdCache()->erase( it );
738 if ( !sDisableOgcCache )
741 if ( !sDisableOgcCache )
743 for (
auto it = sOgcCache()->begin(); it != sOgcCache()->end(); )
745 auto &v = it.value();
746 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
747 it = sOgcCache()->erase( it );
753 if ( !sDisableProjCache )
756 if ( !sDisableProjCache )
758 for (
auto it = sProj4Cache()->begin(); it != sProj4Cache()->end(); )
760 auto &v = it.value();
761 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
762 it = sProj4Cache()->erase( it );
768 if ( !sDisableWktCache )
771 if ( !sDisableWktCache )
773 for (
auto it = sWktCache()->begin(); it != sWktCache()->end(); )
775 auto &v = it.value();
776 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
777 it = sWktCache()->erase( it );
783 if ( !sDisableSrsIdCache )
786 if ( !sDisableSrsIdCache )
788 for (
auto it = sSrsIdCache()->begin(); it != sSrsIdCache()->end(); )
790 auto &v = it.value();
791 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
792 it = sSrsIdCache()->erase( it );
798 if ( !sDisableStringCache )
801 if ( !sDisableStringCache )
803 for (
auto it = sStringCache()->begin(); it != sStringCache()->end(); )
805 auto &v = it.value();
806 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
807 it = sStringCache()->erase( it );
818 if ( d->mAxisInvertedDirty )
820 #if PROJ_VERSION_MAJOR>=6
821 d->mAxisInverted = QgsProjUtils::axisOrderIsSwapped( d->threadLocalProjObject() );
823 OGRAxisOrientation orientation;
824 OSRGetAxis( d->mCRS, OSRIsGeographic( d->mCRS ) ?
"GEOGCS" :
"PROJCS", 0, &orientation );
827 if ( orientation == OAO_Other && d->mAuthId.startsWith( QLatin1String(
"EPSG:" ), Qt::CaseInsensitive ) )
831 if ( OSRImportFromEPSGA(
crs, d->mAuthId.midRef( 5 ).toInt() ) == OGRERR_NONE )
833 OSRGetAxis(
crs, OSRIsGeographic(
crs ) ?
"GEOGCS" :
"PROJCS", 0, &orientation );
836 OSRDestroySpatialReference(
crs );
839 d->mAxisInverted = orientation == OAO_North;
841 d->mAxisInvertedDirty =
false;
844 return d->mAxisInverted;
849 return createFromWktInternal( wkt, QString() );
852 bool QgsCoordinateReferenceSystem::createFromWktInternal(
const QString &wkt,
const QString &description )
860 if ( !sDisableWktCache )
862 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sWktCache()->constFind( wkt );
863 if ( crsIt != sWktCache()->constEnd() )
866 *
this = crsIt.value();
868 if ( !
description.isEmpty() && d->mDescription.isEmpty() )
873 sWktCache()->insert( wkt, *
this );
882 d->mWktPreferred.clear();
885 QgsDebugMsgLevel( QStringLiteral(
"theWkt is uninitialized, operation failed" ), 4 );
890 QgsCoordinateReferenceSystem::RecordMap record = getRecord(
"select * from tbl_srs where wkt=" +
QgsSqliteUtils::quotedString( wkt ) +
" order by deprecated" );
891 if ( !record.empty() )
893 long srsId = record[QStringLiteral(
"srs_id" )].toLong();
906 if ( d->mSrsId == 0 )
908 #if PROJ_VERSION_MAJOR>=6
910 long id = matchToUserCrs();
920 if ( !sDisableWktCache )
921 sWktCache()->insert( wkt, *
this );
939 if ( projString.isEmpty() )
944 if ( projString.trimmed().isEmpty() )
948 d->mWktPreferred.clear();
953 if ( !sDisableProjCache )
955 QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sProj4Cache()->constFind( projString );
956 if ( crsIt != sProj4Cache()->constEnd() )
959 *
this = crsIt.value();
973 QString myProj4String = projString.trimmed();
974 myProj4String.remove( QStringLiteral(
"+type=crs" ) );
975 myProj4String = myProj4String.trimmed();
978 d->mWktPreferred.clear();
979 #if PROJ_VERSION_MAJOR>=6
983 const QString projCrsString = myProj4String + ( myProj4String.contains( QStringLiteral(
"+type=crs" ) ) ? QString() : QStringLiteral(
" +type=crs" ) );
984 QgsProjUtils::proj_pj_unique_ptr
crs( proj_create(
QgsProjContext::get(), projCrsString.toLatin1().constData() ) );
991 const QString
authid = QStringLiteral(
"%1:%2" ).arg( authName, authCode );
995 if ( !sDisableProjCache )
996 sProj4Cache()->insert( projString, *
this );
1003 QgsCoordinateReferenceSystem::RecordMap myRecord = getRecord(
"select * from tbl_srs where parameters=" +
QgsSqliteUtils::quotedString( myProj4String ) +
" order by deprecated" );
1005 if ( !myRecord.empty() )
1007 id = myRecord[QStringLiteral(
"srs_id" )].toLong();
1016 setProjString( myProj4String );
1019 id = matchToUserCrs();
1028 setProjString( myProj4String );
1032 Q_UNUSED( identify )
1034 QRegExp projRegExp( QStringLiteral(
"\\+proj=(\\S+)" ) );
1035 int myStart = projRegExp.indexIn( myProj4String );
1036 if ( myStart == -1 )
1039 if ( !sDisableProjCache )
1040 sProj4Cache()->insert( projString, *
this );
1045 d->mProjectionAcronym = projRegExp.cap( 1 );
1047 QRegExp myEllipseRegExp( QStringLiteral(
"\\+ellps=(\\S+)" ) );
1048 myStart = myEllipseRegExp.indexIn( myProj4String );
1049 if ( myStart == -1 )
1051 d->mEllipsoidAcronym.clear();
1055 d->mEllipsoidAcronym = myEllipseRegExp.cap( 1 );
1058 QRegExp myAxisRegExp( QStringLiteral(
"\\+a=(\\S+)" ) );
1059 myStart = myAxisRegExp.indexIn( myProj4String );
1062 QgsCoordinateReferenceSystem::RecordMap myRecord;
1068 myRecord = getRecord(
"select * from tbl_srs where parameters=" +
QgsSqliteUtils::quotedString( myProj4String ) +
" order by deprecated" );
1069 if ( myRecord.empty() )
1074 QRegExp myLat1RegExp( QStringLiteral(
"\\+lat_1=\\S+" ) );
1075 QRegExp myLat2RegExp( QStringLiteral(
"\\+lat_2=\\S+" ) );
1082 myStart1 = myLat1RegExp.indexIn( myProj4String, myStart1 );
1083 myStart2 = myLat2RegExp.indexIn( myProj4String, myStart2 );
1084 if ( myStart1 != -1 && myStart2 != -1 )
1086 myLength1 = myLat1RegExp.matchedLength();
1087 myLength2 = myLat2RegExp.matchedLength();
1092 if ( !lat1Str.isEmpty() && !lat2Str.isEmpty() )
1095 QString proj4StringModified = myProj4String;
1100 myStart2 = myLat2RegExp.indexIn( projString, myStart2 );
1102 QgsDebugMsgLevel( QStringLiteral(
"trying proj4string match with swapped lat_1,lat_2" ), 4 );
1103 myRecord = getRecord(
"select * from tbl_srs where parameters=" +
QgsSqliteUtils::quotedString( proj4StringModified.trimmed() ) +
" order by deprecated" );
1107 if ( myRecord.empty() )
1114 QString sql = QStringLiteral(
"SELECT * FROM tbl_srs WHERE " );
1121 QStringList myParams;
1122 thread_local
const QRegularExpression regExp(
"\\s+(?=\\+)" );
1124 const auto constSplit = myProj4String.split( regExp, QString::SkipEmptyParts );
1125 for (
const QString ¶m : constSplit )
1127 QString arg = QStringLiteral(
"' '||parameters||' ' LIKE %1" ).arg(
QgsSqliteUtils::quotedString( QStringLiteral(
"% %1 %" ).arg( param.trimmed() ) ) );
1128 if ( param.startsWith( QLatin1String(
"+datum=" ) ) )
1135 delim = QStringLiteral(
" AND " );
1136 myParams << param.trimmed();
1141 if ( !datum.isEmpty() )
1143 myRecord = getRecord( sql + delim + datum +
" order by deprecated" );
1146 if ( myRecord.empty() )
1149 myRecord = getRecord( sql +
" order by deprecated" );
1152 if ( !myRecord.empty() )
1155 QStringList foundParams;
1156 const auto constSplit = myRecord[
"parameters"].split( regExp, QString::SkipEmptyParts );
1157 for (
const QString ¶m : constSplit )
1159 if ( !param.startsWith( QLatin1String(
"+datum=" ) ) )
1160 foundParams << param.trimmed();
1166 if ( myParams != foundParams )
1173 if ( !myRecord.empty() )
1175 mySrsId = myRecord[QStringLiteral(
"srs_id" )].toLong();
1184 setProjString( myProj4String );
1194 d->mIsValid =
false;
1201 QgsDebugMsgLevel( QStringLiteral(
"Projection is not found in databases." ), 4 );
1203 setProjString( myProj4String );
1208 if ( !sDisableProjCache )
1209 sProj4Cache()->insert( projString, *
this );
1215 QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord(
const QString &sql )
1217 QString myDatabaseFileName;
1218 QgsCoordinateReferenceSystem::RecordMap myMap;
1219 QString myFieldName;
1220 QString myFieldValue;
1227 QFileInfo myInfo( myDatabaseFileName );
1228 if ( !myInfo.exists() )
1230 QgsDebugMsg(
"failed : " + myDatabaseFileName +
" does not exist!" );
1235 myResult = openDatabase( myDatabaseFileName, database );
1236 if ( myResult != SQLITE_OK )
1241 statement = database.
prepare( sql, myResult );
1243 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1247 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1249 myFieldName = statement.
columnName( myColNo );
1251 myMap[myFieldName] = myFieldValue;
1253 if ( statement.
step() != SQLITE_DONE )
1255 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1257 #if PROJ_VERSION_MAJOR<6
1267 if ( myMap.empty() )
1270 QFileInfo myFileInfo;
1271 myFileInfo.setFile( myDatabaseFileName );
1272 if ( !myFileInfo.exists() )
1274 QgsDebugMsg( QStringLiteral(
"user qgis.db not found" ) );
1279 myResult = openDatabase( myDatabaseFileName, database );
1280 if ( myResult != SQLITE_OK )
1285 statement = database.
prepare( sql, myResult );
1287 if ( myResult == SQLITE_OK && statement.
step() == SQLITE_ROW )
1291 for (
int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1293 myFieldName = statement.
columnName( myColNo );
1295 myMap[myFieldName] = myFieldValue;
1298 if ( statement.
step() != SQLITE_DONE )
1300 QgsDebugMsgLevel( QStringLiteral(
"Multiple records found in srs.db" ), 4 );
1331 if ( d->mDescription.isNull() )
1337 return d->mDescription;
1343 if ( !
authid().isEmpty() )
1352 return isValid() ? QObject::tr(
"Custom CRS" ) : QObject::tr(
"Unknown CRS" );
1354 return QObject::tr(
"Custom CRS: %1" ).arg(
1357 else if ( !
toProj().isEmpty() )
1358 return QObject::tr(
"Custom CRS: %1" ).arg( type ==
MediumString ? (
toProj().left( 50 ) + QString( QChar( 0x2026 ) ) )
1366 if ( d->mProjectionAcronym.isNull() )
1372 return d->mProjectionAcronym;
1378 if ( d->mEllipsoidAcronym.isNull() )
1380 #if PROJ_VERSION_MAJOR>=6
1381 if ( PJ *obj = d->threadLocalProjObject() )
1383 QgsProjUtils::proj_pj_unique_ptr ellipsoid( proj_get_ellipsoid(
QgsProjContext::get(), obj ) );
1386 const QString ellipsoidAuthName( proj_get_id_auth_name( ellipsoid.get(), 0 ) );
1387 const QString ellipsoidAuthCode( proj_get_id_code( ellipsoid.get(), 0 ) );
1388 if ( !ellipsoidAuthName.isEmpty() && !ellipsoidAuthCode.isEmpty() )
1389 d->mEllipsoidAcronym = QStringLiteral(
"%1:%2" ).arg( ellipsoidAuthName, ellipsoidAuthCode );
1392 double semiMajor, semiMinor, invFlattening;
1393 int semiMinorComputed = 0;
1394 if ( proj_ellipsoid_get_parameters(
QgsProjContext::get(), ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
1396 d->mEllipsoidAcronym = QStringLiteral(
"PARAMETER:%1:%2" ).arg(
qgsDoubleToString( semiMajor ),
1401 d->mEllipsoidAcronym.clear();
1406 return d->mEllipsoidAcronym;
1414 return d->mEllipsoidAcronym;
1428 if ( d->mProj4.isEmpty() )
1430 #if PROJ_VERSION_MAJOR>=6
1431 if ( PJ *obj = d->threadLocalProjObject() )
1433 d->mProj4 = getFullProjString( obj );
1436 char *proj4src =
nullptr;
1437 OSRExportToProj4( d->mCRS, &proj4src );
1438 d->mProj4 = proj4src;
1439 CPLFree( proj4src );
1443 return d->mProj4.trimmed();
1448 return d->mIsGeographic;
1456 return d->mMapUnits;
1464 #if PROJ_VERSION_MAJOR>=6
1465 PJ *obj = d->threadLocalProjObject();
1470 double southLat = 0;
1472 double northLat = 0;
1475 &westLon, &southLat, &eastLon, &northLat,
nullptr ) )
1494 int result = openDatabase( databaseFileName, database );
1495 if ( result != SQLITE_OK )
1500 QString sql = QStringLiteral(
"select west_bound_lon, north_bound_lat, east_bound_lon, south_bound_lat from tbl_bounds "
1503 statement = database.
prepare( sql, result );
1506 if ( result == SQLITE_OK )
1508 if ( statement.
step() == SQLITE_ROW )
1541 void QgsCoordinateReferenceSystem::setProjString(
const QString &proj4String )
1544 d->mProj4 = proj4String;
1545 d->mWktPreferred.clear();
1548 QString trimmed = proj4String.trimmed();
1550 #if PROJ_VERSION_MAJOR>=6
1551 trimmed += QLatin1String(
" +type=crs" );
1555 d->setPj( QgsProjUtils::proj_pj_unique_ptr( proj_create( ctx, trimmed.toLatin1().constData() ) ) );
1561 const int errNo = proj_context_errno( ctx );
1562 QgsDebugMsg( QStringLiteral(
"proj string rejected: %1" ).arg( proj_errno_string( errNo ) ) );
1564 d->mIsValid =
false;
1568 d->mEllipsoidAcronym.clear();
1572 OSRDestroySpatialReference( d->mCRS );
1573 d->mCRS = OSRNewSpatialReference(
nullptr );
1574 d->mIsValid = OSRImportFromProj4( d->mCRS, trimmed.toLatin1().constData() ) == OGRERR_NONE;
1580 projCtx pContext = pj_ctx_alloc();
1581 projPJ proj = pj_init_plus_ctx( pContext, proj4String.trimmed().toLatin1().constData() );
1584 QgsDebugMsgLevel( QStringLiteral(
"proj.4 string rejected by pj_init_plus_ctx()" ), 4 );
1585 d->mIsValid =
false;
1591 pj_ctx_free( pContext );
1597 bool QgsCoordinateReferenceSystem::setWktString(
const QString &wkt,
bool allowProjFallback )
1600 d->mIsValid =
false;
1601 d->mWktPreferred.clear();
1603 #if PROJ_VERSION_MAJOR>=6
1605 ( void )allowProjFallback;
1607 PROJ_STRING_LIST warnings =
nullptr;
1608 PROJ_STRING_LIST grammerErrors =
nullptr;
1610 d->setPj( QgsProjUtils::proj_pj_unique_ptr( proj_create_from_wkt(
QgsProjContext::get(), wkt.toLatin1().constData(),
nullptr, &warnings, &grammerErrors ) ) );
1616 QgsDebugMsg( QStringLiteral(
"\n---------------------------------------------------------------" ) );
1617 QgsDebugMsg( QStringLiteral(
"This CRS could *** NOT *** be set from the supplied Wkt " ) );
1619 for (
auto iter = warnings; iter && *iter; ++iter )
1621 for (
auto iter = grammerErrors; iter && *iter; ++iter )
1623 QgsDebugMsg( QStringLiteral(
"---------------------------------------------------------------\n" ) );
1625 proj_string_list_destroy( warnings );
1626 proj_string_list_destroy( grammerErrors );
1628 QByteArray ba = wkt.toLatin1();
1629 const char *pWkt = ba.data();
1631 OGRErr myInputResult = OSRImportFromWkt( d->mCRS,
const_cast< char **
>( & pWkt ) );
1632 res = myInputResult == OGRERR_NONE;
1635 QgsDebugMsg( QStringLiteral(
"\n---------------------------------------------------------------" ) );
1636 QgsDebugMsg( QStringLiteral(
"This CRS could *** NOT *** be set from the supplied Wkt " ) );
1638 QgsDebugMsg( QStringLiteral(
"UNUSED WKT: %1" ).arg( pWkt ) );
1639 QgsDebugMsg( QStringLiteral(
"---------------------------------------------------------------\n" ) );
1651 if ( !sDisableWktCache )
1652 sWktCache()->insert( wkt, *
this );
1656 #if PROJ_VERSION_MAJOR>=6
1660 QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
1661 QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
1663 if ( authName.isEmpty() || authCode.isEmpty() )
1666 QgsProjUtils::identifyCrs( d->threadLocalProjObject(), authName, authCode );
1669 if ( !authName.isEmpty() && !authCode.isEmpty() )
1671 if ( loadFromAuthCode( authName, authCode ) )
1674 if ( !sDisableWktCache )
1675 sWktCache()->insert( wkt, *
this );
1683 d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
1688 if ( OSRAutoIdentifyEPSG( d->mCRS ) == OGRERR_NONE )
1690 QString
authid = QStringLiteral(
"%1:%2" )
1691 .arg( OSRGetAuthorityName( d->mCRS,
nullptr ),
1692 OSRGetAuthorityCode( d->mCRS,
nullptr ) );
1695 if ( !sDisableWktCache )
1696 sWktCache()->insert( wkt, *
this );
1705 #if PROJ_VERSION_MAJOR<6
1706 if ( allowProjFallback )
1708 d->mIsValid =
false;
1710 char *proj4src =
nullptr;
1711 OSRExportToProj4( d->mCRS, &proj4src );
1718 CPLFree( proj4src );
1720 #if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(2,5,0)
1722 OSRFixup( d->mCRS );
1725 OSRExportToProj4( d->mCRS, &proj4src );
1729 CPLFree( proj4src );
1731 else if ( d->mIsValid )
1739 void QgsCoordinateReferenceSystem::setMapUnits()
1747 #if PROJ_VERSION_MAJOR<6
1748 #if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(2,5,0)
1751 OSRFixup( d->mCRS );
1755 #if PROJ_VERSION_MAJOR>=6
1763 QgsProjUtils::proj_pj_unique_ptr
crs( QgsProjUtils::crsToSingleCrs( d->threadLocalProjObject() ) );
1764 QgsProjUtils::proj_pj_unique_ptr coordinateSystem( proj_crs_get_coordinate_system( context,
crs.get() ) );
1765 if ( !coordinateSystem )
1771 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
1772 if ( axisCount > 0 )
1774 const char *outUnitName =
nullptr;
1776 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
1785 const QString unitName( outUnitName );
1789 if ( unitName.compare( QLatin1String(
"degree" ), Qt::CaseInsensitive ) == 0 ||
1790 unitName.compare( QLatin1String(
"degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1791 unitName.compare( QLatin1String(
"degree minute second hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1792 unitName.compare( QLatin1String(
"degree minute" ), Qt::CaseInsensitive ) == 0 ||
1793 unitName.compare( QLatin1String(
"degree hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1794 unitName.compare( QLatin1String(
"degree minute hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1795 unitName.compare( QLatin1String(
"hemisphere degree" ), Qt::CaseInsensitive ) == 0 ||
1796 unitName.compare( QLatin1String(
"hemisphere degree minute" ), Qt::CaseInsensitive ) == 0 ||
1797 unitName.compare( QLatin1String(
"hemisphere degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1798 unitName.compare( QLatin1String(
"degree (supplier to define representation)" ), Qt::CaseInsensitive ) == 0 )
1800 else if ( unitName.compare( QLatin1String(
"metre" ), Qt::CaseInsensitive ) == 0
1801 || unitName.compare( QLatin1String(
"m" ), Qt::CaseInsensitive ) == 0
1802 || unitName.compare( QLatin1String(
"meter" ), Qt::CaseInsensitive ) == 0 )
1805 else if ( unitName.compare( QLatin1String(
"US survey foot" ), Qt::CaseInsensitive ) == 0 ||
1806 unitName.compare( QLatin1String(
"foot" ), Qt::CaseInsensitive ) == 0 )
1808 else if ( unitName.compare( QLatin1String(
"kilometre" ), Qt::CaseInsensitive ) == 0 )
1810 else if ( unitName.compare( QLatin1String(
"centimetre" ), Qt::CaseInsensitive ) == 0 )
1812 else if ( unitName.compare( QLatin1String(
"millimetre" ), Qt::CaseInsensitive ) == 0 )
1814 else if ( unitName.compare( QLatin1String(
"Statute mile" ), Qt::CaseInsensitive ) == 0 )
1816 else if ( unitName.compare( QLatin1String(
"nautical mile" ), Qt::CaseInsensitive ) == 0 )
1818 else if ( unitName.compare( QLatin1String(
"yard" ), Qt::CaseInsensitive ) == 0 )
1832 char *unitName =
nullptr;
1834 if ( OSRIsProjected( d->mCRS ) )
1836 double toMeter = OSRGetLinearUnits( d->mCRS, &unitName );
1837 QString unit( unitName );
1845 static const double SMALL_NUM = 1e-3;
1848 unit = QStringLiteral(
"Foot" );
1852 else if ( unit == QLatin1String(
"Foot" ) )
1861 OSRGetAngularUnits( d->mCRS, &unitName );
1862 QString unit( unitName );
1863 if ( unit == QLatin1String(
"degree" ) )
1876 if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull()
1879 QgsDebugMsgLevel(
"QgsCoordinateReferenceSystem::findMatchingProj will only "
1880 "work if prj acr ellipsoid acr and proj4string are set"
1881 " and the current projection is valid!", 4 );
1891 QString mySql = QString(
"select srs_id,parameters from tbl_srs where "
1892 "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated" )
1899 myResult = openDatabase( myDatabaseFileName, database );
1900 if ( myResult != SQLITE_OK )
1905 statement = database.
prepare( mySql, myResult );
1906 if ( myResult == SQLITE_OK )
1909 while ( statement.
step() == SQLITE_ROW )
1913 if (
toProj() == myProj4String.trimmed() )
1915 return mySrsId.toLong();
1926 myResult = openDatabase( myDatabaseFileName, database );
1927 if ( myResult != SQLITE_OK )
1932 statement = database.
prepare( mySql, myResult );
1934 if ( myResult == SQLITE_OK )
1936 while ( statement.
step() == SQLITE_ROW )
1940 if (
toProj() == myProj4String.trimmed() )
1942 return mySrsId.toLong();
1956 if ( !d->mIsValid && !srs.d->mIsValid )
1959 if ( !d->mIsValid || !srs.d->mIsValid )
1964 if ( isUser != otherIsUser )
1968 if ( !isUser && ( !d->mAuthId.isEmpty() || !srs.d->mAuthId.isEmpty() ) )
1969 return d->mAuthId == srs.d->mAuthId;
1976 return !( *
this == srs );
1981 #if PROJ_VERSION_MAJOR>=6
1982 if ( PJ *obj = d->threadLocalProjObject() )
1984 const bool isDefaultPreferredFormat = variant ==
WKT_PREFERRED && !multiline;
1985 if ( isDefaultPreferredFormat && !d->mWktPreferred.isEmpty() )
1988 return d->mWktPreferred;
1991 PJ_WKT_TYPE type = PJ_WKT1_GDAL;
1995 type = PJ_WKT1_GDAL;
1998 type = PJ_WKT1_ESRI;
2001 type = PJ_WKT2_2015;
2004 type = PJ_WKT2_2015_SIMPLIFIED;
2007 type = PJ_WKT2_2019;
2010 type = PJ_WKT2_2019_SIMPLIFIED;
2014 const QByteArray multiLineOption = QStringLiteral(
"MULTILINE=%1" ).arg( multiline ? QStringLiteral(
"YES" ) : QStringLiteral(
"NO" ) ).toLocal8Bit();
2015 const QByteArray indentatationWidthOption = QStringLiteral(
"INDENTATION_WIDTH=%1" ).arg( multiline ? QString::number( indentationWidth ) : QStringLiteral(
"0" ) ).toLocal8Bit();
2016 const char *
const options[] = {multiLineOption.constData(), indentatationWidthOption.constData(),
nullptr};
2019 if ( isDefaultPreferredFormat )
2022 d->mWktPreferred = res;
2030 Q_UNUSED( multiline )
2031 Q_UNUSED( indentationWidth )
2032 char *wkt =
nullptr;
2034 if ( OSRExportToWkt( d->mCRS, &wkt ) == OGRERR_NONE )
2047 QDomNode srsNode = node.namedItem( QStringLiteral(
"spatialrefsys" ) );
2049 if ( ! srsNode.isNull() )
2051 bool initialized =
false;
2054 long srsid = srsNode.namedItem( QStringLiteral(
"srsid" ) ).toElement().text().toLong( &ok );
2060 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
2061 if ( !node.isNull() )
2072 node = srsNode.namedItem( QStringLiteral(
"epsg" ) );
2073 if ( !node.isNull() )
2091 const QString
description = srsNode.namedItem( QStringLiteral(
"description" ) ).toElement().text();
2093 const QString wkt = srsNode.namedItem( QStringLiteral(
"wkt" ) ).toElement().text();
2094 initialized = createFromWktInternal( wkt,
description );
2099 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
2100 const QString proj4 = node.toElement().text();
2107 node = srsNode.namedItem( QStringLiteral(
"proj4" ) );
2108 const QString proj4 = node.toElement().text();
2109 if ( !proj4.trimmed().isEmpty() )
2110 setProjString( node.toElement().text() );
2112 node = srsNode.namedItem( QStringLiteral(
"srsid" ) );
2113 d->mSrsId = node.toElement().text().toLong();
2115 node = srsNode.namedItem( QStringLiteral(
"srid" ) );
2116 d->mSRID = node.toElement().text().toLong();
2118 node = srsNode.namedItem( QStringLiteral(
"authid" ) );
2119 d->mAuthId = node.toElement().text();
2121 node = srsNode.namedItem( QStringLiteral(
"description" ) );
2122 d->mDescription = node.toElement().text();
2124 node = srsNode.namedItem( QStringLiteral(
"projectionacronym" ) );
2125 d->mProjectionAcronym = node.toElement().text();
2127 node = srsNode.namedItem( QStringLiteral(
"ellipsoidacronym" ) );
2128 d->mEllipsoidAcronym = node.toElement().text();
2130 node = srsNode.namedItem( QStringLiteral(
"geographicflag" ) );
2131 d->mIsGeographic = node.toElement().text().compare( QLatin1String(
"true" ) );
2133 d->mWktPreferred.clear();
2142 d =
new QgsCoordinateReferenceSystemPrivate();
2150 QDomElement layerNode = node.toElement();
2151 QDomElement srsElement = doc.createElement( QStringLiteral(
"spatialrefsys" ) );
2153 QDomElement wktElement = doc.createElement( QStringLiteral(
"wkt" ) );
2155 srsElement.appendChild( wktElement );
2157 QDomElement proj4Element = doc.createElement( QStringLiteral(
"proj4" ) );
2158 proj4Element.appendChild( doc.createTextNode(
toProj() ) );
2159 srsElement.appendChild( proj4Element );
2161 QDomElement srsIdElement = doc.createElement( QStringLiteral(
"srsid" ) );
2162 srsIdElement.appendChild( doc.createTextNode( QString::number(
srsid() ) ) );
2163 srsElement.appendChild( srsIdElement );
2165 QDomElement sridElement = doc.createElement( QStringLiteral(
"srid" ) );
2166 sridElement.appendChild( doc.createTextNode( QString::number(
postgisSrid() ) ) );
2167 srsElement.appendChild( sridElement );
2169 QDomElement authidElement = doc.createElement( QStringLiteral(
"authid" ) );
2170 authidElement.appendChild( doc.createTextNode(
authid() ) );
2171 srsElement.appendChild( authidElement );
2173 QDomElement descriptionElement = doc.createElement( QStringLiteral(
"description" ) );
2174 descriptionElement.appendChild( doc.createTextNode(
description() ) );
2175 srsElement.appendChild( descriptionElement );
2177 QDomElement projectionAcronymElement = doc.createElement( QStringLiteral(
"projectionacronym" ) );
2178 projectionAcronymElement.appendChild( doc.createTextNode(
projectionAcronym() ) );
2179 srsElement.appendChild( projectionAcronymElement );
2181 QDomElement ellipsoidAcronymElement = doc.createElement( QStringLiteral(
"ellipsoidacronym" ) );
2182 ellipsoidAcronymElement.appendChild( doc.createTextNode(
ellipsoidAcronym() ) );
2183 srsElement.appendChild( ellipsoidAcronymElement );
2185 QDomElement geographicFlagElement = doc.createElement( QStringLiteral(
"geographicflag" ) );
2186 QString geoFlagText = QStringLiteral(
"false" );
2189 geoFlagText = QStringLiteral(
"true" );
2192 geographicFlagElement.appendChild( doc.createTextNode( geoFlagText ) );
2193 srsElement.appendChild( geographicFlagElement );
2195 layerNode.appendChild( srsElement );
2207 QString QgsCoordinateReferenceSystem::projFromSrsId(
const int srsId )
2209 QString myDatabaseFileName;
2210 QString myProjString;
2211 QString mySql = QStringLiteral(
"select parameters from tbl_srs where srs_id = %1 order by deprecated" ).arg( srsId );
2220 QFileInfo myFileInfo;
2221 myFileInfo.setFile( myDatabaseFileName );
2222 if ( !myFileInfo.exists() )
2224 QgsDebugMsg( QStringLiteral(
"users qgis.db not found" ) );
2237 rc = openDatabase( myDatabaseFileName, database );
2243 statement = database.
prepare( mySql, rc );
2245 if ( rc == SQLITE_OK )
2247 if ( statement.
step() == SQLITE_ROW )
2253 return myProjString;
2260 myResult = database.
open_v2( path, SQLITE_OPEN_READONLY,
nullptr );
2262 myResult = database.
open( path );
2264 if ( myResult != SQLITE_OK )
2273 .arg( database.
errorMessage() ), QObject::tr(
"CRS" ) );
2280 sCustomSrsValidation = f;
2285 return sCustomSrsValidation;
2288 void QgsCoordinateReferenceSystem::debugPrint()
2290 QgsDebugMsg( QStringLiteral(
"***SpatialRefSystem***" ) );
2291 QgsDebugMsg(
"* Valid : " + ( d->mIsValid ? QString(
"true" ) : QString(
"false" ) ) );
2292 QgsDebugMsg(
"* SrsId : " + QString::number( d->mSrsId ) );
2298 QgsDebugMsg( QStringLiteral(
"* Units : meters" ) );
2302 QgsDebugMsg( QStringLiteral(
"* Units : feet" ) );
2306 QgsDebugMsg( QStringLiteral(
"* Units : degrees" ) );
2312 mValidationHint = html;
2317 return mValidationHint;
2325 long QgsCoordinateReferenceSystem::getRecordCount()
2330 long myRecordCount = 0;
2333 if ( myResult != SQLITE_OK )
2339 QString mySql = QStringLiteral(
"select count(*) from tbl_srs" );
2340 statement = database.
prepare( mySql, myResult );
2341 if ( myResult == SQLITE_OK )
2343 if ( statement.
step() == SQLITE_ROW )
2345 QString myRecordCountString = statement.
columnAsText( 0 );
2346 myRecordCount = myRecordCountString.toLong();
2349 return myRecordCount;
2352 #if PROJ_VERSION_MAJOR>=6
2353 bool testIsGeographic( PJ *
crs )
2356 bool isGeographic =
false;
2357 QgsProjUtils::proj_pj_unique_ptr coordinateSystem( proj_crs_get_coordinate_system( pjContext,
crs ) );
2358 if ( coordinateSystem )
2360 const int axisCount = proj_cs_get_axis_count( pjContext, coordinateSystem.get() );
2361 if ( axisCount > 0 )
2363 const char *outUnitAuthName =
nullptr;
2364 const char *outUnitAuthCode =
nullptr;
2366 proj_cs_get_axis_info( pjContext, coordinateSystem.get(), 0,
2375 if ( outUnitAuthName && outUnitAuthCode )
2377 const char *unitCategory =
nullptr;
2378 if ( proj_uom_get_info_from_database( pjContext, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
2380 isGeographic = QString( unitCategory ).compare( QLatin1String(
"angular" ), Qt::CaseInsensitive ) == 0;
2385 return isGeographic;
2388 void getOperationAndEllipsoidFromProjString(
const QString &proj, QString &operation, QString &ellipsoid )
2390 thread_local
const QRegularExpression projRegExp( QStringLiteral(
"\\+proj=(\\S+)" ) );
2391 const QRegularExpressionMatch projMatch = projRegExp.match( proj );
2392 if ( !projMatch.hasMatch() )
2394 QgsDebugMsgLevel( QStringLiteral(
"no +proj argument found [%2]" ).arg( proj ), 2 );
2397 operation = projMatch.captured( 1 );
2399 thread_local
const QRegularExpression ellipseRegExp( QStringLiteral(
"\\+(?:ellps|datum)=(\\S+)" ) );
2400 const QRegularExpressionMatch ellipseMatch = projRegExp.match( proj );
2402 if ( !ellipseMatch.hasMatch() )
2404 ellipsoid = ellipseMatch.captured( 1 );
2418 bool QgsCoordinateReferenceSystem::loadFromAuthCode(
const QString &auth,
const QString &code )
2421 d->mIsValid =
false;
2422 d->mWktPreferred.clear();
2425 QgsProjUtils::proj_pj_unique_ptr
crs( proj_create_from_database( pjContext, auth.toUtf8().constData(), code.toUtf8().constData(), PJ_CATEGORY_CRS,
false,
nullptr ) );
2431 switch ( proj_get_type(
crs.get() ) )
2433 case PJ_TYPE_VERTICAL_CRS:
2440 crs = QgsProjUtils::crsToSingleCrs(
crs.get() );
2442 QString proj4 = getFullProjString(
crs.get() );
2443 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2444 proj4 = proj4.trimmed();
2448 d->mWktPreferred.clear();
2449 d->mDescription = QString( proj_get_name(
crs.get() ) );
2450 d->mAuthId = QStringLiteral(
"%1:%2" ).arg( auth, code );
2451 d->mIsGeographic = testIsGeographic(
crs.get() );
2452 d->mAxisInvertedDirty =
true;
2455 getOperationAndEllipsoidFromProjString( proj4, operation, ellipsoid );
2456 d->mProjectionAcronym = operation;
2457 d->mEllipsoidAcronym.clear();
2458 d->setPj( std::move(
crs ) );
2460 const QString dbVals = sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( auth, code ).toUpper() );
2463 if ( !dbVals.isEmpty() )
2465 const QStringList parts = dbVals.split(
',' );
2466 d->mSrsId = parts.at( 0 ).toInt();
2467 d->mSRID = parts.at( 1 ).toInt();
2475 QList<long> QgsCoordinateReferenceSystem::userSrsIds()
2477 QList<long> results;
2481 QFileInfo myInfo( db );
2482 if ( !myInfo.exists() )
2484 QgsDebugMsg(
"failed : " + db +
" does not exist!" );
2492 int result = openDatabase( db, database );
2493 if ( result != SQLITE_OK )
2495 QgsDebugMsg(
"failed : " + db +
" could not be opened!" );
2499 QString sql = QStringLiteral(
"select srs_id from tbl_srs where srs_id >= %1" ).arg(
USER_CRS_START_ID );
2501 statement = database.
prepare( sql, rc );
2504 int ret = statement.
step();
2506 if ( ret == SQLITE_DONE )
2512 if ( ret == SQLITE_ROW )
2518 QgsMessageLog::logMessage( QObject::tr(
"SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr(
"SpatiaLite" ) );
2526 long QgsCoordinateReferenceSystem::matchToUserCrs()
const
2528 PJ *obj = d->threadLocalProjObject();
2532 const QList< long > ids = userSrsIds();
2533 for (
long id : ids )
2536 if ( candidate.projObject() && proj_is_equivalent_to( obj, candidate.projObject(), PJ_COMP_EQUIVALENT ) )
2545 #if PROJ_VERSION_MAJOR<6
2547 bool QgsCoordinateReferenceSystem::loadWkts( QHash<int, QString> &wkts,
const char *filename )
2550 const char *pszFilename = CPLFindFile(
"gdal", filename );
2554 QFile csv( pszFilename );
2555 if ( !csv.open( QIODevice::ReadOnly ) )
2558 QTextStream lines( &csv );
2562 QString line = lines.readLine();
2563 if ( line.isNull() )
2566 if ( line.trimmed().isEmpty() || line.startsWith(
'#' ) )
2570 else if ( line.startsWith( QLatin1String(
"include " ) ) )
2572 if ( !loadWkts( wkts, line.mid( 8 ).toUtf8() ) )
2577 int pos = line.indexOf(
',' );
2582 int epsg = line.leftRef( pos ).toInt( &ok );
2586 wkts.insert( epsg, line.mid( pos + 1 ) );
2595 bool QgsCoordinateReferenceSystem::loadIds( QHash<int, QString> &wkts )
2599 static const QStringList csvs { QStringList() << QStringLiteral(
"gcs.csv" ) << QStringLiteral(
"pcs.csv" ) << QStringLiteral(
"vertcs.csv" ) << QStringLiteral(
"compdcs.csv" ) << QStringLiteral(
"geoccs.csv" ) };
2600 for (
const QString &csv : csvs )
2602 QString filename = CPLFindFile(
"gdal", csv.toUtf8() );
2604 QFile f( filename );
2605 if ( !f.open( QIODevice::ReadOnly ) )
2608 QTextStream lines( &f );
2615 QString line = lines.readLine();
2616 if ( line.isNull() )
2619 if ( line.trimmed().isEmpty() )
2622 int pos = line.indexOf(
',' );
2625 qWarning(
"No id found in: %s", qPrintable( line ) );
2630 int epsg = line.leftRef( pos ).toInt( &ok );
2633 qWarning(
"No valid id found in: %s", qPrintable( line ) );
2638 if ( epsg == 2218 || epsg == 2221 || epsg == 2296 || epsg == 2297 || epsg == 2298 || epsg == 2299 || epsg == 2300 || epsg == 2301 || epsg == 2302 ||
2639 epsg == 2303 || epsg == 2304 || epsg == 2305 || epsg == 2306 || epsg == 2307 || epsg == 2963 || epsg == 2985 || epsg == 2986 || epsg == 3052 ||
2640 epsg == 3053 || epsg == 3139 || epsg == 3144 || epsg == 3145 || epsg == 3173 || epsg == 3295 || epsg == 3993 || epsg == 4087 || epsg == 4088 ||
2641 epsg == 5017 || epsg == 5221 || epsg == 5224 || epsg == 5225 || epsg == 5514 || epsg == 5515 || epsg == 5516 || epsg == 5819 || epsg == 5820 ||
2642 epsg == 5821 || epsg == 6200 || epsg == 6201 || epsg == 6202 || epsg == 6244 || epsg == 6245 || epsg == 6246 || epsg == 6247 || epsg == 6248 ||
2643 epsg == 6249 || epsg == 6250 || epsg == 6251 || epsg == 6252 || epsg == 6253 || epsg == 6254 || epsg == 6255 || epsg == 6256 || epsg == 6257 ||
2644 epsg == 6258 || epsg == 6259 || epsg == 6260 || epsg == 6261 || epsg == 6262 || epsg == 6263 || epsg == 6264 || epsg == 6265 || epsg == 6266 ||
2645 epsg == 6267 || epsg == 6268 || epsg == 6269 || epsg == 6270 || epsg == 6271 || epsg == 6272 || epsg == 6273 || epsg == 6274 || epsg == 6275 ||
2646 epsg == 6966 || epsg == 7082 || epsg == 32600 || epsg == 32663 || epsg == 32700 )
2649 if ( OSRImportFromEPSG(
crs, epsg ) != OGRERR_NONE )
2651 qDebug(
"EPSG %d: not imported", epsg );
2655 char *wkt =
nullptr;
2656 if ( OSRExportToWkt(
crs, &wkt ) != OGRERR_NONE )
2658 qWarning(
"EPSG %d: not exported to WKT", epsg );
2662 wkts.insert( epsg, wkt );
2670 QgsDebugMsgLevel( QStringLiteral(
"Loaded %1/%2 from %3" ).arg( QString::number( n ), QString::number( l ), filename.toUtf8().constData() ), 4 );
2673 OSRDestroySpatialReference(
crs );
2679 #if PROJ_VERSION_MAJOR>=6
2680 static void sync_db_proj_logger(
void * ,
int level,
const char *message )
2685 if ( level == PJ_LOG_ERROR )
2689 else if ( level == PJ_LOG_DEBUG )
2698 setlocale( LC_ALL,
"C" );
2701 #if PROJ_VERSION_MAJOR<6
2702 syncDatumTransform( dbFilePath );
2705 int inserted = 0, updated = 0, deleted = 0, errors = 0;
2710 if ( database.
open( dbFilePath ) != SQLITE_OK )
2716 if ( sqlite3_exec( database.get(),
"BEGIN TRANSACTION",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
2724 char *errMsg =
nullptr;
2726 #if PROJ_VERSION_MAJOR<6
2728 if ( sqlite3_exec( database.get(),
"alter table tbl_srs add noupdate boolean",
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2729 ( void )sqlite3_exec( database.get(),
"update tbl_srs set noupdate=(auth_name='EPSG' and auth_id in (5513,5514,5221,2065,102067,4156,4818))",
nullptr,
nullptr, nullptr );
2731 ( void )sqlite3_exec( database.get(),
"UPDATE tbl_srs SET srid=141001 WHERE srid=41001 AND auth_name='OSGEO' AND auth_id='41001'",
nullptr,
nullptr, nullptr );
2733 if ( sqlite3_exec( database.get(),
"create table tbl_info (proj_major INT, proj_minor INT, proj_patch INT)",
nullptr,
nullptr, nullptr ) == SQLITE_OK )
2735 QString sql = QStringLiteral(
"INSERT INTO tbl_info(proj_major, proj_minor, proj_patch) VALUES (%1, %2,%3)" )
2736 .arg( QString::number( PROJ_VERSION_MAJOR ),
2737 QString::number( PROJ_VERSION_MINOR ),
2738 QString::number( PROJ_VERSION_PATCH ) );
2739 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2741 QgsDebugMsg( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2744 errMsg ? errMsg :
"(unknown error)" ) );
2746 sqlite3_free( errMsg );
2753 QString sql = QStringLiteral(
"SELECT proj_major, proj_minor, proj_patch FROM tbl_info" );
2754 statement = database.
prepare( sql, result );
2755 if ( result != SQLITE_OK )
2760 if ( statement.
step() == SQLITE_ROW )
2765 if ( major == PROJ_VERSION_MAJOR && minor == PROJ_VERSION_MINOR && patch == PROJ_VERSION_PATCH )
2771 QgsDebugMsg( QStringLiteral(
"Could not retrieve previous CRS sync PROJ version number" ) );
2778 #if PROJ_VERSION_MAJOR>=6
2781 proj_log_func( pjContext,
nullptr, sync_db_proj_logger );
2783 PROJ_STRING_LIST authorities = proj_get_authorities_from_database( pjContext );
2785 int nextSrsId = 63321;
2786 int nextSrId = 520003321;
2787 for (
auto authIter = authorities; authIter && *authIter; ++authIter )
2789 const QString authority( *authIter );
2790 QgsDebugMsgLevel( QStringLiteral(
"Loading authority '%1'" ).arg( authority ), 2 );
2791 PROJ_STRING_LIST codes = proj_get_codes_from_database( pjContext, *authIter, PJ_TYPE_CRS,
true );
2793 QStringList allCodes;
2795 for (
auto codesIter = codes; codesIter && *codesIter; ++codesIter )
2797 const QString code( *codesIter );
2800 QgsProjUtils::proj_pj_unique_ptr
crs( proj_create_from_database( pjContext, *authIter, *codesIter, PJ_CATEGORY_CRS,
false,
nullptr ) );
2803 QgsDebugMsg( QStringLiteral(
"Could not load '%1:%2'" ).arg( authority, code ) );
2807 switch ( proj_get_type(
crs.get() ) )
2809 case PJ_TYPE_VERTICAL_CRS:
2816 crs = QgsProjUtils::crsToSingleCrs(
crs.get() );
2818 QString proj4 = getFullProjString(
crs.get() );
2819 proj4.replace( QLatin1String(
"+type=crs" ), QString() );
2820 proj4 = proj4.trimmed();
2822 if ( proj4.isEmpty() )
2824 QgsDebugMsgLevel( QStringLiteral(
"No proj4 for '%1:%2'" ).arg( authority, code ), 2 );
2829 const bool deprecated = proj_is_deprecated(
crs.get() );
2830 const QString name( proj_get_name(
crs.get() ) );
2832 QString sql = QStringLiteral(
"SELECT parameters,description,deprecated FROM tbl_srs WHERE auth_name='%1' AND auth_id='%2'" ).arg( authority, code );
2833 statement = database.
prepare( sql, result );
2834 if ( result != SQLITE_OK )
2842 bool srsDeprecated = deprecated;
2843 if ( statement.
step() == SQLITE_ROW )
2847 srsDeprecated = statement.
columnAsText( 2 ).toInt() != 0;
2850 if ( !srsProj4.isEmpty() || !srsDesc.isEmpty() )
2852 if ( proj4 != srsProj4 || name != srsDesc || deprecated != srsDeprecated )
2855 sql = QStringLiteral(
"UPDATE tbl_srs SET parameters=%1,description=%2,deprecated=%3 WHERE auth_name=%4 AND auth_id=%5" )
2858 .arg( deprecated ? 1 : 0 )
2861 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
2863 QgsDebugMsg( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
2866 errMsg ? errMsg :
"(unknown error)" ) );
2868 sqlite3_free( errMsg );
2880 QString operation =
"";
2882 getOperationAndEllipsoidFromProjString( proj4, operation, ellps );
2886 const QString dbVals = sAuthIdToQgisSrsIdMap.value( QStringLiteral(
"%1:%2" ).arg( authority, code ) );
2889 if ( !dbVals.isEmpty() )
2891 const QStringList parts = dbVals.split(
',' );
2892 srsId = parts.at( 0 );
2893 srId = parts.at( 1 );
2895 if ( srId.isEmpty() )
2897 srId = QString::number( nextSrId );
2900 if ( srsId.isEmpty() )
2902 srsId = QString::number( nextSrsId );
2906 if ( !srsId.isEmpty() )
2908 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)" )
2918 .arg( deprecated ? 1 : 0 );
2922 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)" )
2931 .arg( deprecated ? 1 : 0 );
2935 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) == SQLITE_OK )
2941 qCritical(
"Could not execute: %s [%s/%s]\n",
2942 sql.toLocal8Bit().constData(),
2943 sqlite3_errmsg( database.get() ),
2944 errMsg ? errMsg :
"(unknown error)" );
2948 sqlite3_free( errMsg );
2953 proj_string_list_destroy( codes );
2955 const QString sql = QStringLiteral(
"DELETE FROM tbl_srs WHERE auth_name='%1' AND NOT auth_id IN (%2)" ).arg( authority, allCodes.join(
',' ) );
2956 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
2958 deleted = sqlite3_changes( database.get() );
2963 qCritical(
"Could not execute: %s [%s]\n",
2964 sql.toLocal8Bit().constData(),
2965 sqlite3_errmsg( database.get() ) );
2969 proj_string_list_destroy( authorities );
2977 QHash<int, QString> wkts;
2979 loadWkts( wkts,
"epsg.wkt" );
2981 QgsDebugMsgLevel( QStringLiteral(
"%1 WKTs loaded" ).arg( wkts.count() ), 4 );
2983 for ( QHash<int, QString>::const_iterator it = wkts.constBegin(); it != wkts.constEnd(); ++it )
2985 QByteArray ba( it.value().toUtf8() );
2986 char *psz = ba.data();
2989 OSRDestroySpatialReference(
crs );
2991 crs = OSRNewSpatialReference(
nullptr );
2993 OGRErr ogrErr = OSRImportFromWkt(
crs, &psz );
2994 if ( ogrErr != OGRERR_NONE )
2997 if ( OSRExportToProj4(
crs, &psz ) != OGRERR_NONE )
3004 proj4 = proj4.trimmed();
3008 if ( proj4.isEmpty() )
3011 QString name( OSRIsGeographic(
crs ) ? OSRGetAttrValue(
crs,
"GEOGCS", 0 ) :
3012 OSRIsGeocentric(
crs ) ? OSRGetAttrValue(
crs,
"GEOCCS", 0 ) :
3013 OSRGetAttrValue(
crs,
"PROJCS", 0 ) );
3014 if ( name.isEmpty() )
3015 name = QObject::tr(
"Imported from GDAL" );
3017 bool deprecated = name.contains( QLatin1String(
"(deprecated)" ) );
3019 sql = QStringLiteral(
"SELECT parameters,description,deprecated,noupdate FROM tbl_srs WHERE auth_name='EPSG' AND auth_id='%1'" ).arg( it.key() );
3020 statement = database.
prepare( sql, result );
3021 if ( result != SQLITE_OK )
3029 bool srsDeprecated = deprecated;
3030 if ( statement.
step() == SQLITE_ROW )
3034 srsDeprecated = statement.
columnAsText( 2 ).toInt() != 0;
3042 if ( !srsProj4.isEmpty() || !srsDesc.isEmpty() )
3044 if ( proj4 != srsProj4 || name != srsDesc || deprecated != srsDeprecated )
3047 sql = QStringLiteral(
"UPDATE tbl_srs SET parameters=%1,description=%2,deprecated=%3 WHERE auth_name='EPSG' AND auth_id=%4" )
3050 .arg( deprecated ? 1 : 0 )
3053 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
3055 QgsDebugMsg( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
3058 errMsg ? errMsg :
"(unknown error)" ) );
3060 sqlite3_free( errMsg );
3071 QRegExp projRegExp(
"\\+proj=(\\S+)" );
3072 if ( projRegExp.indexIn( proj4 ) < 0 )
3074 QgsDebugMsgLevel( QStringLiteral(
"EPSG %1: no +proj argument found [%2]" ).arg( it.key() ).arg( proj4 ), 4 );
3078 QRegExp ellipseRegExp(
"\\+ellps=(\\S+)" );
3080 if ( ellipseRegExp.indexIn( proj4 ) >= 0 )
3082 ellps = ellipseRegExp.cap( 1 );
3094 sql = QStringLiteral(
"INSERT INTO tbl_srs(description,projection_acronym,ellipsoid_acronym,parameters,srid,auth_name,auth_id,is_geo,deprecated) VALUES (%1,%2,%3,%4,%5,'EPSG',%5,%6,%7)" )
3100 .arg( OSRIsGeographic(
crs ) )
3101 .arg( deprecated ? 1 : 0 );
3104 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) == SQLITE_OK )
3110 qCritical(
"Could not execute: %s [%s/%s]\n",
3111 sql.toLocal8Bit().constData(),
3112 sqlite3_errmsg( database.get() ),
3113 errMsg ? errMsg :
"(unknown error)" );
3117 sqlite3_free( errMsg );
3123 OSRDestroySpatialReference(
crs );
3126 sql = QStringLiteral(
"DELETE FROM tbl_srs WHERE auth_name='EPSG' AND NOT auth_id IN (" );
3128 QHash<int, QString>::const_iterator it = wkts.constBegin();
3129 for ( ; it != wkts.constEnd(); ++it )
3131 sql += delim + QString::number( it.key() );
3134 sql += QLatin1String(
") AND NOT noupdate" );
3136 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr,
nullptr ) == SQLITE_OK )
3138 deleted = sqlite3_changes( database.get() );
3143 qCritical(
"Could not execute: %s [%s]\n",
3144 sql.toLocal8Bit().constData(),
3145 sqlite3_errmsg( database.get() ) );
3148 projCtx pContext = pj_ctx_alloc();
3150 #if !defined(PJ_VERSION) || PJ_VERSION!=470
3151 sql = QStringLiteral(
"select auth_name,auth_id,parameters from tbl_srs WHERE auth_name<>'EPSG' AND NOT deprecated AND NOT noupdate" );
3152 statement = database.
prepare( sql, result );
3153 if ( result == SQLITE_OK )
3155 while ( statement.
step() == SQLITE_ROW )
3161 QString input = QStringLiteral(
"+init=%1:%2" ).arg( auth_name.toLower(), auth_id );
3162 projPJ pj = pj_init_plus_ctx( pContext, input.toLatin1() );
3165 input = QStringLiteral(
"+init=%1:%2" ).arg( auth_name.toUpper(), auth_id );
3166 pj = pj_init_plus_ctx( pContext, input.toLatin1() );
3171 char *def = pj_get_def( pj, 0 );
3177 input.prepend(
' ' ).append(
' ' );
3178 if ( proj4.startsWith( input ) )
3180 proj4 = proj4.mid( input.size() );
3181 proj4 = proj4.trimmed();
3184 if ( proj4 != params )
3186 sql = QStringLiteral(
"UPDATE tbl_srs SET parameters=%1 WHERE auth_name=%2 AND auth_id=%3" )
3191 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) == SQLITE_OK )
3197 qCritical(
"Could not execute: %s [%s/%s]\n",
3198 sql.toLocal8Bit().constData(),
3199 sqlite3_errmsg( database.get() ),
3200 errMsg ? errMsg :
"(unknown error)" );
3202 sqlite3_free( errMsg );
3209 QgsDebugMsgLevel( QStringLiteral(
"could not retrieve proj string for %1 from PROJ" ).arg( input ), 4 );
3214 QgsDebugMsgLevel( QStringLiteral(
"could not retrieve crs for %1 from PROJ" ).arg( input ), 3 );
3223 QgsDebugMsg( QStringLiteral(
"Could not execute: %1 [%2]\n" ).arg(
3225 sqlite3_errmsg( database.get() ) ) );
3229 pj_ctx_free( pContext );
3233 #if PROJ_VERSION_MAJOR>=6
3234 QString sql = QStringLiteral(
"UPDATE tbl_info set proj_major=%1,proj_minor=%2,proj_patch=%3" )
3235 .arg( QString::number( PROJ_VERSION_MAJOR ),
3236 QString::number( PROJ_VERSION_MINOR ),
3237 QString::number( PROJ_VERSION_PATCH ) );
3238 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr, &errMsg ) != SQLITE_OK )
3240 QgsDebugMsg( QStringLiteral(
"Could not execute: %1 [%2/%3]\n" ).arg(
3243 errMsg ? errMsg :
"(unknown error)" ) );
3245 sqlite3_free( errMsg );
3250 if ( sqlite3_exec( database.get(),
"COMMIT",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
3252 QgsDebugMsg( QStringLiteral(
"Could not commit transaction: %1 [%2]\n" ).arg(
3254 sqlite3_errmsg( database.get() ) )
3260 QgsDebugMsgLevel( QStringLiteral(
"CRS update (inserted:%1 updated:%2 deleted:%3 errors:%4)" ).arg( inserted ).arg( updated ).arg( deleted ).arg( errors ), 4 );
3268 return updated + inserted;
3271 #if PROJ_VERSION_MAJOR<6
3272 bool QgsCoordinateReferenceSystem::syncDatumTransform(
const QString &dbPath )
3274 const char *filename = CSVFilename(
"datum_shift.csv" );
3275 FILE *fp = VSIFOpen( filename,
"rb" );
3281 char **fieldnames = CSVReadParseLine( fp );
3293 {
"SOURCE_CRS_CODE",
"source_crs_code", -1 },
3294 {
"TARGET_CRS_CODE",
"target_crs_code", -1 },
3295 {
"REMARKS",
"remarks", -1 },
3296 {
"COORD_OP_SCOPE",
"scope", -1 },
3297 {
"AREA_OF_USE_CODE",
"area_of_use_code", -1 },
3303 {
"DEPRECATED",
"deprecated", -1 },
3304 {
"COORD_OP_METHOD_CODE",
"coord_op_method_code", -1 },
3312 {
"PREFERRED",
"preferred", -1 },
3313 {
"COORD_OP_CODE",
"coord_op_code", -1 },
3316 QString update = QStringLiteral(
"UPDATE tbl_datum_transform SET " );
3318 const int n = CSLCount( fieldnames );
3319 int idxid = -1, idxrx = -1, idxry = -1, idxrz = -1, idxmcode = -1;
3324 for (
unsigned int i = 0; i <
sizeof( map ) /
sizeof( *map ); i++ )
3326 bool last = i ==
sizeof( map ) /
sizeof( *map ) - 1;
3328 map[i].idx = CSLFindString( fieldnames, map[i].src );
3329 if ( map[i].idx < 0 )
3331 qWarning(
"field %s not found", map[i].src );
3332 CSLDestroy( fieldnames );
3337 if ( strcmp( map[i].src,
"COORD_OP_CODE" ) == 0 )
3339 if ( strcmp( map[i].src,
"RX" ) == 0 )
3341 if ( strcmp( map[i].src,
"RY" ) == 0 )
3343 if ( strcmp( map[i].src,
"RZ" ) == 0 )
3345 if ( strcmp( map[i].src,
"COORD_OP_METHOD_CODE" ) == 0 )
3355 update += QLatin1String(
" WHERE " );
3363 update += QStringLiteral(
"%1=%%2" ).arg( map[i].dst ).arg( i + 1 );
3365 insert += map[i].dst;
3366 values += QStringLiteral(
"%%1" ).arg( i + 1 );
3369 insert =
"INSERT INTO tbl_datum_transform(" + insert +
") VALUES (" + values +
')';
3371 Q_ASSERT( idxid >= 0 );
3372 Q_ASSERT( idxrx >= 0 );
3373 Q_ASSERT( idxry >= 0 );
3374 Q_ASSERT( idxrz >= 0 );
3377 CSLDestroy( fieldnames );
3380 int openResult = database.
open( dbPath );
3381 if ( openResult != SQLITE_OK )
3387 if ( sqlite3_exec( database.get(),
"BEGIN TRANSACTION",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
3395 v.reserve(
sizeof( map ) /
sizeof( *map ) );
3399 char **values = CSVReadParseLine( fp );
3405 if ( CSLCount( values ) == 0 )
3407 CSLDestroy( values );
3411 if ( CSLCount( values ) < n )
3413 qWarning(
"Only %d columns", CSLCount( values ) );
3414 CSLDestroy( values );
3418 for (
unsigned int i = 0; i <
sizeof( map ) /
sizeof( *map ); i++ )
3420 int idx = map[i].idx;
3421 Q_ASSERT( idx != -1 );
3422 Q_ASSERT( idx < n );
3425 CSLDestroy( values );
3428 if ( v.at( idxmcode ).compare( QLatin1String(
"'9607'" ) ) == 0 )
3430 v[ idxmcode ] = QStringLiteral(
"'9606'" );
3431 v[ idxrx ] =
'\'' +
qgsDoubleToString( -( v[ idxrx ].remove(
'\'' ).toDouble() ) ) +
'\'';
3432 v[ idxry ] =
'\'' +
qgsDoubleToString( -( v[ idxry ].remove(
'\'' ).toDouble() ) ) +
'\'';
3433 v[ idxrz ] =
'\'' +
qgsDoubleToString( -( v[ idxrz ].remove(
'\'' ).toDouble() ) ) +
'\'';
3439 QString sql = QStringLiteral(
"SELECT coord_op_code FROM tbl_datum_transform WHERE coord_op_code=%1" ).arg( v[ idxid ] );
3441 statement = database.
prepare( sql, prepareRes );
3442 if ( prepareRes != SQLITE_OK )
3445 if ( statement.
step() == SQLITE_ROW )
3450 sql = cOpCode.isEmpty() ? insert : update;
3451 for (
int i = 0; i < v.size(); i++ )
3453 sql = sql.arg( v[i] );
3456 if ( sqlite3_exec( database.get(), sql.toUtf8(),
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
3458 qCritical(
"SQL: %s", sql.toUtf8().constData() );
3459 qCritical(
"Error: %s", sqlite3_errmsg( database.get() ) );
3463 if ( sqlite3_exec( database.get(),
"COMMIT",
nullptr,
nullptr,
nullptr ) != SQLITE_OK )
3473 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::stringCache()
3475 return *sStringCache();
3478 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::projCache()
3480 return *sProj4Cache();
3483 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::ogcCache()
3485 return *sOgcCache();
3488 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::wktCache()
3490 return *sWktCache();
3493 const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srIdCache()
3495 return *sSrIdCache();
3498 const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srsIdCache()
3500 return *sSrsIdCache();
3509 #if PROJ_VERSION_MAJOR>=6
3510 else if ( PJ *obj = d->threadLocalProjObject() )
3512 QgsProjUtils::proj_pj_unique_ptr geoCrs( proj_crs_get_geodetic_crs(
QgsProjContext::get(), obj ) );
3513 return geoCrs ? QStringLiteral(
"%1:%2" ).arg( proj_get_id_auth_name( geoCrs.get(), 0 ), proj_get_id_code( geoCrs.get(), 0 ) ) : QString();
3518 return OSRGetAuthorityName( d->mCRS,
"GEOGCS" ) + QStringLiteral(
":" ) + OSRGetAuthorityCode( d->mCRS,
"GEOGCS" );
3527 #if PROJ_VERSION_MAJOR>=6
3528 PJ *QgsCoordinateReferenceSystem::projObject()
const
3530 return d->threadLocalProjObject();
3536 QStringList projections;
3538 projections.reserve( res.size() );
3541 projections << QString::number(
crs.
srsid() );
3548 QList<QgsCoordinateReferenceSystem> res;
3552 QStringList projectionsProj4 = settings.
value( QStringLiteral(
"UI/recentProjectionsProj4" ) ).toStringList();
3553 QStringList projectionsWkt = settings.
value( QStringLiteral(
"UI/recentProjectionsWkt" ) ).toStringList();
3554 QStringList projectionsAuthId = settings.
value( QStringLiteral(
"UI/recentProjectionsAuthId" ) ).toStringList();
3555 int max = std::max( projectionsAuthId.size(), std::max( projectionsProj4.size(), projectionsWkt.size() ) );
3557 for (
int i = 0; i < max; ++i )
3559 const QString proj = projectionsProj4.value( i );
3560 const QString wkt = projectionsWkt.value( i );
3561 const QString
authid = projectionsAuthId.value( i );
3584 recent.removeAll(
crs );
3585 recent.insert( 0,
crs );
3588 recent = recent.mid( 0, 30 );
3589 QStringList authids;
3590 authids.reserve( recent.size() );
3592 proj.reserve( recent.size() );
3594 wkt.reserve( recent.size() );
3597 authids <<
c.authid();
3603 settings.
setValue( QStringLiteral(
"UI/recentProjectionsAuthId" ), authids );
3604 settings.
setValue( QStringLiteral(
"UI/recentProjectionsWkt" ), wkt );
3605 settings.
setValue( QStringLiteral(
"UI/recentProjectionsProj4" ), proj );
3610 sSrIdCacheLock()->lockForWrite();
3611 if ( !sDisableSrIdCache )
3614 sDisableSrIdCache =
true;
3615 sSrIdCache()->clear();
3617 sSrIdCacheLock()->unlock();
3619 sOgcLock()->lockForWrite();
3620 if ( !sDisableOgcCache )
3623 sDisableOgcCache =
true;
3624 sOgcCache()->clear();
3626 sOgcLock()->unlock();
3628 sProj4CacheLock()->lockForWrite();
3629 if ( !sDisableProjCache )
3632 sDisableProjCache =
true;
3633 sProj4Cache()->clear();
3635 sProj4CacheLock()->unlock();
3637 sCRSWktLock()->lockForWrite();
3638 if ( !sDisableWktCache )
3641 sDisableWktCache =
true;
3642 sWktCache()->clear();
3644 sCRSWktLock()->unlock();
3646 sCRSSrsIdLock()->lockForWrite();
3647 if ( !sDisableSrsIdCache )
3650 sDisableSrsIdCache =
true;
3651 sSrsIdCache()->clear();
3653 sCRSSrsIdLock()->unlock();
3655 sCrsStringLock()->lockForWrite();
3656 if ( !sDisableStringCache )
3659 sDisableStringCache =
true;
3660 sStringCache()->clear();
3662 sCrsStringLock()->unlock();
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.
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.
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.
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.
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.
QString userFriendlyIdentifier(IdentifierType type=MediumString) const
Returns a user friendly identifier for the CRS.
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.
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.
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.
static Q_DECL_DEPRECATED QgsCoordinateReferenceSystem fromProj4(const QString &proj4)
Creates a CRS from a proj style formatted string.
static void warning(const QString &msg)
Goes to qWarning.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
static QString OGRSpatialReferenceToWkt(OGRSpatialReferenceH srs)
Returns a WKT string corresponding to the specified OGR srs object.
static PJ_CONTEXT * get()
Returns a thread local instance of a proj context, safe for use in the current thread.
@ FlagMatchBoundCrsToUnderlyingSourceCrs
Allow matching a BoundCRS object to its underlying SourceCRS.
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.
double columnAsDouble(int column) const
Gets column value from the current statement row as a double.
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 qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
const int USER_CRS_START_ID
Magick number that determines whether a projection crsid is a system (srs.db) or user (~/....
QHash< QString, QgsCoordinateReferenceSystem > StringCrsCacheHash
QHash< long, QgsCoordinateReferenceSystem > SrIdCrsCacheHash
const int LAT_PREFIX_LEN
The length of the string "+lat_1=".
void * OGRSpatialReferenceH
void(* CUSTOM_CRS_VALIDATION)(QgsCoordinateReferenceSystem &)
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
#define QgsDebugMsgLevel(str, level)
const QgsCoordinateReferenceSystem & crs