Spaces:
Build error
Build error
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel | |
| import httpx | |
| import pyproj | |
| from typing import List, Dict, Optional, Any | |
| from distance import calculate_distance | |
| from area import calculate_area | |
| from coordinate_converter import convert_coordinates | |
| from terrain import extract_terrain_data | |
| app = FastAPI(title="GIS Tools API") | |
| # Configure CORS | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Define request and response models | |
| class CoordinateRequest(BaseModel): | |
| latitude: float | |
| longitude: float | |
| crs: str = "EPSG:4326" | |
| class ElevationResponse(BaseModel): | |
| latitude: float | |
| longitude: float | |
| elevation: float | |
| wgs84_latitude: float | |
| wgs84_longitude: float | |
| original_crs: str | |
| class CRSInfo(BaseModel): | |
| code: str | |
| name: str | |
| class DistanceRequest(BaseModel): | |
| point1: CoordinateRequest | |
| point2: CoordinateRequest | |
| class AreaRequest(BaseModel): | |
| coordinates: List[CoordinateRequest] | |
| class TerrainRequest(BaseModel): | |
| latitude: float | |
| longitude: float | |
| crs: str = "EPSG:4326" | |
| # Helper: Convert to WGS84 | |
| def convert_to_wgs84(lon, lat, source_crs): | |
| if source_crs == "EPSG:4326": | |
| return (lon, lat) | |
| try: | |
| transformer = pyproj.Transformer.from_crs(source_crs, "EPSG:4326", always_xy=True) | |
| lon, lat = transformer.transform(lon, lat) | |
| return (lon, lat) | |
| except Exception as e: | |
| raise Exception(f"Coordinate transformation error: {str(e)}") | |
| # Helper: Query Open-Meteo | |
| async def query_open_meteo(lat, lon): | |
| url = f"https://api.open-meteo.com/v1/elevation?latitude={lat}&longitude={lon}" | |
| async with httpx.AsyncClient() as client: | |
| try: | |
| response = await client.get(url) | |
| response.raise_for_status() | |
| data = response.json() | |
| if "elevation" in data and isinstance(data["elevation"], list) and len(data["elevation"]) > 0: | |
| return float(data["elevation"][0]) | |
| else: | |
| raise Exception("No elevation data found in API response") | |
| except httpx.HTTPStatusError as e: | |
| raise Exception(f"Elevation API error: {str(e)}") | |
| except Exception as e: | |
| raise Exception(f"Error querying elevation: {str(e)}") | |
| # Route for fetching elevation data | |
| async def get_elevation(request: CoordinateRequest): | |
| try: | |
| wgs84_coords = convert_to_wgs84(request.longitude, request.latitude, request.crs) | |
| elevation = await query_open_meteo(wgs84_coords[1], wgs84_coords[0]) | |
| return ElevationResponse( | |
| latitude=request.latitude, | |
| longitude=request.longitude, | |
| elevation=elevation, | |
| wgs84_latitude=wgs84_coords[1], | |
| wgs84_longitude=wgs84_coords[0], | |
| original_crs=request.crs | |
| ) | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| # Route for calculating distance | |
| async def calculate_distance_route(request: DistanceRequest): | |
| # Extract coordinates and CRS | |
| p1 = (request.point1.longitude, request.point1.latitude, request.point1.crs) | |
| p2 = (request.point2.longitude, request.point2.latitude, request.point2.crs) | |
| return {"distance": calculate_distance(p1, p2)} | |
| # Route for calculating area | |
| async def calculate_area_route(request: AreaRequest): | |
| # Convert CoordinateRequest list to list of (lon, lat, crs) | |
| coords = [(c.longitude, c.latitude, c.crs) for c in request.coordinates] | |
| return {"area": calculate_area(coords)} | |
| # Route for converting coordinates | |
| async def coordinate_converter_route(request: CoordinateRequest): | |
| return convert_coordinates(request.longitude, request.latitude, request.crs) | |
| # Route for extracting terrain data | |
| async def terrain_data_route(request: TerrainRequest): | |
| return await extract_terrain_data(request.latitude, request.longitude, request.crs) | |
| # Route for fetching CRS list | |
| async def get_crs_list(): | |
| crs_list = [ | |
| {"code": "EPSG:4326", "name": "WGS 84"}, | |
| {"code": "EPSG:3857", "name": "Web Mercator"}, | |
| {"code": "EPSG:2154", "name": "RGF93 / Lambert-93"}, | |
| {"code": "EPSG:27700", "name": "OSGB 1936 / British National Grid"}, | |
| {"code": "EPSG:3785", "name": "Popular Visualisation CRS / Mercator"}, | |
| {"code": "EPSG:4269", "name": "NAD83"}, | |
| {"code": "EPSG:4267", "name": "NAD27"}, | |
| {"code": "EPSG:32633", "name": "WGS 84 / UTM zone 33N"}, | |
| ] | |
| return crs_list | |
| # Health check endpoint | |
| async def health_check(): | |
| return {"status": "ok"} | |
| if __name__ == "__main__": | |
| import uvicorn | |
| import os | |
| port = int(os.environ.get("PORT", 7860)) | |
| uvicorn.run(app, host="0.0.0.0", port=port) |