06/15 Phase 1 + 2 codes
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
"""Geo helpers: ZIP geocoding, haversine distance, bounding-box radius filter.
|
||||
|
||||
Portable across MySQL and SQLite (no spatial extension needed). For large-scale
|
||||
deployments, see README for the MySQL POINT + SPATIAL INDEX upgrade.
|
||||
"""
|
||||
import math
|
||||
from app.models.geo import ZipGeo
|
||||
|
||||
EARTH_MI = 3958.7613 # mean earth radius, miles
|
||||
|
||||
|
||||
def geocode_zip(zip_code):
|
||||
"""Return (lat, lng, city, state, metro) or None."""
|
||||
row = ZipGeo.query.get((zip_code or "").strip())
|
||||
if row is None:
|
||||
return None
|
||||
return row.lat, row.lng, row.city, row.state, row.metro
|
||||
|
||||
|
||||
def haversine_mi(lat1, lng1, lat2, lng2):
|
||||
rlat1, rlat2 = math.radians(lat1), math.radians(lat2)
|
||||
dlat = math.radians(lat2 - lat1)
|
||||
dlng = math.radians(lng2 - lng1)
|
||||
a = (math.sin(dlat / 2) ** 2
|
||||
+ math.cos(rlat1) * math.cos(rlat2) * math.sin(dlng / 2) ** 2)
|
||||
return EARTH_MI * 2 * math.asin(math.sqrt(a))
|
||||
|
||||
|
||||
def bounding_box(lat, lng, radius_mi):
|
||||
"""Lat/lng min/max box enclosing the radius. Cheap indexed prefilter."""
|
||||
lat_delta = radius_mi / 69.0
|
||||
# guard against poles; cos(lat) shrinks lng degrees-per-mile
|
||||
cos_lat = max(math.cos(math.radians(lat)), 0.01)
|
||||
lng_delta = radius_mi / (69.172 * cos_lat)
|
||||
return (lat - lat_delta, lat + lat_delta, lng - lng_delta, lng + lng_delta)
|
||||
Reference in New Issue
Block a user