Optimize viewport-based loading with zoom-aware batching
Key improvements: - Fix spatial query order: Apply bounds filtering BEFORE limit/offset - Add zoom-level based batch sizing (20-100 packets per batch) - Dynamic batch count based on zoom level (2-5 batches) - Faster loading delays for high zoom levels (25ms vs 50ms) - Include zoom level in cache keys for better cache efficiency Performance benefits: - Zoomed in far (zoom 15+): 2 batches of 20 packets each - Moderately zoomed (zoom 12-14): 3 batches of 35 packets each - Medium zoom (zoom 8-11): 4 batches of 50 packets each - Zoomed out (zoom <8): 5 batches of 75-100 packets each This ensures packets are loaded from the current viewport first, dramatically reducing load times when zoomed in. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
330fe27eec
commit
7318d2dfbc
2 changed files with 60 additions and 15 deletions
|
|
@ -420,22 +420,23 @@ defmodule Aprsme.Packets do
|
|||
limit = Map.get(opts, :limit, 200)
|
||||
offset = Map.get(opts, :offset, 0)
|
||||
|
||||
# Use a more efficient query that leverages the partial indexes
|
||||
# Order by received_at DESC to get the most recent packets first
|
||||
# Build base query with time and position filters
|
||||
base_query =
|
||||
from(p in Packet,
|
||||
where: p.has_position == true,
|
||||
where: p.received_at >= ^one_hour_ago,
|
||||
order_by: [desc: p.received_at],
|
||||
limit: ^limit,
|
||||
offset: ^offset
|
||||
where: p.received_at >= ^one_hour_ago
|
||||
)
|
||||
|
||||
# Apply spatial and other filters BEFORE limiting
|
||||
# This ensures we get the most recent packets within the specified bounds
|
||||
query =
|
||||
base_query
|
||||
|> filter_by_region(opts)
|
||||
|> filter_by_callsign(opts)
|
||||
|> filter_by_map_bounds(opts)
|
||||
|> order_by(desc: :received_at)
|
||||
|> limit(^limit)
|
||||
|> offset(^offset)
|
||||
|> select_with_virtual_coordinates()
|
||||
|
||||
Repo.all(query)
|
||||
|
|
|
|||
|
|
@ -1173,10 +1173,14 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
historical_packets =
|
||||
if packets_module == Aprsme.Packets do
|
||||
# Use cached queries for better performance
|
||||
# Include zoom level in cache key for better cache efficiency
|
||||
zoom = socket.assigns.map_zoom || 5
|
||||
|
||||
Aprsme.CachedQueries.get_recent_packets_cached(%{
|
||||
bounds: bounds,
|
||||
# Reduced limit for faster initial load
|
||||
limit: 200
|
||||
limit: 200,
|
||||
zoom: zoom
|
||||
})
|
||||
else
|
||||
# Fallback for testing
|
||||
|
|
@ -1197,20 +1201,55 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
# Progressive loading functions using LiveView's efficient update mechanisms
|
||||
@spec start_progressive_historical_loading(Socket.t()) :: Socket.t()
|
||||
defp start_progressive_historical_loading(socket) do
|
||||
# Calculate optimal batch count based on zoom level
|
||||
zoom = socket.assigns.map_zoom || 5
|
||||
total_batches = calculate_batch_count_for_zoom(zoom)
|
||||
|
||||
# Start with a small batch for immediate visual feedback
|
||||
socket =
|
||||
socket
|
||||
|> assign(loading_batch: 0, total_batches: 4)
|
||||
|> assign(loading_batch: 0, total_batches: total_batches)
|
||||
|> load_historical_batch(0)
|
||||
|
||||
# Schedule next batches using LiveView's async processing
|
||||
Process.send_after(self(), {:load_historical_batch, 1}, 50)
|
||||
Process.send_after(self(), {:load_historical_batch, 2}, 150)
|
||||
Process.send_after(self(), {:load_historical_batch, 3}, 300)
|
||||
# Use shorter delays for higher zoom levels (fewer batches)
|
||||
base_delay = if zoom >= 12, do: 25, else: 50
|
||||
|
||||
Enum.each(1..(total_batches - 1), fn batch_index ->
|
||||
delay = base_delay * (batch_index * 2)
|
||||
# Progressive delays: 50ms, 100ms, 150ms, etc.
|
||||
Process.send_after(self(), {:load_historical_batch, batch_index}, delay)
|
||||
end)
|
||||
|
||||
socket
|
||||
end
|
||||
|
||||
# Calculate optimal batch size based on zoom level
|
||||
# Higher zoom = smaller viewport = fewer packets needed = smaller batches for faster response
|
||||
@spec calculate_batch_size_for_zoom(integer()) :: integer()
|
||||
# Very zoomed in - small batches
|
||||
defp calculate_batch_size_for_zoom(zoom) when zoom >= 15, do: 20
|
||||
# Moderately zoomed in
|
||||
defp calculate_batch_size_for_zoom(zoom) when zoom >= 12, do: 35
|
||||
# Medium zoom
|
||||
defp calculate_batch_size_for_zoom(zoom) when zoom >= 8, do: 50
|
||||
# Zoomed out
|
||||
defp calculate_batch_size_for_zoom(zoom) when zoom >= 5, do: 75
|
||||
# Very zoomed out
|
||||
defp calculate_batch_size_for_zoom(_), do: 100
|
||||
|
||||
# Calculate optimal number of batches based on zoom level
|
||||
# Higher zoom = fewer batches needed since viewport is smaller
|
||||
@spec calculate_batch_count_for_zoom(integer()) :: integer()
|
||||
# Very zoomed in - fewer batches
|
||||
defp calculate_batch_count_for_zoom(zoom) when zoom >= 15, do: 2
|
||||
# Moderately zoomed in
|
||||
defp calculate_batch_count_for_zoom(zoom) when zoom >= 12, do: 3
|
||||
# Medium zoom
|
||||
defp calculate_batch_count_for_zoom(zoom) when zoom >= 8, do: 4
|
||||
# Zoomed out - more batches
|
||||
defp calculate_batch_count_for_zoom(_), do: 5
|
||||
|
||||
@spec load_historical_batch(Socket.t(), integer()) :: Socket.t()
|
||||
defp load_historical_batch(socket, batch_offset) do
|
||||
if socket.assigns.map_bounds do
|
||||
|
|
@ -1221,8 +1260,9 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
socket.assigns.map_bounds.north
|
||||
]
|
||||
|
||||
# Load smaller batches with offset for progressive loading
|
||||
batch_size = 50
|
||||
# Calculate zoom-based batch size - higher zoom = smaller batches for faster response
|
||||
zoom = socket.assigns.map_zoom || 5
|
||||
batch_size = calculate_batch_size_for_zoom(zoom)
|
||||
offset = batch_offset * batch_size
|
||||
|
||||
packets_module = Application.get_env(:aprsme, :packets_module, Aprsme.Packets)
|
||||
|
|
@ -1230,10 +1270,12 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
historical_packets =
|
||||
if packets_module == Aprsme.Packets do
|
||||
# Use cached queries for better performance
|
||||
# Include zoom level in cache key for better cache efficiency
|
||||
Aprsme.CachedQueries.get_recent_packets_cached(%{
|
||||
bounds: bounds,
|
||||
limit: batch_size,
|
||||
offset: offset
|
||||
offset: offset,
|
||||
zoom: zoom
|
||||
})
|
||||
else
|
||||
# Fallback for testing
|
||||
|
|
@ -1250,11 +1292,13 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
|
||||
if Enum.any?(packet_data_list) do
|
||||
# Use LiveView's efficient push_event for incremental updates
|
||||
total_batches = socket.assigns.total_batches || 4
|
||||
|
||||
socket =
|
||||
push_event(socket, "add_historical_packets_batch", %{
|
||||
packets: packet_data_list,
|
||||
batch: batch_offset,
|
||||
is_final: batch_offset >= 3
|
||||
is_final: batch_offset >= total_batches - 1
|
||||
})
|
||||
|
||||
# Update progress for user feedback
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue