geolocation / src /prompt /fetch /satellite_fetch.py
3v324v23's picture
init prj
eff2be4
import logging
import httpx
from geopy import Point
from geopy.distance import distance
logger = logging.getLogger("uvicorn.error")
def meter_offsets(lat: float, lon: float, extend: float) -> tuple[float, float]:
"""
Returns (lat_offset, lon_offset) in degrees for a given
center point (lat, lon) and radial distance in meters (extend).
"""
origin = Point(lat, lon)
# Move north (bearing=0°) and east (bearing=90°)
north = distance(meters=extend).destination(origin, bearing=0)
east = distance(meters=extend).destination(origin, bearing=90)
return north.latitude - lat, east.longitude - lon
def fetch_satellite_image(
lat: float, lon: float, extend: float, output_path: str = "esri_sat.png"
) -> None:
"""
Fetches a satellite PNG from Esri's World Imagery service.
Parameters:
- lat: Latitude of the center point (decimal degrees).
- lon: Longitude of the center point (decimal degrees).
- extend: Buffer distance from center in meters (radius).
- output_path: File path to save the resulting PNG.
Attempts the highest resolution (1024x1024) first,
halving the dimensions on failure until success.
Retries up to 3 times if all size attempts fail.
"""
# Compute lat/lon degree offsets using geopy
lat_offset, lon_offset = meter_offsets(lat, lon, extend)
# Compute bounding box in lon/lat
minx = lon - lon_offset
miny = lat - lat_offset
maxx = lon + lon_offset
maxy = lat + lat_offset
base_url = (
"https://server.arcgisonline.com/ArcGIS/rest/services/"
"World_Imagery/MapServer/export"
)
# Retry up to 3 times
for attempt in range(3):
logger.info(f"Attempt {attempt + 1}/3 to fetch satellite image...")
# Try descending sizes until success
size = 1024
while size >= 128:
params = {
"bbox": f"{minx},{miny},{maxx},{maxy}",
"bboxSR": "4326",
"size": f"{size},{size}",
"format": "png",
"f": "image",
}
try:
response = httpx.get(base_url, params=params, timeout=30.0)
if response.status_code == 200:
with open(output_path, "wb") as f:
f.write(response.content)
logger.info(f"Saved Esri image to {output_path} ({size}x{size})")
return
else:
logger.info(
f"Failed at size {size} (status {response.status_code}), trying {size // 2}"
)
size //= 2
except Exception as e:
logger.error(f"Network error at size {size}: {e}, trying {size // 2}")
size //= 2
# If this attempt failed for all sizes, log and continue to next attempt
if attempt < 2: # Don't print this message on the last attempt
logger.info(f"Attempt {attempt + 1} failed for all sizes, retrying...")
# If all attempts fail
logger.warning("Unable to fetch Esri imagery: all retry attempts failed.")