Knowledge Base

Geocoding with Arcgis

Prerequisites

  • Create/obtain an Arcgis account.

  • Create application within your account. The application will be assigned a 'client_id' and 'secret'.

APOC

The APOC library provides a apoc.spatial.geocode('address') procedure (as well as reverseGeocode), that supports geocoding against OpenStreetMap and Google Maps. It also supports other providers (ex: opencage) with a more explicit configuration of the API call (in neo4j.conf) :

apoc.spatial.geocode.provider=opencage
apoc.spatial.geocode.opencage.key=<api_key>
apoc.spatial.geocode.opencage.url=http://api.opencagedata.com/geocode/v1/json?q=PLACE&key=KEY
apoc.spatial.geocode.opencage.reverse.url=http://api.opencagedata.com/geocode/v1/json?q=LAT+LNG&key=KEY

with KEY gets replaced by the API key, and PLACE by the address to geocode at run time (resp. LAT/LNG by the coordinates to reverse geocode).

For Arcgis, the key would be the application token, and the url would look like that (that’s the public Arcgis API endpoint) : apoc.spatial.geocode.arcgis.url=https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/findAddressCandidates?f=json&outFields=Match_addr,Addr_typ&singleLine=PLACE&token=KEY

Unfortunately, the apoc procedures expect the json response from the provider to contain a list of "results". That is not the case for the Arcgis API endpoints :

  • endpoint 'findAddressCandidates' returns a list of "candidates"

  • the other bulk geocoding endpoint 'geocodeAddresses' returns a list of "locations"

So the apoc.spatial procedures can’t help here.

Workaround using apoc.load.json

The apoc.load.json procedure lets you call any HTTP/REST API and process the response directly in cypher.

You can use the apoc.static procedures to read the API key and URL from neo4j.conf, similarly to what apoc.spatial.geocode does. The two following properties would be required for geocoding (this is using the public arcgis server ; replace with your own Arcgis server hostname if necessary) :

apoc.static.arcgis.key=<arcgis_token>
apoc.static.arcgis.geocode_url=https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/findAddressCandidates?f=json&outFields=Match_addr,Addr_typ&singleLine=

Then run the following cypher query :

WITH 'Statue of Liberty, Liberty Island New York, NY 10004' as address
// get the configuration properties
CALL apoc.static.getAll("arcgis") yield value AS arcgis
// build the URL
WITH arcgis.geocode_url+apoc.text.urlencode(address)+'&token='+ arcgis.key as url
// extract the top result
CALL apoc.load.json(url, '$.candidates[0].location') YIELD value as location
return location.x, location.y

Temporary Tokens

Arcgis application token may be temporary (by default 2h). That means you may not be able to hardcode a token in your neo4j.conf. To obtain a new token, you’re supposed to call the Authentication API with your application credentials. You can use apoc.load.json again to do that in cypher.

In neo4j.conf, add the building bricks of the token API call:

apoc.static.arcgis.client_id=<application_client_id>
apoc.static.arcgis.client_secret=<secret>
apoc.static.arcgis.token_url=https://www.arcgis.com/sharing/rest/oauth2/token?grant_type=client_credentials

And run the following cypher query :

CALL apoc.static.getAll("arcgis") yield value AS arcgis
WITH arcgis.token_url+'&client_id='+arcgis.client_id+'&client_secret='+arcgis.client_secret as tokenUrl
CALL apoc.load.json(tokenUrl) YIELD value as tokenResponse
WITH tokenResponse.access_token as token
// proceed with geocoding using 'token'