Skip to content
Permalink
Browse files

Add method to retrieve all valid srs ids from CRS databases

  • Loading branch information
nyalldawson committed Feb 13, 2017
1 parent f2ac60a commit fbf99afd01a9dbf7bc8a09cea0f9e9d6d0729a67
@@ -438,6 +438,53 @@ template <TYPE>
};


%MappedType QList<long>
{
%TypeHeaderCode
#include <QList>
%End

%ConvertFromTypeCode
// Create the list.
PyObject *l;

if ((l = PyList_New(sipCpp->size())) == NULL)
return NULL;

// Set the list elements.
QList<long>::iterator it = sipCpp->begin();
for (int i = 0; it != sipCpp->end(); ++it, ++i)
{
PyObject *tobj;

if ((tobj = PyLong_FromLong(*it)) == NULL)
{
Py_DECREF(l);
return NULL;
}
PyList_SET_ITEM(l, i, tobj);
}

return l;
%End

%ConvertToTypeCode
// Check the type if that is all that is required.
if (sipIsErr == NULL)
return PyList_Check(sipPy);

QList<long> *qlist = new QList<long>;

for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i)
{
*qlist << PyLong_AsLong(PyList_GET_ITEM(sipPy, i));
}

*sipCppPtr = qlist;
return sipGetState(sipTransferObj);
%End
};

