neiam
/apollos_crib
It's Apollo's Crib
public · Issues · Pulls 1 · Labels · Forks · Compare · Actions failure · Badges · Packages
⭐
Log in to mark this repository.
Read a foci the way this app writes one
b4ce49f · Gabriel Morell · 2026-08-11 00:38
Message
{commit_body(@commit)}
Files changed
modified
apps/room_sanctum/lib/room_sanctum_web/components/query_geospatial_map.ex
+17
−1
@@ -579,9 +579,15 @@ defmodule RoomSanctumWeb.Components.QueryGeospatialMap do
| 579 | 579 | # The field is :place. This read :point, and since that was in a guard the |
| 580 | 580 | # KeyError was swallowed and the clause simply never matched -- so a foci |
| 581 | 581 | # has never resolved to anywhere, and no aqi query has ever been placed. |
| 582 | + # | |
| 583 | + # A foci stores {lat, lon}, which is the reverse of the PostGIS convention | |
| 584 | + # everything else here follows -- gbfs stations and free bikes are {lon, lat}. | |
| 585 | + # That is how the foci picker writes it and how room_icarus reads it, so it | |
| 586 | + # is the convention, not a bug to correct: reading it the standard way put | |
| 587 | + # SFO in Antarctica, latitude clamped to -90. | |
| 582 | 588 | defp get_foci_coordinates(foci_id) do |
| 583 | 589 | case RoomSanctum.Storage.get_foci_by_id(foci_id) do |
| 584 | − %{place: %Geo.Point{coordinates: {lng, lat}}} -> {lat, lng} | |
| 590 | + %{place: %Geo.Point{coordinates: {lat, lon}}} -> on_earth(lat, lon) | |
| 585 | 591 | _ -> false |
| 586 | 592 | end |
| 587 | 593 | rescue |
@@ -589,6 +595,16 @@ defmodule RoomSanctumWeb.Components.QueryGeospatialMap do
| 589 | 595 | Ecto.NoResultsError -> false |
| 590 | 596 | end |
| 591 | 597 | |
| 598 | + # Leaflet clamps an out-of-range latitude to the pole, so a point stored the | |
| 599 | + # other way round would be drawn confidently in the wrong hemisphere. Better | |
| 600 | + # to leave it off the map. | |
| 601 | + defp on_earth(lat, lon) | |
| 602 | + when is_number(lat) and is_number(lon) and | |
| 603 | + lat >= -90 and lat <= 90 and lon >= -180 and lon <= 180, | |
| 604 | + do: {lat, lon} | |
| 605 | + | |
| 606 | + defp on_earth(_lat, _lon), do: false | |
| 607 | + | |
| 592 | 608 | # Get coordinates from GTFS vehicle positions |
| 593 | 609 | defp get_gtfs_vehicle_coordinates(source_id, query) do |
| 594 | 610 | try do |
modified
apps/room_sanctum/test/room_sanctum_web/components/query_placement_test.exs
+50
−1
@@ -24,7 +24,9 @@ defmodule RoomSanctumWeb.QueryPlacementTest do
| 24 | 24 | Configuration.create_foci(%{ |
| 25 | 25 | name: "Home", |
| 26 | 26 | user_id: user.id, |
| 27 | − place: %Geo.Point{coordinates: {-71.0589, 42.3601}, srid: 4326} | |
| 27 | + # {lat, lon} -- how the foci picker writes it, verified against the | |
| 28 | + # rows in the running database | |
| 29 | + place: %Geo.Point{coordinates: {42.3601, -71.0589}, srid: 4326} | |
| 28 | 30 | }) |
| 29 | 31 | |
| 30 | 32 | %{user: user, foci: foci} |
@@ -108,6 +110,53 @@ defmodule RoomSanctumWeb.QueryPlacementTest do
| 108 | 110 | refute html =~ ~s(lat="0") |
| 109 | 111 | end |
| 110 | 112 | |
| 113 | + test "a foci is read the way the app writes it, not the PostGIS way", %{user: user} do | |
| 114 | + # Read as {lon, lat} this is latitude -71, which Leaflet clamps to the | |
| 115 | + # south pole -- SFO drawn in Antarctica. | |
| 116 | + {:ok, sfo} = | |
| 117 | + Configuration.create_foci(%{ | |
| 118 | + name: "SFO", | |
| 119 | + user_id: user.id, | |
| 120 | + place: %Geo.Point{coordinates: {37.6226, -122.3843}, srid: 4326} | |
| 121 | + }) | |
| 122 | + | |
| 123 | + src = source(user, :ephem, %{"__type__" => "ephem"}) | |
| 124 | + | |
| 125 | + {:ok, query} = | |
| 126 | + Configuration.create_query(%{ | |
| 127 | + name: "Above SFO", notes: "", source_id: src.id, user_id: user.id, | |
| 128 | + query: %{"__type__" => "ephem", "foci_id" => sfo.id} | |
| 129 | + }) | |
| 130 | + | |
| 131 | + html = render_queries([Configuration.get_query!(query.id)]) | |
| 132 | + | |
| 133 | + assert html =~ ~s(lat="37.6226") | |
| 134 | + assert html =~ ~s(lng="-122.3843") | |
| 135 | + refute html =~ ~s(lat="-122.3843") | |
| 136 | + end | |
| 137 | + | |
| 138 | + test "a foci stored somewhere impossible is left off rather than drawn at the pole", | |
| 139 | + %{user: user} do | |
| 140 | + {:ok, broken} = | |
| 141 | + Configuration.create_foci(%{ | |
| 142 | + name: "Broken", | |
| 143 | + user_id: user.id, | |
| 144 | + place: %Geo.Point{coordinates: {-122.3843, 37.6226}, srid: 4326} | |
| 145 | + }) | |
| 146 | + | |
| 147 | + src = source(user, :ephem, %{"__type__" => "ephem"}) | |
| 148 | + | |
| 149 | + {:ok, query} = | |
| 150 | + Configuration.create_query(%{ | |
| 151 | + name: "Nowhere", notes: "", source_id: src.id, user_id: user.id, | |
| 152 | + query: %{"__type__" => "ephem", "foci_id" => broken.id} | |
| 153 | + }) | |
| 154 | + | |
| 155 | + html = render_queries([Configuration.get_query!(query.id)]) | |
| 156 | + | |
| 157 | + refute html =~ ~s(name="Nowhere") | |
| 158 | + end | |
| 159 | + | |
| 111 | 160 | test "placeable and unplaceable queries mix without either breaking", %{user: user, foci: foci} do |
| 112 | 161 | ephem = source(user, :ephem, %{"__type__" => "ephem"}) |
| 113 | 162 | markets = source(user, :bourse, %{"__type__" => "bourse"}) |
Parents: fc3f047