Spaces:
Running
Running
| """CPU-safe Gradio Space for MapFix-Spatial coordinate correction. | |
| The public Space exposes the deterministic correction path so the demo remains | |
| available without API keys or browser-only map services. | |
| """ | |
| from __future__ import annotations | |
| import math | |
| import gradio as gr | |
| def correct(coords: str, projection: str): | |
| lat, lon = _parse_lat_lon(coords) | |
| corrected_lat = lat + 0.00018 * math.cos(math.radians(lat)) | |
| corrected_lon = lon - 0.00022 * math.cos(math.radians(lon)) | |
| delta_m = ( | |
| ((corrected_lat - lat) * 111_000) ** 2 | |
| + ((corrected_lon - lon) * 85_000) ** 2 | |
| ) ** 0.5 | |
| lines = [ | |
| "MapFix-Spatial CPU-safe correction demo", | |
| f"input lat/lon: ({lat:.7f}, {lon:.7f})", | |
| f"corrected lat/lon: ({corrected_lat:.7f}, {corrected_lon:.7f})", | |
| f"estimated shift: {delta_m:.2f} m", | |
| f"target projection: {projection}", | |
| ] | |
| if projection in {"EPSG:3857", "Web Mercator"}: | |
| x, y = _to_web_mercator(corrected_lat, corrected_lon) | |
| lines.append(f"web mercator: x={x:.2f}, y={y:.2f}") | |
| elif projection == "Local UTM": | |
| zone = int((corrected_lon + 180) / 6) + 1 | |
| lines.append(f"local UTM proxy: zone={zone}, easting/northing computed in browser demo") | |
| stats = ( | |
| "engine=deterministic CPU fallback\n" | |
| "api_keys_required=false\n" | |
| "full_demo=https://arunshar.github.io/mapfix-spatial/\n" | |
| "correction_model=lat/lon harmonic bias proxy" | |
| ) | |
| return "\n".join(lines), stats | |
| def _parse_lat_lon(coords: str) -> tuple[float, float]: | |
| cleaned = coords | |
| for char in "[](){}": | |
| cleaned = cleaned.replace(char, "") | |
| parts = [part.strip() for part in cleaned.replace(";", ",").split(",") if part.strip()] | |
| if len(parts) < 2: | |
| raise gr.Error("Enter coordinates as 'lat,lon'.") | |
| try: | |
| lat = float(parts[0]) | |
| lon = float(parts[1]) | |
| except ValueError as exc: | |
| raise gr.Error("Latitude and longitude must be numeric.") from exc | |
| if not (-90 <= lat <= 90 and -180 <= lon <= 180): | |
| raise gr.Error("Latitude must be in [-90, 90] and longitude in [-180, 180].") | |
| return lat, lon | |
| def _to_web_mercator(lat: float, lon: float) -> tuple[float, float]: | |
| clipped_lat = max(min(lat, 85.05112878), -85.05112878) | |
| x = lon * 20037508.34 / 180 | |
| y = math.log(math.tan((90 + clipped_lat) * math.pi / 360)) / (math.pi / 180) | |
| y = y * 20037508.34 / 180 | |
| return x, y | |
| def build_ui(): | |
| with gr.Blocks(title="MapFix-Spatial") as demo: | |
| gr.Markdown( | |
| "# MapFix-Spatial\n" | |
| "CPU-safe deterministic geospatial correction demo. The full interactive browser workflow is " | |
| "available at arunshar.github.io/mapfix-spatial." | |
| ) | |
| with gr.Row(): | |
| coords = gr.Textbox(label="Coordinates ('lat,lon' or list)", value="44.97,-93.27") | |
| proj = gr.Dropdown(["EPSG:4326", "EPSG:3857", "Web Mercator", "Local UTM"], label="Target projection", value="EPSG:4326") | |
| run = gr.Button("Correct", variant="primary") | |
| with gr.Row(): | |
| out = gr.Textbox(label="Corrected coordinates", lines=8) | |
| stats = gr.Textbox(label="Run stats", lines=3) | |
| run.click(correct, [coords, proj], [out, stats]) | |
| return demo | |
| if __name__ == "__main__": | |
| build_ui().launch(server_name="0.0.0.0") | |