Skip to content

Commit b0b2d9b

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 54498f0 commit b0b2d9b

File tree

6 files changed

+184
-53
lines changed

6 files changed

+184
-53
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() );
@@ -788,6 +801,10 @@ void QgsOapifFeatureDownloaderImpl::run( bool serializeFeatures, long long maxFe
788801
}
789802
}
790803

804+
if ( mShared->mSourceCrs
805+
!= QgsCoordinateReferenceSystem::fromOgcWmsCrs( QgsOapifProvider::OAPIF_PROVIDER_DEFAULT_CRS ) )
806+
url += QStringLiteral( "&crs=%1" ).arg( mShared->mSourceCrs.toOgcUri() );
807+
791808
while ( !url.isEmpty() )
792809
{
793810
url = mShared->appendExtraQueryParameters( url );
@@ -843,7 +860,13 @@ void QgsOapifFeatureDownloaderImpl::run( bool serializeFeatures, long long maxFe
843860
// as the layer, convert them
844861
const QgsFeature &f = pair.first;
845862
QgsFeature dstFeat( dstFields, f.id() );
846-
dstFeat.setGeometry( f.geometry() );
863+
if ( f.hasGeometry() )
864+
{
865+
QgsGeometry g = f.geometry();
866+
if ( mShared->mSourceCrs.hasAxisInverted() )
867+
g.transform( QTransform( 0, 1, 1, 0, 0, 0 ) );
868+
dstFeat.setGeometry( g );
869+
}
847870
const auto srcAttrs = f.attributes();
848871
for ( int j = 0; j < dstFields.size(); j++ )
849872
{

‎src/providers/wfs/qgsbasenetworkrequest.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,6 @@ bool QgsBaseNetworkRequest::sendGET( const QUrl &url, const QString &acceptHeade
122122
}
123123
#endif
124124

125-
// For REST API using URL subpaths, normalize the subpaths
126-
const int afterEndpointStartPos = modifiedUrlString.indexOf( "fake_qgis_http_endpoint" ) + strlen( "fake_qgis_http_endpoint" );
127-
QString afterEndpointStart = modifiedUrlString.mid( afterEndpointStartPos );
128-
afterEndpointStart.replace( QLatin1String( "/" ), QLatin1String( "_" ) );
129-
modifiedUrlString = modifiedUrlString.mid( 0, afterEndpointStartPos ) + afterEndpointStart;
130-
131125
if ( !acceptHeader.isEmpty() )
132126
{
133127
if ( modifiedUrlString.indexOf( '?' ) > 0 )
@@ -139,6 +133,13 @@ bool QgsBaseNetworkRequest::sendGET( const QUrl &url, const QString &acceptHeade
139133
modifiedUrlString += QStringLiteral( "?Accept=" ) + acceptHeader;
140134
}
141135
}
136+
137+
// For REST API using URL subpaths, normalize the subpaths
138+
const int afterEndpointStartPos = static_cast<int>( modifiedUrlString.indexOf( "fake_qgis_http_endpoint" ) + strlen( "fake_qgis_http_endpoint" ) );
139+
QString afterEndpointStart = modifiedUrlString.mid( afterEndpointStartPos );
140+
afterEndpointStart.replace( QLatin1String( "/" ), QLatin1String( "_" ) );
141+
modifiedUrlString = modifiedUrlString.mid( 0, afterEndpointStartPos ) + afterEndpointStart;
142+
142143
const auto posQuotationMark = modifiedUrlString.indexOf( '?' );
143144
if ( posQuotationMark > 0 )
144145
{

‎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: 80 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,29 @@
3131
from providertestbase import ProviderTestCase
3232

3333

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

@@ -56,7 +70,8 @@ def GDAL_COMPUTE_VERSION(maj, min, rev):
5670

5771
def create_landing_page_api_collection(endpoint,
5872
extraparam='',
59-
crs_url="http://www.opengis.net/def/crs/EPSG/0/4326",
73+
storageCrs=None,
74+
crsList=None,
6075
bbox=[-71.123, 66.33, -65.32, 78.3]):
6176

6277
questionmark_extraparam = '?' + extraparam if extraparam else ''
@@ -102,9 +117,10 @@ def add_params(x, y):
102117
}
103118
}
104119
}
105-
if crs_url:
106-
collection["crs"] = [crs_url]
107-
collection["extent"]["spatial"]["crs"] = crs_url
120+
if storageCrs:
121+
collection["storageCrs"] = storageCrs
122+
if crsList:
123+
collection["crs"] = crsList
108124

109125
with open(sanitize(endpoint, '/collections/mycollection?' + add_params(extraparam, ACCEPT_COLLECTION)), 'wb') as f:
110126
f.write(json.dumps(collection).encode('UTF-8'))
@@ -175,6 +191,9 @@ def tearDownClass(cls):
175191
shutil.rmtree(cls.basetestpath, True)
176192
cls.vl = None # so as to properly close the provider and remove any temporary file
177193

194+
def testCrs(self):
195+
self.assertEqual(self.source.sourceCrs().authid(), 'OGC:CRS84')
196+
178197
def testExtentSubsetString(self):
179198
# can't run the base provider test suite here - WFS/OAPIF extent handling is different
180199
# to other providers
@@ -253,14 +272,14 @@ def testFeaturePaging(self):
253272
def testBbox(self):
254273

255274
endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_testBbox'
256-
create_landing_page_api_collection(endpoint)
275+
create_landing_page_api_collection(endpoint, storageCrs="http://www.opengis.net/def/crs/EPSG/0/4326")
257276

258277
# first items
259278
first_items = {
260279
"type": "FeatureCollection",
261280
"features": [
262281
{"type": "Feature", "id": "feat.1", "properties": {"pk": 1, "cnt": 100},
263-
"geometry": {"type": "Point", "coordinates": [-70.332, 66.33]}}
282+
"geometry": {"type": "Point", "coordinates": [66.33, -70.332]}}
264283
]
265284
}
266285
with open(sanitize(endpoint, '/collections/mycollection/items?limit=10&' + ACCEPT_ITEMS), 'wb') as f:
@@ -274,12 +293,12 @@ def testBbox(self):
274293
"type": "FeatureCollection",
275294
"features": [
276295
{"type": "Feature", "id": "feat.1", "properties": {"pk": 1, "cnt": 100},
277-
"geometry": {"type": "Point", "coordinates": [-70.332, 66.33]}},
296+
"geometry": {"type": "Point", "coordinates": [66.33, -70.332]}},
278297
{"type": "Feature", "id": "feat.2", "properties": {"pk": 2, "cnt": 200},
279-
"geometry": {"type": "Point", "coordinates": [-68.2, 70.8]}}
298+
"geometry": {"type": "Point", "coordinates": [70.8, -68.2]}}
280299
]
281300
}
282-
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),
301+
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),
283302
'wb') as f:
284303
f.write(json.dumps(items).encode('UTF-8'))
285304

