Spaces:
Sleeping
Sleeping
| 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()) | |