Files
CA/backend/utils/geo.py
Akiba So e95e2f1338 feat: add analysis pages and raster risk map
Ship a new app version with broader analytics, restructured
dashboards, and a server-rendered risk map.

Frontend:
- Add Overview, Demographic, Disease, and Environmental Health
  analysis pages
- Add AnomalyMarkers, CalendarHeatmap, and MetricHeatmapTable
  components
- Rebuild Alerts map onto server-rendered raster risk tiles;
  expand Monitoring, Trend, and District Comparison views
- Extend API client, stores, and TypeScript types

Backend:
- Add environment router (pollutants, lag correlations)
- Add risk_raster util serving XYZ 100m risk tiles
- Expand cases endpoints (demographics, seasonality, diagnoses)
  and insights; harden auth and file-based loaders

Data & tooling:
- Add processed outpatient/inpatient/combined case parquet (LFS)
- Add nested CLAUDE.md guides, pyrightconfig, and test updates
2026-06-21 17:35:03 +08:00

46 lines
1.5 KiB
Python

"""
Geographic utilities: point-in-polygon testing via ray casting.
"""
def point_in_polygon(lat: float, lon: float, polygon_coords: list) -> bool:
"""Check if a point is inside a polygon (supports Polygon and MultiPolygon)."""
if not polygon_coords:
return False
# MultiPolygon: check each polygon
if isinstance(polygon_coords[0], list) and polygon_coords[0] and isinstance(polygon_coords[0][0], list):
for polygon in polygon_coords:
if polygon and isinstance(polygon[0], list):
ring = polygon[0] if isinstance(polygon[0][0], list) else polygon
if point_in_ring(lat, lon, ring):
return True
return False
# Single Polygon: use first ring (outer boundary)
ring = polygon_coords[0] if isinstance(polygon_coords[0], list) else polygon_coords
return point_in_ring(lat, lon, ring)
def point_in_ring(lat: float, lon: float, ring: list) -> bool:
"""Ray casting algorithm for point-in-ring test."""
n = len(ring)
if n < 3:
return False
inside = False
x, y = lon, lat
p1x, p1y = ring[0]
for i in range(1, n + 1):
p2x, p2y = ring[i % n]
if y > min(p1y, p2y):
if y <= max(p1y, p2y):
if x <= max(p1x, p2x):
xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) if p1y != p2y else p1x
if p1x == p2x or x <= xinters:
inside = not inside
p1x, p1y = p2x, p2y
return inside