@@ -297,7 +316,7 @@ def testBbox(self):
297316

298317
# Test clamping of bbox
299318
with open(
300-
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),
319+
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),
301320
'wb') as f:
302321
f.write(json.dumps(items).encode('UTF-8'))
303322

@@ -317,14 +336,14 @@ def testBbox(self):
317336
"type": "FeatureCollection",
318337
"features": [
319338
{"type": "Feature", "id": "feat.1", "properties": {"pk": 1, "cnt": 100},
320-
"geometry": {"type": "Point", "coordinates": [-70.332, 66.33]}},
339+
"geometry": {"type": "Point", "coordinates": [66.33, -70.332]}},
321340
{"type": "Feature", "id": "feat.2", "properties": {"pk": 2, "cnt": 200},
322-
"geometry": {"type": "Point", "coordinates": [-68.2, 70.8]}},
341+
"geometry": {"type": "Point", "coordinates": [70.8, -68.2]}},
323342
{"type": "Feature", "id": "feat.3", "properties": {"pk": 4, "cnt": 400},
324-
"geometry": {"type": "Point", "coordinates": [-65.32, 78.3]}}
343+
"geometry": {"type": "Point", "coordinates": [78.3, -65.32]}}
325344
]
326345
}
327-
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:
346+
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:
328347
f.write(json.dumps(items).encode('UTF-8'))
329348

