Navigation Menu

Skip to content

Commit

Permalink
Add a QgsOgcCrsUtils::parseCrsName() function to parse the different …
Browse files Browse the repository at this point in the history
…styles of OGC CRS names (fixes #29391)

Recognizes the following flavors:
```
    //! CRS flavor
    enum class CRSFlavor
    {
      UNKNOWN, //! unknown/unhandled flavor
      AUTH_CODE, //! e.g EPSG:4326
      HTTP_EPSG_DOT_XML, //! e.g. http://www.opengis.net/gml/srs/epsg.xml#4326 (called "OGC HTTP URL" in GeoServer WFS configuration panel)
      OGC_URN, //! e.g. urn:ogc:def:crs:EPSG::4326
      X_OGC_URN, //! e.g. urn:x-ogc:def:crs:EPSG::4326
      OGC_HTTP_URI, //! e.g. http://www.opengis.net/def/crs/EPSG/0/4326
    };
```
  • Loading branch information
rouault authored and github-actions[bot] committed Sep 28, 2022
1 parent 91a8b1c commit 4684519
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 0 deletions.
45 changes: 45 additions & 0 deletions src/core/qgsogcutils.cpp
Expand Up @@ -3629,3 +3629,48 @@ QString QgsOgcUtilsExpressionFromFilter::errorMessage() const
{
return mErrorMessage;
}

QgsOgcCrsUtils::CRSFlavor QgsOgcCrsUtils::parseCrsName( const QString &crsName, QString &authority, QString &code )
{
const thread_local QRegularExpression re_url( QRegularExpression::anchoredPattern( QStringLiteral( "http://www\\.opengis\\.net/gml/srs/epsg\\.xml#(.+)" ) ), QRegularExpression::CaseInsensitiveOption );
if ( const QRegularExpressionMatch match = re_url.match( crsName ); match.hasMatch() )
{
authority = QStringLiteral( "EPSG" );
code = match.captured( 1 );
return CRSFlavor::HTTP_EPSG_DOT_XML;
}

const thread_local QRegularExpression re_ogc_urn( QRegularExpression::anchoredPattern( QStringLiteral( "urn:ogc:def:crs:([^:]+).+(?<=:)([^:]+)" ) ), QRegularExpression::CaseInsensitiveOption );
if ( const QRegularExpressionMatch match = re_ogc_urn.match( crsName ); match.hasMatch() )
{
authority = match.captured( 1 );
code = match.captured( 2 );
return CRSFlavor::OGC_URN;
}

const thread_local QRegularExpression re_x_ogc_urn( QRegularExpression::anchoredPattern( QStringLiteral( "urn:x-ogc:def:crs:([^:]+).+(?<=:)([^:]+)" ) ), QRegularExpression::CaseInsensitiveOption );
if ( const QRegularExpressionMatch match = re_x_ogc_urn.match( crsName ); match.hasMatch() )
{
authority = match.captured( 1 );
code = match.captured( 2 );
return CRSFlavor::X_OGC_URN;
}

const thread_local QRegularExpression re_http_uri( QRegularExpression::anchoredPattern( QStringLiteral( "http://www\\.opengis\\.net/def/crs/([^/]+).+/([^/]+)" ) ), QRegularExpression::CaseInsensitiveOption );
if ( const QRegularExpressionMatch match = re_http_uri.match( crsName ); match.hasMatch() )
{
authority = match.captured( 1 );
code = match.captured( 2 );
return CRSFlavor::OGC_HTTP_URI;
}

const thread_local QRegularExpression re_auth_code( QRegularExpression::anchoredPattern( QStringLiteral( "([^:]+):(.+)" ) ), QRegularExpression::CaseInsensitiveOption );
if ( const QRegularExpressionMatch match = re_auth_code.match( crsName ); match.hasMatch() )
{
authority = match.captured( 1 );
code = match.captured( 2 );
return CRSFlavor::AUTH_CODE;
}

return CRSFlavor::UNKNOWN;
}
36 changes: 36 additions & 0 deletions src/core/qgsogcutils.h
Expand Up @@ -552,6 +552,42 @@ class QgsOgcUtilsSQLStatementToFilter
QString &srsName,
bool &axisInversion );
};

/**
* \ingroup core
* \brief Utilities related to OGC CRS encodings.
* \note not available in Python bindings
* \since QGIS 3.28
*/
class CORE_EXPORT QgsOgcCrsUtils
{
public:

//! CRS flavor
enum class CRSFlavor
{
UNKNOWN, //! unknown/unhandled flavor
AUTH_CODE, //! e.g EPSG:4326
HTTP_EPSG_DOT_XML, //! e.g. http://www.opengis.net/gml/srs/epsg.xml#4326 (called "OGC HTTP URL" in GeoServer WFS configuration panel)
OGC_URN, //! e.g. urn:ogc:def:crs:EPSG::4326
X_OGC_URN, //! e.g. urn:x-ogc:def:crs:EPSG::4326
OGC_HTTP_URI, //! e.g. http://www.opengis.net/def/crs/EPSG/0/4326
};

/**
* Parse a CRS name in one of the flavors of OGC services, and decompose it
* as authority and code.
*
* \param crsName CRS name, like "EPSG:4326", "http://www.opengis.net/gml/srs/epsg.xml#4326", "urn:ogc:def:crs:EPSG::4326", etc.
* \param[out] authority CRS authority.
* \param[out] code CRS code.
* \return CRS flavor (UNKNOWN if crsName could not been parsed.
*/
static CRSFlavor parseCrsName( const QString &crsName, QString &authority, QString &code );
};

