Skip to content
Permalink
Browse files
Move retrieval of lists of known ellipsoids to QgsEllipsoidUtils
  • Loading branch information
nyalldawson committed Apr 18, 2017
1 parent 147bb7f commit 5f1656b8e9991063a49af348e64a4127ca1b8ffe
Showing with 169 additions and 56 deletions.
  1. +32 −0 python/core/qgsellipsoidutils.sip
  2. +9 −54 src/app/qgsprojectproperties.cpp
  3. +70 −1 src/core/qgsellipsoidutils.cpp
  4. +32 −1 src/core/qgsellipsoidutils.h
  5. +26 −0 tests/src/python/test_qgsellipsoidutils.py
@@ -53,13 +53,45 @@ 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``.
Results are cached to allow for fast retrieval of parameters.
: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

};


@@ -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 )
@@ -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::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;
}
@@ -19,6 +19,7 @@
#include "qgis_core.h"
#include "qgis.h"
#include "qgscoordinatereferencesystem.h"
#include <QStringList>

/**
* \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;

};

@@ -29,6 +29,15 @@ def testParams(self):

# 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 @@ def testParams(self):
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()

0 comments on commit 5f1656b

Please sign in to comment.