330349
extent = QgsRectangle(-181, -91, 181, 91)
@@ -397,8 +416,7 @@ def testLayerMetadata(self):
397416
["1980-01-01T12:34:56.789Z", None],
398417
[None, "2020-01-01T00:00:00Z"]
399418
]
400-
},
401-
"crs": ["http://www.opengis.net/def/crs/EPSG/0/4326"]
419+
}
402420
},
403421
"links": [
404422
{"href": "href_self", "rel": "self", "type": "application/json", "title": "my self link"},
@@ -456,6 +474,7 @@ def testLayerMetadata(self):
456474
assert md.keywords()['keywords'] == ["keyword_a", "keyword_b"]
457475

458476
assert md.crs().isValid()
477+
assert md.crs().authid() == "OGC:CRS84"
459478
assert md.crs().isGeographic()
460479
assert not md.crs().hasAxisInverted()
461480

@@ -554,6 +573,40 @@ def testLayerMetadata(self):
554573
assert len(md.licenses()) == 1
555574
assert md.licenses()[0] == 'proprietary'
556575

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

559612
endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_testDateTimeFiltering'
@@ -722,7 +775,6 @@ def testDefaultCRS(self):
722775
endpoint = basetestpath + '/fake_qgis_http_endpoint_ogc84'
723776

724777
create_landing_page_api_collection(endpoint,
725-
crs_url="", # OGC norm says that if crs is not explicitly defined it is OGC:CRS84
726778
bbox=[66.33, -71.123, 78.3, -65.32])
727779

728780
items = {
@@ -769,8 +821,8 @@ def testCRS2056(self):
769821
endpoint = basetestpath + '/fake_qgis_http_endpoint_epsg_2056'
770822

771823
create_landing_page_api_collection(endpoint,
772-
crs_url="http://www.opengis.net/def/crs/EPSG/0/2056",
773-
bbox=[2508500, 1152000, 2513450, 1156950])
824+
storageCrs="http://www.opengis.net/def/crs/EPSG/0/2056",
825+
crsList=["http://www.opengis.net/def/crs/OGC/0/CRS84", "http://www.opengis.net/def/crs/EPSG/0/2056"])
774826

775827
items = {
776828
"type": "FeatureCollection",
@@ -815,9 +867,7 @@ def testFeatureCountFallback(self):
815867
basetestpath = tempfile.mkdtemp().replace('\\', '/')
816868
endpoint = basetestpath + '/fake_qgis_http_endpoint_feature_count_fallback'
817869

818-
create_landing_page_api_collection(endpoint,
819-
crs_url="http://www.opengis.net/def/crs/EPSG/0/2056",
820-
bbox=[2508500, 1152000, 2513450, 1156950])
870+
create_landing_page_api_collection(endpoint, storageCrs="http://www.opengis.net/def/crs/EPSG/0/2056")
821871

822872
items = {
823873
"type": "FeatureCollection",
@@ -846,7 +896,7 @@ def testFeatureCountFallback(self):
846896
f.write(json.dumps(items).encode('UTF-8'))
847897

848898
# real page
849-
with open(sanitize(endpoint, '/collections/mycollection/items?limit=1000&' + ACCEPT_ITEMS), 'wb') as f:
899+
with open(sanitize(endpoint, '/collections/mycollection/items?limit=1000&crs=http://www.opengis.net/def/crs/EPSG/0/2056&' + ACCEPT_ITEMS), 'wb') as f:
850900
f.write(json.dumps(items).encode('UTF-8'))
851901

852902
# Create test layer

0 commit comments

Comments
 (0)
Please sign in to comment.