Optimize viewport discovery and enable automatic updates
This commit is contained in:
parent
d3114e211b
commit
afc0040a65
|
|
@ -337,7 +337,6 @@ const createAidMap = element => {
|
|||
element,
|
||||
map: null,
|
||||
active: true,
|
||||
pendingViewportAction: null,
|
||||
resizeFrame: null,
|
||||
resizeObserver: null,
|
||||
renderedSize: null,
|
||||
|
|
@ -948,11 +947,19 @@ const createAidMap = element => {
|
|||
const viewport = mapViewport(state.map)
|
||||
if (!viewport) return
|
||||
|
||||
state.pendingViewportAction = {
|
||||
viewport,
|
||||
clusterExpansion: state.clusterExpansionPending
|
||||
}
|
||||
const clusterExpansion = state.clusterExpansionPending
|
||||
state.clusterExpansionPending = false
|
||||
|
||||
// `moveend` already means that the camera transition has finished. Do
|
||||
// not wait for MapLibre's `idle` event here: `idle` also waits for every
|
||||
// requested map tile and fade animation, so a slow tile server can delay
|
||||
// discovery refreshes for seconds even though the user stopped moving
|
||||
// the map earlier.
|
||||
if (clusterExpansion) {
|
||||
state.onClusterExpand?.(viewport)
|
||||
} else {
|
||||
state.onViewportChange?.(viewport)
|
||||
}
|
||||
}
|
||||
|
||||
state.onIdle = () => {
|
||||
|
|
@ -964,15 +971,6 @@ const createAidMap = element => {
|
|||
}
|
||||
|
||||
state.syncVisibleAreas()
|
||||
|
||||
const action = state.pendingViewportAction
|
||||
state.pendingViewportAction = null
|
||||
|
||||
if (action?.clusterExpansion) {
|
||||
state.onClusterExpand?.(action.viewport)
|
||||
} else if (action) {
|
||||
state.onViewportChange?.(action.viewport)
|
||||
}
|
||||
}
|
||||
|
||||
state.onRequestHighlight = event => state.highlightRequest(event.detail?.id)
|
||||
|
|
|
|||
|
|
@ -62,8 +62,7 @@ defmodule WhoNeedHelp.Activities do
|
|||
|
||||
def map_discovery_items(%Scope{user: user}, filters, %DiscoveryViewport{} = viewport) do
|
||||
level = DiscoveryCluster.level_for_zoom(viewport.zoom)
|
||||
cell_size = DiscoveryCluster.cell_size_meters(level)
|
||||
world_half = DiscoveryCluster.world_half_meters()
|
||||
cell_count = Integer.pow(2, level) * 1.0
|
||||
|
||||
points =
|
||||
user
|
||||
|
|
@ -75,93 +74,44 @@ defmodule WhoNeedHelp.Activities do
|
|||
)
|
||||
|> select([activity], %{
|
||||
id: activity.id,
|
||||
title: activity.title,
|
||||
location_label: activity.location_label,
|
||||
public_location:
|
||||
fragment(
|
||||
"""
|
||||
CASE
|
||||
WHEN ? @> '{"scale_fixture": true}'::jsonb THEN ?
|
||||
ELSE
|
||||
ST_SetSRID(
|
||||
ST_MakePoint(
|
||||
round(ST_X(?)::numeric, 2)::double precision,
|
||||
round(ST_Y(?)::numeric, 2)::double precision
|
||||
),
|
||||
4326
|
||||
)
|
||||
END
|
||||
""",
|
||||
activity.structured_data,
|
||||
activity.location,
|
||||
activity.location,
|
||||
activity.location
|
||||
)
|
||||
public_longitude: activity.public_longitude,
|
||||
public_latitude: activity.public_latitude
|
||||
})
|
||||
|
||||
points
|
||||
|> subquery()
|
||||
|> group_by([point], [
|
||||
fragment(
|
||||
"floor((ST_X(ST_Transform(?, 3857)) + ?) / ?)::bigint",
|
||||
point.public_location,
|
||||
constant(^world_half),
|
||||
constant(^cell_size)
|
||||
),
|
||||
fragment(
|
||||
"floor((ST_Y(ST_Transform(?, 3857)) + ?) / ?)::bigint",
|
||||
point.public_location,
|
||||
constant(^world_half),
|
||||
constant(^cell_size)
|
||||
)
|
||||
])
|
||||
|> group_by([_point], [selected_as(:cell_x), selected_as(:cell_y)])
|
||||
|> select([point], %{
|
||||
count: count(point.id),
|
||||
activity_id: fragment("CASE WHEN count(*) = 1 THEN min(?::text) END", point.id),
|
||||
title: fragment("CASE WHEN count(*) = 1 THEN min(?) END", point.title),
|
||||
location_label: fragment("CASE WHEN count(*) = 1 THEN min(?) END", point.location_label),
|
||||
longitude: fragment("avg(ST_X(?))", point.public_location),
|
||||
latitude: fragment("avg(ST_Y(?))", point.public_location),
|
||||
west: fragment("min(ST_X(?))", point.public_location),
|
||||
south: fragment("min(ST_Y(?))", point.public_location),
|
||||
east: fragment("max(ST_X(?))", point.public_location),
|
||||
north: fragment("max(ST_Y(?))", point.public_location),
|
||||
west_m: fragment("min(ST_X(ST_Transform(?, 3857)))", point.public_location),
|
||||
south_m: fragment("min(ST_Y(ST_Transform(?, 3857)))", point.public_location),
|
||||
east_m: fragment("max(ST_X(ST_Transform(?, 3857)))", point.public_location),
|
||||
north_m: fragment("max(ST_Y(ST_Transform(?, 3857)))", point.public_location),
|
||||
activity_id: fragment("min(?::text)", point.id),
|
||||
longitude: min(point.public_longitude),
|
||||
latitude: min(point.public_latitude),
|
||||
west: min(point.public_longitude),
|
||||
south: min(point.public_latitude),
|
||||
east: max(point.public_longitude),
|
||||
north: max(point.public_latitude),
|
||||
cell_x:
|
||||
fragment(
|
||||
"floor((ST_X(ST_Transform(?, 3857)) + ?) / ?)::bigint",
|
||||
point.public_location,
|
||||
constant(^world_half),
|
||||
constant(^cell_size)
|
||||
selected_as(
|
||||
fragment(
|
||||
"floor(((? + 180.0) / 360.0) * ?)::bigint",
|
||||
point.public_longitude,
|
||||
constant(^cell_count)
|
||||
),
|
||||
:cell_x
|
||||
),
|
||||
cell_y:
|
||||
fragment(
|
||||
"floor((ST_Y(ST_Transform(?, 3857)) + ?) / ?)::bigint",
|
||||
point.public_location,
|
||||
constant(^world_half),
|
||||
constant(^cell_size)
|
||||
selected_as(
|
||||
fragment(
|
||||
"floor(((ln(tan(pi() / 4.0 + radians(least(85.05112878, greatest(-85.05112878, ?))) / 2.0)) / pi() + 1.0) / 2.0) * ?)::bigint",
|
||||
point.public_latitude,
|
||||
constant(^cell_count)
|
||||
),
|
||||
:cell_y
|
||||
)
|
||||
})
|
||||
|> order_by([point],
|
||||
asc:
|
||||
fragment(
|
||||
"floor((ST_X(ST_Transform(?, 3857)) + ?) / ?)::bigint",
|
||||
point.public_location,
|
||||
constant(^world_half),
|
||||
constant(^cell_size)
|
||||
),
|
||||
asc:
|
||||
fragment(
|
||||
"floor((ST_Y(ST_Transform(?, 3857)) + ?) / ?)::bigint",
|
||||
point.public_location,
|
||||
constant(^world_half),
|
||||
constant(^cell_size)
|
||||
)
|
||||
)
|
||||
|> order_by([_point], asc: selected_as(:cell_x), asc: selected_as(:cell_y))
|
||||
|> Repo.all()
|
||||
|> hydrate_activity_map_singletons()
|
||||
|> Enum.map(&map_discovery_item(&1, level))
|
||||
end
|
||||
|
||||
|
|
@ -894,28 +844,15 @@ defmodule WhoNeedHelp.Activities do
|
|||
^condition or
|
||||
fragment(
|
||||
"""
|
||||
ST_Intersects(
|
||||
CASE
|
||||
WHEN ? @> '{"scale_fixture": true}'::jsonb THEN ?
|
||||
ELSE
|
||||
ST_SetSRID(
|
||||
ST_MakePoint(
|
||||
round(ST_X(?)::numeric, 2)::double precision,
|
||||
round(ST_Y(?)::numeric, 2)::double precision
|
||||
),
|
||||
4326
|
||||
)
|
||||
END,
|
||||
ST_MakeEnvelope(?, ?, ?, ?, 4326)
|
||||
)
|
||||
? >= ? AND ? <= ? AND ? >= ? AND ? <= ?
|
||||
""",
|
||||
activity.structured_data,
|
||||
activity.location,
|
||||
activity.location,
|
||||
activity.location,
|
||||
activity.public_longitude,
|
||||
^west,
|
||||
^south,
|
||||
activity.public_longitude,
|
||||
^east,
|
||||
activity.public_latitude,
|
||||
^south,
|
||||
activity.public_latitude,
|
||||
^north
|
||||
)
|
||||
)
|
||||
|
|
@ -1022,12 +959,12 @@ defmodule WhoNeedHelp.Activities do
|
|||
hierarchy_level: level,
|
||||
point_count: row.count,
|
||||
expansion_zoom:
|
||||
DiscoveryCluster.expansion_zoom(
|
||||
DiscoveryCluster.expansion_zoom_for_bounds(
|
||||
level,
|
||||
row.west_m,
|
||||
row.south_m,
|
||||
row.east_m,
|
||||
row.north_m
|
||||
row.west,
|
||||
row.south,
|
||||
row.east,
|
||||
row.north
|
||||
),
|
||||
count: row.count,
|
||||
latitude: latitude,
|
||||
|
|
@ -1041,6 +978,36 @@ defmodule WhoNeedHelp.Activities do
|
|||
}
|
||||
end
|
||||
|
||||
defp hydrate_activity_map_singletons(rows) do
|
||||
singleton_ids =
|
||||
rows
|
||||
|> Enum.filter(&(&1.count == 1))
|
||||
|> Enum.map(& &1.activity_id)
|
||||
|
||||
details = activity_map_singleton_details(singleton_ids)
|
||||
|
||||
Enum.map(rows, fn
|
||||
%{count: 1, activity_id: activity_id} = row ->
|
||||
Map.merge(row, Map.fetch!(details, activity_id))
|
||||
|
||||
row ->
|
||||
row
|
||||
end)
|
||||
end
|
||||
|
||||
defp activity_map_singleton_details([]), do: %{}
|
||||
|
||||
defp activity_map_singleton_details(singleton_ids) do
|
||||
Activity
|
||||
|> where([activity], activity.id in ^singleton_ids)
|
||||
|> select([activity], {
|
||||
activity.id,
|
||||
%{title: activity.title, location_label: activity.location_label}
|
||||
})
|
||||
|> Repo.all()
|
||||
|> Map.new(fn {id, detail} -> {to_string(id), detail} end)
|
||||
end
|
||||
|
||||
defp escape_like(value) do
|
||||
value
|
||||
|> String.replace("\\", "\\\\")
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ defmodule WhoNeedHelp.Activities.Activity do
|
|||
field :structured_data, :map, default: %{}
|
||||
field :location_label, :string
|
||||
field :location, Geo.PostGIS.Geometry
|
||||
field :public_longitude, :float
|
||||
field :public_latitude, :float
|
||||
field :status, Ecto.Enum, values: [:open, :completed, :cancelled], default: :open
|
||||
|
||||
field :location_visibility, Ecto.Enum,
|
||||
|
|
|
|||
|
|
@ -73,8 +73,7 @@ defmodule WhoNeedHelp.Help do
|
|||
|
||||
def map_discovery_items(%Scope{user: user}, filters, %DiscoveryViewport{} = viewport) do
|
||||
level = DiscoveryCluster.level_for_zoom(viewport.zoom)
|
||||
cell_size = DiscoveryCluster.cell_size_meters(level)
|
||||
world_half = DiscoveryCluster.world_half_meters()
|
||||
cell_count = Integer.pow(2, level) * 1.0
|
||||
|
||||
points =
|
||||
user
|
||||
|
|
@ -86,106 +85,46 @@ defmodule WhoNeedHelp.Help do
|
|||
|> filter_request_map_cluster_viewport(viewport, level)
|
||||
|> select([request], %{
|
||||
id: request.id,
|
||||
title: request.title,
|
||||
location_label: request.location_label,
|
||||
location_visibility: request.location_visibility,
|
||||
radius_meters: request.location_radius_meters,
|
||||
urgency: request.urgency,
|
||||
public_location:
|
||||
fragment(
|
||||
"""
|
||||
CASE
|
||||
WHEN ? @> '{"scale_fixture": true}'::jsonb THEN ?
|
||||
WHEN ? = 'exact_for_active_match' THEN
|
||||
ST_SetSRID(
|
||||
ST_MakePoint(
|
||||
round(ST_X(?)::numeric, 2)::double precision,
|
||||
round(ST_Y(?)::numeric, 2)::double precision
|
||||
),
|
||||
4326
|
||||
)
|
||||
ELSE ?
|
||||
END
|
||||
""",
|
||||
request.structured_data,
|
||||
request.location,
|
||||
request.location_visibility,
|
||||
request.location,
|
||||
request.location,
|
||||
request.location
|
||||
)
|
||||
public_longitude: request.public_longitude,
|
||||
public_latitude: request.public_latitude
|
||||
})
|
||||
|
||||
points
|
||||
|> subquery()
|
||||
|> group_by([point], [
|
||||
fragment(
|
||||
"floor((ST_X(ST_Transform(?, 3857)) + ?) / ?)::bigint",
|
||||
point.public_location,
|
||||
constant(^world_half),
|
||||
constant(^cell_size)
|
||||
),
|
||||
fragment(
|
||||
"floor((ST_Y(ST_Transform(?, 3857)) + ?) / ?)::bigint",
|
||||
point.public_location,
|
||||
constant(^world_half),
|
||||
constant(^cell_size)
|
||||
)
|
||||
])
|
||||
|> group_by([_point], [selected_as(:cell_x), selected_as(:cell_y)])
|
||||
|> select([point], %{
|
||||
count: count(point.id),
|
||||
urgent_count: fragment("count(*) FILTER (WHERE ? = 'now')::bigint", point.urgency),
|
||||
request_id: fragment("CASE WHEN count(*) = 1 THEN min(?::text) END", point.id),
|
||||
title: fragment("CASE WHEN count(*) = 1 THEN min(?) END", point.title),
|
||||
location_label: fragment("CASE WHEN count(*) = 1 THEN min(?) END", point.location_label),
|
||||
location_visibility:
|
||||
fragment(
|
||||
"CASE WHEN count(*) = 1 THEN min(?::text) END",
|
||||
point.location_visibility
|
||||
),
|
||||
radius_meters: fragment("CASE WHEN count(*) = 1 THEN max(?) END", point.radius_meters),
|
||||
longitude: fragment("avg(ST_X(?))", point.public_location),
|
||||
latitude: fragment("avg(ST_Y(?))", point.public_location),
|
||||
west: fragment("min(ST_X(?))", point.public_location),
|
||||
south: fragment("min(ST_Y(?))", point.public_location),
|
||||
east: fragment("max(ST_X(?))", point.public_location),
|
||||
north: fragment("max(ST_Y(?))", point.public_location),
|
||||
west_m: fragment("min(ST_X(ST_Transform(?, 3857)))", point.public_location),
|
||||
south_m: fragment("min(ST_Y(ST_Transform(?, 3857)))", point.public_location),
|
||||
east_m: fragment("max(ST_X(ST_Transform(?, 3857)))", point.public_location),
|
||||
north_m: fragment("max(ST_Y(ST_Transform(?, 3857)))", point.public_location),
|
||||
request_id: fragment("min(?::text)", point.id),
|
||||
longitude: min(point.public_longitude),
|
||||
latitude: min(point.public_latitude),
|
||||
west: min(point.public_longitude),
|
||||
south: min(point.public_latitude),
|
||||
east: max(point.public_longitude),
|
||||
north: max(point.public_latitude),
|
||||
cell_x:
|
||||
fragment(
|
||||
"floor((ST_X(ST_Transform(?, 3857)) + ?) / ?)::bigint",
|
||||
point.public_location,
|
||||
constant(^world_half),
|
||||
constant(^cell_size)
|
||||
selected_as(
|
||||
fragment(
|
||||
"floor(((? + 180.0) / 360.0) * ?)::bigint",
|
||||
point.public_longitude,
|
||||
constant(^cell_count)
|
||||
),
|
||||
:cell_x
|
||||
),
|
||||
cell_y:
|
||||
fragment(
|
||||
"floor((ST_Y(ST_Transform(?, 3857)) + ?) / ?)::bigint",
|
||||
point.public_location,
|
||||
constant(^world_half),
|
||||
constant(^cell_size)
|
||||
selected_as(
|
||||
fragment(
|
||||
"floor(((ln(tan(pi() / 4.0 + radians(least(85.05112878, greatest(-85.05112878, ?))) / 2.0)) / pi() + 1.0) / 2.0) * ?)::bigint",
|
||||
point.public_latitude,
|
||||
constant(^cell_count)
|
||||
),
|
||||
:cell_y
|
||||
)
|
||||
})
|
||||
|> order_by([point],
|
||||
asc:
|
||||
fragment(
|
||||
"floor((ST_X(ST_Transform(?, 3857)) + ?) / ?)::bigint",
|
||||
point.public_location,
|
||||
constant(^world_half),
|
||||
constant(^cell_size)
|
||||
),
|
||||
asc:
|
||||
fragment(
|
||||
"floor((ST_Y(ST_Transform(?, 3857)) + ?) / ?)::bigint",
|
||||
point.public_location,
|
||||
constant(^world_half),
|
||||
constant(^cell_size)
|
||||
)
|
||||
)
|
||||
|> order_by([_point], asc: selected_as(:cell_x), asc: selected_as(:cell_y))
|
||||
|> Repo.all()
|
||||
|> hydrate_request_map_singletons()
|
||||
|> Enum.map(&map_discovery_item(&1, level))
|
||||
end
|
||||
|
||||
|
|
@ -227,21 +166,7 @@ defmodule WhoNeedHelp.Help do
|
|||
(
|
||||
? && ST_MakeEnvelope(?, ?, ?, ?, 4326)
|
||||
AND
|
||||
ST_Intersects(
|
||||
CASE
|
||||
WHEN ? @> '{"scale_fixture": true}'::jsonb THEN ?
|
||||
WHEN ? = 'exact_for_active_match' THEN
|
||||
ST_SetSRID(
|
||||
ST_MakePoint(
|
||||
round(ST_X(?)::numeric, 2)::double precision,
|
||||
round(ST_Y(?)::numeric, 2)::double precision
|
||||
),
|
||||
4326
|
||||
)
|
||||
ELSE ?
|
||||
END,
|
||||
ST_MakeEnvelope(?, ?, ?, ?, 4326)
|
||||
)
|
||||
? >= ? AND ? <= ? AND ? >= ? AND ? <= ?
|
||||
)
|
||||
""",
|
||||
request.location,
|
||||
|
|
@ -249,15 +174,13 @@ defmodule WhoNeedHelp.Help do
|
|||
^(south - 0.005),
|
||||
^(east + 0.005),
|
||||
^(north + 0.005),
|
||||
request.structured_data,
|
||||
request.location,
|
||||
request.location_visibility,
|
||||
request.location,
|
||||
request.location,
|
||||
request.location,
|
||||
request.public_longitude,
|
||||
^west,
|
||||
^south,
|
||||
request.public_longitude,
|
||||
^east,
|
||||
request.public_latitude,
|
||||
^south,
|
||||
request.public_latitude,
|
||||
^north
|
||||
)
|
||||
)
|
||||
|
|
@ -1273,7 +1196,7 @@ defmodule WhoNeedHelp.Help do
|
|||
location: row.location_label,
|
||||
latitude: row.latitude,
|
||||
longitude: row.longitude,
|
||||
exact: row.location_visibility == "exact_public",
|
||||
exact: row.location_visibility in ["exact_public", :exact_public],
|
||||
radius_meters: row.radius_meters
|
||||
}
|
||||
end
|
||||
|
|
@ -1290,12 +1213,12 @@ defmodule WhoNeedHelp.Help do
|
|||
hierarchy_level: level,
|
||||
point_count: row.count,
|
||||
expansion_zoom:
|
||||
DiscoveryCluster.expansion_zoom(
|
||||
DiscoveryCluster.expansion_zoom_for_bounds(
|
||||
level,
|
||||
row.west_m,
|
||||
row.south_m,
|
||||
row.east_m,
|
||||
row.north_m
|
||||
row.west,
|
||||
row.south,
|
||||
row.east,
|
||||
row.north
|
||||
),
|
||||
count: row.count,
|
||||
urgent_count: row.urgent_count,
|
||||
|
|
@ -1310,6 +1233,41 @@ defmodule WhoNeedHelp.Help do
|
|||
}
|
||||
end
|
||||
|
||||
defp hydrate_request_map_singletons(rows) do
|
||||
singleton_ids =
|
||||
rows
|
||||
|> Enum.filter(&(&1.count == 1))
|
||||
|> Enum.map(& &1.request_id)
|
||||
|
||||
details = request_map_singleton_details(singleton_ids)
|
||||
|
||||
Enum.map(rows, fn
|
||||
%{count: 1, request_id: request_id} = row ->
|
||||
Map.merge(row, Map.fetch!(details, request_id))
|
||||
|
||||
row ->
|
||||
row
|
||||
end)
|
||||
end
|
||||
|
||||
defp request_map_singleton_details([]), do: %{}
|
||||
|
||||
defp request_map_singleton_details(singleton_ids) do
|
||||
HelpRequest
|
||||
|> where([request], request.id in ^singleton_ids)
|
||||
|> select([request], {
|
||||
request.id,
|
||||
%{
|
||||
title: request.title,
|
||||
location_label: request.location_label,
|
||||
location_visibility: request.location_visibility,
|
||||
radius_meters: request.location_radius_meters
|
||||
}
|
||||
})
|
||||
|> Repo.all()
|
||||
|> Map.new(fn {id, detail} -> {to_string(id), detail} end)
|
||||
end
|
||||
|
||||
defp normalize_urgency(value) when value in [:now, "now"], do: {:ok, :now}
|
||||
defp normalize_urgency(value) when value in [:today, "today"], do: {:ok, :today}
|
||||
defp normalize_urgency(value) when value in [:scheduled, "scheduled"], do: {:ok, :scheduled}
|
||||
|
|
|
|||
|
|
@ -124,6 +124,27 @@ defmodule WhoNeedHelp.Help.DiscoveryCluster do
|
|||
|> clamp(0, @maximum_map_zoom)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the first expansion zoom for member bounds expressed as WGS84
|
||||
longitude and latitude values.
|
||||
|
||||
Discovery aggregation already calculates geographic bounds. Projecting the
|
||||
handful of returned clusters in Elixir avoids repeating `ST_Transform` for
|
||||
every matching database row while preserving the same Web Mercator
|
||||
hierarchy.
|
||||
"""
|
||||
def expansion_zoom_for_bounds(level, west, south, east, north)
|
||||
when is_integer(level) and is_number(west) and is_number(south) and is_number(east) and
|
||||
is_number(north) do
|
||||
expansion_zoom(
|
||||
level,
|
||||
longitude_to_mercator_x(west),
|
||||
latitude_to_mercator_y(south),
|
||||
longitude_to_mercator_x(east),
|
||||
latitude_to_mercator_y(north)
|
||||
)
|
||||
end
|
||||
|
||||
defp cell_index(coordinate, level) do
|
||||
coordinate
|
||||
|> Kernel.+(@world_half_meters)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ defmodule WhoNeedHelp.Help.HelpRequest do
|
|||
field :structured_data, :map, default: %{}
|
||||
field :location_label, :string
|
||||
field :location, Geo.PostGIS.Geometry
|
||||
field :public_longitude, :float
|
||||
field :public_latitude, :float
|
||||
field :location_radius_meters, :integer, default: 1_000
|
||||
|
||||
field :status, Ecto.Enum,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do
|
|||
|> assign(:discovery_mode, "nearby")
|
||||
|> assign(:view_mode, "split")
|
||||
|> assign(:view_explicit, false)
|
||||
|> assign(:map_update_mode, "manual")
|
||||
|> assign(:map_update_mode, "auto")
|
||||
|> assign(:viewport, nil)
|
||||
|> assign(:pending_viewport, nil)
|
||||
|> assign(:activities, [])
|
||||
|
|
@ -402,7 +402,7 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do
|
|||
defp normalize_view_mode(_view_mode), do: "split"
|
||||
|
||||
defp normalize_map_update_mode(mode) when mode in @map_update_modes, do: mode
|
||||
defp normalize_map_update_mode(_mode), do: "manual"
|
||||
defp normalize_map_update_mode(_mode), do: "auto"
|
||||
|
||||
defp activity_path(socket, overrides) do
|
||||
filters = Keyword.get(overrides, :filters, socket.assigns.filters)
|
||||
|
|
@ -427,8 +427,8 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do
|
|||
)
|
||||
)
|
||||
|> then(fn params ->
|
||||
if map_update_mode == "auto",
|
||||
do: Map.put(params, "map_update", "auto"),
|
||||
if map_update_mode == "manual",
|
||||
do: Map.put(params, "map_update", "manual"),
|
||||
else: params
|
||||
end)
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do
|
|||
|> assign(:discovery_mode, "nearby")
|
||||
|> assign(:view_mode, "split")
|
||||
|> assign(:view_explicit, false)
|
||||
|> assign(:map_update_mode, "manual")
|
||||
|> assign(:map_update_mode, "auto")
|
||||
|> assign(:viewport, nil)
|
||||
|> assign(:pending_viewport, nil)
|
||||
|> assign(:discovery_topics, [])
|
||||
|
|
@ -379,7 +379,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do
|
|||
defp normalize_view_mode(_view_mode), do: "split"
|
||||
|
||||
defp normalize_map_update_mode(mode) when mode in @map_update_modes, do: mode
|
||||
defp normalize_map_update_mode(_mode), do: "manual"
|
||||
defp normalize_map_update_mode(_mode), do: "auto"
|
||||
|
||||
defp normalize_discovery_mode(discovery_mode) when discovery_mode in @discovery_modes,
|
||||
do: discovery_mode
|
||||
|
|
@ -413,8 +413,8 @@ defmodule WhoNeedHelpWeb.RequestLive.Index do
|
|||
)
|
||||
)
|
||||
|> then(fn params ->
|
||||
if map_update_mode == "auto",
|
||||
do: Map.put(params, "map_update", "auto"),
|
||||
if map_update_mode == "manual",
|
||||
do: Map.put(params, "map_update", "manual"),
|
||||
else: params
|
||||
end)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
defmodule WhoNeedHelp.Repo.Migrations.AddPublicDiscoveryCoordinates do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:help_requests) do
|
||||
add :public_longitude, :float,
|
||||
generated: """
|
||||
ALWAYS AS (
|
||||
CASE
|
||||
WHEN location IS NULL OR location_visibility = 'hidden' THEN NULL
|
||||
WHEN structured_data @> '{"scale_fixture": true}'::jsonb THEN ST_X(location)
|
||||
WHEN location_visibility = 'exact_for_active_match' THEN
|
||||
round(ST_X(location)::numeric, 2)::double precision
|
||||
ELSE ST_X(location)
|
||||
END
|
||||
) STORED
|
||||
"""
|
||||
|
||||
add :public_latitude, :float,
|
||||
generated: """
|
||||
ALWAYS AS (
|
||||
CASE
|
||||
WHEN location IS NULL OR location_visibility = 'hidden' THEN NULL
|
||||
WHEN structured_data @> '{"scale_fixture": true}'::jsonb THEN ST_Y(location)
|
||||
WHEN location_visibility = 'exact_for_active_match' THEN
|
||||
round(ST_Y(location)::numeric, 2)::double precision
|
||||
ELSE ST_Y(location)
|
||||
END
|
||||
) STORED
|
||||
"""
|
||||
end
|
||||
|
||||
alter table(:activities) do
|
||||
add :public_longitude, :float,
|
||||
generated: """
|
||||
ALWAYS AS (
|
||||
CASE
|
||||
WHEN location IS NULL OR location_visibility = 'hidden' THEN NULL
|
||||
WHEN structured_data @> '{"scale_fixture": true}'::jsonb THEN ST_X(location)
|
||||
ELSE round(ST_X(location)::numeric, 2)::double precision
|
||||
END
|
||||
) STORED
|
||||
"""
|
||||
|
||||
add :public_latitude, :float,
|
||||
generated: """
|
||||
ALWAYS AS (
|
||||
CASE
|
||||
WHEN location IS NULL OR location_visibility = 'hidden' THEN NULL
|
||||
WHEN structured_data @> '{"scale_fixture": true}'::jsonb THEN ST_Y(location)
|
||||
ELSE round(ST_Y(location)::numeric, 2)::double precision
|
||||
END
|
||||
) STORED
|
||||
"""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -76,4 +76,22 @@ defmodule WhoNeedHelp.Help.DiscoveryClusterTest do
|
|||
assert DiscoveryCluster.expansion_zoom(level, west, south, west, south) == 22
|
||||
assert DiscoveryCluster.expansion_zoom(24, west, south, west, south) == 22
|
||||
end
|
||||
|
||||
test "geographic bounds preserve Web Mercator expansion levels" do
|
||||
assert DiscoveryCluster.expansion_zoom_for_bounds(
|
||||
7,
|
||||
30.50,
|
||||
50.40,
|
||||
30.60,
|
||||
50.50
|
||||
) == 8
|
||||
|
||||
assert DiscoveryCluster.expansion_zoom_for_bounds(
|
||||
7,
|
||||
30.50,
|
||||
50.40,
|
||||
30.50,
|
||||
50.40
|
||||
) == 22
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -385,7 +385,8 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do
|
|||
} do
|
||||
category = Catalog.seed_defaults()
|
||||
{:ok, request} = Help.create_request(scope, request_attrs(category))
|
||||
{:ok, view, _html} = live(conn, requests_in_kyiv_path())
|
||||
manual_path = requests_in_kyiv_path() <> "&map_update=manual"
|
||||
{:ok, view, _html} = live(conn, manual_path)
|
||||
|
||||
render_async(view)
|
||||
|
||||
|
|
@ -413,7 +414,7 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do
|
|||
|
||||
assert_patch(
|
||||
view,
|
||||
~p"/requests?#{Map.merge(moved, %{"east" => "30.6", "north" => "50.5", "south" => "50.4", "view" => "split", "west" => "30.5"})}"
|
||||
~p"/requests?#{Map.merge(moved, %{"east" => "30.6", "map_update" => "manual", "north" => "50.5", "south" => "50.4", "view" => "split", "west" => "30.5"})}"
|
||||
)
|
||||
|
||||
render_async(view)
|
||||
|
|
@ -421,7 +422,7 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do
|
|||
|
||||
test "request discovery can update automatically after map movement", %{conn: conn} do
|
||||
path =
|
||||
~p"/requests?#{%{west: 30, south: 50, east: 31, north: 51, zoom: 10, width: 1200, height: 720, view: "split", map_update: "auto"}}"
|
||||
~p"/requests?#{%{west: 30, south: 50, east: 31, north: 51, zoom: 10, width: 1200, height: 720, view: "split"}}"
|
||||
|
||||
{:ok, view, _html} = live(conn, path)
|
||||
render_async(view)
|
||||
|
|
@ -444,7 +445,7 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do
|
|||
|
||||
assert_patch(
|
||||
view,
|
||||
~p"/requests?#{Map.merge(moved, %{"east" => "30.6", "map_update" => "auto", "north" => "50.5", "south" => "50.4", "view" => "split", "west" => "30.5"})}",
|
||||
~p"/requests?#{Map.merge(moved, %{"east" => "30.6", "north" => "50.5", "south" => "50.4", "view" => "split", "west" => "30.5"})}",
|
||||
1_000
|
||||
)
|
||||
|
||||
|
|
@ -454,6 +455,8 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do
|
|||
test "activity cluster expansion immediately searches the expanded viewport", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, activities_in_kyiv_path())
|
||||
|
||||
assert has_element?(view, "#activity-map-auto-update[checked]")
|
||||
|
||||
expanded = %{
|
||||
"west" => "30.40",
|
||||
"south" => "50.35",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user