Skip to content

Commit 7edb71b

Browse files
rouaultnyalldawson
authored andcommittedApr 3, 2023
[OAPIF provider] OGC API features 2 / CRS related fixes (fixes #52228)
- Takes into account storageCrs in /collection response to get the default crs in priority - Append &crs=... to /items requests when the crs is not OGC:CRS84 - Make sure geometry axis order is flipped to easting/northing or long/lat when reading geometries from a CRS with inverted axis order - Report the list of available CRS in the dialog box where one can select one - Resolve "#/crs"
1 parent 9fa128a commit 7edb71b

File tree

6 files changed

+182
-49
lines changed

6 files changed

+182
-49
lines changed
 

‎src/providers/wfs/oapif/qgsoapifcollection.cpp

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ using namespace nlohmann;
2525

2626
#include <QTextCodec>
2727

28-
bool QgsOapifCollection::deserialize( const json &j )
28+
bool QgsOapifCollection::deserialize( const json &j, const json &jCollections )
2929
{
3030
if ( !j.is_object() )
3131
return false;
@@ -121,7 +121,6 @@ bool QgsOapifCollection::deserialize( const json &j )
121121
crs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( QString::fromStdString( jCrs.get<std::string>() ) );
122122
}
123123
}
124-
mLayerMetadata.setCrs( crs );
125124

126125
const auto jBboxes = spatial["bbox"];
127126
if ( jBboxes.is_array() )
@@ -148,6 +147,7 @@ bool QgsOapifCollection::deserialize( const json &j )
148147
{
149148
if ( firstBbox )
150149
{
150+
mBboxCrs = crs;
151151
mBbox.set( values[0], values[1], values[2], values[3] );
152152
}
153153
spatialExtent.bounds = QgsBox3d( mBbox );
@@ -156,6 +156,7 @@ bool QgsOapifCollection::deserialize( const json &j )
156156
{
157157
if ( firstBbox )
158158
{
159+
mBboxCrs = crs;
159160
mBbox.set( values[0], values[1], values[3], values[4] );
160161
}
161162
spatialExtent.bounds = QgsBox3d( values[0], values[1], values[2],
@@ -298,25 +299,74 @@ bool QgsOapifCollection::deserialize( const json &j )
298299
}
299300
}
300301

302+
// Usage storageCrs from Part 2 in priority
303+
bool layerCrsSet = false;
304+
if ( j.contains( "storageCrs" ) )
305+
{
306+
const auto crsUrl = j["storageCrs"];
307+
if ( crsUrl.is_string() )
308+
{
309+
QString crsStr = QString::fromStdString( crsUrl.get<std::string>() );
310+
QgsCoordinateReferenceSystem crs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( crsStr );
311+
312+
if ( j.contains( "storageCrsCoordinateEpoch" ) )
313+
{
314+
const auto storageCrsCoordinateEpoch = j["storageCrsCoordinateEpoch"];
315+
if ( storageCrsCoordinateEpoch.is_number() )
316+
{
317+
crs.setCoordinateEpoch( storageCrsCoordinateEpoch.get<double>() );
318+
}
319+
}
320+
321+
layerCrsSet = true;
322+
mLayerMetadata.setCrs( crs );
323+
mCrsList.append( crs.authid() );
324+
}
325+
}
326+
301327
if ( j.contains( "crs" ) )
302328
{
303-
const auto crsUrls = j["crs"];
304-
if ( crsUrls.is_array() )
329+
json jCrs = j["crs"];
330+
// Resolve "#/crs" link
331+
if ( jCrs.is_array() && jCrs.size() == 1 &&
332+
jCrs[0].is_string() && jCrs[0].get<std::string>() == "#/crs" &&
333+
jCollections.is_object() && jCollections.contains( "crs" ) )
305334
{
306-
for ( const auto &crsUrl : crsUrls )
335+
jCrs = jCollections["crs"];
336+
}
337+
338+
if ( jCrs.is_array() )
339+
{
340+
for ( const auto &crsUrl : jCrs )
307341
{
308342
if ( crsUrl.is_string() )
309343
{
310-
QString crs = QString::fromStdString( crsUrl.get<std::string>() );
311-
mLayerMetadata.setCrs( QgsCoordinateReferenceSystem::fromOgcWmsCrs( crs ) );
344+
QString crsStr = QString::fromStdString( crsUrl.get<std::string>() );
345+
QgsCoordinateReferenceSystem crs( QgsCoordinateReferenceSystem::fromOgcWmsCrs( crsStr ) );
346+
if ( !layerCrsSet )
347+
{
348+
// Take the first CRS of the list
349+
layerCrsSet = true;
350+
mLayerMetadata.setCrs( crs );
351+
}
312352

313-
// Take the first CRS of the list
314-
break;
353+
if ( !mCrsList.contains( crs.authid() ) )
354+
{
355+
mCrsList.append( crs.authid() );
356+
}
315357
}
316358
}
317359
}
318360
}
319361

362+
if ( mCrsList.isEmpty() )
363+
{
364+
QgsCoordinateReferenceSystem crs = QgsCoordinateReferenceSystem::fromOgcWmsCrs(
365+
QgsOapifProvider::OAPIF_PROVIDER_DEFAULT_CRS );
366+
mLayerMetadata.setCrs( QgsCoordinateReferenceSystem::fromOgcWmsCrs( crs.authid() ) );
367+
mCrsList.append( crs.authid() );
368+
}
369+
320370
return true;
321371
}
322372

