Initial travel knowledge graph release
This commit is contained in:
57
app/kg_core/spatial.py
Normal file
57
app/kg_core/spatial.py
Normal file
@@ -0,0 +1,57 @@
|
||||
"""Spatial helper functions for H3 recall and distance filtering.
|
||||
|
||||
These helpers are intentionally small and framework-free so they can be used by
|
||||
agents, API routes, tests, and offline benchmarks.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
|
||||
import h3
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Point:
|
||||
lng: float
|
||||
lat: float
|
||||
|
||||
|
||||
def haversine_m(a: Point, b: Point) -> float:
|
||||
"""Return approximate great-circle distance in meters.
|
||||
|
||||
Production radius filtering should use PostGIS `geography` + `ST_DWithin`.
|
||||
This function is a fallback for tests or local benchmarks.
|
||||
"""
|
||||
radius = 6_371_008.8
|
||||
d_lng = math.radians(b.lng - a.lng)
|
||||
d_lat = math.radians(b.lat - a.lat)
|
||||
part = (
|
||||
math.sin(d_lat / 2) ** 2
|
||||
+ math.cos(math.radians(a.lat))
|
||||
* math.cos(math.radians(b.lat))
|
||||
* math.sin(d_lng / 2) ** 2
|
||||
)
|
||||
return 2 * radius * math.asin(math.sqrt(part))
|
||||
|
||||
|
||||
def h3_cell(point: Point, resolution: int = 9) -> str:
|
||||
return h3.latlng_to_cell(point.lat, point.lng, resolution)
|
||||
|
||||
|
||||
def h3_k_for_radius(radius_m: float, resolution: int = 9, safety_ring: int = 2) -> int:
|
||||
"""Compute a conservative H3 grid_disk k for a radius search.
|
||||
|
||||
H3 recall should be slightly larger than the user's circle so boundary
|
||||
points are not missed. PostGIS does the final exact circle filter.
|
||||
"""
|
||||
edge_m = h3.average_hexagon_edge_length(resolution, unit="m")
|
||||
center_gap_m = math.sqrt(3) * edge_m
|
||||
return max(1, math.ceil(radius_m / center_gap_m) + safety_ring)
|
||||
|
||||
|
||||
def h3_neighbor_cells(point: Point, radius_m: float, resolution: int = 9) -> set[str]:
|
||||
center = h3_cell(point, resolution)
|
||||
k = h3_k_for_radius(radius_m, resolution)
|
||||
return set(h3.grid_disk(center, k))
|
||||
|
||||
Reference in New Issue
Block a user