ui: OSM panel (#1515)

* param to control stock vs sp ui

* init styles

* SP Toggles

* Lint

* optimizations

* sp raylib preview

* fix callback

* fix ui preview

* dialog txt

* compare vs what used to be done before InputDialog

* merge origin raylib toggles

* tree dialog

* less trees for the planet

* the heck

* save the trees we got icons

* Update process.py

* Remove 'sunnypilot_ui' 

Removed 'sunnypilot_ui' parameter from params_keys.h

* Update raylib_screenshots.py

Removed the parameter setting for 'sunnypilot_ui' in the test.

* ui: fuzzy search helper

* better tree. fully dynamic and stuff

* rm

* more indent

* Squashed commit of the following:

commit 6b5b686fa5
Author: discountchubbs <alexgrant990@gmail.com>
Date:   Mon Nov 24 17:16:17 2025 -0800

    more indent

commit 76bc538ac7
Merge: 53eb821dc4 c53e2134e2
Author: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com>
Date:   Mon Nov 24 17:15:48 2025 -0800

    Merge branch 'master' into rl-tree-dialog

commit 53eb821dc4
Merge: 82e1ebe97e 844f4cbc74
Author: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com>
Date:   Mon Nov 24 11:54:55 2025 -0800

    Merge branch 'master' into rl-tree-dialog

commit 82e1ebe97e
Author: discountchubbs <alexgrant990@gmail.com>
Date:   Mon Nov 24 10:23:35 2025 -0800

    rm

commit da3ff45bb6
Merge: 41da513fca a829a1b972
Author: discountchubbs <alexgrant990@gmail.com>
Date:   Mon Nov 24 10:19:08 2025 -0800

    Merge remote-tracking branch 'origin/rl-tree-dialog' into rl-tree-dialog

commit 41da513fca
Author: discountchubbs <alexgrant990@gmail.com>
Date:   Mon Nov 24 10:18:43 2025 -0800

    better tree. fully dynamic and stuff

commit b2950149fb
Merge: 4fb8e4beed 924e5a3211
Author: discountchubbs <alexgrant990@gmail.com>
Date:   Mon Nov 24 10:17:51 2025 -0800

    Merge remote-tracking branch 'origin/input-dialog' into rl-tree-dialog

commit a829a1b972
Merge: 848290d07e 9edc36ca66
Author: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com>
Date:   Mon Nov 24 10:16:28 2025 -0800

    Merge branch 'master' into rl-tree-dialog

commit 4fb8e4beed
Merge: 848290d07e af4f0f8372
Author: discountchubbs <alexgrant990@gmail.com>
Date:   Mon Nov 24 10:16:20 2025 -0800

    Merge remote-tracking branch 'origin/fuzzy-dialog' into rl-tree-dialog

commit af4f0f8372
Merge: 1d5f0ab282 3cd55260d9
Author: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com>
Date:   Mon Nov 24 06:39:29 2025 -0800

    Merge branch 'master' into fuzzy-dialog

commit 1d5f0ab282
Author: discountchubbs <alexgrant990@gmail.com>
Date:   Sun Nov 23 11:28:59 2025 -0800

    ui: fuzzy search helper

commit 848290d07e
Author: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com>
Date:   Fri Nov 21 21:08:07 2025 -0800

    Update raylib_screenshots.py

    Removed the parameter setting for 'sunnypilot_ui' in the test.

commit 6694928a46
Author: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com>
Date:   Fri Nov 21 21:06:57 2025 -0800

    Remove 'sunnypilot_ui'

    Removed 'sunnypilot_ui' parameter from params_keys.h

commit b3c90ef7b2
Merge: 0d3bc959c8 457b6634fd
Author: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com>
Date:   Fri Nov 21 21:06:04 2025 -0800

    Merge branch 'master' into rl-tree-dialog

commit 924e5a3211
Merge: a4ee4ba76d d92d2cb683
Author: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com>
Date:   Fri Nov 21 19:33:18 2025 -0800

    Merge branch 'master' into input-dialog

commit a4ee4ba76d
Merge: e911de5968 4f13a0f775
Author: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com>
Date:   Fri Nov 21 16:24:31 2025 -0800

    Merge branch 'master' into input-dialog

commit e911de5968
Merge: cea6e00819 0ba5cbea91
Author: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com>
Date:   Fri Nov 21 13:50:33 2025 -0800

    Merge branch 'master' into input-dialog

commit cea6e00819
Merge: d7b8ce86ed 8184cd8a6a
Author: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com>
Date:   Fri Nov 21 12:01:45 2025 -0800

    Merge branch 'master' into input-dialog

commit 0d3bc959c8
Author: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com>
Date:   Wed Nov 19 20:29:30 2025 -0800

    Update process.py

commit 4f3c19ffb5
Author: James Vecellio <alexgrant990@gmail.com>
Date:   Wed Nov 19 20:28:59 2025 -0800

    save the trees we got icons

commit ae5c44355d
Author: discountchubbs <alexgrant990@gmail.com>
Date:   Wed Nov 19 13:38:04 2025 -0800

    the heck

commit 066438ad10
Merge: 9532675814 e74460f3a8
Author: discountchubbs <alexgrant990@gmail.com>
Date:   Wed Nov 19 12:18:17 2025 -0800

    Merge remote-tracking branch 'origin/rl-tree-dialog' into rl-tree-dialog

commit 9532675814
Author: discountchubbs <alexgrant990@gmail.com>
Date:   Wed Nov 19 12:17:52 2025 -0800

    less trees for the planet

commit e74460f3a8
Merge: c347db376a 423a7d2ed0
Author: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com>
Date:   Wed Nov 19 09:37:44 2025 -0800

    Merge branch 'rl-sp-toggles' into rl-tree-dialog

commit c347db376a
Author: discountchubbs <alexgrant990@gmail.com>
Date:   Wed Nov 19 09:36:33 2025 -0800

    tree dialog

commit c9bd67b261
Author: discountchubbs <alexgrant990@gmail.com>
Date:   Wed Nov 19 09:34:08 2025 -0800

    merge origin raylib toggles

commit d7b8ce86ed
Author: discountchubbs <alexgrant990@gmail.com>
Date:   Mon Nov 17 20:21:33 2025 -0800

    compare vs what used to be done before InputDialog

commit 2d3d104658
Merge: ded02895f4 f1025f6ee9
Author: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com>
Date:   Mon Nov 17 19:24:20 2025 -0800

    Merge branch 'master' into input-dialog

commit ded02895f4
Author: discountchubbs <alexgrant990@gmail.com>
Date:   Mon Nov 17 19:22:01 2025 -0800

    dialog txt

commit 9778a925b0
Merge: cb03d08397 08e85808c5
Author: Jason Wen <haibin.wen3@gmail.com>
Date:   Sun Nov 16 03:16:58 2025 -0500

    input dialog

commit 423a7d2ed0
Author: nayan <nayan8teen@gmail.com>
Date:   Sun Nov 16 11:15:28 2025 -0500

    fix ui preview

commit e4e10d4b87
Author: nayan <nayan8teen@gmail.com>
Date:   Sun Nov 16 11:15:22 2025 -0500

    fix callback

commit 362e9ce04b
Author: nayan <nayan8teen@gmail.com>
Date:   Sun Nov 16 09:53:28 2025 -0500

    sp raylib preview

commit 3946e643f6
Author: nayan <nayan8teen@gmail.com>
Date:   Sat Nov 15 20:24:20 2025 -0500

    optimizations

commit 0c37a38596
Author: nayan <nayan8teen@gmail.com>
Date:   Sat Nov 15 09:42:12 2025 -0500

    Lint

commit 9c5acf61c0
Author: nayan <nayan8teen@gmail.com>
Date:   Sat Nov 15 09:29:07 2025 -0500

    SP Toggles

commit 121b304fe0
Author: nayan <nayan8teen@gmail.com>
Date:   Sat Nov 15 09:28:58 2025 -0500

    init styles

commit 47d848293b
Author: nayan <nayan8teen@gmail.com>
Date:   Sat Nov 15 09:28:43 2025 -0500

    param to control stock vs sp ui

* Squashed commit of the following:

commit 70ad001add
Merge: 142663c490 844f4cbc74
Author: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com>
Date:   Mon Nov 24 11:54:58 2025 -0800

    Merge branch 'master' into rl-progress-bar

commit 142663c490
Author: discountchubbs <alexgrant990@gmail.com>
Date:   Sat Nov 22 20:12:44 2025 -0800

    smoother updating

commit 4476e418dd
Author: discountchubbs <alexgrant990@gmail.com>
Date:   Sat Nov 22 09:55:17 2025 -0800

    easier to see

commit ad66c22e88
Merge: 0c46ef5948 457b6634fd
Author: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com>
Date:   Sat Nov 22 07:45:05 2025 -0800

    Merge branch 'master' into rl-progress-bar

commit 0c46ef5948
Author: discountchubbs <alexgrant990@gmail.com>
Date:   Sat Nov 22 07:42:59 2025 -0800

    freaking test dir

commit 11c19aad24
Merge: 7785238d54 8184cd8a6a
Author: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com>
Date:   Fri Nov 21 12:01:36 2025 -0800

    Merge branch 'master' into rl-progress-bar

commit 7785238d54
Author: discountchubbs <alexgrant990@gmail.com>
Date:   Wed Nov 19 07:47:10 2025 -0800

    raylib: progress bar

* OSM panel

* fetching

* only show if fav_param is used in the call

* flattened and custom search query

* conditional for mypy

* sunny's new x,y makes this even easier!

* download all

* add back the rough estimate

* not sure i like the 'Download'

* simplify the path

* actual size as of today

* format

* more simple

* only show on download or delete

* loathing loathing, unadulterated loathing, i loathe it all

* loathing loathing, unadulterated loathing, i loathe it all

* # Conflicts:
#	system/ui/sunnypilot/lib/styles.py
#	system/ui/sunnypilot/widgets/tree_dialog.py

* search

* st

* Update osm.py

* one second updates:

 its heavy process, which isnt really noticeable during downloads ayways. the once a second ensures responsiveness on the ui, while also maintaining 20fps on device for country/state downloads.

* efficient? i hope

* big boi texts

* big boi texts

* use our own classes

* need to clear all params when delete all

* more

* collateral lol

* do not behave as selected if canceled during US->States dialog

* more

* instead of timestamp, let's just show formatted time

* disable button when downloading dbs

* should be the buttons being disabled

* well gotta re-enable them too

* empty country

* might be bigger now

* fixes for mapd manager

* should stay as a json

* sanitize it a bit

* revert

* only nuke if the cancel button is called

* always try to update the labels

* Revert "always try to update the labels"

This reverts commit ba0988fc06.

* re-enable button after download is complete

* disable all while downloading (till we could cancel and re-download)

* fix progress bar not filling up as intended for smaller total counts

* revert

* use new

---------

Co-authored-by: nayan <nayan8teen@gmail.com>
Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
This commit is contained in:
James Vecellio-Grant
2025-12-11 23:40:33 -08:00
committed by GitHub
parent 928672999b
commit 436ff5aa42
2 changed files with 213 additions and 18 deletions
+212 -10
View File
@@ -4,27 +4,229 @@ Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import datetime
import os
import platform
import requests
import shutil
import threading
from pathlib import Path
from time import monotonic
from openpilot.common.params import Params
from openpilot.selfdrive.ui.ui_state import device, ui_state
from openpilot.selfdrive.ui.layouts.settings.software import time_ago
from openpilot.system.hardware.hw import Paths
from openpilot.system.ui.lib.application import gui_app
from openpilot.system.ui.lib.multilang import tr
from openpilot.system.ui.widgets import DialogResult, Widget
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
from openpilot.system.ui.widgets.list_view import text_item
from openpilot.system.ui.widgets.scroller_tici import Scroller
from openpilot.system.ui.widgets import Widget
from openpilot.system.ui.sunnypilot.lib.utils import NoElideButtonAction
from openpilot.system.ui.sunnypilot.widgets.list_view import ListItemSP
from openpilot.system.ui.sunnypilot.widgets.tree_dialog import TreeFolder, TreeNode, TreeOptionDialog
from openpilot.system.ui.sunnypilot.widgets.progress_bar import progress_item
MAP_PATH = Path(Paths.mapd_root()) / "offline"
class OSMLayout(Widget):
def __init__(self):
super().__init__()
self._params = Params()
items = self._initialize_items()
self._scroller = Scroller(items, line_separator=True, spacing=0)
self._current_percent = 0
self._last_map_size_update = 0
self._mem_params = Params("/dev/shm/params") if platform.system() != "Darwin" else ui_state.params
self._initialize_items()
self._update_map_size()
self._progress.set_visible(False)
self._state_btn.set_visible(False)
self._mapd_version.action_item.set_text(ui_state.params.get("MapdVersion") or "Loading...")
self._scroller = Scroller(self.items, line_separator=True, spacing=0)
def _initialize_items(self):
items = [
self._mapd_version = text_item(tr("Mapd Version"), lambda: ui_state.params.get("MapdVersion") or "Loading...")
self._delete_maps_btn = ListItemSP(tr("Downloaded Maps"), action_item=NoElideButtonAction(tr("DELETE"), enabled=True), callback=self._delete_maps)
self._progress = progress_item(tr("Downloading Map"))
self._update_btn = ListItemSP(tr("Database Update"), action_item=NoElideButtonAction(tr("CHECK"), enabled=True), callback=self._update_db)
self._country_btn = ListItemSP(tr("Country"), action_item=NoElideButtonAction(tr("SELECT"), enabled=True), callback=lambda: self._select_region("Country"))
self._state_btn = ListItemSP(tr("State"), action_item=NoElideButtonAction(tr("SELECT"), enabled=True), callback=lambda: self._select_region("State"))
]
return items
self.items = [self._mapd_version, self._delete_maps_btn, self._progress, self._update_btn, self._country_btn, self._state_btn]
def _render(self, rect):
self._scroller.render(rect)
def _show_confirm(self, msg, confirm_text, func):
gui_app.set_modal_overlay(ConfirmDialog(msg, confirm_text), lambda res: func() if res == DialogResult.CONFIRM else None)
def calculate_size(self):
total_size = 0
directories_to_scan = [MAP_PATH] if MAP_PATH.exists() else []
while directories_to_scan:
try:
for entry in os.scandir(directories_to_scan.pop()):
if entry.is_file():
total_size += entry.stat().st_size
elif entry.is_dir():
directories_to_scan.append(entry.path)
except OSError:
pass
self._delete_maps_btn.action_item.set_value(f"{total_size / 1024 ** 2:.2f} MB" if total_size < 1024 ** 3 else f"{total_size / 1024 ** 3:.2f} GB")
def _update_map_size(self):
threading.Thread(target=self.calculate_size, daemon=True).start()
def _do_delete_maps(self):
if MAP_PATH.exists():
shutil.rmtree(MAP_PATH)
for param in ("OsmDownloadedDate", "OsmLocal", "OsmLocationName", "OsmLocationTitle", "OsmStateName", "OsmStateTitle"):
ui_state.params.remove(param)
self._delete_maps_btn.action_item.set_enabled(True)
self._delete_maps_btn.action_item.set_text(tr("DELETE"))
self._update_map_size()
def _on_confirm_delete_maps(self):
self._delete_maps_btn.action_item.set_enabled(False)
self._delete_maps_btn.action_item.set_text("DELETING...")
threading.Thread(target=self._do_delete_maps).start()
def _delete_maps(self):
self._show_confirm(tr("This will delete ALL downloaded maps\n\nAre you sure you want to delete all maps?"),
tr("Yes, delete all maps"), self._on_confirm_delete_maps)
def _update_db(self):
self._show_confirm(tr("This will start the download process and it might take a while to complete."), tr("Start Download"),
lambda: ui_state.params.put_bool("OsmDbUpdatesCheck", True))
def _select_region(self, region_type):
is_country = region_type == "Country"
btn = self._country_btn if is_country else self._state_btn
btn.action_item.set_enabled(False)
btn.action_item.set_text(tr("FETCHING..."))
threading.Thread(target=self._do_select_region, args=(region_type, btn)).start()
def _handle_region_selection(self, region_type, locations, key, res, ref):
if res != DialogResult.CONFIRM or not ref:
if region_type == "State" and res == DialogResult.CANCEL:
if ui_state.params.get("OsmLocationName") == "US" and not ui_state.params.get("OsmStateName"):
ui_state.params.remove("OsmLocationName")
ui_state.params.remove("OsmLocationTitle")
ui_state.params.remove("OsmLocal")
self._update_labels()
return
if region_type == "Country":
ui_state.params.put_bool("OsmLocal", True)
ui_state.params.remove("OsmStateName")
ui_state.params.remove("OsmStateTitle")
ui_state.params.put(f"{key}Name", ref)
name = next((n.data['display_name'] for n in locations if n.ref == ref), ref)
ui_state.params.put(f"{key}Title", name)
if ref == "US" and region_type == "Country":
self._select_region("State")
else:
self._update_db()
def _do_select_region(self, region_type, btn):
base_url = "https://raw.githubusercontent.com/pfeiferj/openpilot-mapd/main/"
url = base_url + ("nation_bounding_boxes.json" if region_type == "Country" else "us_states_bounding_boxes.json")
try:
data = requests.get(url, timeout=10).json()
locations = sorted([TreeNode(ref=k, data={'display_name': v['full_name']}) for k, v in data.items()], key=lambda n: n.data['display_name'])
except Exception:
locations = []
if region_type == "State":
locations.insert(0, TreeNode(ref="All", data={'display_name': tr("All states (~6.0 GB)")}))
btn.action_item.set_enabled(True)
btn.action_item.set_text(tr("SELECT"))
key = "OsmLocation" if region_type == "Country" else "OsmState"
current = ui_state.params.get(f"{key}Name") or ""
dialog = TreeOptionDialog(tr(f"Select {region_type}"), [TreeFolder(folder="", nodes=locations)], current_ref=current, search_prompt="Perform a search")
dialog.on_exit = lambda res: self._handle_region_selection(region_type, locations, key, res, dialog.selection_ref)
gui_app.set_modal_overlay(dialog, callback=lambda res: self._handle_region_selection(region_type, locations, key, res, dialog.selection_ref))
def _update_labels(self):
downloading = bool(self._mem_params.get("OSMDownloadLocations"))
self._country_btn.set_enabled(not downloading)
self._state_btn.set_enabled(not downloading)
self._state_btn.set_visible(ui_state.params.get("OsmLocationName") == "US")
self._update_btn.set_visible(bool(ui_state.params.get("OsmLocationName")))
self._country_btn.action_item.set_value(ui_state.params.get("OsmLocationTitle") or "")
self._state_btn.action_item.set_value(ui_state.params.get("OsmStateTitle") or "")
pending = ui_state.params.get_bool("OsmDbUpdatesCheck")
if downloading or pending:
if downloading:
device.reset_interactive_timeout()
self._update_map_size()
self._progress.set_visible(True)
progress = ui_state.params.get("OSMDownloadProgress")
total = progress.get('total_files', 0) if progress else 0
done = progress.get('downloaded_files', 0) if progress else 0
failed = total > 0 and not downloading and done < total
if total > 0:
progress_perc = max(0.0, min(100.0, (done / total) * 100.0))
else:
progress_perc = 0.0
if failed:
text = "0% - Downloading Maps"
btn_text = tr("Error: Invalid download. Retry.")
self._current_percent = 0.0
elif total > 0 and downloading:
self._current_percent = progress_perc
perc_int = int(progress_perc)
text = f"{perc_int}% - Downloading Maps"
btn_text = f"{done}/{total} ({perc_int}%)"
else:
self._current_percent = 0.0
text = "0% - Downloading Maps"
btn_text = tr("Downloading Maps...")
self._progress.action_item.update(self._current_percent, text, show_progress=total > 0 and downloading and not failed)
self._update_btn.action_item.set_enabled(not downloading) # TODO-SP: introduce CANCEL database download with mapd
self._update_btn.action_item.set_value(btn_text)
self._country_btn.action_item.set_enabled(not downloading)
self._state_btn.action_item.set_enabled(not downloading)
self._delete_maps_btn.action_item.set_enabled(not downloading)
else:
self._progress.set_visible(False)
self._update_btn.action_item.set_enabled(True)
self._country_btn.action_item.set_enabled(True)
self._state_btn.action_item.set_enabled(True)
self._delete_maps_btn.action_item.set_enabled(True)
ts = ui_state.params.get("OsmDownloadedDate")
dt: datetime.datetime | None = None
if ts:
try:
ts_f = float(ts)
if ts_f > 0:
dt = datetime.datetime.fromtimestamp(ts_f, tz=datetime.UTC)
except (ValueError, TypeError):
dt = None
formatted = time_ago(dt)
self._update_btn.action_item.set_value(tr("Last checked {}").format(formatted))
def show_event(self):
self._scroller.show_event()
def _update_state(self):
now = monotonic()
if now - self._last_map_size_update >= 1.0:
self._last_map_size_update = now
self._update_labels()
def _render(self, rect):
self._scroller.render(rect)
+1 -8
View File
@@ -64,12 +64,7 @@ def request_refresh_osm_location_data(nations: list[str], states: list[str] = No
"states": states or []
}
osm_download_locations_dump = json.dumps({
"nations": nations,
"states": states or []
})
print(f"Downloading maps for {osm_download_locations_dump}")
print(f"Downloading maps for {json.dumps(osm_download_locations)}")
mem_params.put("OSMDownloadLocations", osm_download_locations)
@@ -103,8 +98,6 @@ def filter_nations_and_states(nations: list[str], states: list[str] = None) -> t
def update_osm_db() -> None:
# last_downloaded_date = params.get("OsmDownloadedDate", return_default=True)
# if params.get_bool("OsmDbUpdatesCheck") or time.monotonic() - last_downloaded_date >= 604800: # 7 days * 24 hours/day * 60
if params.get_bool("OsmDbUpdatesCheck"):
cleanup_old_osm_data(get_files_for_cleanup())
country = params.get("OsmLocationName", return_default=True)