get address efficiency by getting only centroids

This commit is contained in:
zyphlar
2026-04-03 23:07:33 -07:00
parent 1bb092d6da
commit 33224d9fd1
+62 -37
View File
@@ -7,8 +7,6 @@ Usage:
python download-overpass.py --type addresses "Lake County" "Florida" output/addresses.geojson python download-overpass.py --type addresses "Lake County" "Florida" output/addresses.geojson
python download-overpass.py --type multimodal "Sumter County" "Florida" output/paths.geojson python download-overpass.py --type multimodal "Sumter County" "Florida" output/paths.geojson
TODO:
- Don't just download roads. Probably ignore relations also.
""" """
import argparse import argparse
@@ -49,9 +47,19 @@ def build_overpass_query(county_name, state_name, data_type="highways"):
"""Build Overpass API query for specified data type in a county.""" """Build Overpass API query for specified data type in a county."""
area_id = get_county_area_id(county_name, state_name) area_id = get_county_area_id(county_name, state_name)
base_query = f"""[out:json][timeout:60]; # Addresses: include nwr so building ways and relations are captured too,
# but use "out center;" instead of "out geom;" so ways/relations return a
# single centroid rather than their full coordinate list — much smaller payload.
if data_type == "addresses":
query = f"""[out:json][timeout:180];
area(id:{area_id})->.searchArea;
nwr["addr:housenumber"](area.searchArea);
out center;"""
return query
base_query = f"""[out:json][timeout:120];
area(id:{area_id})->.searchArea;""" area(id:{area_id})->.searchArea;"""
if data_type == "highways": if data_type == "highways":
selector = '(' selector = '('
selector += 'way["highway"="motorway"](area.searchArea);' selector += 'way["highway"="motorway"](area.searchArea);'
@@ -65,68 +73,85 @@ area(id:{area_id})->.searchArea;"""
selector += 'way["highway"="service"](area.searchArea);' selector += 'way["highway"="service"](area.searchArea);'
selector += 'way["highway"="track"](area.searchArea);' selector += 'way["highway"="track"](area.searchArea);'
selector += ');' selector += ');'
elif data_type == "addresses":
selector = 'nwr["addr:housenumber"](area.searchArea);'
elif data_type == "multimodal": elif data_type == "multimodal":
selector = '(way["highway"="path"](area.searchArea);way["highway"="cycleway"](area.searchArea););' selector = '(way["highway"="path"](area.searchArea);way["highway"="cycleway"](area.searchArea););'
else: else:
raise ValueError(f"Unknown data type: {data_type}") raise ValueError(f"Unknown data type: {data_type}")
query = base_query + selector + "out geom;" query = base_query + selector + "out geom;"
return query return query
def query_overpass(query): def query_overpass(query):
"""Send query to Overpass API and return JSON response.""" """Send query to Overpass API and return JSON response, with retries."""
url = "https://overpass-api.de/api/interpreter" url = "https://overpass-api.de/api/interpreter"
data = urllib.parse.urlencode({"data": query}).encode("utf-8") data = urllib.parse.urlencode({"data": query}).encode("utf-8")
max_attempts = 3
try: for attempt in range(1, max_attempts + 1):
with urllib.request.urlopen(url, data=data) as response:
return json.loads(response.read().decode("utf-8"))
except urllib.error.HTTPError as e:
print(f"HTTP Error {e.code}: {e.reason}", file=sys.stderr)
try: try:
error_body = e.read().decode("utf-8") with urllib.request.urlopen(url, data=data, timeout=300) as response:
return json.loads(response.read().decode("utf-8"))
except urllib.error.HTTPError as e:
try:
error_body = e.read().decode("utf-8")
except Exception:
error_body = "(unreadable)"
print(f"HTTP Error {e.code} on attempt {attempt}: {e.reason}", file=sys.stderr)
print(f"Error response body: {error_body}", file=sys.stderr) print(f"Error response body: {error_body}", file=sys.stderr)
except: if attempt < max_attempts and e.code in (429, 504):
print("Could not read error response body", file=sys.stderr) wait = 30 * attempt
sys.exit(1) print(f"Retrying in {wait}s...", file=sys.stderr)
except Exception as e: time.sleep(wait)
print(f"Error querying Overpass API: {e}", file=sys.stderr) else:
sys.exit(1)
except Exception as e:
print(f"Error querying Overpass API: {e}", file=sys.stderr)
sys.exit(1) sys.exit(1)
def convert_to_geojson(overpass_data): def convert_to_geojson(overpass_data):
"""Convert Overpass API response to GeoJSON format.""" """Convert Overpass API response to GeoJSON format."""
features = [] features = []
for element in overpass_data.get("elements", []): for element in overpass_data.get("elements", []):
if element["type"] == "way" and "geometry" in element: if element["type"] == "node":
coordinates = [[coord["lon"], coord["lat"]] for coord in element["geometry"]]
feature = { feature = {
"type": "Feature", "type": "Feature",
"properties": element.get("tags", {}), "properties": element.get("tags", {}),
"geometry": {
"type": "LineString",
"coordinates": coordinates
}
}
features.append(feature)
elif element["type"] == "node":
feature = {
"type": "Feature",
"properties": element.get("tags", {}),
"geometry": { "geometry": {
"type": "Point", "type": "Point",
"coordinates": [element["lon"], element["lat"]] "coordinates": [element["lon"], element["lat"]]
} }
} }
features.append(feature) features.append(feature)
elif element["type"] in ("way", "relation"):
if "geometry" in element:
# out geom; — full coordinate list (used for highways/paths)
coordinates = [[coord["lon"], coord["lat"]] for coord in element["geometry"]]
feature = {
"type": "Feature",
"properties": element.get("tags", {}),
"geometry": {
"type": "LineString",
"coordinates": coordinates
}
}
features.append(feature)
elif "center" in element:
# out center; — single centroid point (used for addresses on buildings)
c = element["center"]
feature = {
"type": "Feature",
"properties": element.get("tags", {}),
"geometry": {
"type": "Point",
"coordinates": [c["lon"], c["lat"]]
}
}
features.append(feature)
return { return {
"type": "FeatureCollection", "type": "FeatureCollection",
"features": features "features": features