dopecarpet/lookup.py

61 lines
2.5 KiB
Python
Raw Normal View History

2025-02-26 14:01:09 +00:00
import json
import click
import pygeohash as pgh
import pyarrow.parquet as pq
2025-03-05 18:15:40 +00:00
import geopandas as gpd
2025-02-26 14:01:09 +00:00
from shapely.geometry import Point, LineString, MultiLineString
from pyproj import Geod
2025-03-05 18:15:40 +00:00
import numpy as np
2025-02-26 14:01:09 +00:00
@click.command()
@click.argument("latitude", type=float)
@click.argument("longitude", type=float)
@click.option("--distance", type=float, default=10, help="Distance threshold in meters for nearby lines.")
@click.option("--parquet-dir", type=str, default="geohash", help="Directory containing geohash Parquet files.")
def geohash_lookup(latitude, longitude, distance, parquet_dir):
"""
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 = f"{parquet_dir}/{geohash}.parquet"
try:
2025-03-05 18:15:40 +00:00
# Load the Parquet file directly into a GeoDataFrame
df = gpd.read_parquet(parquet_file)
2025-02-26 14:01:09 +00:00
except FileNotFoundError:
click.echo(json.dumps({"error": f"No Parquet file found for geohash {geohash}."}))
return
point = Point(longitude, latitude)
geod = Geod(ellps="WGS84") # Accurate geodetic distance calculations
found_geometries = []
2025-03-05 18:15:40 +00:00
# Vectorized distance calculation for LineStrings and MultiLineStrings
def calculate_min_distance(geom):
if isinstance(geom, LineString):
return min(geod.inv(point.x, point.y, p[0], p[1])[2] for p in geom.coords)
elif isinstance(geom, MultiLineString):
return min(min(geod.inv(point.x, point.y, p[0], p[1])[2] for p in line.coords) for line in geom.geoms)
return np.inf
for row in df.itertuples(index=False):
geom = row.geometry # Directly use the Shapely geometry object
2025-02-26 14:01:09 +00:00
metadata = {}
2025-03-05 18:15:40 +00:00
if isinstance(row.tags, dict):
metadata = {k: v for k, v in row.tags.items() if v is not None} # Remove empty values
2025-02-26 14:01:09 +00:00
if geom.contains(point):
found_geometries.append({"type": "polygon", **metadata})
2025-03-05 18:15:40 +00:00
elif isinstance(geom, (LineString, MultiLineString)):
min_distance = calculate_min_distance(geom)
if min_distance <= distance:
found_geometries.append({"type": "line" if isinstance(geom, LineString) else "multi_line", **metadata})
2025-02-26 14:01:09 +00:00
# Output JSON result
click.echo(json.dumps(found_geometries, indent=2, ensure_ascii=False)) # Ensure proper Unicode display
if __name__ == "__main__":
2025-03-05 18:15:40 +00:00
geohash_lookup()