wrd_drishti / scripts /cache_maharashtra_districts_geojson.py
devarshia5's picture
Upload 30 files
523c564 verified
Raw
History Blame Contribute Delete
4.76 kB
import json
import os
import random
import sys
import time
import urllib.error
import urllib.parse
import urllib.request
DISTRICTS = [
"Kolhapur",
"Sangli",
"Satara",
"Pune",
"Ahmednagar",
"Nashik",
"Aurangabad",
"Thane",
"Raigad",
"Ratnagiri",
]
# Approximate bounding boxes for Maharashtra districts (lon_min, lat_min, lon_max, lat_max)
# Used as fallback when Nominatim is rate-limited
DISTRICT_BBOXES = {
"Kolhapur": (73.6, 16.0, 74.8, 17.2),
"Sangli": (73.8, 16.4, 75.0, 17.2),
"Satara": (73.3, 17.0, 74.5, 18.0),
"Pune": (73.2, 18.0, 74.5, 19.0),
"Ahmednagar": (74.0, 18.5, 75.5, 19.5),
"Nashik": (73.0, 19.5, 74.5, 20.5),
"Aurangabad": (74.5, 19.0, 76.0, 20.0),
"Thane": (72.8, 19.0, 73.6, 20.0),
"Raigad": (72.8, 18.0, 73.5, 18.8),
"Ratnagiri": (72.8, 16.5, 73.8, 17.5),
}
def _request_json(url: str, timeout: int = 10) -> object:
req = urllib.request.Request(
url,
headers={
"Accept": "application/json",
"User-Agent": "wrd-insight-dashboard/1.0 (cache script; contact: local-demo)",
},
)
with urllib.request.urlopen(req, timeout=timeout) as resp:
body = resp.read().decode("utf-8")
return json.loads(body)
def fetch_district_geojson_from_nominatim(district: str) -> dict | None:
"""Try to fetch district boundary from Nominatim. Returns None on any error."""
params = {
"county": district,
"state": "Maharashtra",
"country": "India",
"polygon_geojson": 1,
"format": "json",
}
url = "https://nominatim.openstreetmap.org/search?" + urllib.parse.urlencode(params)
try:
data = _request_json(url, timeout=10)
except Exception:
return None
if isinstance(data, list) and data and isinstance(data[0], dict) and data[0].get("geojson"):
geo = data[0]["geojson"]
return {
"type": "Feature",
"properties": {"name": district, "source": "nominatim"},
"geometry": geo,
}
return None
def create_bbox_polygon(district: str) -> dict:
"""Create a simple rectangular polygon from bounding box as fallback."""
bbox = DISTRICT_BBOXES.get(district)
if not bbox:
# Default to a placeholder in central Maharashtra
bbox = (73.5, 18.0, 75.0, 19.5)
lon_min, lat_min, lon_max, lat_max = bbox
# Create a simple rectangular polygon (5 points to close the ring)
coords = [
[lon_min, lat_min],
[lon_max, lat_min],
[lon_max, lat_max],
[lon_min, lat_max],
[lon_min, lat_min], # Close the ring
]
return {
"type": "Feature",
"properties": {"name": district, "source": "bbox_fallback"},
"geometry": {"type": "Polygon", "coordinates": [coords]},
}
def main() -> int:
repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
out_path = os.path.join(repo_root, "public", "data", "maharashtra-districts.geojson")
os.makedirs(os.path.dirname(out_path), exist_ok=True)
features: list[dict] = []
nominatim_success = 0
fallback_used = 0
print("Fetching Maharashtra district boundaries...")
print("Strategy: Try Nominatim first (quick timeout), use bbox fallback if rate-limited.\n")
for district in DISTRICTS:
# Try Nominatim with just 1 attempt (quick)
feature = fetch_district_geojson_from_nominatim(district)
if feature:
features.append(feature)
nominatim_success += 1
print(f" ✓ {district}: fetched from Nominatim")
else:
# Use bounding box fallback immediately
feature = create_bbox_polygon(district)
features.append(feature)
fallback_used += 1
print(f" ○ {district}: using bbox fallback (Nominatim unavailable)")
# Minimal delay to be polite (Nominatim asks for 1 req/sec)
time.sleep(1.0)
# Write the FeatureCollection
fc = {"type": "FeatureCollection", "features": features}
with open(out_path, "w", encoding="utf-8") as f:
json.dump(fc, f, ensure_ascii=False, indent=2)
print(f"\n✓ Wrote {len(features)} district features to: {out_path}")
print(f" - From Nominatim: {nominatim_success}")
print(f" - Bbox fallback: {fallback_used}")
if fallback_used > 0:
print(
"\nNote: Some districts use approximate bounding boxes. "
"For precise boundaries, re-run when Nominatim is available "
"or download a proper Maharashtra district GeoJSON from a trusted source."
)
return 0
if __name__ == "__main__":
raise SystemExit(main())