This commit is contained in:
Graham McIntire 2025-07-09 16:22:58 -05:00
parent 514d1b66d5
commit ef95f597b0
No known key found for this signature in database
6 changed files with 180 additions and 142 deletions

View file

@ -1,13 +1,13 @@
defmodule AprsmeWeb.AprsSymbol do defmodule AprsmeWeb.AprsSymbol do
@moduledoc """ @moduledoc """
Shared library for APRS symbol handling and rendering. Shared library for APRS symbol handling and rendering.
This module provides centralized functions for: This module provides centralized functions for:
- Symbol table and code normalization - Symbol table and code normalization
- Sprite file mapping - Sprite file mapping
- Symbol positioning calculations - Symbol positioning calculations
- HTML generation for symbols - HTML generation for symbols
All APRS symbol logic should use this module to ensure consistency All APRS symbol logic should use this module to ensure consistency
across the application. across the application.
""" """
@ -15,9 +15,9 @@ defmodule AprsmeWeb.AprsSymbol do
@doc """ @doc """
Gets sprite information for a given symbol table and code. Gets sprite information for a given symbol table and code.
Returns a map with sprite_file, background_position, and background_size. Returns a map with sprite_file, background_position, and background_size.
## Examples ## Examples
iex> AprsmeWeb.AprsSymbol.get_sprite_info("/", "_") iex> AprsmeWeb.AprsSymbol.get_sprite_info("/", "_")
%{ %{
sprite_file: "/aprs-symbols/aprs-symbols-128-0@2x.png", sprite_file: "/aprs-symbols/aprs-symbols-128-0@2x.png",
@ -34,21 +34,22 @@ defmodule AprsmeWeb.AprsSymbol do
# Normal symbol table processing # Normal symbol table processing
symbol_table = normalize_symbol_table(symbol_table) symbol_table = normalize_symbol_table(symbol_table)
symbol_code = normalize_symbol_code(symbol_code) symbol_code = normalize_symbol_code(symbol_code)
# Map symbol table to sprite file ID # Map symbol table to sprite file ID
table_id = get_table_id(symbol_table) table_id = get_table_id(symbol_table)
sprite_file = "/aprs-symbols/aprs-symbols-128-#{table_id}@2x.png" sprite_file = "/aprs-symbols/aprs-symbols-128-#{table_id}@2x.png"
# Get symbol position using ASCII-based calculation # Get symbol position using ASCII-based calculation
symbol_code_ord = symbol_code symbol_code_ord =
|> String.to_charlist() symbol_code
|> List.first() |> String.to_charlist()
|> (fn c -> if is_integer(c), do: c, else: 63 end).() |> List.first()
|> then(fn c -> if is_integer(c), do: c, else: 63 end)
index = symbol_code_ord - 33 index = symbol_code_ord - 33
safe_index = max(0, min(index, 93)) safe_index = max(0, min(index, 93))
# Calculate positioning for 16-column grid # Calculate positioning for 16-column grid
column = rem(safe_index, 16) column = rem(safe_index, 16)
row = div(safe_index, 16) row = div(safe_index, 16)
@ -72,16 +73,17 @@ defmodule AprsmeWeb.AprsSymbol do
# Check which table to use based on the symbol code # Check which table to use based on the symbol code
table_id = get_overlay_base_table_id(base_symbol_code) table_id = get_overlay_base_table_id(base_symbol_code)
sprite_file = "/aprs-symbols/aprs-symbols-128-#{table_id}@2x.png" sprite_file = "/aprs-symbols/aprs-symbols-128-#{table_id}@2x.png"
# Get position of the base symbol in the appropriate table # Get position of the base symbol in the appropriate table
base_symbol_ord = base_symbol_code base_symbol_ord =
|> String.to_charlist() base_symbol_code
|> List.first() |> String.to_charlist()
|> (fn c -> if is_integer(c), do: c, else: 63 end).() |> List.first()
|> then(fn c -> if is_integer(c), do: c, else: 63 end)
index = base_symbol_ord - 33 index = base_symbol_ord - 33
safe_index = max(0, min(index, 93)) safe_index = max(0, min(index, 93))
# Calculate positioning for 16-column grid # Calculate positioning for 16-column grid
column = rem(safe_index, 16) column = rem(safe_index, 16)
row = div(safe_index, 16) row = div(safe_index, 16)
@ -104,15 +106,21 @@ defmodule AprsmeWeb.AprsSymbol do
# Most overlay symbols are in the alternate table (1) # Most overlay symbols are in the alternate table (1)
case base_symbol_code do case base_symbol_code do
# Digipeater symbols are often in the alternate table (1) and have colored backgrounds # Digipeater symbols are often in the alternate table (1) and have colored backgrounds
"#" -> "1" # Digipeater - green star background # Digipeater - green star background
"a" -> "1" # Diamond shape - APRS overlay symbol (alternate table) "#" -> "1"
"A" -> "1" # Square shape - APRS overlay symbol (alternate table) # Diamond shape - APRS overlay symbol (alternate table)
"&" -> "1" # Diamond shape - alternate table "a" -> "1"
">" -> "1" # Arrow symbols # Square shape - APRS overlay symbol (alternate table)
"A" -> "1"
# Diamond shape - alternate table
"&" -> "1"
# Arrow symbols
">" -> "1"
"<" -> "1" "<" -> "1"
"^" -> "1" "^" -> "1"
"v" -> "1" "v" -> "1"
"i" -> "1" # Black square background - alternate table # Black square background - alternate table
"i" -> "1"
# Most other symbols that can be overlaid are in the alternate table # Most other symbols that can be overlaid are in the alternate table
_ -> "1" _ -> "1"
end end
@ -125,16 +133,17 @@ defmodule AprsmeWeb.AprsSymbol do
def get_overlay_character_sprite_info(overlay_char) do def get_overlay_character_sprite_info(overlay_char) do
# Use overlay table (table 2) for the overlay character # Use overlay table (table 2) for the overlay character
sprite_file = "/aprs-symbols/aprs-symbols-128-2@2x.png" sprite_file = "/aprs-symbols/aprs-symbols-128-2@2x.png"
# Get position of the overlay character in the overlay table # Get position of the overlay character in the overlay table
overlay_char_ord = overlay_char overlay_char_ord =
|> String.to_charlist() overlay_char
|> List.first() |> String.to_charlist()
|> (fn c -> if is_integer(c), do: c, else: 63 end).() |> List.first()
|> then(fn c -> if is_integer(c), do: c, else: 63 end)
index = overlay_char_ord - 33 index = overlay_char_ord - 33
safe_index = max(0, min(index, 93)) safe_index = max(0, min(index, 93))
# Calculate positioning for 16-column grid # Calculate positioning for 16-column grid
column = rem(safe_index, 16) column = rem(safe_index, 16)
row = div(safe_index, 16) row = div(safe_index, 16)
@ -150,9 +159,9 @@ defmodule AprsmeWeb.AprsSymbol do
@doc """ @doc """
Normalizes a symbol table identifier. Normalizes a symbol table identifier.
## Examples ## Examples
iex> AprsmeWeb.AprsSymbol.normalize_symbol_table("/") iex> AprsmeWeb.AprsSymbol.normalize_symbol_table("/")
"/" "/"
@ -172,9 +181,9 @@ defmodule AprsmeWeb.AprsSymbol do
@doc """ @doc """
Normalizes a symbol code. Normalizes a symbol code.
## Examples ## Examples
iex> AprsmeWeb.AprsSymbol.normalize_symbol_code("_") iex> AprsmeWeb.AprsSymbol.normalize_symbol_code("_")
"_" "_"
@ -190,9 +199,9 @@ defmodule AprsmeWeb.AprsSymbol do
@doc """ @doc """
Maps a symbol table to its sprite file ID. Maps a symbol table to its sprite file ID.
## Examples ## Examples
iex> AprsmeWeb.AprsSymbol.get_table_id("/") iex> AprsmeWeb.AprsSymbol.get_table_id("/")
"0" "0"
@ -204,59 +213,64 @@ defmodule AprsmeWeb.AprsSymbol do
""" """
def get_table_id(symbol_table) do def get_table_id(symbol_table) do
case symbol_table do case symbol_table do
"/" -> "0" # Primary table # Primary table
"\\" -> "1" # Alternate table "/" -> "0"
"]" -> "2" # Overlay table (A-Z, 0-9) # Alternate table
_ -> "0" # Default to primary table "\\" -> "1"
# Overlay table (A-Z, 0-9)
"]" -> "2"
# Default to primary table
_ -> "0"
end end
end end
@doc """ @doc """
Renders an APRS symbol as HTML for use in Leaflet markers. Renders an APRS symbol as HTML for use in Leaflet markers.
Returns HTML string that can be used as marker content. Returns HTML string that can be used as marker content.
## Examples ## Examples
iex> AprsmeWeb.AprsSymbol.render_marker_html("/", "_", "W1AW") iex> AprsmeWeb.AprsSymbol.render_marker_html("/", "_", "W1AW")
"<div style=\"position: relative; width: 32px; height: 32px; display: flex; align-items: center;\">..." "<div style=\"position: relative; width: 32px; height: 32px; display: flex; align-items: center;\">..."
""" """
def render_marker_html(symbol_table, symbol_code, callsign \\ nil, size \\ 32) do def render_marker_html(symbol_table, symbol_code, callsign \\ nil, size \\ 32) do
sprite_info = get_sprite_info(symbol_table, symbol_code) sprite_info = get_sprite_info(symbol_table, symbol_code)
# Check if this is an overlay symbol # Check if this is an overlay symbol
is_overlay = symbol_table && String.match?(symbol_table, ~r/^[A-Z0-9]$/) is_overlay = symbol_table && String.match?(symbol_table, ~r/^[A-Z0-9]$/)
symbol_html = if is_overlay do symbol_html =
# For overlay symbols, we need both the base symbol background and the overlay character if is_overlay do
overlay_sprite_info = get_overlay_character_sprite_info(symbol_table) # For overlay symbols, we need both the base symbol background and the overlay character
overlay_sprite_info = get_overlay_character_sprite_info(symbol_table)
"""
<div style=" """
position: relative; <div style="
width: #{size}px; position: relative;
height: #{size}px; width: #{size}px;
background-image: url(#{overlay_sprite_info.sprite_file}), url(#{sprite_info.sprite_file}); height: #{size}px;
background-position: #{overlay_sprite_info.background_position}, #{sprite_info.background_position}; background-image: url(#{overlay_sprite_info.sprite_file}), url(#{sprite_info.sprite_file});
background-size: #{overlay_sprite_info.background_size}, #{sprite_info.background_size}; background-position: #{overlay_sprite_info.background_position}, #{sprite_info.background_position};
background-repeat: no-repeat, no-repeat; background-size: #{overlay_sprite_info.background_size}, #{sprite_info.background_size};
image-rendering: pixelated; background-repeat: no-repeat, no-repeat;
" title="#{symbol_table}#{symbol_code}"> image-rendering: pixelated;
</div> " title="#{symbol_table}#{symbol_code}">
""" </div>
else """
""" else
<div style=" """
width: #{size}px; <div style="
height: #{size}px; width: #{size}px;
background-image: url(#{sprite_info.sprite_file}); height: #{size}px;
background-position: #{sprite_info.background_position}; background-image: url(#{sprite_info.sprite_file});
background-size: #{sprite_info.background_size}; background-position: #{sprite_info.background_position};
background-repeat: no-repeat; background-size: #{sprite_info.background_size};
image-rendering: pixelated; background-repeat: no-repeat;
" title="#{symbol_table}#{symbol_code}"></div> image-rendering: pixelated;
""" " title="#{symbol_table}#{symbol_code}"></div>
end """
end
if callsign do if callsign do
""" """
<div style="position: relative; width: #{size}px; height: #{size}px; display: flex; align-items: center;"> <div style="position: relative; width: #{size}px; height: #{size}px; display: flex; align-items: center;">
@ -289,23 +303,23 @@ defmodule AprsmeWeb.AprsSymbol do
@doc """ @doc """
Renders an APRS symbol as a style string for use in templates. Renders an APRS symbol as a style string for use in templates.
Returns a CSS style string that can be used directly in HTML. Returns a CSS style string that can be used directly in HTML.
## Examples ## Examples
iex> AprsmeWeb.AprsSymbol.render_style("/", "_", 32) iex> AprsmeWeb.AprsSymbol.render_style("/", "_", 32)
"width: 32px; height: 32px; background-image: url(/aprs-symbols/aprs-symbols-128-0@2x.png); ..." "width: 32px; height: 32px; background-image: url(/aprs-symbols/aprs-symbols-128-0@2x.png); ..."
""" """
def render_style(symbol_table, symbol_code, size \\ 32) do def render_style(symbol_table, symbol_code, size \\ 32) do
sprite_info = get_sprite_info(symbol_table, symbol_code) sprite_info = get_sprite_info(symbol_table, symbol_code)
"width: #{size}px; height: #{size}px; background-image: url(#{sprite_info.sprite_file}); background-position: #{sprite_info.background_position}; background-size: #{sprite_info.background_size}; background-repeat: no-repeat; image-rendering: pixelated; opacity: 1.0; display: inline-block; vertical-align: middle; margin-bottom: -6px;" "width: #{size}px; height: #{size}px; background-image: url(#{sprite_info.sprite_file}); background-position: #{sprite_info.background_position}; background-size: #{sprite_info.background_size}; background-repeat: no-repeat; image-rendering: pixelated; opacity: 1.0; display: inline-block; vertical-align: middle; margin-bottom: -6px;"
end end
@doc """ @doc """
Extracts symbol information from a packet with fallbacks. Extracts symbol information from a packet with fallbacks.
## Examples ## Examples
iex> AprsmeWeb.AprsSymbol.extract_from_packet(%{symbol_table_id: "/", symbol_code: "_"}) iex> AprsmeWeb.AprsSymbol.extract_from_packet(%{symbol_table_id: "/", symbol_code: "_"})
{"/", "_"} {"/", "_"}
@ -315,7 +329,7 @@ defmodule AprsmeWeb.AprsSymbol do
def extract_from_packet(packet) do def extract_from_packet(packet) do
symbol_table_id = get_packet_field(packet, :symbol_table_id, "/") symbol_table_id = get_packet_field(packet, :symbol_table_id, "/")
symbol_code = get_packet_field(packet, :symbol_code, ">") symbol_code = get_packet_field(packet, :symbol_code, ">")
{symbol_table_id, symbol_code} {symbol_table_id, symbol_code}
end end
@ -329,4 +343,4 @@ defmodule AprsmeWeb.AprsSymbol do
Map.get(data_extended, to_string(field)) || Map.get(data_extended, to_string(field)) ||
default default
end end
end end

View file

@ -91,7 +91,6 @@ defmodule AprsmeWeb.SymbolRenderer do
AprsmeWeb.AprsSymbol.get_sprite_info(symbol_table, symbol_code) AprsmeWeb.AprsSymbol.get_sprite_info(symbol_table, symbol_code)
end end
@doc """ @doc """
Renders an APRS symbol for use in Leaflet markers. Renders an APRS symbol for use in Leaflet markers.
Returns HTML string that can be used as marker content. Returns HTML string that can be used as marker content.

View file

@ -4,8 +4,8 @@ defmodule AprsmeWeb.InfoLive.Show do
use Gettext, backend: AprsmeWeb.Gettext use Gettext, backend: AprsmeWeb.Gettext
alias Aprsme.Packets alias Aprsme.Packets
alias AprsmeWeb.MapLive.PacketUtils
alias AprsmeWeb.AprsSymbol alias AprsmeWeb.AprsSymbol
alias AprsmeWeb.MapLive.PacketUtils
@neighbor_radius_km 10 @neighbor_radius_km 10
@neighbor_limit 10 @neighbor_limit 10
@ -326,14 +326,14 @@ defmodule AprsmeWeb.InfoLive.Show do
def render_symbol_html(packet, size \\ 32) do def render_symbol_html(packet, size \\ 32) do
if packet do if packet do
{symbol_table_id, symbol_code} = AprsSymbol.extract_from_packet(packet) {symbol_table_id, symbol_code} = AprsSymbol.extract_from_packet(packet)
# Check if this is an overlay symbol # Check if this is an overlay symbol
if symbol_table_id && String.match?(symbol_table_id, ~r/^[A-Z0-9]$/) do if symbol_table_id && String.match?(symbol_table_id, ~r/^[A-Z0-9]$/) do
# Use layered sprite backgrounds for overlay symbols # Use layered sprite backgrounds for overlay symbols
sprite_info = AprsSymbol.get_sprite_info(symbol_table_id, symbol_code) sprite_info = AprsSymbol.get_sprite_info(symbol_table_id, symbol_code)
overlay_sprite_info = AprsSymbol.get_overlay_character_sprite_info(symbol_table_id) overlay_sprite_info = AprsSymbol.get_overlay_character_sprite_info(symbol_table_id)
raw """ raw("""
<div style=" <div style="
position: relative; position: relative;
width: #{size}px; width: #{size}px;
@ -348,16 +348,16 @@ defmodule AprsmeWeb.InfoLive.Show do
margin-bottom: -6px; margin-bottom: -6px;
"> ">
</div> </div>
""" """)
else else
# Use style rendering for non-overlay symbols # Use style rendering for non-overlay symbols
raw """ raw("""
<div style="#{AprsSymbol.render_style(symbol_table_id, symbol_code, size)}"></div> <div style="#{AprsSymbol.render_style(symbol_table_id, symbol_code, size)}"></div>
""" """)
end end
else else
# Return empty if no packet # Return empty if no packet
raw "" raw("")
end end
end end

View file

@ -1,4 +1,3 @@
<div class="min-h-screen bg-base-200"> <div class="min-h-screen bg-base-200">
<!-- Page header --> <!-- Page header -->
<div class="bg-base-100 shadow-sm"> <div class="bg-base-100 shadow-sm">
@ -15,9 +14,13 @@
</span> </span>
<%= if @packet do %> <%= if @packet do %>
<% {symbol_table, symbol_code} = AprsmeWeb.AprsSymbol.extract_from_packet(@packet) %> <% {symbol_table, symbol_code} = AprsmeWeb.AprsSymbol.extract_from_packet(@packet) %>
<% display_symbol = if symbol_table && String.match?(symbol_table, ~r/^[A-Z0-9]$/), do: symbol_table, else: "#{AprsmeWeb.AprsSymbol.normalize_symbol_table(symbol_table)}#{AprsmeWeb.AprsSymbol.normalize_symbol_code(symbol_code)}" %> <% display_symbol =
if symbol_table && String.match?(symbol_table, ~r/^[A-Z0-9]$/),
do: symbol_table,
else:
"#{AprsmeWeb.AprsSymbol.normalize_symbol_table(symbol_table)}#{AprsmeWeb.AprsSymbol.normalize_symbol_code(symbol_code)}" %>
<div title={display_symbol}> <div title={display_symbol}>
<%= render_symbol_html(@packet) %> {render_symbol_html(@packet)}
</div> </div>
<% end %> <% end %>
</div> </div>
@ -251,10 +254,15 @@
{ssid_info.callsign} {ssid_info.callsign}
</.link> </.link>
<%= if ssid_info.packet do %> <%= if ssid_info.packet do %>
<% {symbol_table, symbol_code} = AprsmeWeb.AprsSymbol.extract_from_packet(ssid_info.packet) %> <% {symbol_table, symbol_code} =
<% display_symbol = if symbol_table && String.match?(symbol_table, ~r/^[A-Z0-9]$/), do: symbol_table, else: "#{AprsmeWeb.AprsSymbol.normalize_symbol_table(symbol_table)}#{AprsmeWeb.AprsSymbol.normalize_symbol_code(symbol_code)}" %> AprsmeWeb.AprsSymbol.extract_from_packet(ssid_info.packet) %>
<% display_symbol =
if symbol_table && String.match?(symbol_table, ~r/^[A-Z0-9]$/),
do: symbol_table,
else:
"#{AprsmeWeb.AprsSymbol.normalize_symbol_table(symbol_table)}#{AprsmeWeb.AprsSymbol.normalize_symbol_code(symbol_code)}" %>
<div title={display_symbol}> <div title={display_symbol}>
<%= render_symbol_html(ssid_info.packet) %> {render_symbol_html(ssid_info.packet)}
</div> </div>
<% end %> <% end %>
</div> </div>
@ -353,10 +361,15 @@
{neighbor.callsign} {neighbor.callsign}
</.link> </.link>
<%= if neighbor.packet do %> <%= if neighbor.packet do %>
<% {symbol_table, symbol_code} = AprsmeWeb.AprsSymbol.extract_from_packet(neighbor.packet) %> <% {symbol_table, symbol_code} =
<% display_symbol = if symbol_table && String.match?(symbol_table, ~r/^[A-Z0-9]$/), do: symbol_table, else: "#{AprsmeWeb.AprsSymbol.normalize_symbol_table(symbol_table)}#{AprsmeWeb.AprsSymbol.normalize_symbol_code(symbol_code)}" %> AprsmeWeb.AprsSymbol.extract_from_packet(neighbor.packet) %>
<% display_symbol =
if symbol_table && String.match?(symbol_table, ~r/^[A-Z0-9]$/),
do: symbol_table,
else:
"#{AprsmeWeb.AprsSymbol.normalize_symbol_table(symbol_table)}#{AprsmeWeb.AprsSymbol.normalize_symbol_code(symbol_code)}" %>
<div title={display_symbol}> <div title={display_symbol}>
<%= render_symbol_html(neighbor.packet) %> {render_symbol_html(neighbor.packet)}
</div> </div>
<% end %> <% end %>
</div> </div>

View file

@ -1,15 +1,16 @@
defmodule AprsmeWeb.AprsSymbolTest do defmodule AprsmeWeb.AprsSymbolTest do
use ExUnit.Case, async: true use ExUnit.Case, async: true
alias AprsmeWeb.AprsSymbol alias AprsmeWeb.AprsSymbol
describe "overlay symbol rendering" do describe "overlay symbol rendering" do
test "D& should show black diamond background from table 1" do test "D& should show black diamond background from table 1" do
# Test the get_sprite_info for overlay symbol D& # Test the get_sprite_info for overlay symbol D&
sprite_info = AprsSymbol.get_sprite_info("D", "&") sprite_info = AprsSymbol.get_sprite_info("D", "&")
# Should use table 1 for the diamond background (alternate table) # Should use table 1 for the diamond background (alternate table)
assert sprite_info.sprite_file == "/aprs-symbols/aprs-symbols-128-1@2x.png" assert sprite_info.sprite_file == "/aprs-symbols/aprs-symbols-128-1@2x.png"
# Calculate expected position for & (ASCII 38) # Calculate expected position for & (ASCII 38)
# index = 38 - 33 = 5 # index = 38 - 33 = 5
# column = 5 % 16 = 5 # column = 5 % 16 = 5
@ -23,10 +24,10 @@ defmodule AprsmeWeb.AprsSymbolTest do
test "N# should show green star background from table 1" do test "N# should show green star background from table 1" do
# Test the get_sprite_info for overlay symbol N# # Test the get_sprite_info for overlay symbol N#
sprite_info = AprsSymbol.get_sprite_info("N", "#") sprite_info = AprsSymbol.get_sprite_info("N", "#")
# Should use table 1 for the green star background # Should use table 1 for the green star background
assert sprite_info.sprite_file == "/aprs-symbols/aprs-symbols-128-1@2x.png" assert sprite_info.sprite_file == "/aprs-symbols/aprs-symbols-128-1@2x.png"
# Calculate expected position for # (ASCII 35) # Calculate expected position for # (ASCII 35)
# index = 35 - 33 = 2 # index = 35 - 33 = 2
# column = 2 % 16 = 2 # column = 2 % 16 = 2
@ -40,10 +41,10 @@ defmodule AprsmeWeb.AprsSymbolTest do
test "overlay character sprite info for D" do test "overlay character sprite info for D" do
# Test getting the overlay character sprite for letter D # Test getting the overlay character sprite for letter D
overlay_info = AprsSymbol.get_overlay_character_sprite_info("D") overlay_info = AprsSymbol.get_overlay_character_sprite_info("D")
# Should use table 2 for overlay characters # Should use table 2 for overlay characters
assert overlay_info.sprite_file == "/aprs-symbols/aprs-symbols-128-2@2x.png" assert overlay_info.sprite_file == "/aprs-symbols/aprs-symbols-128-2@2x.png"
# Calculate expected position for D (ASCII 68) # Calculate expected position for D (ASCII 68)
# index = 68 - 33 = 35 # index = 68 - 33 = 35
# column = 35 % 16 = 3 # column = 35 % 16 = 3
@ -57,10 +58,10 @@ defmodule AprsmeWeb.AprsSymbolTest do
test "overlay character sprite info for N" do test "overlay character sprite info for N" do
# Test getting the overlay character sprite for letter N # Test getting the overlay character sprite for letter N
overlay_info = AprsSymbol.get_overlay_character_sprite_info("N") overlay_info = AprsSymbol.get_overlay_character_sprite_info("N")
# Should use table 2 for overlay characters # Should use table 2 for overlay characters
assert overlay_info.sprite_file == "/aprs-symbols/aprs-symbols-128-2@2x.png" assert overlay_info.sprite_file == "/aprs-symbols/aprs-symbols-128-2@2x.png"
# Calculate expected position for N (ASCII 78) # Calculate expected position for N (ASCII 78)
# index = 78 - 33 = 45 # index = 78 - 33 = 45
# column = 45 % 16 = 13 # column = 45 % 16 = 13
@ -73,26 +74,28 @@ defmodule AprsmeWeb.AprsSymbolTest do
test "render_marker_html for overlay symbol D&" do test "render_marker_html for overlay symbol D&" do
html = AprsSymbol.render_marker_html("D", "&", "W5MRC-15", 32) html = AprsSymbol.render_marker_html("D", "&", "W5MRC-15", 32)
# Should contain both background images (overlay first from table 2, base from table 1) # Should contain both background images (overlay first from table 2, base from table 1)
assert html =~ "background-image: url(/aprs-symbols/aprs-symbols-128-2@2x.png), url(/aprs-symbols/aprs-symbols-128-1@2x.png)" assert html =~
"background-image: url(/aprs-symbols/aprs-symbols-128-2@2x.png), url(/aprs-symbols/aprs-symbols-128-1@2x.png)"
# Should have both background positions (overlay first, then base) # Should have both background positions (overlay first, then base)
assert html =~ "background-position: -96.0px -64.0px, -160.0px 0.0px" assert html =~ "background-position: -96.0px -64.0px, -160.0px 0.0px"
# Should include the callsign # Should include the callsign
assert html =~ "W5MRC-15" assert html =~ "W5MRC-15"
end end
test "render_marker_html for overlay symbol N#" do test "render_marker_html for overlay symbol N#" do
html = AprsSymbol.render_marker_html("N", "#", "TEST-1", 32) html = AprsSymbol.render_marker_html("N", "#", "TEST-1", 32)
# Should contain both background images (overlay first from table 2, base from table 1) # Should contain both background images (overlay first from table 2, base from table 1)
assert html =~ "background-image: url(/aprs-symbols/aprs-symbols-128-2@2x.png), url(/aprs-symbols/aprs-symbols-128-1@2x.png)" assert html =~
"background-image: url(/aprs-symbols/aprs-symbols-128-2@2x.png), url(/aprs-symbols/aprs-symbols-128-1@2x.png)"
# Should have both background positions (overlay first, then base) # Should have both background positions (overlay first, then base)
assert html =~ "background-position: -416.0px -64.0px, -64.0px 0.0px" assert html =~ "background-position: -416.0px -64.0px, -64.0px 0.0px"
# Should include the callsign # Should include the callsign
assert html =~ "TEST-1" assert html =~ "TEST-1"
end end
@ -100,9 +103,9 @@ defmodule AprsmeWeb.AprsSymbolTest do
test "normal symbol /_" do test "normal symbol /_" do
# Test a normal symbol from primary table # Test a normal symbol from primary table
sprite_info = AprsSymbol.get_sprite_info("/", "_") sprite_info = AprsSymbol.get_sprite_info("/", "_")
assert sprite_info.sprite_file == "/aprs-symbols/aprs-symbols-128-0@2x.png" assert sprite_info.sprite_file == "/aprs-symbols/aprs-symbols-128-0@2x.png"
# Calculate expected position for _ (ASCII 95) # Calculate expected position for _ (ASCII 95)
# index = 95 - 33 = 62 # index = 95 - 33 = 62
# column = 62 % 16 = 14 # column = 62 % 16 = 14
@ -116,9 +119,9 @@ defmodule AprsmeWeb.AprsSymbolTest do
test "alternate table symbol \\#" do test "alternate table symbol \\#" do
# Test a symbol from alternate table # Test a symbol from alternate table
sprite_info = AprsSymbol.get_sprite_info("\\", "#") sprite_info = AprsSymbol.get_sprite_info("\\", "#")
assert sprite_info.sprite_file == "/aprs-symbols/aprs-symbols-128-1@2x.png" assert sprite_info.sprite_file == "/aprs-symbols/aprs-symbols-128-1@2x.png"
# Calculate expected position for # (ASCII 35) # Calculate expected position for # (ASCII 35)
# index = 35 - 33 = 2 # index = 35 - 33 = 2
# column = 2 % 16 = 2 # column = 2 % 16 = 2
@ -131,12 +134,18 @@ defmodule AprsmeWeb.AprsSymbolTest do
test "get_overlay_base_table_id mapping" do test "get_overlay_base_table_id mapping" do
# Test the table mapping for overlay base symbols # Test the table mapping for overlay base symbols
assert AprsSymbol.get_overlay_base_table_id("#") == "1" # Green star in alternate table # Green star in alternate table
assert AprsSymbol.get_overlay_base_table_id("&") == "1" # Diamond in alternate table assert AprsSymbol.get_overlay_base_table_id("#") == "1"
assert AprsSymbol.get_overlay_base_table_id("i") == "1" # Black square in alternate table # Diamond in alternate table
assert AprsSymbol.get_overlay_base_table_id(">") == "1" # Arrow in alternate table assert AprsSymbol.get_overlay_base_table_id("&") == "1"
assert AprsSymbol.get_overlay_base_table_id("^") == "1" # Arrow in alternate table # Black square in alternate table
assert AprsSymbol.get_overlay_base_table_id("?") == "1" # Default to alternate table assert AprsSymbol.get_overlay_base_table_id("i") == "1"
# Arrow in alternate table
assert AprsSymbol.get_overlay_base_table_id(">") == "1"
# Arrow in alternate table
assert AprsSymbol.get_overlay_base_table_id("^") == "1"
# Default to alternate table
assert AprsSymbol.get_overlay_base_table_id("?") == "1"
end end
end end
end end

View file

@ -1,5 +1,6 @@
defmodule AprsmeWeb.MapLive.OverlayRenderingTest do defmodule AprsmeWeb.MapLive.OverlayRenderingTest do
use ExUnit.Case, async: true use ExUnit.Case, async: true
alias AprsmeWeb.MapLive.PacketUtils alias AprsmeWeb.MapLive.PacketUtils
describe "overlay symbol rendering in map" do describe "overlay symbol rendering in map" do
@ -21,18 +22,19 @@ defmodule AprsmeWeb.MapLive.OverlayRenderingTest do
# Process the packet through PacketUtils # Process the packet through PacketUtils
result = PacketUtils.build_packet_data(packet, true, "en-US") result = PacketUtils.build_packet_data(packet, true, "en-US")
# Check that symbol_html was generated # Check that symbol_html was generated
assert Map.has_key?(result, "symbol_html") assert Map.has_key?(result, "symbol_html")
symbol_html = result["symbol_html"] symbol_html = result["symbol_html"]
# Verify the overlay symbol is rendered correctly with overlay character on top # Verify the overlay symbol is rendered correctly with overlay character on top
# The overlay character (D) should be first in the background-image list # The overlay character (D) should be first in the background-image list
assert symbol_html =~ "background-image: url(/aprs-symbols/aprs-symbols-128-2@2x.png), url(/aprs-symbols/aprs-symbols-128-2@2x.png)" assert symbol_html =~
"background-image: url(/aprs-symbols/aprs-symbols-128-2@2x.png), url(/aprs-symbols/aprs-symbols-128-2@2x.png)"
# Verify the positions (overlay character D first at -96.0px -64.0px, then base & at -160.0px 0.0px) # Verify the positions (overlay character D first at -96.0px -64.0px, then base & at -160.0px 0.0px)
assert symbol_html =~ "background-position: -96.0px -64.0px, -160.0px 0.0px" assert symbol_html =~ "background-position: -96.0px -64.0px, -160.0px 0.0px"
# Verify callsign is included # Verify callsign is included
assert symbol_html =~ "W5MRC-15" assert symbol_html =~ "W5MRC-15"
end end
@ -56,14 +58,15 @@ defmodule AprsmeWeb.MapLive.OverlayRenderingTest do
# Process the packet # Process the packet
result = PacketUtils.build_packet_data(packet, true, "en-US") result = PacketUtils.build_packet_data(packet, true, "en-US")
symbol_html = result["symbol_html"] symbol_html = result["symbol_html"]
# Verify the overlay uses different sprite tables # Verify the overlay uses different sprite tables
# Overlay character N from table 2, base # from table 1 # Overlay character N from table 2, base # from table 1
assert symbol_html =~ "background-image: url(/aprs-symbols/aprs-symbols-128-2@2x.png), url(/aprs-symbols/aprs-symbols-128-1@2x.png)" assert symbol_html =~
"background-image: url(/aprs-symbols/aprs-symbols-128-2@2x.png), url(/aprs-symbols/aprs-symbols-128-1@2x.png)"
# Verify the positions (overlay N first, then base #) # Verify the positions (overlay N first, then base #)
assert symbol_html =~ "background-position: -416.0px -64.0px, -64.0px 0.0px" assert symbol_html =~ "background-position: -416.0px -64.0px, -64.0px 0.0px"
# Verify callsign # Verify callsign
assert symbol_html =~ "TEST-1" assert symbol_html =~ "TEST-1"
end end
@ -87,16 +90,16 @@ defmodule AprsmeWeb.MapLive.OverlayRenderingTest do
# Process the packet # Process the packet
result = PacketUtils.build_packet_data(packet, true, "en-US") result = PacketUtils.build_packet_data(packet, true, "en-US")
symbol_html = result["symbol_html"] symbol_html = result["symbol_html"]
# Verify it's a single background image (no overlay) # Verify it's a single background image (no overlay)
assert symbol_html =~ "background-image: url(/aprs-symbols/aprs-symbols-128-0@2x.png)" assert symbol_html =~ "background-image: url(/aprs-symbols/aprs-symbols-128-0@2x.png)"
assert not (symbol_html =~ "background-image: url(/aprs-symbols/aprs-symbols-128-0@2x.png), url") assert not (symbol_html =~ "background-image: url(/aprs-symbols/aprs-symbols-128-0@2x.png), url")
# Verify the position for _ symbol # Verify the position for _ symbol
assert symbol_html =~ "background-position: -448.0px -96.0px" assert symbol_html =~ "background-position: -448.0px -96.0px"
# Verify callsign # Verify callsign
assert symbol_html =~ "WEATHER-1" assert symbol_html =~ "WEATHER-1"
end end
end end
end end