@@ -408,7 +458,7 @@ void QgsOapifCollectionsRequest::processReply()
408458
for ( const auto &jCollection : collections )
409459
{
410460
QgsOapifCollection collection;
411-
if ( collection.deserialize( jCollection ) )
461+
if ( collection.deserialize( jCollection, j ) )
412462
{
413463
if ( collection.mLayerMetadata.licenses().isEmpty() )
414464
{
@@ -502,7 +552,7 @@ void QgsOapifCollectionRequest::processReply()
502552
try
503553
{
504554
const json j = json::parse( utf8Text.toStdString() );
505-
mCollection.deserialize( j );
555+
mCollection.deserialize( j, json() );
506556
}
507557
catch ( const json::parse_error &ex )
508558
{

‎src/providers/wfs/oapif/qgsoapifcollection.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,20 @@ struct QgsOapifCollection
3939
//! Description
4040
QString mDescription;
4141

42-
//! Bounding box (in CRS84)
42+
//! Bounding box
4343
QgsRectangle mBbox;
4444

45+
//! Bounding box Crs
46+
QgsCoordinateReferenceSystem mBboxCrs;
47+
48+
//! List of available CRS
49+
QList<QString> mCrsList;
50+
4551
//! Layer metadata
4652
QgsLayerMetadata mLayerMetadata;
4753

4854
//! Fills a collection from its JSON serialization
49-
bool deserialize( const json &j );
55+
bool deserialize( const json &j, const json &jCollections );
5056
};
5157

5258
//! Manages the /collections request

‎src/providers/wfs/oapif/qgsoapifprovider.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,20 +151,33 @@ bool QgsOapifProvider::init()
151151
return false;
152152
}
153153
}
154-
mShared->mCapabilityExtent = collectionRequest->collection().mBbox;
154+
155155
mLayerMetadata = collectionRequest->collection().mLayerMetadata;
156156

157157
if ( mLayerMetadata.crs().isValid() )
158158
{
159159
// WORKAROUND: Recreate a CRS object with fromOgcWmsCrs because when copying the
160160
// CRS his mPj pointer gets deleted and it is impossible to create a transform
161161
mShared->mSourceCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( mLayerMetadata.crs().authid() );
162+
mShared->mSourceCrs.setCoordinateEpoch( mLayerMetadata.crs().coordinateEpoch() );
162163
}
163164
else
164165
{
165166
mShared->mSourceCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs(
166167
QgsOapifProvider::OAPIF_PROVIDER_DEFAULT_CRS );
167168
}
169+
mShared->mCapabilityExtent = collectionRequest->collection().mBbox;
170+
171+
// Reproject extent of /collection request to the layer CRS
172+
if ( !mShared->mCapabilityExtent.isNull() &&
173+
collectionRequest->collection().mBboxCrs != mShared->mSourceCrs )
174+
{
175+
QgsCoordinateTransform ct( collectionRequest->collection().mBboxCrs, mShared->mSourceCrs, transformContext() );
176+
ct.setBallparkTransformsAreAppropriate( true );
177+
QgsDebugMsgLevel( "before ext:" + mShared->mCapabilityExtent.toString(), 4 );
178+
mShared->mCapabilityExtent = ct.transformBoundingBox( mShared->mCapabilityExtent );
179+
QgsDebugMsgLevel( "after ext:" + mShared->mCapabilityExtent.toString(), 4 );
180+
}
168181

169182
// Merge contact info from /api
170183
mLayerMetadata.setContacts( apiRequest.metadata().contacts() );
@@ -759,6 +772,10 @@ void QgsOapifFeatureDownloaderImpl::run( bool serializeFeatures, long long maxFe
759772
}
760773
}
761774

