Add map altitude overlay and info toggle

This commit is contained in:
liu weikai
2026-07-06 22:33:36 +08:00
parent 906a061c55
commit 480c7ac94e
5 changed files with 216 additions and 3 deletions
@@ -18,6 +18,7 @@ struct GpsStatusSnapshot
bool fix_valid = false;
double latitude = 0.0;
double longitude = 0.0;
bool has_altitude = false;
float altitude_m = 0.0f;
float speed_mps = 0.0f;
float course_deg = 0.0f;
@@ -14,6 +14,7 @@ int main()
source.snapshot_value.fix_valid = true;
source.snapshot_value.latitude = 31.2304;
source.snapshot_value.longitude = 121.4737;
source.snapshot_value.has_altitude = true;
source.snapshot_value.altitude_m = 12.5f;
source.snapshot_value.speed_mps = 1.25f;
source.snapshot_value.course_deg = 48.0f;
@@ -35,6 +36,7 @@ int main()
assert(snapshot.fix_valid);
assert(snapshot.latitude == 31.2304);
assert(snapshot.longitude == 121.4737);
assert(snapshot.has_altitude);
assert(snapshot.satellites == 8);
assert(snapshot.time_synced);
assert(snapshot.epoch_seconds == 1700000000ULL);
@@ -64,7 +64,8 @@ bool RuntimeGpsStatusSource::buildGpsStatusSnapshot(ui::gps::GpsStatusSnapshot&
out.fix_valid = state.valid || diagnostics.has_fix;
out.latitude = state.lat;
out.longitude = state.lng;
out.altitude_m = state.has_alt ? static_cast<float>(state.alt_m) : 0.0f;
out.has_altitude = state.valid && state.has_alt;
out.altitude_m = out.has_altitude ? static_cast<float>(state.alt_m) : 0.0f;
out.speed_mps = state.has_speed ? static_cast<float>(state.speed_mps) : 0.0f;
out.course_deg = state.has_course ? static_cast<float>(state.course_deg) : 0.0f;
platform::ui::gps::GnssStatus gnss_status{};
@@ -84,6 +84,8 @@ constexpr lv_coord_t kMapControlButtonWideWidth = 44;
constexpr lv_coord_t kMapControlButtonContourWidth = 56;
constexpr lv_coord_t kMapControlButtonTrackerWidth = 42;
constexpr lv_coord_t kMapSideRailWidth = 72;
constexpr lv_coord_t kMapAltitudePanelHeight = 18;
constexpr lv_coord_t kMapAltitudePanelWidth = 82;
constexpr std::uint32_t kLvglFunctionKeyF1 = 0x110001U;
constexpr std::uint32_t kInvalidMemberId = 0xFFFFFFFFU;
constexpr std::size_t kMaxTrackOverlayPoints = 48;
@@ -123,10 +125,12 @@ lv_obj_t* s_root = nullptr;
lv_timer_t* s_timer = nullptr;
::ui::widgets::TopBar s_top_bar;
::ui::widgets::map::Runtime s_map_runtime;
lv_obj_t* s_map_viewport = nullptr;
int s_map_zoom = kCardputerZeroMapDefaultZoom;
int s_map_pan_x = 0;
int s_map_pan_y = 0;
bool s_map_view_initialized = false;
bool s_map_info_visible = true;
UI_GPS_PAGE_STATE_RAM_ATTR ::ui::map::MapOverlaySnapshot s_overlay_snapshot;
Projection s_projection = Projection::Map;
bool s_gps_power_lease_active = false;
@@ -146,6 +150,8 @@ lv_obj_t* s_map_layer_btn = nullptr;
lv_obj_t* s_map_contour_btn = nullptr;
lv_obj_t* s_map_help_btn = nullptr;
lv_obj_t* s_map_tracker_btn = nullptr;
lv_obj_t* s_map_altitude_panel = nullptr;
lv_obj_t* s_map_altitude_label = nullptr;
lv_obj_t* s_map_notice_panel = nullptr;
lv_obj_t* s_map_notice_label = nullptr;
lv_obj_t* s_map_context_rail = nullptr;
@@ -178,6 +184,8 @@ void open_tracker_modal();
void add_map_controls_to_group(lv_group_t* group);
void request_refresh_view();
void consume_key_event(lv_event_t* e);
void sync_map_chrome_visibility();
void rebuild_map_control_group();
bool load_map_track_file_impl(const char* path, bool show_fail_toast);
void append_track_point(std::vector<TrackOverlayPoint>& out,
double lat,
@@ -453,6 +461,7 @@ void set_button_label(lv_obj_t* btn, const char* text)
void clear_map_controls()
{
s_map_viewport = nullptr;
s_map_control_bar = nullptr;
s_map_zoom_label = nullptr;
s_map_zoom_out_btn = nullptr;
@@ -462,6 +471,8 @@ void clear_map_controls()
s_map_contour_btn = nullptr;
s_map_help_btn = nullptr;
s_map_tracker_btn = nullptr;
s_map_altitude_panel = nullptr;
s_map_altitude_label = nullptr;
s_map_notice_panel = nullptr;
s_map_notice_label = nullptr;
s_map_context_rail = nullptr;
@@ -502,6 +513,112 @@ bool map_control_visible(lv_obj_t* obj)
return obj && lv_obj_is_valid(obj) && !lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN);
}
void resize_map_runtime_to_viewport()
{
if (!s_root || !s_map_viewport || !lv_obj_is_valid(s_map_viewport))
{
return;
}
lv_obj_update_layout(s_root);
lv_obj_update_layout(s_map_viewport);
::ui::widgets::map::set_size(s_map_runtime,
lv_obj_get_content_width(s_map_viewport),
lv_obj_get_content_height(s_map_viewport));
const auto& widgets = ::ui::widgets::map::widgets(s_map_runtime);
if (widgets.root && lv_obj_is_valid(widgets.root))
{
lv_obj_align(widgets.root, LV_ALIGN_CENTER, 0, 0);
}
}
void sync_map_chrome_visibility()
{
if (s_projection != Projection::Map)
{
return;
}
const bool show_info = s_map_info_visible;
set_hidden(s_top_bar.container, !show_info);
set_hidden(s_map_control_bar, !show_info);
if (!show_info)
{
set_hidden(s_map_altitude_panel, true);
set_hidden(s_map_context_rail, true);
set_hidden(s_map_notice_panel, true);
}
if (app_g && !(s_map_help_modal && lv_obj_is_valid(s_map_help_modal)) &&
!(s_tracker_modal && lv_obj_is_valid(s_tracker_modal)))
{
if (show_info)
{
rebuild_map_control_group();
}
else if (s_root && lv_obj_is_valid(s_root))
{
lv_group_remove_all_objs(app_g);
lv_group_add_obj(app_g, s_root);
lv_group_focus_obj(s_root);
lv_group_set_editing(app_g, false);
}
}
resize_map_runtime_to_viewport();
}
bool format_map_altitude_label(char* out, size_t out_len)
{
if (!out || out_len == 0)
{
return false;
}
const auto snapshot = gps_status_model().snapshot();
if (snapshot.header.valid &&
snapshot.fix_valid &&
snapshot.has_altitude &&
std::isfinite(snapshot.altitude_m))
{
std::snprintf(out, out_len, "Alt %.0f m", static_cast<double>(snapshot.altitude_m));
return true;
}
std::snprintf(out, out_len, "Alt --");
return false;
}
void sync_map_altitude_overlay()
{
if (!s_map_altitude_panel || !lv_obj_is_valid(s_map_altitude_panel) ||
!s_map_altitude_label || !lv_obj_is_valid(s_map_altitude_label))
{
return;
}
if (!s_map_info_visible)
{
set_hidden(s_map_altitude_panel, true);
return;
}
char label[24]{};
(void)format_map_altitude_label(label, sizeof(label));
set_compact_label(s_map_altitude_label, label);
set_hidden(s_map_altitude_panel, false);
lv_obj_move_foreground(s_map_altitude_panel);
}
void toggle_map_info_visibility()
{
s_map_info_visible = !s_map_info_visible;
sync_map_chrome_visibility();
refresh_view();
}
bool help_uses_f1()
{
#if defined(TRAIL_MATE_CARDPUTER_ZERO_LINUX) || defined(TRAIL_MATE_ESP_BOARD_T_DISPLAY_P4)
@@ -784,6 +901,12 @@ void sync_map_notice_overlay()
return;
}
if (!s_map_info_visible)
{
set_hidden(s_map_notice_panel, true);
return;
}
const uint32_t now = sys::millis_now();
if (s_map_notice_text[0] != '\0' && now < s_map_notice_until_ms)
{
@@ -800,6 +923,14 @@ void sync_map_notice_overlay()
void sync_map_context_buttons(const ::ui::map::MapWorkspaceSnapshot& snapshot)
{
if (!s_map_info_visible)
{
set_hidden(s_map_route_btn, true);
set_hidden(s_map_context_rail, true);
(void)snapshot;
return;
}
const bool show_route = route_context_available();
::team::ui::TeamUiSnapshot team_snapshot;
const bool has_team_members = load_team_snapshot(team_snapshot) && !team_snapshot.members.empty();
@@ -871,6 +1002,18 @@ void sync_map_control_labels(const ::ui::map::MapWorkspaceSnapshot& snapshot)
return;
}
if (!s_map_info_visible)
{
set_hidden(s_map_control_bar, true);
sync_map_altitude_overlay();
sync_map_context_buttons(snapshot);
sync_map_notice_overlay();
return;
}
set_hidden(s_map_control_bar, false);
sync_map_altitude_overlay();
const auto layers = ::ui::widgets::map::current_layer_state();
set_button_label(s_map_layer_btn, compact_map_source_label(layers.map_source));
set_button_label(s_map_contour_btn, layers.contour_enabled ? "Contour*" : "Contour");
@@ -960,7 +1103,7 @@ void refresh_gps_status_view()
set_compact_label(s_gps_sat_label, snapshot.satellite_label.c_str());
char line[48]{};
if (snapshot.fix_valid && std::isfinite(snapshot.altitude_m))
if (snapshot.fix_valid && snapshot.has_altitude && std::isfinite(snapshot.altitude_m))
{
std::snprintf(line, sizeof(line), "%.0f m", static_cast<double>(snapshot.altitude_m));
}
@@ -1516,6 +1659,26 @@ void append_track_overlay(::ui::map::MapOverlaySnapshot& snapshot)
}
}
void keep_only_current_position_overlay(::ui::map::MapOverlaySnapshot& snapshot)
{
std::size_t write = 0;
for (std::size_t read = 0; read < snapshot.item_count; ++read)
{
const auto& item = snapshot.items[read];
if (item.kind != ::ui::map::MapOverlayKind::CurrentPosition)
{
continue;
}
if (write != read)
{
snapshot.items[write] = item;
}
++write;
}
snapshot.item_count = write;
snapshot.truncated = false;
}
bool load_map_track_file_impl(const char* path, bool show_fail_toast)
{
if (!path || path[0] == '\0')
@@ -1643,6 +1806,10 @@ void refresh_view()
auto snapshot = map_workspace_model().snapshot();
(void)map_overlay_source().buildMapOverlaySnapshot(s_overlay_snapshot);
append_track_overlay(s_overlay_snapshot);
if (!s_map_info_visible)
{
keep_only_current_position_overlay(s_overlay_snapshot);
}
if (snapshot.header.valid)
{
@@ -2026,6 +2193,7 @@ void open_map_help_modal()
add_help_row("L", nullptr, "Change base layer");
add_help_row("O", "Contour", "Toggle contour overlay");
add_help_row("T", "Track", "Select track file");
add_help_row("I", nullptr, "Hide info");
add_help_row("Route", nullptr, "Shown when route active");
add_help_row("Members", nullptr, "Shown when team active");
add_help_row(help_key_label(), "Back", "Close help");
@@ -2385,6 +2553,11 @@ bool handle_map_key(uint32_t key, lv_event_t* e)
adjust_map_zoom(1);
consume_key_event(e);
return true;
case 'i':
case 'I':
toggle_map_info_visibility();
consume_key_event(e);
return true;
case 'c':
case 'C':
case 'p':
@@ -2553,6 +2726,36 @@ void create_map_control_bar(lv_obj_t* viewport)
lv_obj_move_foreground(s_map_control_bar);
}
void create_map_altitude_overlay(lv_obj_t* viewport)
{
s_map_altitude_panel = lv_obj_create(viewport);
lv_obj_set_size(s_map_altitude_panel, kMapAltitudePanelWidth, kMapAltitudePanelHeight);
lv_obj_align(s_map_altitude_panel,
LV_ALIGN_BOTTOM_LEFT,
4,
-(kMapControlBarHeight + 4));
lv_obj_add_flag(s_map_altitude_panel, LV_OBJ_FLAG_IGNORE_LAYOUT);
lv_obj_set_style_bg_color(s_map_altitude_panel, lv_color_hex(0x25170D), 0);
lv_obj_set_style_bg_opa(s_map_altitude_panel, LV_OPA_70, 0);
lv_obj_set_style_border_width(s_map_altitude_panel, 0, 0);
lv_obj_set_style_radius(s_map_altitude_panel, 4, 0);
lv_obj_set_style_pad_left(s_map_altitude_panel, 5, 0);
lv_obj_set_style_pad_right(s_map_altitude_panel, 5, 0);
lv_obj_set_style_pad_top(s_map_altitude_panel, 1, 0);
lv_obj_set_style_pad_bottom(s_map_altitude_panel, 1, 0);
lv_obj_clear_flag(s_map_altitude_panel, LV_OBJ_FLAG_SCROLLABLE);
s_map_altitude_label = lv_label_create(s_map_altitude_panel);
lv_label_set_text(s_map_altitude_label, "Alt --");
lv_obj_set_width(s_map_altitude_label, kMapAltitudePanelWidth - 10);
lv_obj_set_style_text_font(s_map_altitude_label, &lv_font_montserrat_10, 0);
lv_obj_set_style_text_color(s_map_altitude_label, lv_color_hex(0xFFF3DF), 0);
lv_obj_set_style_text_align(s_map_altitude_label, LV_TEXT_ALIGN_LEFT, 0);
lv_label_set_long_mode(s_map_altitude_label, LV_LABEL_LONG_CLIP);
lv_obj_center(s_map_altitude_label);
lv_obj_move_foreground(s_map_altitude_panel);
}
void create_map_notice_overlay(lv_obj_t* viewport)
{
s_map_notice_panel = lv_obj_create(viewport);
@@ -2666,6 +2869,7 @@ void create_gps_status_content(lv_obj_t* content)
void create_map_content(lv_obj_t* content)
{
lv_obj_t* viewport = lv_obj_create(content);
s_map_viewport = viewport;
lv_obj_set_size(viewport, LV_PCT(100), 0);
lv_obj_set_flex_grow(viewport, 1);
lv_obj_set_style_bg_color(viewport, lv_color_hex(0xEAD9B2), 0);
@@ -2688,6 +2892,7 @@ void create_map_content(lv_obj_t* content)
lv_obj_align(map_widgets.root, LV_ALIGN_CENTER, 0, 0);
}
create_map_control_bar(viewport);
create_map_altitude_overlay(viewport);
create_map_notice_overlay(viewport);
create_map_context_rail(viewport);
@@ -2738,6 +2943,10 @@ void enter(const shell::Host* host, lv_obj_t* parent, shell::Projection projecti
s_projection = projection;
clear_gps_status_labels();
clear_map_controls();
if (s_projection == Projection::Map)
{
s_map_info_visible = true;
}
if (s_projection == Projection::Map && !s_map_view_initialized)
{
s_map_zoom = kCardputerZeroMapDefaultZoom;
@@ -317,7 +317,7 @@ MapWorkspaceSnapshot UConsoleMapWorkspaceModel::snapshot() const
out.lat = gps.latitude;
out.lon = gps.longitude;
out.altitude_m = static_cast<double>(gps.altitude_m);
out.has_altitude = out.has_fix;
out.has_altitude = out.has_fix && gps.has_altitude;
out.speed_mps = static_cast<double>(gps.speed_mps);
out.has_speed = out.has_fix;
out.satellites = gps.satellites;