create backend

This commit is contained in:
2026-01-24 10:23:37 +01:00
parent 73625bf6a5
commit f059b54936
17 changed files with 1611 additions and 3 deletions

View 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