3

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...

2 Answers 2

4

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')
3
  • Thanks - however I tried both of the above code snippets, and while they did result in valid layers, they didn't actually implement the filtering. They both returned ~919k records, which is the whole dataset. If "classsubtype = 12" was correctly filtering, it should return just 205 records. Commented Mar 27, 2023 at 1:20
  • 1
    I just tested the second code block again, and it creates a layer with 205 features, so I am not sure why it isn't working on your end. I am using QGIS 3.28 and testing in the Python Console of an open QGIS session.
    – bixb0012
    Commented Mar 27, 2023 at 14:13
  • 1
    You're right - I had tested it on QGIS 3.22 and 3.26, but it looks like that feature was added in 3.28! qgis.org/en/site/forusers/visualchangelog328/… Commented Mar 28, 2023 at 21:30
0

While it does not exactly answer the question (because it is not adding the ArcGIS layer directly), the following approach may be useful for some people.

The code

  • builds up the ArcGIS Feature Server query string manually, and adds it to the layer base URL
  • uses ogr2ogr via a command window to create a new (GeoPackage) layer, using the URL
  • loads the resulting layer into QGIS.

This approach could be extended to include other fields not currently supported by the QgsDataSourceUri for ArcGIS Feature Server requests.

import urllib.parse, subprocess

params = {}
params['project_crs_authid'] = 'EPSG:4326'
params['temp_folder'] = 'C:/Windows/Temp/'

layerParams = {
    "layerName": "beacon",
    "url": "https://portal.spatial.nsw.gov.au/server/rest/services/NSW_Features_of_Interest_Category/MapServer/2",
    "where": "classsubtype = 12",
}

arcGisParams = {
    "where": layerParams['where'],
    "outfields":"*",
    "f":"json"
}

# Build query string component of URL
queryStr='?'
for key, value in arcGisParams.items():
    queryStr += key
    queryStr += '='
    queryStr += urllib.parse.quote(value)
    queryStr += '&'

if len(arcGisParams) > 0:
    queryStr = queryStr.rstrip('&')
else:
    queryStr = ''

cmd = 'ogr2ogr -f GPKG '+params['temp_folder']+layerParams['layerName']+'.gpkg "'+layerParams['url']+'/query'+queryStr+'" -t_srs '+params['project_crs_authid']
subprocess.run (cmd)
layer = QgsVectorLayer(params['temp_folder']+layerParams['layerName']+'.gpkg', 'Test Layer', 'ogr')
QgsProject.instance().addMapLayer(layer)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.