lookup endpoint, better tag filtering

This commit is contained in:
randogoth 2025-03-05 19:22:56 +00:00
parent be5938ffb0
commit 9168a7cbb8
3 changed files with 102 additions and 10 deletions

65
api.py
View file

@ -1,9 +1,13 @@
from fastapi import FastAPI, HTTPException
from fastapi.responses import Response, FileResponse
from fastapi import FastAPI, HTTPException, Query
from fastapi.responses import Response, FileResponse, JSONResponse
from fastapi.middleware.gzip import GZipMiddleware
import geopandas as gpd
import cbor2
from pathlib import Path
import pygeohash as pgh
from shapely.geometry import Point, LineString, MultiLineString
from pyproj import Geod
import json
app = FastAPI()
@ -44,4 +48,59 @@ async def get_geodata(geohash: str, format: str = "cbor"):
return Response(content=cbor_data, media_type="application/cbor",
headers={"Content-Disposition": f"attachment; filename={base_geohash}.cbor"})
raise HTTPException(status_code=400, detail="Invalid format. Use 'parquet', 'geojson', or 'cbor'.")
raise HTTPException(status_code=400, detail="Invalid format. Use 'parquet', 'geojson', or 'cbor'.")
@app.get("/lookup/")
async def lookup(
latitude: float = Query(..., description="Latitude of the point"),
longitude: float = Query(..., description="Longitude of the point"),
distance: float = Query(10, description="Distance threshold in meters for nearby lines")
):
"""
Finds polygons containing the given point and lines within a threshold (in meters).
"""
# Compute geohash (precision can be adjusted)
geohash = pgh.encode(latitude, longitude, precision=4) # Adjust precision as needed
parquet_file = GEOHASH_FOLDER / f"{geohash}.parquet"
if not parquet_file.exists():
raise HTTPException(status_code=404, detail=f"No data found for geohash '{geohash}'.")
try:
# Load the Parquet file into a GeoDataFrame
gdf = gpd.read_parquet(parquet_file)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to read Parquet file: {str(e)}")
point = Point(longitude, latitude)
geod = Geod(ellps="WGS84") # Accurate geodetic distance calculations
found_geometries = []
# Process each row
for _, row in gdf.iterrows():
geom = row.geometry # Directly use the Shapely geometry object
metadata = {}
if isinstance(row.get("tags"), dict):
metadata = {k: v for k, v in row["tags"].items() if v is not None} # Remove empty values
if geom.contains(point):
found_geometries.append({"type": "polygon", **metadata})
elif isinstance(geom, (LineString, MultiLineString)):
try:
if isinstance(geom, LineString):
min_distance = min(geod.inv(point.x, point.y, p[0], p[1])[2] for p in geom.coords)
elif isinstance(geom, MultiLineString):
min_distance = min(
min(geod.inv(point.x, point.y, p[0], p[1])[2] for p in line.coords)
for line in geom.geoms
)
if min_distance <= distance:
found_geometries.append({"type": "line" if isinstance(geom, LineString) else "multi_line", **metadata})
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed distance calculation: {str(e)}")
# Return JSON result
return JSONResponse(content=found_geometries)