PyQGIS ArcGIS Feature Service – How to Add ArcGIS Feature Service Layer to QGIS with Query

arcgis-serverfeature-servicepyqgisqgis

I want to add an ArcGIS feature layer to QGIS using PyQGIS, with a query.

If I just want to add the layer, I can use the following code. Though I would point out that the arcgisfeatureserver provider is not documented at https://qgis.org/pyqgis/master/core/QgsVectorLayer.html. The server is a public one so you should be able to easily replicate this:

uri = QgsDataSourceUri()
uri.setParam('crs', 'EPSG:3857')
uri.setParam('url', 'https://portal.spatial.nsw.gov.au/server/rest/services/NSW_Features_of_Interest_Category/MapServer/2')
layer = QgsVectorLayer(uri.uri(), "Test Layer" , 'arcgisfeatureserver')
if layer.isValid():
    QgsProject.instance().addMapLayer(layer)
else:
    print('Invalid layer: failed to add layer')

I now want to add a basic query to the PyQGIS code, to avoid retrieving all records – for example:

where classsubtype = 12

To query the server directly, you can modify the REST URL eg:

https://portal.spatial.nsw.gov.au/server/rest/services/NSW_Features_of_Interest_Category/MapServer/2/query?where=classsubtype+%3D+12&f=json

However, if I try to do this by modifying the url parameter in PyQGIS, I get an invalid layer:

uri.setParam('url', 'https://portal.spatial.nsw.gov.au/server/rest/services/NSW_Features_of_Interest_Category/MapServer/2/query?where=classsubtype+%3D+12&f=json')

Is it possible to use queries or filters on ArcGIS feature layers via PyQGIS?

I'd note that I also cannot see how to do querying or filtering of ArcGIS feature layers, even if I try to add them via the GUI – Add Layer->Add ArcGIS REST Server Layer…

Best Answer

There is a reason ArcGIS Feature Server documentation · Issue #6317 · qgis/QGIS-Documentation · GitHub was logged a few years ago, the documentation on this specific topic is quite poor.

One approach I have found that works, since figuring out the URI parameters is so elusive, is to create the layer without any filtering, and then set a filter using setSubsetString - Class: QgsVectorLayer before adding it to the project.

uri = QgsDataSourceUri()
uri.setParam('crs', 'EPSG:3857')
uri.setParam('url', 'https://portal.spatial.nsw.gov.au/server/rest/services/NSW_Features_of_Interest_Category/MapServer/2')
layer = QgsVectorLayer(uri.uri(), "Test Layer" , 'arcgisfeatureserver')
layer.setSubsetString('"classsubtype" = 12')
if layer.isValid():
    QgsProject.instance().addMapLayer(layer)
else:
    print('Invalid layer: failed to add layer')

UPDATE:

After some tinkering, it appears QgsDataSourceUri.setParam() sets the parameters in alphabetical order, and that having a SQL statement before the URL causes the layer data source to become invalid, at least with arcgisfeatureserver. The related "set" functions of QgsDataSourceUri do put the parameters in the correct order, so using them results in the following code that works:

uri = QgsDataSourceUri()
uri.setParam('url', 'https://portal.spatial.nsw.gov.au/server/rest/services/NSW_Features_of_Interest_Category/MapServer/2')
uri.setSrid('EPSG:3857')
uri.setSql('"classsubtype" = 12')
layer = QgsVectorLayer(uri.uri(), "Test Layer" , 'arcgisfeatureserver')
if layer.isValid():
    QgsProject.instance().addMapLayer(layer)
else:
    print('Invalid layer: failed to add layer')
Related Question