From 5f1656b8e9991063a49af348e64a4127ca1b8ffe Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 18 Apr 2017 10:18:48 +1000 Subject: [PATCH] Move retrieval of lists of known ellipsoids to QgsEllipsoidUtils --- python/core/qgsellipsoidutils.sip | 32 ++++++++++ src/app/qgsprojectproperties.cpp | 63 +++---------------- src/core/qgsellipsoidutils.cpp | 71 +++++++++++++++++++++- src/core/qgsellipsoidutils.h | 33 +++++++++- tests/src/python/test_qgsellipsoidutils.py | 26 ++++++++ 5 files changed, 169 insertions(+), 56 deletions(-) diff --git a/python/core/qgsellipsoidutils.sip b/python/core/qgsellipsoidutils.sip index 92d28e917de..35e67f79a88 100644 --- a/python/core/qgsellipsoidutils.sip +++ b/python/core/qgsellipsoidutils.sip @@ -53,6 +53,22 @@ Associated coordinate reference system %End }; + struct EllipsoidDefinition + { + QString acronym; +%Docstring +Acronym for ellipsoid +%End + QString description; +%Docstring +Description of ellipsoid +%End + QgsEllipsoidUtils::EllipsoidParameters parameters; +%Docstring +Ellipsoid parameters +%End + }; + static EllipsoidParameters ellipsoidParameters( const QString &ellipsoid ); %Docstring Returns the parameters for the specified ``ellipsoid``. @@ -60,6 +76,22 @@ Associated coordinate reference system :rtype: EllipsoidParameters %End + static QList< QgsEllipsoidUtils::EllipsoidDefinition > definitions(); +%Docstring + Returns a list of the definitions for all known ellipsoids from the + internal ellipsoid database. + \see acronyms() + :rtype: list of QgsEllipsoidUtils.EllipsoidDefinition +%End + + static QStringList acronyms(); +%Docstring + Returns a list of all known ellipsoid acronyms from the internal + ellipsoid database. + \see definitions() + :rtype: list of str +%End + }; diff --git a/src/app/qgsprojectproperties.cpp b/src/app/qgsprojectproperties.cpp index 146a57baf38..c8b3b7a9d6a 100644 --- a/src/app/qgsprojectproperties.cpp +++ b/src/app/qgsprojectproperties.cpp @@ -1805,10 +1805,6 @@ void QgsProjectProperties::populateEllipsoidList() // // Populate the ellipsoid list // - sqlite3 *myDatabase = nullptr; - const char *myTail = nullptr; - sqlite3_stmt *myPreparedStatement = nullptr; - int myResult; EllipsoidDefs myItem; myItem.acronym = GEO_NONE; @@ -1823,58 +1819,17 @@ void QgsProjectProperties::populateEllipsoidList() myItem.semiMinor = 6370997.0; mEllipsoidList.append( myItem ); - //check the db is available - myResult = sqlite3_open_v2( QgsApplication::srsDatabaseFilePath().toUtf8().data(), &myDatabase, SQLITE_OPEN_READONLY, nullptr ); - if ( myResult ) + Q_FOREACH ( const QgsEllipsoidUtils::EllipsoidDefinition &def, QgsEllipsoidUtils::definitions() ) { - QgsDebugMsg( QString( "Can't open database: %1" ).arg( sqlite3_errmsg( myDatabase ) ) ); - // XXX This will likely never happen since on open, sqlite creates the - // database if it does not exist. - Q_ASSERT( myResult == 0 ); + myItem.acronym = def.acronym; + myItem.description = def.description; + // Fall-back values + myItem.semiMajor = 0.0; + myItem.semiMinor = 0.0; + myItem.semiMajor = def.parameters.semiMajor; + myItem.semiMinor = def.parameters.semiMinor; + mEllipsoidList.append( myItem ); } - - // Set up the query to retrieve the projection information needed to populate the ELLIPSOID list - QString mySql = QStringLiteral( "select acronym, name, radius, parameter2 from tbl_ellipsoid order by name" ); - myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(), mySql.toUtf8().length(), &myPreparedStatement, &myTail ); - // XXX Need to free memory from the error msg if one is set - if ( myResult == SQLITE_OK ) - { - while ( sqlite3_step( myPreparedStatement ) == SQLITE_ROW ) - { - QString para1, para2; - myItem.acronym = ( const char * )sqlite3_column_text( myPreparedStatement, 0 ); - myItem.description = ( const char * )sqlite3_column_text( myPreparedStatement, 1 ); - - // Copied from QgsDistanecArea. Should perhaps be moved there somehow? - // No error checking, this values are for show only, never used in calculations. - - // Fall-back values - myItem.semiMajor = 0.0; - myItem.semiMinor = 0.0; - // Crash if no column? - para1 = ( const char * )sqlite3_column_text( myPreparedStatement, 2 ); - para2 = ( const char * )sqlite3_column_text( myPreparedStatement, 3 ); - myItem.semiMajor = para1.midRef( 2 ).toDouble(); - if ( para2.left( 2 ) == QLatin1String( "b=" ) ) - { - myItem.semiMinor = para2.midRef( 2 ).toDouble(); - } - else if ( para2.left( 3 ) == QLatin1String( "rf=" ) ) - { - double invFlattening = para2.midRef( 3 ).toDouble(); - if ( invFlattening != 0.0 ) - { - myItem.semiMinor = myItem.semiMajor - ( myItem.semiMajor / invFlattening ); - } - } - mEllipsoidList.append( myItem ); - } - } - - // close the sqlite3 statement - sqlite3_finalize( myPreparedStatement ); - sqlite3_close( myDatabase ); - // Add all items to selector Q_FOREACH ( const EllipsoidDefs &i, mEllipsoidList ) diff --git a/src/core/qgsellipsoidutils.cpp b/src/core/qgsellipsoidutils.cpp index 54e99af72cc..373ba940bf1 100644 --- a/src/core/qgsellipsoidutils.cpp +++ b/src/core/qgsellipsoidutils.cpp @@ -21,7 +21,8 @@ QReadWriteLock QgsEllipsoidUtils::sEllipsoidCacheLock; QHash< QString, QgsEllipsoidUtils::EllipsoidParameters > QgsEllipsoidUtils::sEllipsoidCache; - +QReadWriteLock QgsEllipsoidUtils::sDefinitionCacheLock; +QList< QgsEllipsoidUtils::EllipsoidDefinition > QgsEllipsoidUtils::sDefinitionCache; QgsEllipsoidUtils::EllipsoidParameters QgsEllipsoidUtils::ellipsoidParameters( const QString &ellipsoid ) { @@ -177,3 +178,71 @@ QgsEllipsoidUtils::EllipsoidParameters QgsEllipsoidUtils::ellipsoidParameters( c sEllipsoidCacheLock.unlock(); return params; } + +QList QgsEllipsoidUtils::definitions() +{ + sDefinitionCacheLock.lockForRead(); + if ( !sDefinitionCache.isEmpty() ) + { + QList defs = sDefinitionCache; + sDefinitionCacheLock.unlock(); + return defs; + } + sDefinitionCacheLock.unlock(); + + sDefinitionCacheLock.lockForWrite(); + sqlite3 *database = nullptr; + const char *tail = nullptr; + sqlite3_stmt *preparedStatement = nullptr; + int result; + + QList defs; + + //check the db is available + result = sqlite3_open_v2( QgsApplication::srsDatabaseFilePath().toUtf8().data(), &database, SQLITE_OPEN_READONLY, nullptr ); + if ( result ) + { + QgsDebugMsg( QString( "Can't open database: %1" ).arg( sqlite3_errmsg( database ) ) ); + // XXX This will likely never happen since on open, sqlite creates the + // database if it does not exist. + Q_ASSERT( result == 0 ); + } + + // Set up the query to retrieve the projection information needed to populate the ELLIPSOID list + QString sql = QStringLiteral( "select acronym, name from tbl_ellipsoid order by name" ); + result = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &preparedStatement, &tail ); + // XXX Need to free memory from the error msg if one is set + if ( result == SQLITE_OK ) + { + while ( sqlite3_step( preparedStatement ) == SQLITE_ROW ) + { + EllipsoidDefinition def; + def.acronym = ( const char * )sqlite3_column_text( preparedStatement, 0 ); + def.description = ( const char * )sqlite3_column_text( preparedStatement, 1 ); + + // use ellipsoidParameters so that result is cached + def.parameters = ellipsoidParameters( def.acronym ); + + defs << def; + } + } + + // close the sqlite3 statement + sqlite3_finalize( preparedStatement ); + sqlite3_close( database ); + + sDefinitionCache = defs; + sDefinitionCacheLock.unlock(); + + return defs; +} + +QStringList QgsEllipsoidUtils::acronyms() +{ + QStringList result; + Q_FOREACH ( const QgsEllipsoidUtils::EllipsoidDefinition &def, definitions() ) + { + result << def.acronym; + } + return result; +} diff --git a/src/core/qgsellipsoidutils.h b/src/core/qgsellipsoidutils.h index 97b5fbe2de9..f4fe66850cc 100644 --- a/src/core/qgsellipsoidutils.h +++ b/src/core/qgsellipsoidutils.h @@ -19,6 +19,7 @@ #include "qgis_core.h" #include "qgis.h" #include "qgscoordinatereferencesystem.h" +#include /** * \class QgsEllipsoidUtils @@ -32,7 +33,7 @@ class CORE_EXPORT QgsEllipsoidUtils public: /** - * Contains parameter definitions for an ellipsoid. + * Contains parameters for an ellipsoid. * \since QGIS 3.0 */ struct EllipsoidParameters @@ -55,17 +56,47 @@ class CORE_EXPORT QgsEllipsoidUtils QgsCoordinateReferenceSystem crs; }; + /** + * Contains definition of an ellipsoid. + * \since QGIS 3.0 + */ + struct EllipsoidDefinition + { + //! Acronym for ellipsoid + QString acronym; + //! Description of ellipsoid + QString description; + //! Ellipsoid parameters + QgsEllipsoidUtils::EllipsoidParameters parameters; + }; + /** * Returns the parameters for the specified \a ellipsoid. * Results are cached to allow for fast retrieval of parameters. */ static EllipsoidParameters ellipsoidParameters( const QString &ellipsoid ); + /** + * Returns a list of the definitions for all known ellipsoids from the + * internal ellipsoid database. + * \see acronyms() + */ + static QList< QgsEllipsoidUtils::EllipsoidDefinition > definitions(); + + /** + * Returns a list of all known ellipsoid acronyms from the internal + * ellipsoid database. + * \see definitions() + */ + static QStringList acronyms(); + private: // ellipsoid cache static QReadWriteLock sEllipsoidCacheLock; static QHash< QString, EllipsoidParameters > sEllipsoidCache; + static QReadWriteLock sDefinitionCacheLock; + static QList< QgsEllipsoidUtils::EllipsoidDefinition > sDefinitionCache; }; diff --git a/tests/src/python/test_qgsellipsoidutils.py b/tests/src/python/test_qgsellipsoidutils.py index 7c2c6a5be03..b480ecd06a8 100644 --- a/tests/src/python/test_qgsellipsoidutils.py +++ b/tests/src/python/test_qgsellipsoidutils.py @@ -29,6 +29,15 @@ class TestQgsEllipsoidUtils(unittest.TestCase): # run each test twice, so that ellipsoid is fetched from cache on the second time + for i in range(2): + params = QgsEllipsoidUtils.ellipsoidParameters("WGS84") + self.assertTrue(params.valid) + self.assertEqual(params.semiMajor, 6378137.0) + self.assertAlmostEqual(params.semiMinor, 6356752.314245179, 5) + self.assertAlmostEqual(params.inverseFlattening, 298.257223563, 5) + self.assertFalse(params.useCustomParameters) + self.assertEqual(params.crs.authid(), 'EPSG:4030') + for i in range(2): params = QgsEllipsoidUtils.ellipsoidParameters("Ganymede2000") self.assertTrue(params.valid) @@ -53,6 +62,23 @@ class TestQgsEllipsoidUtils(unittest.TestCase): params = QgsEllipsoidUtils.ellipsoidParameters("Babies first ellipsoid!") self.assertFalse(params.valid) + def testAcronyms(self): + self.assertTrue('WGS84' in QgsEllipsoidUtils.acronyms()) + self.assertTrue('Ganymede2000' in QgsEllipsoidUtils.acronyms()) + + def testDefinitions(self): + defs = QgsEllipsoidUtils.definitions() + + gany_defs = [d for d in defs if d.acronym == 'Ganymede2000'][0] + self.assertEqual(gany_defs.acronym, 'Ganymede2000') + self.assertEqual(gany_defs.description, 'Ganymede2000') + self.assertTrue(gany_defs.parameters.valid) + self.assertEqual(gany_defs.parameters.semiMajor, 2632400.0) + self.assertEqual(gany_defs.parameters.semiMinor, 2632350.0) + self.assertEqual(gany_defs.parameters.inverseFlattening, 52648.0) + self.assertFalse(gany_defs.parameters.useCustomParameters) + self.assertEqual(gany_defs.parameters.crs.authid(), '') + if __name__ == '__main__': unittest.main()