create backend
This commit is contained in:
121
lib/mound_hunters/boundary.ex
Normal file
121
lib/mound_hunters/boundary.ex
Normal file
@@ -0,0 +1,121 @@
|
||||
defmodule MoundHunters.Boundary do
|
||||
@moduledoc """
|
||||
Ohio state boundary checking using point-in-polygon algorithm.
|
||||
"""
|
||||
|
||||
# Simplified Ohio bounding box for fast preliminary check
|
||||
# More precise polygon would be loaded from GeoJSON
|
||||
@ohio_bbox %{
|
||||
min_lat: 38.403,
|
||||
max_lat: 42.327,
|
||||
min_lng: -84.820,
|
||||
max_lng: -80.519
|
||||
}
|
||||
|
||||
# Ohio state boundary polygon (simplified)
|
||||
# Source: Github -> PublicaMundi/MappingAPI us-states.json
|
||||
# Coordinates are [lng, lat] pairs per GeoJSON spec
|
||||
@ohio_polygon [
|
||||
{-80.518598, 41.978802},
|
||||
{-80.518598, 40.636951},
|
||||
{-80.666475, 40.582182},
|
||||
{-80.595275, 40.472643},
|
||||
{-80.600752, 40.319289},
|
||||
{-80.737675, 40.078303},
|
||||
{-80.830783, 39.711348},
|
||||
{-81.219646, 39.388209},
|
||||
{-81.345616, 39.344393},
|
||||
{-81.455155, 39.410117},
|
||||
{-81.570170, 39.267716},
|
||||
{-81.685186, 39.273193},
|
||||
{-81.811156, 39.081500},
|
||||
{-81.783771, 38.966484},
|
||||
{-81.887833, 38.873376},
|
||||
{-82.035710, 39.026731},
|
||||
{-82.221926, 38.785745},
|
||||
{-82.172634, 38.632391},
|
||||
{-82.293127, 38.577622},
|
||||
{-82.331465, 38.446175},
|
||||
{-82.594358, 38.424267},
|
||||
{-82.731282, 38.561191},
|
||||
{-82.846298, 38.588575},
|
||||
{-82.890113, 38.758361},
|
||||
{-83.032514, 38.725499},
|
||||
{-83.142052, 38.626914},
|
||||
{-83.519961, 38.703591},
|
||||
{-83.678792, 38.632391},
|
||||
{-83.903347, 38.769315},
|
||||
{-84.215533, 38.807653},
|
||||
{-84.231963, 38.895284},
|
||||
{-84.434610, 39.103408},
|
||||
{-84.817996, 39.103408},
|
||||
{-84.801565, 40.500028},
|
||||
{-84.807042, 41.694001},
|
||||
{-83.454238, 41.732339},
|
||||
{-83.065375, 41.595416},
|
||||
{-82.933929, 41.513262},
|
||||
{-82.835344, 41.589939},
|
||||
{-82.616266, 41.431108},
|
||||
{-82.479343, 41.381815},
|
||||
{-82.013803, 41.513262},
|
||||
{-81.739956, 41.485877},
|
||||
{-81.444201, 41.672093},
|
||||
{-81.011523, 41.852832},
|
||||
{-80.518598, 41.978802},
|
||||
{-80.518598, 41.978802}
|
||||
]
|
||||
|
||||
@doc """
|
||||
Check if coordinates are within Ohio boundaries.
|
||||
Returns :ok or {:error, reason}
|
||||
"""
|
||||
def check_bounds(lat, lng) when is_number(lat) and is_number(lng) do
|
||||
cond do
|
||||
not in_bounding_box?(lat, lng) ->
|
||||
{:error, "Coordinates outside Ohio bounding box"}
|
||||
|
||||
not in_polygon?(lng, lat, @ohio_polygon) ->
|
||||
{:error, "Coordinates outside Ohio boundary"}
|
||||
|
||||
true ->
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
def check_bounds(_lat, _lng) do
|
||||
{:error, "Invalid coordinates"}
|
||||
end
|
||||
|
||||
defp in_bounding_box?(lat, lng) do
|
||||
lat >= @ohio_bbox.min_lat and lat <= @ohio_bbox.max_lat and
|
||||
lng >= @ohio_bbox.min_lng and lng <= @ohio_bbox.max_lng
|
||||
end
|
||||
|
||||
# Ray casting algorithm for point-in-polygon test
|
||||
# Returns true if point (x, y) is inside the polygon
|
||||
defp in_polygon?(x, y, polygon) do
|
||||
n = length(polygon)
|
||||
|
||||
polygon
|
||||
|> Enum.with_index()
|
||||
|> Enum.reduce(false, fn {{xi, yi}, i}, inside ->
|
||||
j = rem(i + n - 1, n)
|
||||
{xj, yj} = Enum.at(polygon, j)
|
||||
|
||||
intersects =
|
||||
yi > y != yj > y and
|
||||
x < (xj - xi) * (y - yi) / (yj - yi) + xi
|
||||
|
||||
if intersects, do: not inside, else: inside
|
||||
end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Format coordinates to 6 decimal places for consistent lookups.
|
||||
"""
|
||||
def format_lookup_id(lat, lng) do
|
||||
lat_str = :erlang.float_to_binary(lat / 1.0, decimals: 6)
|
||||
lng_str = :erlang.float_to_binary(lng / 1.0, decimals: 6)
|
||||
"#{lat_str},#{lng_str}"
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user