Skip to content

Commit 244ba5c

Browse files
committedOct 25, 2018
[afs] Handle invalid responses returned from FeatureServer multipoint
layers, where individual features may have point geometries Not sure if this is a bug in ArcGIS server (probably, yeah, let's go with definitely. I couldn't check the source to see.) But in general QGIS approach is to be forgiving and do our best to make up for badly behaved servers). See https://community.esri.com/thread/14037
1 parent b039cd1 commit 244ba5c

File tree

2 files changed

+102
-6
lines changed

2 files changed

+102
-6
lines changed
 

‎src/providers/arcgisrest/qgsarcgisrestutils.cpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -202,21 +202,31 @@ static std::unique_ptr< QgsPoint > parseEsriGeometryPoint( const QVariantMap &ge
202202
static std::unique_ptr< QgsMultiPoint > parseEsriGeometryMultiPoint( const QVariantMap &geometryData, QgsWkbTypes::Type pointType )
203203
{
204204
// {"points" : [[ <x1>, <y1>, <z1>, <m1> ] , [ <x2>, <y2>, <z2>, <m2> ], ... ]}
205-
QVariantList coordsList = geometryData[QStringLiteral( "points" )].toList();
206-
if ( coordsList.isEmpty() )
207-
return nullptr;
205+
const QVariantList coordsList = geometryData[QStringLiteral( "points" )].toList();
208206

209207
std::unique_ptr< QgsMultiPoint > multiPoint = qgis::make_unique< QgsMultiPoint >();
210-
Q_FOREACH ( const QVariant &coordData, coordsList )
208+
for ( const QVariant &coordData : coordsList )
211209
{
212-
QVariantList coordList = coordData.toList();
210+
const QVariantList coordList = coordData.toList();
213211
std::unique_ptr< QgsPoint > p = parsePoint( coordList, pointType );
214212
if ( !p )
215213
{
216-
return nullptr;
214+
continue;
217215
}
218216
multiPoint->addGeometry( p.release() );
219217
}
218+
219+
// second chance -- sometimes layers are reported as multipoint but features have single
220+
// point geometries. Silently handle this and upgrade to multipoint.
221+
std::unique_ptr< QgsPoint > p = parseEsriGeometryPoint( geometryData, pointType );
222+
if ( p )
223+
multiPoint->addGeometry( p.release() );
224+
225+
if ( multiPoint->numGeometries() == 0 )
226+
{
227+
// didn't find any points, so reset geometry to null
228+
multiPoint.reset();
229+
}
220230
return multiPoint;
221231
}
222232

‎tests/src/python/test_provider_afs.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,92 @@ def testBboxRestriction(self):
710710
self.assertEqual(vl.featureCount(), 2)
711711
self.assertEqual([f['pk'] for f in vl.getFeatures()], [2, 4])
712712

713+
def testBadMultiPoints(self):
714+
"""
715+
Test invalid server response where a layer's type is multipoint but single point geometries
716+
are returned. Thanks Jack. Thack.
717+
"""
718+
endpoint = self.basetestpath + '/multipoint_fake_qgis_http_endpoint'
719+
with open(sanitize(endpoint, '?f=json'), 'wb') as f:
720+
f.write("""
721+
{"currentVersion":10.22,"id":1,"name":"QGIS Test","type":"Feature Layer","description":
722+
"QGIS Provider Test Layer.\n","geometryType":"esriGeometryMultipoint","copyrightText":"","parentLayer":{"id":0,"name":"QGIS Tests"},"subLayers":[],
723+
"minScale":72225,"maxScale":0,
724+
"defaultVisibility":true,
725+
"extent":{"xmin":-71.123,"ymin":66.33,"xmax":-65.32,"ymax":78.3,
726+
"spatialReference":{"wkid":4326,"latestWkid":4326}},
727+
"hasAttachments":false,"htmlPopupType":"esriServerHTMLPopupTypeAsHTMLText",
728+
"displayField":"LABEL","typeIdField":null,
729+
"fields":[{"name":"OBJECTID","type":"esriFieldTypeOID","alias":"OBJECTID","domain":null}],
730+
"relationships":[],"canModifyLayer":false,"canScaleSymbols":false,"hasLabels":false,
731+
"capabilities":"Map,Query,Data","maxRecordCount":1000,"supportsStatistics":true,
732+
"supportsAdvancedQueries":true,"supportedQueryFormats":"JSON, AMF",
733+
"ownershipBasedAccessControlForFeatures":{"allowOthersToQuery":true},"useStandardizedQueries":true}""".encode(
734+
'UTF-8'))
735+
736+
with open(sanitize(endpoint, '/query?f=json_where=OBJECTID=OBJECTID_returnIdsOnly=true'), 'wb') as f:
737+
f.write("""
738+
{
739+
"objectIdFieldName": "OBJECTID",
740+
"objectIds": [
741+
1,
742+
2,
743+
3
744+
]
745+
}
746+
""".encode('UTF-8'))
747+
748+
# Create test layer
749+
vl = QgsVectorLayer("url='http://" + endpoint + "' crs='epsg:4326'", 'test', 'arcgisfeatureserver')
750+
751+
self.assertTrue(vl.isValid())
752+
with open(sanitize(endpoint,
753+
'/query?f=json&objectIds=1,2,3&inSR=4326&outSR=4326&returnGeometry=true&outFields=OBJECTID&returnM=false&returnZ=false'), 'wb') as f:
754+
f.write("""
755+
{
756+
"displayFieldName": "name",
757+
"fieldAliases": {
758+
"name": "name"
759+
},
760+
"geometryType": "esriGeometryMultipoint",
761+
"spatialReference": {
762+
"wkid": 4326,
763+
"latestWkid": 4326
764+
},
765+
"fields":[{"name":"OBJECTID","type":"esriFieldTypeOID","alias":"OBJECTID","domain":null},
766+
{"name":"Shape","type":"esriFieldTypeGeometry","alias":"Shape","domain":null}],
767+
"features": [
768+
{
769+
"attributes": {
770+
"OBJECTID": 1
771+
},
772+
"geometry": {
773+
"x": -70,
774+
"y": 66
775+
}
776+
},
777+
{
778+
"attributes": {
779+
"OBJECTID": 2
780+
},
781+
"geometry": null
782+
},
783+
{
784+
"attributes": {
785+
"OBJECTID": 3
786+
},
787+
"geometry":
788+
{"points" :[[-68,70],
789+
[-22,21]]
790+
}
791+
}
792+
]
793+
}""".encode('UTF-8'))
794+
795+
features = [f for f in vl.getFeatures()]
796+
self.assertEqual(len(features), 3)
797+
self.assertEqual([f.geometry().asWkt() for f in features], ['MultiPoint ((-70 66))', '', 'MultiPoint ((-68 70),(-22 21))'])
798+
713799

714800
if __name__ == '__main__':
715801
unittest.main()

0 commit comments

Comments
 (0)