Q_DECLARE_METATYPE( QgsOgcCrsUtils::CRSFlavor )

#endif // #ifndef SIP_RUN

#endif // QGSOGCUTILS_H
38 changes: 38 additions & 0 deletions tests/src/core/testqgsogcutils.cpp
Expand Up @@ -75,6 +75,9 @@ class TestQgsOgcUtils : public QObject

void testSQLStatementToOgcFilter();
void testSQLStatementToOgcFilter_data();

void testParseCrsName();
void testParseCrsName_data();
};


Expand Down Expand Up @@ -1213,5 +1216,40 @@ void TestQgsOgcUtils::testSQLStatementToOgcFilter_data()
"</fes:Filter>" );
}


void TestQgsOgcUtils::testParseCrsName()
{
QFETCH( QString, crsName );
QFETCH( QgsOgcCrsUtils::CRSFlavor, expectedFlavor );
QFETCH( QString, expectedAuthority );
QFETCH( QString, expectedCode );

QString authority;
QString code;
const QgsOgcCrsUtils::CRSFlavor crsFlavor = QgsOgcCrsUtils::parseCrsName( crsName, authority, code );
QCOMPARE( expectedFlavor, crsFlavor );
QCOMPARE( expectedAuthority, authority );
QCOMPARE( expectedCode, code );
}

void TestQgsOgcUtils::testParseCrsName_data()
{
QTest::addColumn<QString>( "crsName" );
QTest::addColumn<QgsOgcCrsUtils::CRSFlavor>( "expectedFlavor" );
QTest::addColumn<QString>( "expectedAuthority" );
QTest::addColumn<QString>( "expectedCode" );

QTest::newRow( "unknown" ) << QStringLiteral( "foo" ) << QgsOgcCrsUtils::CRSFlavor::UNKNOWN << QString() << QString();
QTest::newRow( "unknown2" ) << QStringLiteral( "EPSG:" ) << QgsOgcCrsUtils::CRSFlavor::UNKNOWN << QString() << QString();
QTest::newRow( "AUTH_CODE" ) << QStringLiteral( "EPSG:1234" ) << QgsOgcCrsUtils::CRSFlavor::AUTH_CODE << QStringLiteral( "EPSG" ) << QStringLiteral( "1234" );
QTest::newRow( "HTTP_EPSG_DOT_XML" ) << QStringLiteral( "http://www.opengis.net/gml/srs/epsg.xml#1234" ) << QgsOgcCrsUtils::CRSFlavor::HTTP_EPSG_DOT_XML << QStringLiteral( "EPSG" ) << QStringLiteral( "1234" );
QTest::newRow( "OGC_URN" ) << QStringLiteral( "urn:ogc:def:crs:EPSG::1234" ) << QgsOgcCrsUtils::CRSFlavor::OGC_URN << QStringLiteral( "EPSG" ) << QStringLiteral( "1234" );
QTest::newRow( "OGC_URN missing col" ) << QStringLiteral( "urn:ogc:def:crs:EPSG:1234" ) << QgsOgcCrsUtils::CRSFlavor::OGC_URN << QStringLiteral( "EPSG" ) << QStringLiteral( "1234" );
QTest::newRow( "X_OGC_URN" ) << QStringLiteral( "urn:x-ogc:def:crs:EPSG::1234" ) << QgsOgcCrsUtils::CRSFlavor::X_OGC_URN << QStringLiteral( "EPSG" ) << QStringLiteral( "1234" );
QTest::newRow( "X_OGC_URN missing col" ) << QStringLiteral( "urn:x-ogc:def:crs:EPSG:1234" ) << QgsOgcCrsUtils::CRSFlavor::X_OGC_URN << QStringLiteral( "EPSG" ) << QStringLiteral( "1234" );
QTest::newRow( "OGC_HTTP_URI" ) << QStringLiteral( "http://www.opengis.net/def/crs/EPSG/0/1234" ) << QgsOgcCrsUtils::CRSFlavor::OGC_HTTP_URI << QStringLiteral( "EPSG" ) << QStringLiteral( "1234" );
}


QGSTEST_MAIN( TestQgsOgcUtils )
#include "testqgsogcutils.moc"

0 comments on commit 4684519

Please sign in to comment.