9/7/2020
Here is a situation I find myself in frequently. I have data in an Esri Feature Service, and I want to add the data to a non-Esri web map. In my common situation this might be a Leaflet or Mapbox GL map. This is a popular enough workflow that Esri made a plugin for Leaflet named Esri Leaflet. While Esri Leaflet is a neat tool, sometimes you just don't need it.
This article is about Esri's REST API using Javascript to query data to geojson which can then be displayed in web maps made with Leaflet, Mapbox GL and other APIs. Querying data in Esri's REST API is rather powerful and has pretty quick response times from my experience. Using the REST API to query data in non-Esri apps seems like a lesser used technique in the geospatial world. Maybe we can change that today.
In a nutshell, the Esri REST API allows web developers to request ArcServer or Hosted Feature layers via URL request to what is commonly referred to the data's "REST endpoint". A series of parameters are added to the end of the layer's endpoint URL to specify what is being requested. The most common operation is to query the data, but you can also do data editing operations and even administrative tasks like changing service definitions. This article will focus on querying the data and will cover doing spatial queries which is lesser known and is super cool.
So what is a REST Endpoint? You can find them via Esri open data portals that many organizations use. A URL endpoint will end with an array based integer, pointing to the specific layer of a Feature or Map Service. Joseph Elfelt, manages a list of thousands of GIS Servers to help you find the public data you need. Below are some examples.
//EXAMPLE REST ENDPOINTS
//USDA
https://www.usda.gov/giseas1/rest/services/ExcessFood/ExcessFood/MapServer/2
//ALLEGHENY COUNTY
https://gisdata.alleghenycounty.us/arcgis/rest/services/OPENDATA/Parcels/MapServer/0
//PENNDOT
https://gis.penndot.gov/arcgis/rest/services/opendata/countyboundary/MapServer/0
//DVRPC
https://arcgis.dvrpc.org/portal/rest/services/Planning/DVRPC_LandUse_2015/FeatureServer/0
Now that we know what an Esri REST endpoint looks like, next we need to understand the REST API's query perameters. At the end of the endpoint URL a query starts with /query? then the parameter list each separated by an &. Since this article focuses on returning the data as geojson, we need to tack on the parameter f=geojson. Copy and paste these examples into your browser to see what is returned.
//BASIC FULL DATASET QUERY, ADD TO END OF URL ENDPOINT => /query?where=1=1&outFields=*&outSR=4326&f=geojson
https://gis.penndot.gov/arcgis/rest/services/opendata/parkandride/MapServer/0/query?where=1=1&outFields=*&outSR=4326&f=geojson
//ONLY NEED 2 ATTRIBUTE FIELDS?
https://gis.penndot.gov/arcgis/rest/services/opendata/parkandride/MapServer/0/query?where=1=1&outFields=CTY_CODE,DESCRIPTION&outSR=4326&f=geojson
//LIMIT GEOMETRY COORDINATES TO 3 DECIMAL PLACES
https://gis.penndot.gov/arcgis/rest/services/opendata/parkandride/MapServer/0/query?where=1=1&outFields=*&outSR=4326&geometryPrecision=3&f=geojson
//INCLUDE A WHERE STATEMENT TO FILTER DATA
https://gis.penndot.gov/arcgis/rest/services/opendata/parkandride/MapServer/0/query?where=CTY_CODE='02'&outFields=*&outSR=4326&f=geojson
Using Javascript, these query requests are done in an async manner and I like using the fetch web API for doing these async requests. I like to package the fetch async request inside a function so I can request the data when I need it by calling the function. My example function below takes 3 arguments and concatenates everything together for my request.
function getGeoJsonData(dataUrlString,outFieldsArray,geoPrecisionInteger){
var fields = "*";
if (outFieldsArray.length > 0){
fields = outFieldsArray.join();
}
var query = "/query?where=1=1&outFields=" + fields + "&outSR=4326&geometryPrecision=" + geoPrecisionInteger + "&f=geojson";
fetch(dataUrlString + query)
.then(function(response){
return response.json();
})
.then(function(data){
//DO SOMETHING WITH DATA
console.log(data);
})
.catch(function(error){
//CATCH AN ERROR
console.log(error);
});
}
//THE REST ENDPOINT URL FOR THE DATA
var url = "https://gis.penndot.gov/arcgis/rest/services/opendata/parkandride/MapServer/0";
//AN ARRAY OF ATTRIBUTE FIELDS YOU WANT, IF YOU WANT ALL ATTRIBUTES PASS AN EMPTY ARRAY []
var fields = ["EXIT_NAME","DESCRIPTION","CTY_CODE","HCP_SPACES"];
//GEOMETRY PRECISION IS THE NUMBER OF DIGITS AFTER THE DECIMAL PLACE TYPICALLY 6 PROVIDES ENOUGH ACCURACY
var geometryPrecision = 6;
//CALL FUNCTION WHEN YOU WANT TO FETCH YOUR DATA
getGeoJsonData(url,fields,geometryPrecision);
An example pulling data from PennDOT's Open Data portal of Park and Ride Locations and adds the geojson data to the map.
Now, let's do a classic point-in-polygon spatial query. Provide and XY coordinate pair and return the polygons it intersects. A great place to get started with REST API spatial queries is the API Explorer tab that you find on many open data portal pages. This example will query counties of Pennsylvania from PennDOT's Open Data Portal. In the code block below be sure to see the new query parameters that have been added. In this example, we are doing a spatial relationship of intersect with a point in lng,lat in decimal degrees (inSR 4326) with our REST endpoint layer.
function getCounty(dataUrlString,lat,lng){
fetch(dataUrlString + "/query?where=1=1&outFields=COUNTY_NAME&geometry=" + lng + "," + lat + "&geometryType=esriGeometryPoint&inSR=4326&spatialRel=esriSpatialRelIntersects&outSR=4326&f=geojson")
.then(function(response){
return response.json();
})
.then(function(data){
//DO SOMETHING WITH DATA
console.log(data);
})
.catch(function(error){
console.log(error);
});
}
//URL TO REST ENDPOINT
var url = "https://gis.penndot.gov/arcgis/rest/services/opendata/countyboundary/MapServer/0";
//CALL FUNCTION
getCounty(url, 40.439827, -79.999325);
Example of the function above. County is N/A
So much can be done with the Esri REST API. These two examples just scratch the surface for geo-web developers. In my next post, I will show an editing example where you can add features to a layer by doing a POST request with the Fetch API.