%MappedType QList<qint64>
{
%TypeHeaderCode
@@ -183,6 +183,8 @@ class QgsCoordinateReferenceSystem
// TODO QGIS 3: remove theType and always use EPSG code
QgsCoordinateReferenceSystem( const long theId, CrsType theType = PostgisCrsId );

static QList< long > validSrsIds();

// static creators

/** Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
@@ -90,6 +90,66 @@ QgsCoordinateReferenceSystem& QgsCoordinateReferenceSystem::operator=( const Qgs
return *this;
}

QList<long> QgsCoordinateReferenceSystem::validSrsIds()
{
QList<long> results;
// check both standard & user defined projection databases
QStringList dbs = QStringList() << QgsApplication::srsDatabaseFilePath() << QgsApplication::qgisUserDatabaseFilePath();

Q_FOREACH ( const QString& db, dbs )
{
QFileInfo myInfo( db );
if ( !myInfo.exists() )
{
QgsDebugMsg( "failed : " + db + " does not exist!" );
continue;
}

sqlite3 *database = nullptr;
const char *tail = nullptr;
sqlite3_stmt *statement = nullptr;

//check the db is available
int result = openDatabase( db, &database );
if ( result != SQLITE_OK )
{
QgsDebugMsg( "failed : " + db + " could not be opened!" );
continue;
}

QString sql = "select srs_id from tbl_srs";
result = sqlite3_prepare( database, sql.toUtf8(),
sql.toUtf8().length(),
&statement, &tail );
while ( 1 )
{
// this one is an infinitive loop, intended to fetch any row
int ret = sqlite3_step( statement );

if ( ret == SQLITE_DONE )
{
// there are no more rows to fetch - we can stop looping
break;
}

if ( ret == SQLITE_ROW )
{
results.append( sqlite3_column_int( statement, 0 ) );
}
else
{
QgsMessageLog::logMessage( QObject::tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database ) ), QObject::tr( "SpatiaLite" ) );
break;
}
}

sqlite3_finalize( statement );
sqlite3_close( database );
}
std::sort( results.begin(), results.end() );
return results;
}

QgsCoordinateReferenceSystem QgsCoordinateReferenceSystem::fromOgcWmsCrs( const QString& ogcCrs )
{
QgsCoordinateReferenceSystem crs;
@@ -236,6 +236,15 @@ class CORE_EXPORT QgsCoordinateReferenceSystem
//! Assignment operator
QgsCoordinateReferenceSystem& operator=( const QgsCoordinateReferenceSystem& srs );

/**
* Returns a list of all valid SRS IDs present in the CRS database. Any of the
* returned values can be safely passed to fromSrsId() to create a new, valid
* QgsCoordinateReferenceSystem object.
* @see fromSrsId()
* @note added in QGIS 3.0
*/
static QList< long > validSrsIds();

This comment has been minimized.

Copy link
@m-kuhn

m-kuhn Sep 26, 2017

Member

@nyalldawson

With GDAL 2.2.1:

27223 in QgsCoordinateReferenceSystem.validSrsIds()
> True
QgsCoordinateReferenceSystem.fromSrsId(27223).isValid()
> False
QgsCoordinateReferenceSystem.fromEpsgId(27223).isValid()
> True

Should this method be renamed to validEpsgIds() or is there something else broken?

This comment has been minimized.

Copy link
@nyalldawson

nyalldawson Sep 27, 2017

Author Collaborator

No - the method returns the internal srs id. I can't see what would possibly be happening here - both methods are using the same database, with one just returning all "srs_id" from the table, and the other retrieving a record by matching srs_id...

This comment has been minimized.

Copy link
@nyalldawson

nyalldawson Sep 27, 2017

Author Collaborator

Actually the only thing I can think of is that validSrsIds includes the srs_ids from custom user projections, but fromSrsId only matches user projections if the srs id is greater than 100000. Maybe something (an earlier test?) has added an entry to the custom user projection database?

This comment has been minimized.

Copy link
@m-kuhn

m-kuhn Sep 27, 2017

Member

I just checked, it's in the main srs.db and not in the user qgis.db

sqlite3 /tmp/srs.db

sqlite> select * from tbl_srs where srs_id = 27223;
27223|Sphere_Hotine|omerc||+proj=omerc +lat_0=40 +lon_1=0 +lat_1=0 +lon_2=0 +lat_2=0 +k=1 +x_0=0 +y_0=0 +a=6371000 +b=6371000 +units=m +no_defs|53025|EPSG|53025|0|0|
sqlite3 /home/mku/.local/share/QGIS/QGIS3/profiles/default/qgis.db

sqlite> select count(*) from tbl_srs where srs_id = 27223;
0

This comment has been minimized.

Copy link
@m-kuhn

m-kuhn Sep 27, 2017

Member

Probably related to this:

ERROR 6: EPSG PCS/GCS code 53025 not found in EPSG support files. Is this a valid
EPSG coordinate system?

This comment has been minimized.

Copy link
@m-kuhn

m-kuhn Sep 27, 2017

Member

@rouault is this related to some change in the GDAL srs db?

This comment has been minimized.

Copy link
@rouault

rouault Sep 27, 2017

Contributor

gdalsrsinfo EPSG:53025 returns for me

PROJCS["Sphere_Hotine",
    GEOGCS["GCS_Sphere",
        DATUM["Not_specified_based_on_Authalic_Sphere",
            SPHEROID["Sphere",6371000,0]],
        PRIMEM["Greenwich",0],
        UNIT["Degree",0.017453292519943295]],
    PROJECTION["Hotine_Oblique_Mercator_Two_Point_Natural_Origin"],
    PARAMETER["False_Easting",0],
    PARAMETER["False_Northing",0],
    PARAMETER["Latitude_Of_Point_1",0],
    PARAMETER["Latitude_Of_Point_2",60],
    PARAMETER["Scale_Factor",1],
    PARAMETER["Longitude_Of_Point_1",0],
    PARAMETER["Longitude_Of_Point_2",60],
    PARAMETER["Latitude_Of_Center",40],
    UNIT["Meter",1],
    AUTHORITY["EPSG","53025"]]

But I'd note that this is not a true EPSG code, but one coming from ESRI:

Frmo GDAL tree,

$ grep 53025 data/*
data/esri_extra.wkt:53025,PROJCS["Sphere_Hotine",GEOGCS["GCS_Sphere",DATUM["Not_specified_based_on_Authalic_Sphere",SPHEROID["Sphere",6371000,0]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Hotine_Oblique_Mercator_Two_Point_Natural_Origin"],PARAMETER["False_Easting",0],PARAMETER["False_Northing",0],PARAMETER["Latitude_Of_Point_1",0],PARAMETER["Latitude_Of_Point_2",60],PARAMETER["Scale_Factor",1],PARAMETER["Longitude_Of_Point_1",0],PARAMETER["Longitude_Of_Point_2",60],PARAMETER["Latitude_Of_Center",40],UNIT["Meter",1],AUTHORITY["EPSG","53025"]]

The rights of data/esri_extra.wkt are apparently unclear, so some Linux distributions strip this file off when building their GDAL package

This comment has been minimized.

Copy link
@nyalldawson

nyalldawson Sep 27, 2017

Author Collaborator

@m-kuhn that test is probably overly sensitive. It might be easiest just to give it a tolerance of 100 missing srses and it'll still be good enough.

This comment has been minimized.

Copy link
@m-kuhn

m-kuhn Sep 27, 2017

Member

Apparently at least Ubuntu 16.04 and Fedora 26 strip them away.
I'll try the tolerance approach, thanks @nyalldawson and @rouault


// static creators

/** Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
@@ -274,6 +283,7 @@ class CORE_EXPORT QgsCoordinateReferenceSystem
* @returns matching CRS, or an invalid CRS if ID could not be found
* @note added in QGIS 3.0
* @see createFromSrsId()
* @see validSrsIds()
*/
static QgsCoordinateReferenceSystem fromSrsId( long srsId );

@@ -73,6 +73,8 @@ class TestQgsCoordinateReferenceSystem: public QObject
void setValidationHint();
void hasAxisInverted();
void createFromProj4Invalid();
void validSrsIds();

private:
void debugPrint( QgsCoordinateReferenceSystem &theCrs );
// these used by createFromESRIWkt()
@@ -724,5 +726,19 @@ void TestQgsCoordinateReferenceSystem::createFromProj4Invalid()
QVERIFY( !myCrs.createFromProj4( "+proj=longlat +no_defs" ) );
}

void TestQgsCoordinateReferenceSystem::validSrsIds()
{
QList< long > ids = QgsCoordinateReferenceSystem::validSrsIds();
QVERIFY( ids.contains( 3857 ) );
QVERIFY( ids.contains( 28356 ) );

// check that all returns ids are valid
Q_FOREACH ( long id, ids )
{
QgsCoordinateReferenceSystem c = QgsCoordinateReferenceSystem::fromSrsId( id );
QVERIFY( c.isValid() );
}
}

QGSTEST_MAIN( TestQgsCoordinateReferenceSystem )
#include "testqgscoordinatereferencesystem.moc"

0 comments on commit fbf99af

Please sign in to comment.
You can’t perform that action at this time.