# map.svelte + navigation_plan.svelte + interest_locations.svelte — Map & Navigation Components

### map.svelte

<span style="white-space: pre-wrap;">The core map display component. Loads a map file from </span>`<span class="editor-theme-code"><appDataDir>/maps/</span>`<span style="white-space: pre-wrap;">, renders it in a letterboxed </span>`<span class="editor-theme-code"><img></span>`<span style="white-space: pre-wrap;"> element, and overlays interactive pins on top. Accepts a </span>`<span class="editor-theme-code">mode</span>`<span style="white-space: pre-wrap;"> prop that controls what happens when the operator clicks the map.</span>

<table id="bkmrk-modeclick-behaviourp"><colgroup><col></col><col style="width: 223px;"></col><col></col></colgroup><tbody><tr><th>Mode

</th><th>Click behaviour

</th><th>Pins shown

</th></tr><tr><td>`<span class="editor-theme-code">navigation</span>`

</td><td><span style="white-space: pre-wrap;">Adds a </span>`<span class="editor-theme-code">PinnedCoord</span>`<span style="white-space: pre-wrap;"> to the </span>

`<span class="editor-theme-code">pinnedCoords</span>`<span style="white-space: pre-wrap;"> store</span>

</td><td>Unassigned pins + start/waypoint/end markers

</td></tr><tr><td>`<span class="editor-theme-code">science</span>`

</td><td><span style="white-space: pre-wrap;">Adds an </span>`<span class="editor-theme-code">InterestLocation</span>`<span style="white-space: pre-wrap;"> to </span>`<span class="editor-theme-code">scienceLocations</span>`

</td><td>Science location pins

</td></tr><tr><td>`<span class="editor-theme-code">probing</span>`

</td><td><span style="white-space: pre-wrap;">Adds an </span>`<span class="editor-theme-code">InterestLocation</span>`<span style="white-space: pre-wrap;"> to </span>`<span class="editor-theme-code">probingLocations</span>`

</td><td>Probing location pins

</td></tr></tbody></table>

***Map loading***<span style="white-space: pre-wrap;"> — On mount, if </span>`<span class="editor-theme-code">displayedMap</span>`<span style="white-space: pre-wrap;"> store already holds a filename it is opened immediately; otherwise the component calls </span>`<span class="editor-theme-code">list_task_files("maps")</span>`<span style="white-space: pre-wrap;"> and shows a selection modal. If only one map file exists it is auto-selected. The reload button (⟳) clears all state and returns to the selection modal.</span>

3D formats (`<span class="editor-theme-code">.obj</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">.las</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">.laz</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">.e57</span>`<span style="white-space: pre-wrap;">) trigger a </span>`<span class="editor-theme-code">render_map</span>`<span style="white-space: pre-wrap;"> invoke before display, showing a spinner while the backend works. Plain image formats are loaded directly via </span>`<span class="editor-theme-code">convertFileSrc</span>`<span style="white-space: pre-wrap;">. After rendering, the </span>`<span class="editor-theme-code">_preview.png</span>`<span style="white-space: pre-wrap;"> path is used for all subsequent display.</span>

***Coordinate geometry*** <span style="white-space: pre-wrap;">— </span>`<span class="editor-theme-code">getRenderedRect()</span>`<span style="white-space: pre-wrap;"> computes the actual rendered rectangle of the image inside its element (accounting for letterboxing / </span>`<span class="editor-theme-code">object-fit</span>`<span style="white-space: pre-wrap;"> behaviour). </span>`<span class="editor-theme-code">eventToImgPixel()</span>`<span style="white-space: pre-wrap;"> converts a raw </span>`<span class="editor-theme-code">MouseEvent</span>`<span style="white-space: pre-wrap;"> into a pixel coordinate within the PNG, with Y flipped so the origin is bottom-left. </span>`<span class="editor-theme-code">worldToCSSPos()</span>`<span style="white-space: pre-wrap;"> is the inverse — takes a world-space </span>`<span class="editor-theme-code">(x, y)</span>`<span style="white-space: pre-wrap;"> in metres and returns a </span>`<span class="editor-theme-code">left/top</span>`<span style="white-space: pre-wrap;"> percentage suitable for absolute positioning an overlay element on top of the image. Both functions short-circuit to </span>`<span class="editor-theme-code">null</span>`<span style="white-space: pre-wrap;"> when </span>`<span class="editor-theme-code">mapMeta</span>`<span style="white-space: pre-wrap;"> is unavailable.</span>

***Mouse interaction***<span style="white-space: pre-wrap;"> — </span>`<span class="editor-theme-code">onMouseMove</span>`<span style="white-space: pre-wrap;"> calls </span>`<span class="editor-theme-code">eventToImgPixel</span>`<span style="white-space: pre-wrap;"> and then invokes </span>`<span class="editor-theme-code">pixel_to_world</span>`<span style="white-space: pre-wrap;"> to display a live coordinate overlay in the bottom-left corner (pixel position + world metres + "Click to pin" hint). The overlay disappears when the cursor leaves the image.</span>

***GPS marker***<span style="white-space: pre-wrap;"> — Listens for </span>`<span class="editor-theme-code">gps-update</span>`<span style="white-space: pre-wrap;"> Tauri events on mount. The payload's </span>`<span class="editor-theme-code">longitude</span>`/`<span class="editor-theme-code">latitude</span>`<span style="white-space: pre-wrap;"> fields are reused directly as map-space X/Y metres. When a valid GPS position is in the store, a directional arrow marker (</span>`<span class="editor-theme-code">▶</span>`<span style="white-space: pre-wrap;">) is rendered at the corresponding map position, rotated by </span>`<span class="editor-theme-code">heading</span>`<span style="white-space: pre-wrap;"> via a CSS custom property </span>`<span class="editor-theme-code">--heading</span>`.

