1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
#!/usr/bin/env python3
# © Copyright 2022, Scott Gasch
"""Wrapper around US Census address geocoder API described here:
https://www2.census.gov/geo/pdfs/maps-data/data/Census_Geocoder_User_Guide.pdf"""
import logging
import re
from typing import Any, Dict, Optional
import requests
from bs4 import BeautifulSoup
from requests.utils import requote_uri
import string_utils
logger = logging.getLogger(__name__)
def geocode_address(address: str) -> Optional[Dict[str, Any]]:
"""Send a single address to the US Census geocoding API.
>>> out = geocode_address('4600 Silver Hill Rd,, 20233')
>>> out['Matched Address']
'4600 SILVER HILL RD, WASHINGTON, DC, 20233'
>>> out['Interpolated Longitude (X) Coordinates']
-76.92743
>>> out['Interpolated Latitude (Y) Coordinates']
38.84599
"""
url = 'https://geocoding.geo.census.gov/geocoder/geographies/onelineaddress'
url += f'?address={address}'
url += '&layers=all&benchmark=4&vintage=4'
url = requote_uri(url)
logger.debug('GET: %s', url)
try:
r = requests.get(url)
except Exception as e:
logger.exception(e)
return None
if r.status_code != 200:
logger.error('Unexpected response code %d, wanted 200. Fail.', r.status_code)
return None
else:
soup = BeautifulSoup(r.text, 'html.parser')
result = soup.find('div', id='pl_gov_census_geo_geocoder_domain_AddressResult')
logger.debug('Unhelpful result blurb: "%s"', result)
output = result.get_text('\n')
label = None
out = {}
for line in output.split('\n'):
if re.match(r'.*: *$', line):
line = line.strip()
label = line[:-1]
logger.debug('Label is: "%s"', label)
else:
if label:
value = line.strip()
if string_utils.is_integer_number(value):
value = int(value)
elif string_utils.is_number(value):
value = float(value)
logger.debug('Value is: "%s"', value)
out[label] = value
return out
if __name__ == '__main__':
import doctest
doctest.testmod()
|