Redesign UI as an OsmAnd-style sidebar, add live GNSS position via gpsd

Restructure the map controls from MapLibre corner buttons into a left
sidebar of icons with flyouts/panels (radar, GPX, GNSS), matching
OsmAnd's layout more closely:

- Base map and GPX selection are now full-height side panels docked
  next to the sidebar (not small dropdown flyouts or a right-docked
  panel), opening/closing exclusively via a small shared event bus
  (panels.js) so only one is ever open at a time.
- The GPX panel's upload/new-folder controls are now icon buttons in
  the header instead of an always-visible dropzone and form; dragging
  a GPX file onto the tree, a specific folder, or straight onto the
  map itself all upload it.
- New: Maptool::GpsdClient (lib/maptool/gpsd_client.rb) streams live
  GNSS fixes from a local gpsd daemon over Server-Sent Events
  (/api/gnss/stream) to a marker on the map (public/js/gnss.js) — one
  background gpsd connection shared by every browser tab.
- The online map style (public/js/osmand-style.js) is now styled to
  resemble OsmAnd's own rendering, applied to both the online and
  offline (PMTiles) styles.

Also adds a GeolocateControl (browser geolocation) next to the zoom
controls.
This commit is contained in:
randy committed 2026-08-25 19:39:13 +02:00
1 parent 008e034932
commit 87df90f75c
14 files changed
+939 -200

No files matched your search

+33 -11
View File
@@ -1,21 +1,43 @@
<div id="map"></div>
<div id="app">
<header class="topbar">
<span class="topbar__brand">MapTool</span>
</header>
<div class="app-body">
<nav id="sidebar" class="sidebar"></nav>
<div id="map"></div>
</div>
</div>
<div id="basemap-panel" class="basemap-panel" hidden>
<div class="basemap-panel__header">
<strong>Base map</strong>
<button type="button" class="basemap-panel__close" aria-label="Close">&times;</button>
</div>
<div class="basemap-panel__list">
<button type="button" class="basemap-panel__option" data-style="online">
<span>Online</span>
<span class="basemap-panel__check">&check;</span>
</button>
<button type="button" class="basemap-panel__option" data-style="offline">
<span>Offline</span>
<span class="basemap-panel__check">&check;</span>
</button>
</div>
</div>
<div id="gpx-panel" class="gpx-panel" hidden>
<div class="gpx-panel__header">
<strong>GPX</strong>
<button type="button" class="gpx-panel__close" aria-label="Close">&times;</button>
<div class="gpx-panel__actions">
<button type="button" class="gpx-panel__action" data-action="new-folder" title="New folder" aria-label="New folder"></button>
<button type="button" class="gpx-panel__action" data-action="upload" title="Upload GPX file" aria-label="Upload GPX file"></button>
<button type="button" class="gpx-panel__close" aria-label="Close">&times;</button>
</div>
</div>
<div class="gpx-dropzone">
Drop .gpx files here, or click to browse
<input type="file" class="gpx-dropzone__input" accept=".gpx" multiple hidden>
</div>
<input type="file" class="gpx-file-input" accept=".gpx" multiple hidden>
<p class="gpx-upload__status" hidden></p>
<form class="gpx-new-folder">
<input type="text" name="path" placeholder="new folder path (e.g. Hiking/2026)">
<button type="submit">+ folder</button>
</form>
<div class="gpx-tree"></div>
</div>