775+
if ( mShared->mSourceCrs
776+
!= QgsCoordinateReferenceSystem::fromOgcWmsCrs( QgsOapifProvider::OAPIF_PROVIDER_DEFAULT_CRS ) )
777+
url += QStringLiteral( "&crs=%1" ).arg( mShared->mSourceCrs.toOgcUri() );
778+
762779
while ( !url.isEmpty() )
763780
{
764781
url = mShared->appendExtraQueryParameters( url );
@@ -814,7 +831,13 @@ void QgsOapifFeatureDownloaderImpl::run( bool serializeFeatures, long long maxFe
814831
// as the layer, convert them
815832
const QgsFeature &f = pair.first;
816833
QgsFeature dstFeat( dstFields, f.id() );
817-
dstFeat.setGeometry( f.geometry() );
834+
if ( f.hasGeometry() )
835+
{
836+
QgsGeometry g = f.geometry();
837+
if ( mShared->mSourceCrs.hasAxisInverted() )
838+
g.transform( QTransform( 0, 1, 1, 0, 0, 0 ) );
839+
dstFeat.setGeometry( g );
840+
}
818841
const auto srcAttrs = f.attributes();
819842
for ( int j = 0; j < dstFields.size(); j++ )
820843
{

‎src/providers/wfs/qgsbasenetworkrequest.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,6 @@ bool QgsBaseNetworkRequest::sendGET( const QUrl &url, const QString &acceptHeade
7878
}
7979
#endif
8080

81-
// For REST API using URL subpaths, normalize the subpaths
82-
const int afterEndpointStartPos = modifiedUrlString.indexOf( "fake_qgis_http_endpoint" ) + strlen( "fake_qgis_http_endpoint" );
83-
QString afterEndpointStart = modifiedUrlString.mid( afterEndpointStartPos );
84-
afterEndpointStart.replace( QLatin1String( "/" ), QLatin1String( "_" ) );
85-
modifiedUrlString = modifiedUrlString.mid( 0, afterEndpointStartPos ) + afterEndpointStart;
86-
8781
if ( !acceptHeader.isEmpty() )
8882
{
8983
if ( modifiedUrlString.indexOf( '?' ) > 0 )
@@ -95,6 +89,13 @@ bool QgsBaseNetworkRequest::sendGET( const QUrl &url, const QString &acceptHeade
9589
modifiedUrlString += QStringLiteral( "?Accept=" ) + acceptHeader;
9690
}
9791
}
92+
93+
// For REST API using URL subpaths, normalize the subpaths
94+
const int afterEndpointStartPos = static_cast<int>( modifiedUrlString.indexOf( "fake_qgis_http_endpoint" ) + strlen( "fake_qgis_http_endpoint" ) );
95+
QString afterEndpointStart = modifiedUrlString.mid( afterEndpointStartPos );
96+
afterEndpointStart.replace( QLatin1String( "/" ), QLatin1String( "_" ) );
97+
modifiedUrlString = modifiedUrlString.mid( 0, afterEndpointStartPos ) + afterEndpointStart;
98+
9899
const auto posQuotationMark = modifiedUrlString.indexOf( '?' );
99100
if ( posQuotationMark > 0 )
100101
{

‎src/providers/wfs/qgswfssourceselect.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ void QgsWFSSourceSelect::oapifCollectionsReplyFinished()
380380
return;
381381
}
382382

383+
mAvailableCRS.clear();
383384
for ( const auto &collection : mOAPIFCollections->collections() )
384385
{
385386
// insert the typenames, titles and abstracts into the tree view
@@ -393,8 +394,8 @@ void QgsWFSSourceSelect::oapifCollectionsReplyFinished()
393394
typedef QList< QStandardItem * > StandardItemList;
394395
mModel->appendRow( StandardItemList() << titleItem << nameItem << abstractItem << filterItem );
395396

396-
gbCRS->setEnabled( false );
397-
labelCoordRefSys->setText( collection.mLayerMetadata.crs().authid() );
397+
// insert the available CRS into mAvailableCRS
398+
mAvailableCRS.insert( collection.mId, collection.mCrsList );
398399
}
399400

400401
if ( !mOAPIFCollections->nextUrl().isEmpty() )

‎tests/src/python/test_provider_oapif.py

Lines changed: 78 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,29 @@
3333
from providertestbase import ProviderTestCase
3434

3535

36-
def sanitize(endpoint, x):
37-
if len(endpoint + x) > 256:
38-
ret = endpoint + hashlib.md5(x.replace('/', '_').encode()).hexdigest()
39-
# print('Before: ' + endpoint + x)
36+
def sanitize(endpoint, query_params):
37+
# Implement the logic of QgsBaseNetworkRequest::sendGET()
38+
# Note query_params can actually contain subpaths, so create the full url
39+
# by concatenating boths, and then figure out things...
40+
41+
url = endpoint + query_params
42+
# For REST API using URL subpaths, normalize the subpaths
43+
afterEndpointStartPos = url.find("fake_qgis_http_endpoint") + len("fake_qgis_http_endpoint")
44+
afterEndpointStart = url[afterEndpointStartPos:]
45+
afterEndpointStart = afterEndpointStart.replace('/', '_')
46+
url = url[0:afterEndpointStartPos] + afterEndpointStart
47+
posQuotationMark = url.find('?')
48+
endpoint = url[0:posQuotationMark]
49+
query_params = url[posQuotationMark:]
50+
51+
if len(endpoint + query_params) > 256:
52+
ret = endpoint + hashlib.md5(query_params.encode()).hexdigest()
53+
# print('Before: ' + endpoint + query_params)
4054
# print('After: ' + ret)
4155
return ret
42-
ret = endpoint + x.replace('?', '_').replace('&', '_').replace('<', '_').replace('>', '_').replace('"',
43-
'_').replace("'",
44-
'_').replace(
56+
ret = endpoint + query_params.replace('?', '_').replace('&', '_').replace('<', '_').replace('>', '_').replace('"',
57+
'_').replace("'",
58+
'_').replace(
4559
' ', '_').replace(':', '_').replace('/', '_').replace('\n', '_')
4660
return ret
4761

@@ -58,7 +72,8 @@ def GDAL_COMPUTE_VERSION(maj, min, rev):
5872

5973
def create_landing_page_api_collection(endpoint,
6074
extraparam='',
61-
crs_url="http://www.opengis.net/def/crs/EPSG/0/4326",
75+
storageCrs=None,
76+
crsList=None,
6277
bbox=[-71.123, 66.33, -65.32, 78.3]):
6378

6479
questionmark_extraparam = '?' + extraparam if extraparam else ''
@@ -104,9 +119,10 @@ def add_params(x, y):
104119
}
105120
}
106121
}
107-
if crs_url:
108-
collection["crs"] = [crs_url]
109-
collection["extent"]["spatial"]["crs"] = crs_url
122+
if storageCrs:
123+
collection["storageCrs"] = storageCrs
124+
if crsList:
125+
collection["crs"] = crsList
110126

111127
with open(sanitize(endpoint, '/collections/mycollection?' + add_params(extraparam, ACCEPT_COLLECTION)), 'wb') as f:
112128
f.write(json.dumps(collection).encode('UTF-8'))
@@ -172,6 +188,9 @@ def tearDownClass(cls):
172188
shutil.rmtree(cls.basetestpath, True)
173189
cls.vl = None # so as to properly close the provider and remove any temporary file
174190

191+
def testCrs(self):
192+
self.assertEqual(self.source.sourceCrs().authid(), 'OGC:CRS84')
193+
175194
def testExtentSubsetString(self):
176195
# can't run the base provider test suite here - WFS/OAPIF extent handling is different
177196
# to other providers
@@ -250,14 +269,14 @@ def testFeaturePaging(self):
250269
def testBbox(self):
251270

252271
endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_testBbox'
253-
create_landing_page_api_collection(endpoint)
272+
create_landing_page_api_collection(endpoint, storageCrs="http://www.opengis.net/def/crs/EPSG/0/4326")
254273

255274
# first items
256275
first_items = {
257276
"type": "FeatureCollection",
258277
"features": [
259278
{"type": "Feature", "id": "feat.1", "properties": {"pk": 1, "cnt": 100},
260-
"geometry": {"type": "Point", "coordinates": [-70.332, 66.33]}}
279+
"geometry": {"type": "Point", "coordinates": [66.33, -70.332]}}
261280
]
262281
}
263282
with open(sanitize(endpoint, '/collections/mycollection/items?limit=10&' + ACCEPT_ITEMS), 'wb') as f:
@@ -271,12 +290,12 @@ def testBbox(self):
271290
"type": "FeatureCollection",
272291
"features": [
273292
{"type": "Feature", "id": "feat.1", "properties": {"pk": 1, "cnt": 100},
274-
"geometry": {"type": "Point", "coordinates": [-70.332, 66.33]}},
293+
"geometry": {"type": "Point", "coordinates": [66.33, -70.332]}},
275294
{"type": "Feature", "id": "feat.2", "properties": {"pk": 2, "cnt": 200},
276-
"geometry": {"type": "Point", "coordinates": [-68.2, 70.8]}}
295+
"geometry": {"type": "Point", "coordinates": [70.8, -68.2]}}
277296
]
278297
}
279-
with open(sanitize(endpoint, '/collections/mycollection/items?limit=1000&bbox=65.5,-71,78,-65&bbox-crs=http://www.opengis.net/def/crs/EPSG/0/4326&' + ACCEPT_ITEMS),
298+
with open(sanitize(endpoint, '/collections/mycollection/items?limit=1000&bbox=65.5,-71,78,-65&bbox-crs=http://www.opengis.net/def/crs/EPSG/0/4326&crs=http://www.opengis.net/def/crs/EPSG/0/4326&' + ACCEPT_ITEMS),
280299
'wb') as f:
281300
f.write(json.dumps(items).encode('UTF-8'))
282301

@@ -294,7 +313,7 @@ def testBbox(self):
294313

295314
# Test clamping of bbox
296315
with open(
297-
sanitize(endpoint, '/collections/mycollection/items?limit=1000&bbox=64.5,-180,78,-65&bbox-crs=http://www.opengis.net/def/crs/EPSG/0/4326&' + ACCEPT_ITEMS),
316+
sanitize(endpoint, '/collections/mycollection/items?limit=1000&bbox=64.5,-180,78,-65&bbox-crs=http://www.opengis.net/def/crs/EPSG/0/4326&crs=http://www.opengis.net/def/crs/EPSG/0/4326&' + ACCEPT_ITEMS),
298317
'wb') as f:
299318
f.write(json.dumps(items).encode('UTF-8'))
300319

@@ -314,14 +333,14 @@ def testBbox(self):
314333
"type": "FeatureCollection",
315334
"features": [
316335
{"type": "Feature", "id": "feat.1", "properties": {"pk": 1, "cnt": 100},
317-
"geometry": {"type": "Point", "coordinates": [-70.332, 66.33]}},
336+
"geometry": {"type": "Point", "coordinates": [66.33, -70.332]}},
318337
{"type": "Feature", "id": "feat.2", "properties": {"pk": 2, "cnt": 200},
319-
"geometry": {"type": "Point", "coordinates": [-68.2, 70.8]}},
338+
"geometry": {"type": "Point", "coordinates": [70.8, -68.2]}},
320339
{"type": "Feature", "id": "feat.3", "properties": {"pk": 4, "cnt": 400},
321-
"geometry": {"type": "Point", "coordinates": [-65.32, 78.3]}}
340+
"geometry": {"type": "Point", "coordinates": [78.3, -65.32]}}
322341
]
323342
}
324-
with open(sanitize(endpoint, '/collections/mycollection/items?limit=1000&bbox=-90,-180,90,180&bbox-crs=http://www.opengis.net/def/crs/EPSG/0/4326&' + ACCEPT_ITEMS), 'wb') as f:
343+
with open(sanitize(endpoint, '/collections/mycollection/items?limit=1000&bbox=-90,-180,90,180&bbox-crs=http://www.opengis.net/def/crs/EPSG/0/4326&crs=http://www.opengis.net/def/crs/EPSG/0/4326&' + ACCEPT_ITEMS), 'wb') as f:
325344
f.write(json.dumps(items).encode('UTF-8'))
326345

