Mike
feat: vectorised graph build
ab42110
"""Generate a regular grid over the water area."""
import numpy as np
from shapely import contains_xy
# Approximate meters per degree at ~60°N latitude
METERS_PER_DEG_LAT = 111_320.0
METERS_PER_DEG_LON = 55_660.0 # 111320 * cos(60°)
def generate_grid(
water_polygon,
cell_size_m: float = 200.0,
) -> tuple[np.ndarray, np.ndarray, float, float]:
"""Generate grid points that lie within the water polygon.
Parameters
----------
water_polygon : shapely geometry
Union of all water zone polygons.
cell_size_m : float
Grid cell size in meters.
Returns
-------
lats : ndarray of shape (N,)
Latitudes of water grid cells.
lons : ndarray of shape (N,)
Longitudes of water grid cells.
dlat : float
Grid step in latitude degrees.
dlon : float
Grid step in longitude degrees.
"""
dlat = cell_size_m / METERS_PER_DEG_LAT
dlon = cell_size_m / METERS_PER_DEG_LON
minx, miny, maxx, maxy = water_polygon.bounds
# Extend slightly to catch boundary cells
lat_range = np.arange(miny, maxy + dlat, dlat)
lon_range = np.arange(minx, maxx + dlon, dlon)
lon_grid, lat_grid = np.meshgrid(lon_range, lat_range)
lon_flat = lon_grid.ravel()
lat_flat = lat_grid.ravel()
mask = contains_xy(water_polygon, lon_flat, lat_flat)
return lat_flat[mask], lon_flat[mask], dlat, dlon
def snap_to_grid(
lat: float,
lon: float,
grid_lats: np.ndarray,
grid_lons: np.ndarray,
) -> int:
"""Find the nearest grid cell index for a given coordinate.
Uses approximate Euclidean distance weighted for latitude.
"""
dlat = grid_lats - lat
dlon = (grid_lons - lon) * (METERS_PER_DEG_LON / METERS_PER_DEG_LAT)
dist_sq = dlat**2 + dlon**2
return int(np.argmin(dist_sq))