I have a script I use to take a list of addresses and geocode them to points in a new feature class. You just need to download the requests module and supply a service url for a Geocoding REST service. I do not really know of any open source geocoding services though.
import requests
import json
import arcpy
import os
import sys
arcpy.env.overwriteOutput = True
def Message(msg):
print str(msg)
arcpy.AddMessage(str(msg))
return msg
def assertJsonSuccess(data):
import json
obj = json.loads(data)
if 'status' in obj and obj['status'] == "error":
Message("Error: JSON object returns an error. " + str(obj))
return False
else:
return True
def geocode_addresses(new_fc, addresses=[], serviceURL='', sr=3857):
import json
import requests
"""Geocodes a list of addresses using a Geocode Service and returns a
point feature class
Quick and dirty geocode. There may be a limit on how many addresses can be
used before the server rejects the requests
Required:
new_fc -- new point feature class for geocoded addresses
addresses -- dictionary of address and feature name {'address' : 'feature description',..}
or a list of addresses ['address, city, state, zip'...]
serviceURL -- url to a REST endpoint for a Geocoding service
sr -- spatial reference or WKID for new feature class (should be same as geocoding service)
Addresses can be in one of the below formats.
# example: pass addresses with description as dictionary
addresses = {"400 N 1st Ave W, Hartley, IA, 51346" : "Prins Laundromat",
"120 S 8th Ave W, Hartley, IA, 51346" : "Hengeveld Construction",
"173 S Central Ave, Hartley, IA, 51346" : "Legal Eyes"}
# example: pass addresses in as a list
addresses = ["901 N Broadway, Saint Louis, Missouri, 63102",
"10701 Lambert International Blvd, Saint Louis,
"15193 Olive Blvd, Chesterfield, Missouri, 63017"]
"""
# check to see if addresses are provided as dict
hasFeature = True
if not isinstance(addresses, dict):
addresses = dict((a, '') for a in addresses)
hasFeature = False
# dictionary to store addresses and points
addr = {}
bad = []
for address, name in addresses.iteritems():
# This request only needs the single line address as text and the response formatting parameter .
params = {'text': address, 'f': 'pjson'}
headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}
# Connect to service to get its current JSON definition.
r = requests.post(serviceURL, params=params, headers=headers)
if (r.status_code != 200):
Message("Could not geocode address {0}.\r\nhttp status code {1}.".format(address, r.status_code))
bad.append(address)
else:
if not assertJsonSuccess(r.text):
Message('Error when reading service information: {0}'.format(r.text))
bad.append(address)
#Get the first returned location
loc = False
candidates = r.json()
if 'locations' in candidates:
if candidates['locations']:
loc = True
candidate = candidates['locations'][0]
geo = candidate['feature']['geometry']
# Create (X,Y) tuple and Point objects and score
pt = (geo['x'], geo['y'])
score = candidate['feature']['attributes']['Score']
addr[(address, name)] = (arcpy.PointGeometry(arcpy.Point(*pt), prj), score)
if not loc:
Message("Could not geocode address \"{0}\"".format(address))
# create new feature class
f_length = min([max(len(a) for a in addresses), 255])
if arcpy.Exists(new_fc):
arcpy.Delete_management(new_fc)
arcpy.CreateFeatureclass_management(os.path.dirname(new_fc),
os.path.basename(new_fc),
'POINT', spatial_reference=sr)
arcpy.AddField_management(new_fc, 'Address', 'TEXT', field_length=f_length)
arcpy.AddField_management(new_fc, 'Feature', 'TEXT', field_length=100)
arcpy.AddField_management(new_fc, 'Score', 'SHORT')
# insert rows
with arcpy.da.InsertCursor(new_fc, ['SHAPE@', 'Address', 'Feature', 'Score']) as rows:
for attributes, pt in addr.iteritems():
add, name = attributes
xy, score = pt
rows.insertRow([xy, add, name, score])
Message('Geocoded address: {0}'.format(add))
# Delete feature field if no feature names supplied
if not hasFeature:
arcpy.DeleteField_management(new_fc, ['Feature'])
# if bad records
if bad:
date = time.strftime('_%m_%d_%Y')
txt = r'C:\Users\{0}\Desktop\Geocode_fail_{1}.txt'.format(os.environ['USERNAME'], date)
with open(txt, 'w') as f:
f.writelines('\n'.join(bad))
os.startfile(txt)
return new_fc