327346
extent = QgsRectangle(-181, -91, 181, 91)
@@ -394,8 +413,7 @@ def testLayerMetadata(self):
394413
["1980-01-01T12:34:56.789Z", None],
395414
[None, "2020-01-01T00:00:00Z"]
396415
]
397-
},
398-
"crs": ["http://www.opengis.net/def/crs/EPSG/0/4326"]
416+
}
399417
},
400418
"links": [
401419
{"href": "href_self", "rel": "self", "type": "application/json", "title": "my self link"},
@@ -453,6 +471,7 @@ def testLayerMetadata(self):
453471
assert md.keywords()['keywords'] == ["keyword_a", "keyword_b"]
454472

455473
assert md.crs().isValid()
474+
assert md.crs().authid() == "OGC:CRS84"
456475
assert md.crs().isGeographic()
457476
assert not md.crs().hasAxisInverted()
458477

@@ -551,6 +570,40 @@ def testLayerMetadata(self):
551570
assert len(md.licenses()) == 1
552571
assert md.licenses()[0] == 'proprietary'
553572

573+
# Variant with storageCrs
574+
collection = copy.deepcopy(base_collection)
575+
collection['storageCrs'] = "http://www.opengis.net/def/crs/EPSG/0/4258"
576+
collection['storageCrsCoordinateEpoch'] = 2020.0
577+
with open(sanitize(endpoint, '/collections/mycollection?' + ACCEPT_COLLECTION), 'wb') as f:
578+
f.write(json.dumps(collection).encode('UTF-8'))
579+
580+
vl = QgsVectorLayer("url='http://" + endpoint + "' typename='mycollection' restrictToRequestBBOX=1", 'test',
581+
'OAPIF')
582+
self.assertTrue(vl.isValid())
583+
584+
md = vl.metadata()
585+
assert vl.sourceCrs().isValid()
586+
assert vl.sourceCrs().authid() == "EPSG:4258"
587+
assert vl.sourceCrs().isGeographic()
588+
assert vl.sourceCrs().coordinateEpoch() == 2020.0
589+
assert vl.sourceCrs().hasAxisInverted()
590+
591+
# Variant with a list of crs
592+
collection = copy.deepcopy(base_collection)
593+
collection['crs'] = ["http://www.opengis.net/def/crs/EPSG/0/4258", "http://www.opengis.net/def/crs/EPSG/0/4326"]
594+
with open(sanitize(endpoint, '/collections/mycollection?' + ACCEPT_COLLECTION), 'wb') as f:
595+
f.write(json.dumps(collection).encode('UTF-8'))
596+
597+
vl = QgsVectorLayer("url='http://" + endpoint + "' typename='mycollection' restrictToRequestBBOX=1", 'test',
598+
'OAPIF')
599+
self.assertTrue(vl.isValid())
600+
601+
md = vl.metadata()
602+
assert vl.sourceCrs().isValid()
603+
assert vl.sourceCrs().authid() == "EPSG:4258"
604+
assert vl.sourceCrs().isGeographic()
605+
assert vl.sourceCrs().hasAxisInverted()
606+
554607
def testDateTimeFiltering(self):
555608

556609
endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_testDateTimeFiltering'
@@ -719,7 +772,6 @@ def testDefaultCRS(self):
719772
endpoint = basetestpath + '/fake_qgis_http_endpoint_ogc84'
720773

721774
create_landing_page_api_collection(endpoint,
722-
crs_url="", # OGC norm says that if crs is not explicitly defined it is OGC:CRS84
723775
bbox=[66.33, -71.123, 78.3, -65.32])
724776

725777
items = {
@@ -766,8 +818,8 @@ def testCRS2056(self):
766818
endpoint = basetestpath + '/fake_qgis_http_endpoint_epsg_2056'
767819

768820
create_landing_page_api_collection(endpoint,
769-
crs_url="http://www.opengis.net/def/crs/EPSG/0/2056",
770-
bbox=[2508500, 1152000, 2513450, 1156950])
821+
storageCrs="http://www.opengis.net/def/crs/EPSG/0/2056",
822+
crsList=["http://www.opengis.net/def/crs/OGC/0/CRS84", "http://www.opengis.net/def/crs/EPSG/0/2056"])
771823

772824
items = {
773825
"type": "FeatureCollection",

0 commit comments

Comments
 (0)
Please sign in to comment.