***Pinned coordinate list*** <span style="white-space: pre-wrap;">— In </span>`<span class="editor-theme-code">navigation</span>`<span style="white-space: pre-wrap;"> mode, a sidebar panel lists all </span>`<span class="editor-theme-code">pinnedCoords</span>`<span style="white-space: pre-wrap;"> with copy-to-clipboard (📋) and remove (✕) buttons. Hovering a row highlights the corresponding pin on the map, and vice versa, via </span>`<span class="editor-theme-code">hoveredPinId</span>`.

### navigation\_plan.svelte

<span style="white-space: pre-wrap;">A sidebar panel for building a navigation route from map-pinned coordinates. Displays a structured plan of start point → ordered waypoints → end point, and surfaces any </span>`<span class="editor-theme-code">pinnedCoords</span>`<span style="white-space: pre-wrap;"> that haven't yet been assigned a role.</span>

***Route structure***<span style="white-space: pre-wrap;"> — The plan always shows three sections in order: a Start card, a draggable Waypoints list, and an End card. Unset slots show "Not set". Hovering any card cross-highlights its corresponding pin on the map via the </span>`<span class="editor-theme-code">hoveredNavId</span>`<span style="white-space: pre-wrap;"> store (shared with </span>`<span class="editor-theme-code">map.svelte</span>`).

***Waypoint reordering*** <span style="white-space: pre-wrap;">— The waypoint list uses </span>`<span class="editor-theme-code">svelte-dnd-action</span>`<span style="white-space: pre-wrap;"> (</span>`<span class="editor-theme-code">dndzone</span>`<span style="white-space: pre-wrap;">) for drag-and-drop reordering. Both </span>`<span class="editor-theme-code">consider</span>`<span style="white-space: pre-wrap;"> and </span>`<span class="editor-theme-code">finalize</span>`<span style="white-space: pre-wrap;"> events write the new order directly to the </span>`<span class="editor-theme-code">waypoints</span>`<span style="white-space: pre-wrap;"> store.</span>

***Promoting pinned coords*** <span style="white-space: pre-wrap;">— Each </span>`<span class="editor-theme-code">PinnedCoord</span>`<span style="white-space: pre-wrap;"> from the map appears in a "Pinned from map" section at the bottom of the list. Three buttons let the operator promote a pin to Start, add it as a new Waypoint, or set it as End. In all cases the pin is removed from </span>`<span class="editor-theme-code">pinnedCoords</span>`<span style="white-space: pre-wrap;"> after promotion.</span>

***Destructive actions***<span style="white-space: pre-wrap;"> — Removing a waypoint and clearing the entire plan both trigger a native </span>`<span class="editor-theme-code">confirm()</span>`<span style="white-space: pre-wrap;"> dialog before proceeding.</span>

***Map import***<span style="white-space: pre-wrap;"> — The "+ Add Map File" button opens a file picker (via </span>`<span class="editor-theme-code">@tauri-apps/plugin-dialog</span>`<span style="white-space: pre-wrap;">) filtered to </span>`<span class="editor-theme-code">.json</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">.geojson</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">.txt</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">.jpeg</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">.obj</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">.las</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">.laz</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">.e57</span>`<span style="white-space: pre-wrap;">, then calls </span>`<span class="editor-theme-code">invoke("import_map_file")</span>`<span style="white-space: pre-wrap;"> to copy the chosen file into the app's maps directory. The "▶︎ Plan Route" button is present but not yet wired to a backend command.</span>

### interest\_locations.svelte

A generic, reusable sidebar list for named locations of interest. Used by both the Science and Probing task panels, which pass in their respective stores (`<span class="editor-theme-code">scienceLocations</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">probingLocations</span>`<span style="white-space: pre-wrap;"> and the matching hovered-id store) as props.</span>

<span style="white-space: pre-wrap;">Props: </span>`<span class="editor-theme-code">locations</span>`<span style="white-space: pre-wrap;"> — a </span>`<span class="editor-theme-code">Writable<InterestLocation[]></span>`<span style="white-space: pre-wrap;"> store; </span>`<span class="editor-theme-code">hoveredId</span>`<span style="white-space: pre-wrap;"> — a </span>`<span class="editor-theme-code">Writable<string | null></span>`<span style="white-space: pre-wrap;"> store shared with </span>`<span class="editor-theme-code">map.svelte</span>`<span style="white-space: pre-wrap;"> for cross-highlighting.</span>

<span style="white-space: pre-wrap;">Each location row shows its auto-generated name and its </span>`<span class="editor-theme-code">(x, y)</span>`<span style="white-space: pre-wrap;"> coordinates in metres. Hovering a row sets </span>`<span class="editor-theme-code">hoveredId</span>`, which causes the corresponding pin on the map to highlight. Two actions are available per row:

- **Rename (✏️)**<span style="white-space: pre-wrap;"> — Switches the name label to an inline </span>`<span class="editor-theme-code"><input></span>`. The edit is committed on blur or Enter; if the input is left empty the original name is kept. Only one location can be in edit mode at a time (`<span class="editor-theme-code">editingId</span>`<span style="white-space: pre-wrap;"> state).</span>
- **Remove (✕)**<span style="white-space: pre-wrap;"> — Removes the location from the store immediately with no confirmation.</span>

When the list is empty a hint instructs the operator to click the map to add a location.