Move retrieval of lists of known ellipsoids to QgsEllipsoidUtils

This commit is contained in:
Nyall Dawson 2017-04-18 10:18:48 +10:00
parent 147bb7f1ed
commit 5f1656b8e9
5 changed files with 169 additions and 56 deletions

View File

@ -53,6 +53,22 @@ Associated coordinate reference system
%End %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 ); static EllipsoidParameters ellipsoidParameters( const QString &ellipsoid );
%Docstring %Docstring
Returns the parameters for the specified ``ellipsoid``. Returns the parameters for the specified ``ellipsoid``.
@ -60,6 +76,22 @@ Associated coordinate reference system
:rtype: EllipsoidParameters :rtype: EllipsoidParameters
%End %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
}; };

View File

@ -1805,10 +1805,6 @@ void QgsProjectProperties::populateEllipsoidList()
// //
// Populate the ellipsoid list // Populate the ellipsoid list
// //
sqlite3 *myDatabase = nullptr;
const char *myTail = nullptr;
sqlite3_stmt *myPreparedStatement = nullptr;
int myResult;
EllipsoidDefs myItem; EllipsoidDefs myItem;
myItem.acronym = GEO_NONE; myItem.acronym = GEO_NONE;
@ -1823,58 +1819,17 @@ void QgsProjectProperties::populateEllipsoidList()
myItem.semiMinor = 6370997.0; myItem.semiMinor = 6370997.0;
mEllipsoidList.append( myItem ); mEllipsoidList.append( myItem );
//check the db is available Q_FOREACH ( const QgsEllipsoidUtils::EllipsoidDefinition &def, QgsEllipsoidUtils::definitions() )
myResult = sqlite3_open_v2( QgsApplication::srsDatabaseFilePath().toUtf8().data(), &myDatabase, SQLITE_OPEN_READONLY, nullptr );
if ( myResult )
{ {
QgsDebugMsg( QString( "Can't open database: %1" ).arg( sqlite3_errmsg( myDatabase ) ) ); myItem.acronym = def.acronym;
// XXX This will likely never happen since on open, sqlite creates the myItem.description = def.description;
// database if it does not exist. // Fall-back values
Q_ASSERT( myResult == 0 ); 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 // Add all items to selector
Q_FOREACH ( const EllipsoidDefs &i, mEllipsoidList ) Q_FOREACH ( const EllipsoidDefs &i, mEllipsoidList )

View File

@ -21,7 +21,8 @@
QReadWriteLock QgsEllipsoidUtils::sEllipsoidCacheLock; QReadWriteLock QgsEllipsoidUtils::sEllipsoidCacheLock;
QHash< QString, QgsEllipsoidUtils::EllipsoidParameters > QgsEllipsoidUtils::sEllipsoidCache; QHash< QString, QgsEllipsoidUtils::EllipsoidParameters > QgsEllipsoidUtils::sEllipsoidCache;
QReadWriteLock QgsEllipsoidUtils::sDefinitionCacheLock;
QList< QgsEllipsoidUtils::EllipsoidDefinition > QgsEllipsoidUtils::sDefinitionCache;
QgsEllipsoidUtils::EllipsoidParameters QgsEllipsoidUtils::ellipsoidParameters( const QString &ellipsoid ) QgsEllipsoidUtils::EllipsoidParameters QgsEllipsoidUtils::ellipsoidParameters( const QString &ellipsoid )
{ {
@ -177,3 +178,71 @@ QgsEllipsoidUtils::EllipsoidParameters QgsEllipsoidUtils::ellipsoidParameters( c
sEllipsoidCacheLock.unlock(); sEllipsoidCacheLock.unlock();
return params; return params;
} }
QList<QgsEllipsoidUtils::EllipsoidDefinition> QgsEllipsoidUtils::definitions()
{
sDefinitionCacheLock.lockForRead();
if ( !sDefinitionCache.isEmpty() )
{
QList<QgsEllipsoidUtils::EllipsoidDefinition> defs = sDefinitionCache;
sDefinitionCacheLock.unlock();
return defs;
}
sDefinitionCacheLock.unlock();
sDefinitionCacheLock.lockForWrite();
sqlite3 *database = nullptr;
const char *tail = nullptr;
sqlite3_stmt *preparedStatement = nullptr;
int result;
QList<QgsEllipsoidUtils::EllipsoidDefinition> 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;
}

View File

@ -19,6 +19,7 @@
#include "qgis_core.h" #include "qgis_core.h"
#include "qgis.h" #include "qgis.h"
#include "qgscoordinatereferencesystem.h" #include "qgscoordinatereferencesystem.h"
#include <QStringList>
/** /**
* \class QgsEllipsoidUtils * \class QgsEllipsoidUtils
@ -32,7 +33,7 @@ class CORE_EXPORT QgsEllipsoidUtils
public: public:
/** /**
* Contains parameter definitions for an ellipsoid. * Contains parameters for an ellipsoid.
* \since QGIS 3.0 * \since QGIS 3.0
*/ */
struct EllipsoidParameters struct EllipsoidParameters
@ -55,17 +56,47 @@ class CORE_EXPORT QgsEllipsoidUtils
QgsCoordinateReferenceSystem crs; 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. * Returns the parameters for the specified \a ellipsoid.
* Results are cached to allow for fast retrieval of parameters. * Results are cached to allow for fast retrieval of parameters.
*/ */
static EllipsoidParameters ellipsoidParameters( const QString &ellipsoid ); 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: private:
// ellipsoid cache // ellipsoid cache
static QReadWriteLock sEllipsoidCacheLock; static QReadWriteLock sEllipsoidCacheLock;
static QHash< QString, EllipsoidParameters > sEllipsoidCache; static QHash< QString, EllipsoidParameters > sEllipsoidCache;
static QReadWriteLock sDefinitionCacheLock;
static QList< QgsEllipsoidUtils::EllipsoidDefinition > sDefinitionCache;
}; };

View File

@ -29,6 +29,15 @@ class TestQgsEllipsoidUtils(unittest.TestCase):
# run each test twice, so that ellipsoid is fetched from cache on the second time # 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): for i in range(2):
params = QgsEllipsoidUtils.ellipsoidParameters("Ganymede2000") params = QgsEllipsoidUtils.ellipsoidParameters("Ganymede2000")
self.assertTrue(params.valid) self.assertTrue(params.valid)
@ -53,6 +62,23 @@ class TestQgsEllipsoidUtils(unittest.TestCase):
params = QgsEllipsoidUtils.ellipsoidParameters("Babies first ellipsoid!") params = QgsEllipsoidUtils.ellipsoidParameters("Babies first ellipsoid!")
self.assertFalse(params.valid) 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__': if __name__ == '__main__':
unittest.main() unittest.main()