GARN TOPO Documentation

Complete reference for every feature in GARN TOPO — an offline-capable topographic mapping application for Android and Web. No account required. No subscription. Your data stays on your device.

Current version: 0.5.0 · This build adds Slope Angle Shading (DEM-computed, avalanche-style ramp), zoom-in-all-the-way overzoom on every base layer, a boot screen that holds until the real map is painted plus a background world-overview prewarm for instant global panning, and ATAK GeoChat bridging (two-way chat with ATAK) alongside affiliation-aware markers and TAK-Protocol "Mesh SA" header handling. The offline-area download button moved into the top bar to declutter the main controls. Prior builds added ten no-key vector basemaps, the draggable Area Saver, infinite world wrap, and offline 3D terrain.

Getting Started

Install, permissions, and your first map.

Map Interface

Layers, 3D terrain, controls, and compass.

Team Sync

P2P, LAN, Reticulum, drone, ATAK.

Offline Maps

PMTiles archives, Area Saver, storage.

SAR Operations

Periods, resources, clues, and gear.

Bridge Tools

LAN relay, Reticulum, ATAK/soRD bridges.


Installation

GARN TOPO runs on Android and the web. Choose the one that fits your device.

Android

  1. Install from Google Play (search "GARN TOPO").
  2. On first launch, the app shows a prominent disclosure and requests Location (for GPS position and track recording). The Microphone is requested only when you record a voice note. You can update these any time in Android Settings.
  3. The app works immediately — no sign-in, no setup wizard.

Web / PWA

Open garntopoapp.pages.dev in any modern browser. For offline use, install it as a PWA: in Chrome/Edge click Install app in the address bar; in Firefox/Safari use Add to home screen.

First Launch

On first launch GARN TOPO shows a prominent disclosure, then requests Location using the native OS dialog (foreground only — never in the background). The Microphone permission is requested in-context the first time you record a voice note. On Web, the browser asks for Location when you first use GPS.

You can decline any permission dialog. The app still works — only the corresponding feature (GPS, voice notes) is unavailable until you grant it in Settings.

The default base layer on first launch is OpenFreeMap Liberty — a full-detail vector street map rendered via MapLibre GL. Your map starts centered at the contiguous US (z4) or at your last-saved position if you've used the app before.

After your first session, all key UI state is automatically persisted — base layer selection, map position and zoom, and hillshade opacity are restored exactly as you left them on every subsequent launch.

Basic Navigation

The interface has three main areas:

  • Map canvas — the full-screen map. Pan with one finger (touch) or mouse drag. Pinch or scroll to zoom. Rotate with two fingers.
  • Top bar — map title, layer switcher, GPS button, search, and the More drawer trigger.
  • More drawer — opens from the hamburger menu: Map Objects, Folders, Operational Periods, Team Members, Team Sync, Gear, Settings, Offline Tiles, and Import/Export.

Long-press anywhere on the map to open the context menu: drop a marker here, start a line from here, or view point info.


Base Layers

Tap the layer icon (stack icon in top bar) to open the layer panel. Twenty-four base layers are included — 21 raster layers organized into four groups, plus three OpenFreeMap vector layers — plus a built-in browser for 1,345 additional worldwide imagery sources (OSM Editor Layer Index).

Topo & Terrain

LayerBest forMax zoomArea Saver
USGS TopoUSGS composite topo, US-focused16
USGS Imagery + TopoAerial imagery overlaid with topo labels16
OpenTopoMapWorldwide topographic contours, elevation, terrain17
NatGeo WorldNational Geographic styled topographic map16
Esri World TopoEsri's worldwide topographic basemap19
USA Topo (Historical)Historical USGS quadrangle scans15
Esri World PhysicalPhysical terrain without labels — clean reference8

Imagery

LayerBest forMax zoomArea Saver
Esri SatelliteHigh-resolution aerial imagery worldwide19
USGS ImageryUSGS-sourced US aerial imagery16

Water / Bathymetric

LayerBest forMax zoomArea Saver
Esri Ocean BasemapBathymetry, depth contours, GEBCO / NOAA marine data — strong for water-SAR13

Street & General

LayerBest forMax zoomArea Saver
OpenStreetMapStandard OSM street map (also the never-blank fallback)19
OSM HumanitarianHumanitarian OSM Team styled map19
CyclOSMRoads + cycle infrastructure detail18

Vector Basemaps

Ten no-key vector basemaps are available directly from the base-layer dropdown — no PMTiles import required — rendered by MapLibre GL JS. Five come from OpenFreeMap and five from VersaTiles. Both sets are free, require no API key, and overzoom cleanly (they stay sharp at any zoom). As of v0.3.16 their style, glyphs, and sprites are bundled in the app, so a vector map renders even from a cold offline start (only the tiles need a connection until you download an area).

LayerStyleBest for
OFM Liberty (default)Detailed street + terrain labelsGeneral navigation, hiking, field ops
OFM BrightHigh-contrast colorfulReadability in bright sunlight
OFM Positron · VersaTiles NeutrinoMinimal lightData overlay base, planning
OFM Dark · VersaTiles Eclipse / ShadowDark / nightLow-light, night ops, battery save
OFM Fiord · VersaTiles GraybeardMuted / grayscaleSubtle base under overlays
VersaTiles ColorfulVivid general-purposeEveryday navigation
The vector style/glyphs/sprites are bundled, but the vector tiles still stream from OpenFreeMap / VersaTiles until cached. For a fully offline vector map, use Area Saver to download an area (or import a .pmtiles vector archive). As of v0.3.16 the redundant raster street maps (CartoDB, Esri Streets/Light-Gray, Wikimedia, OSM-DE) were removed since these vector basemaps replace them; topo, satellite, and bathymetric layers stay raster (no no-key vector equivalent exists).

Imagery Catalog (NEW in 0.2.5)

Beyond the 23 built-in layers, GARN TOPO ships with a filtered copy of the OSM Editor Layer Index — 1,345 worldwide imagery sources curated for OSM editing (regional aerials, government topo maps, historical photos, etc.). Tap Layers → Custom WMS / WMTS → Browse Imagery Catalog and filter by:

  • Search — name, country code, description
  • Type — XYZ (TMS) or WMS
  • Category — aerial photo, map, historic photo, historic map, elevation, OSM-based, QA
  • Country — ISO country code dropdown

One-tap Add converts the entry into a custom source (TMS templates have {zoom} rewritten to {z}; WMS endpoints have LAYERS and FORMAT auto-extracted) and persists it across sessions. Added sources appear in the dropdown alongside the built-in 23 layers.

The Area Saver column indicates whether the layer's tile server allows reading bytes from JavaScript (CORS-enabled). All listed base layers are CORS-friendly and can be downloaded into a local .pmtiles archive — see Offline Maps (PMTiles) below.

You can also add custom tile sources manually: tap Add WMS/WMTS Source at the bottom of the layer panel, enter a tile URL template (https://example.com/{z}/{x}/{y}.png), a name, and tap Add.

Overlays

Overlays layer on top of any base map. Multiple overlays can be active simultaneously, each with an adjustable opacity slider.

Terrain & Topography

  • Slope Angle Shading (NEW in 0.5.0) — terrain coloured by steepness, computed on-device from the elevation (DEM) data — no external service. The avalanche-oriented ramp keeps gentle terrain clear and heats up through the critical band: 27–29° yellow, 30–34° orange, 35–45° red (where most slab avalanches release), 46–50° purple, 51–59° blue, 60°+ grey (cliffs). Toggle it under Layers → Conditions → Slope Angle with an opacity slider. Works online (Mapterhorn DEM) and offline once DEM tiles are cached or a terrain archive is imported. Renders from zoom 8 in.
  • Hillshade — shaded relief from Esri's World_Hillshade service (DEM-derived). Pairs well with imagery and any topo layer.
  • USGS Topo Overlay — overlays USGS Topo at adjustable opacity for blending with satellite imagery.
  • USGS Hydrography — streams, rivers, water bodies from USGS National Map.

Reference

  • Boundaries & Places — Esri labels overlay (great companion for satellite imagery).
  • Transportation — Esri's roads/highways reference layer.

Route Discovery

  • Hiking Trails — Waymarked Trails hiking-route overlay (display only — see CORS note).
  • Cycling Routes — Waymarked Trails cycling network (display only).
  • MTB Trails — Waymarked Trails mountain-bike network (display only).
  • Public Lands (BLM) — US Bureau of Land Management ownership and surface management. Available at zoom 9+ (display only).
  • OpenSeaMap — nautical seamarks for coastal/marine ops.
  • OpenSnowMap pistes (NEW) — worldwide ski/snowboard pistes and lifts — great for winter SAR and backcountry skiing.
  • OpenRailwayMap (NEW) — global rail infrastructure: tracks, stations, signals.
  • OpenInfraMap power (NEW) — high-voltage transmission lines and substations from OSM data.
  • Esri Reference Labels (NEW) — transparent label-only overlay; pairs with satellite/physical bases.
CORS / Area Saver note: Waymarked Trails and BLM tiles render fine in the map viewer (the browser's <img> loader doesn't need CORS), but their servers don't send Access-Control-Allow-Origin, so the Area Saver can't read tile bytes for offline pre-download. To take those overlays offline, you'd need a third-party PMTiles export of the same data and import it via Settings → Offline Maps → Import .pmtiles.

3D Terrain View

Tap the 3D badge in the layer panel or the top bar to switch to 3D terrain mode, powered by MapLibre GL with WebGL rendering.

  • Real DEM elevation data from Mapterhorn (Terrarium-encoded WebP)
  • Drapes any base layer over the 3D terrain mesh
  • Hillshade and sky atmosphere included
  • Two-finger drag to pitch the view; two-finger rotate to spin
  • Use the pitch slider to adjust terrain tilt
  • Terrain exaggeration can be increased in Settings

Offline 3D Terrain (NEW in 0.2.5)

Previously, 3D mode required internet to fetch DEM tiles from Mapterhorn. Now you can save terrain data offline:

  1. Pan/zoom to your AOI
  2. Open Layers → Save Terrain
  3. Confirm the tile count + size estimate; GARN TOPO downloads the Mapterhorn DEM tiles (z0–10 by default) and packs them into a .pmtiles archive tagged with kind: 'dem'
  4. From then on, any 3D activation prefers the local DEM archive over the network — fully air-gapped 3D terrain

The DEM archive is independent from base-tile archives. Both can coexist; the 3D engine combines them via MapLibre's raster-dem source pointed at a pmtiles:// URL.

You can also bundle a starter DEM archive with the app by adding an entry to public/maps/manifest.json with "kind": "dem" — see Offline Maps.

For the best offline 3D experience: run the Area Saver for your base-tile imagery and tap Save Terrain in the same view. Both layers go into separate PMTiles archives and combine automatically the next time you enable 3D.

Vector PMTiles

GARN TOPO can display vector PMTiles archives (OpenFreeMap exports, Protomaps Basemaps, tippecanoe output, etc.) — not just raster ones. The app uses MapLibre GL's addProtocol('pmtiles') to read tiles directly from the in-OPFS archive without any network round-trip.

Ten no-key vector basemaps (five OpenFreeMap + five VersaTiles) are available directly from the layer dropdown — no import required, and their style/glyphs/sprites are bundled for offline use. See Base Layers → Vector Basemaps. Use Area Saver (or import a .pmtiles) to take the vector tiles fully offline.
  • Import any .pmtiles file (raster or vector) via Layers → Offline Maps → Import .pmtiles.
  • Activate a vector archive: tap Use (3D) in the offline maps list. The app enters 3D mode and switches to a MapLibre style.
  • A default style is auto-generated from the archive's vector_layers metadata: every layer gets a fill, line, and circle entry filtered by geometry type, plus label symbols where the layer has a name field.
  • Vector tiles drape over the DEM just like raster bases — so vector maps get real 3D terrain too.
  • Raster + vector PMTiles can coexist; switch between them from the base-layer dropdown.
The auto-generated style is generic. Power users with a custom MapLibre style JSON can point it at pmtiles://<fileName>. A "load custom style" picker is planned for a future version.

Controls & Compass

All map controls are accessible from the map canvas:

  • Zoom buttons (+/−) — bottom-right corner
  • Download map area — the offline-area button now lives in the top bar, to the right of Point Info (moved out of the bottom-right stack in 0.5.0 to declutter the main controls). Tap it to draw/drag a box and download that region. See Area Saver.
  • Compass rose — tap to reset rotation to North-up; long-press to enable heading-up mode (map rotates with your bearing)
  • Center crosshair — a fixed crosshair at the map center for precise location reading
  • Context menu — long-press the map anywhere for: drop marker here, start line from here, view point info, navigate here
Zoom in all the way (NEW in 0.5.0): every base layer now keeps zooming to z22. Past a source's native detail, the deepest tile is smoothly scaled up (overzoom) instead of the map blanking or locking — the same behaviour as Google/Organic Maps. Vector basemaps stay crisp at any zoom.

Interface Layout

GARN TOPO now ships with two interface layouts that can be switched from Settings → Display → Interface Layout.

Mobile Layout (default on Android)

The original full-screen interface designed for phones. The map fills the entire screen. A slide-out drawer gives access to saved maps, layers, and settings. All panels open full-screen. This is the default when running as an APK or AAB.

Desktop / Tablet Layout (default on Web ≥ 900 px)

A persistent left sidebar — inspired by CalTopo's web interface — stays anchored at 280 px wide at all times. The map fills the remaining space to the right. Panels (Layers, Map Items, Settings, Team Sync, etc.) open inside the sidebar column, overlaying it without touching the map. A slim 44 px topbar spans the full width.

  • Sidebar is always visible — no drawer toggle needed
  • Panels appear within the sidebar column, leaving the map undisturbed
  • All map controls, GPS buttons, and floating overlays shift to stay inside the map area
  • Crosshair tracks the true center of the map viewport, not the full screen

Auto Detection

PlatformDefault Layout
Android APK / AAB (Capacitor native)Mobile
Web browser, screen width ≥ 900 pxDesktop / Tablet
Web browser, screen width < 900 pxMobile

Set Interface Layout → Auto in Settings to use the platform default. You can override to Mobile or Desktop / Tablet at any time; the preference persists across sessions.


Night Mode

Tap the moon icon to cycle through three states: Off → Dim → Dark. Night mode applies a CSS filter to the map canvas to reduce brightness and blue light without affecting the UI. It works on all base layers and overlays and is preserved across sessions.

Coordinate Display

Your current GPS position is always shown in the bottom status bar. Choose your preferred format in Settings → Coordinate Format:

FormatExample
Decimal Degrees (DD)39.0835° N, 108.5601° W
Degrees Minutes Seconds (DMS)39°05'00" N, 108°33'36" W
Degrees Decimal Minutes (DDM)39° 05.010' N, 108° 33.606' W
UTM12S 741234 4328765
MGRS12SVJ4123428765

Tap any displayed coordinate to copy it to the clipboard.


Markers & Waypoints

Drop a marker by long-pressing the map and selecting Drop Marker Here, or tap GPS marker to drop one at your current location. A details panel opens immediately.

Marker properties

  • Name — any text label
  • Notes — multi-line description or clue notes
  • Icon — choose from 50+ typed icons: trailhead, summit, camp, water, warning, medical, wildlife, and more
  • Color — full color picker with preset swatches
  • Folder — assign to any existing folder
  • SAR attributes — mark as clue, assign to operational period, set find status

Tap an existing marker to open its info card. From there: edit, delete, navigate to, copy coordinates, or zoom-in.

Lines & Routes

Tap Draw in the top bar then select Line. Tap the map to place vertices. A live distance display shows the running total. When done, tap Finish.

  • Each line segment shows distance in your preferred units
  • Edit mode: drag existing vertices, insert new ones, or delete
  • Assign name, color, and folder
  • Freehand mode: tap-and-hold while drawing to record a continuous freehand path

Polygons & Areas

Select Polygon in the Draw menu. Tap to place polygon vertices; the shape closes automatically when you tap Finish. Area is calculated and shown in the info panel (acres, sq km, sq mi).

Buffer zones — tap an existing object and choose Buffer Zone to create a polygon offset by a specified distance.

Sectors / wedges — from the context menu choose Draw Sector to create a pie-wedge search area with configurable bearing and radius.

Range Rings

Long-press a marker or tap More → Range Rings to draw concentric search rings around a point. Configure:

  • Number of rings — 1 to 10
  • Spacing — distance between each ring (meters or feet)
  • Custom radii — override with specific values per ring
  • Bearing offset — rotate the ring set for directional probability

Range rings are stored as a single object and can be edited or deleted from the Objects panel.

Folders & Organization

Open More → Folders to manage folders. Folders help group objects by purpose — e.g., "Day 1 Search", "Water Sources", "Hazards".

  • Create folder with name and color
  • Assign any marker, line, or polygon to a folder
  • Toggle folder visibility — hides/shows all objects inside on the map
  • Export a single folder as GeoJSON

Operational Periods

Open More → Operational Periods. Operational periods (OPs) are standard SAR planning containers — typically one per 12 or 24 hour shift.

  • Create an OP with a name, start time, and active status
  • Assign any map object to an OP
  • Objects belonging to an inactive OP can be filtered out of view
  • Export a single OP as GeoJSON
For ICS-compatible SAR operations, create one OP per official operational period and assign all that period's assignments and finds to it before exporting for the Situation Unit.

GPS Modes

The GPS button (bottom-right of map) cycles through three states:

  1. Off — no GPS. Button is gray.
  2. Follow — map re-centers on your position every few seconds. Button is blue.
  3. Heading-up — map rotates to match your bearing AND follows your position. Button is blue with rotation indicator.

An accuracy circle is displayed around your position marker. In poor GPS conditions (dense canopy, canyon) the circle will be large — always verify position against known landmarks.

GARN TOPO uses the device's built-in GPS. Accuracy depends entirely on hardware quality and sky visibility. Always carry a dedicated GPS device as backup on critical operations.

Track Recording

Tap the track icon (footprints, top bar) to start recording. A live track is drawn on the map in real time. The bottom status bar shows:

  • Elapsed time
  • Distance traveled
  • Current speed (mph or km/h)

Tap Stop to end the recording. The track is saved as a line object in your map objects list and can be exported as GPX or GeoJSON.

Measurement Tools

Tap More → Measure to open the measure tool. Tap points on the map to build a measurement path:

  • Point-to-point distance with running total
  • Bearing between last two points
  • Area measurement — close the shape to get area in acres or sq km
  • Elevation profile — tap two points to see the elevation chart along the path

Tap Clear to reset without saving the measurement.


Offline Maps (PMTiles)

The primary offline mechanism is a real .pmtiles archive stored as a single file in the device's Origin Private File System (OPFS). This replaces fragile per-tile browser caching with something that survives storage pressure, works across reloads, and can be transferred to teammates as a single file.

Why PMTiles instead of a tile cache?

  • Single file — random-access via byte ranges. No millions of Cache API entries.
  • No eviction — when the app requests persistent storage, the OS won't drop the file under storage pressure.
  • Sneakernet-friendly — copy a 500 MB regional archive over USB, drop it via Reticulum, share over LAN.
  • Same format used by Protomaps, OpenFreeMap, BBBike — bring your own archive from any of these sources.

What ships with the app

As of v0.3.0, no bundled starter archive is included. The app ships without any pre-packaged PMTiles files to keep the download lean. On first launch the default basemap is OpenFreeMap Liberty (hosted vector, requires network). To go offline, either use the Area Saver to build your own archive, or import any .pmtiles file you have.

Importing a PMTiles file

  1. Open Settings → OFFLINE MAPS (.PMTILES).
  2. Tap Import .pmtiles and pick a file from your device.
  3. The bytes stream-copy into OPFS (multi-GB safe), the header is parsed, and the archive appears in your list with bounds, zoom range, and tile format.
  4. Switch to it in the base-layer dropdown under My Maps (offline). Disable Wi-Fi to confirm — tiles render straight from the file.

Recommended sources: Protomaps, OpenFreeMap, BBBike Extract, or any tippecanoe / pmtiles convert output.

Area Saver — Build Your Own Archive

The Area Saver downloads tiles for the current viewport at any zoom range and packs them into a brand-new .pmtiles archive on your device. This is the answer to "all zoom levels for my area" — without trying to bundle the whole world.

How it works

  1. Pan the map to your area of interest (a county, a search grid, a trail system).
  2. Open Settings → OFFLINE MAPS → Select Area, or tap the offline-tiles button on the map toolbar. A green selection box appears on the map.
  3. Drag the box to reposition and pull any corner to resize, then set the min and max zoom. A live estimate shows tile count + size.
  4. Tap Download Area → confirm the download size → progress bar shows tiles downloaded and bytes written.
  5. The archive lands in your My Maps list automatically, ready to use.

Select Area on Map (NEW in 0.3.16)

For a precise, Google-Maps-style selection, open Settings → OFFLINE MAPS → Select Area. A green box appears on the map — drag it to reposition and pull any corner to resize until it frames exactly the area you want. Pick the min/max zoom (the panel shows a live tile-count and size estimate), then tap Download Area. It downloads that exact rectangle of the current base map — raster or vector — into a .pmtiles archive via the same background, resumable engine.

Download by Region (NEW in 0.3.15)

Don't want to frame the viewport by hand? Open Settings → OFFLINE MAPS → Download Offline Map → Browse and tap Pick a region to download. Choose a preset continent, country, or mountain range, pick the zoom range with a live size estimate, and the current base map is saved for that whole area — no panning required. The same sheet also lists ready-made packages and accepts a direct .pmtiles / .mbtiles URL.

  • Preset regions — continents, countries, and mountain ranges with one-tap download
  • Per-download zoom — choose min/max zoom with a live tile-count & size estimate before you commit
  • Background & resumable — downloads run in the background and resume automatically after an app restart, so a dropped connection or a closed app won't lose progress

Why it scales

  • Streaming write — tiles go straight from the network to OPFS via the FileSystemWritableFileStream. The app never holds the whole archive in memory, so multi-GB regions are safe even on phones.
  • SHA-1 deduplication — identical tiles (ocean, forest canopy, blank areas) are stored once. Typical savings: 30–50% on raster regions with lots of homogeneous terrain.
  • Cancellable — tap Cancel and the partial OPFS file is removed cleanly.
  • Polite — concurrency capped at 4 simultaneous fetches.

Realistic sizing

RegionZoom rangeTilesApprox size
Single county (~50 × 50 km)z6–z14~10K~120 MB
Single countyz6–z16~50K~600 MB
Single statez6–z14~80K~1 GB
Single statez6–z16~400K~5 GB
Bundling every zoom level for the entire world isn't physically possible — z0–z18 worldwide is ~91 billion tiles (~250 TB). The Area Saver is the answer: pick the area you actually need, save it once on Wi-Fi, then go offline with all zoom levels for that region.
CORS layers: Layers whose tile servers don't send Access-Control-Allow-Origin headers (Waymarked Trails, BLM, USFS) silently skip in the Area Saver — the browser can't read their bytes from JavaScript. They render fine while online but can't be pre-downloaded. CARTO, Esri ArcGIS, USGS National Map, OSM, OpenSeaMap all work for offline pre-download.

Online Tile Cache (Fallback)

For tiles you happen to view while online — outside any imported PMTiles archive — the service worker still caches them to Cache API storage as a stale-while-revalidate fallback. This is now a secondary mechanism; PMTiles archives are the canonical offline source.

  • All base layers and overlays cached independently while online
  • Cached tiles continue to serve when the connection drops
  • DEM terrain tiles (for 3D) cached alongside base layers
  • Retina-aware: {r}=@2x URL substitution matches between cache write and live <img> request
  • Subject to OS storage-pressure eviction (which is exactly why PMTiles archives are the canonical mechanism — they're not evicted)

Storage Management

Open Settings → OFFLINE MAPS for archive management, or scroll down for online-cache controls:

  • My archive list — name, size, zoom range, and tile format per imported archive. Each row has Use / Remove buttons. Bundled archives are flagged.
  • Tile cache size — total storage used by the online fallback cache
  • Clear tile cache — removes only the online cache; PMTiles archives are unaffected
  • Clear app cache — clears app-level cache (does not delete map objects or archives)
Map objects (markers, lines, polygons), PMTiles archives, and the online tile cache are all stored separately. Clearing one never affects the others.

Team Sync — Overview

Team Sync lets multiple GARN TOPO users share a live map — markers, lines, and polygons propagate to all connected peers in real time, along with each user's GPS position. Open via More → Team Sync.

How it works

  • Objects are synced with a last-write-wins CRDT — the most recent edit always wins
  • GPS positions broadcast every 4 seconds; peers appear as colored circles on the map
  • Peer identity is ephemeral per session (no accounts)
  • Five transport modes are available depending on connectivity
ModeInternetLAN onlyOfflineRange
P2P (Nostr)Global
LAN HubLAN (~150m WiFi)
ReticulumOptionalLoRa/WiFi mesh
MAVLink DroneWiFi/serial

Team Sync — P2P (Internet)

Uses Trystero over Nostr relays — serverless peer-to-peer WebRTC. No server infrastructure to maintain; peers discover each other via public Nostr relay nodes.

  1. In Team Sync, enter your display name and role, pick a color.
  2. Select Internet / P2P mode (default).
  3. Enter or generate a 6-character session code and share it with teammates (QR code button or copy).
  4. Tap Join Session. Teammates enter the same code.
P2P mode requires each device to have an internet connection to reach the Nostr relay for peer discovery. The actual map data flows peer-to-peer directly after discovery — no relay stores your data.

Team Sync — LAN Hub

For use when all devices are on the same WiFi network — ideal for incident command posts and field ops with a local hotspot. Run relay.mjs on any Node.js device on the network:

node bridge/relay.mjs [port]
# Default port: 8080
# Example output:
#   URL:  ws://192.168.1.45:8080

In Team Sync, switch to Advanced → LAN Hub transport and enter ws://<relay-ip>:8080.

Bonus: relay.mjs automatically joins the ATAK CoT multicast group (239.2.3.1:6969). If soRD or any ATAK-compatible device is broadcasting detections on the same network, they flow to all connected GARN clients with no extra configuration.

Team Sync — Reticulum & LXMF

Reticulum is a cryptographic mesh networking stack supporting LoRa radios, WiFi, TCP/IP, and more. GARN bridges Reticulum to its WebSocket protocol via reticulum_bridge.py, and includes full LXMF support so you can exchange messages with Sideband and other LXMF-compatible apps.

Run the command below on any computer on your mesh network; GARN TOPO (Android or web) connects to it over WebSocket.
pip install --user --break-system-packages rns lxmf websockets msgpack
python3 bridge/reticulum_bridge.py --port 4242

The bridge creates a persistent identity at ~/.garntopo/bridge.identity and announces it on the Reticulum mesh. In Team Sync, use Advanced → Reticulum transport and enter the bridge's WebSocket URL (default ws://localhost:4242, or its LAN IP).

LXMF address — your mesh identity

Once connected, the Team Sync drawer shows an LXMF Mesh Devices section with:

  • Your LXMF address — a 32-character hex string, with Copy / Share / Announce / Refresh buttons
  • Add a Device — paste another address (from a teammate or a Sideband contact) and tap Connect; the bridge requests a path on the mesh and registers the device as a known peer
  • Connected Devices — every peer ever announced or added is listed with display name, online status (green dot), and last-seen time

Display names from Sideband peers are decoded from the announce app_data field (msgpack [name, stamp_cost] for LXMF ≥ 0.5, or legacy UTF-8). Bidirectional messaging works: when you send an LXMF message, the bridge picks DIRECT delivery and registers delivered/failed callbacks so you see status in the UI.

Over LoRa (e.g., RNode hardware), expect latency of 3–30 seconds — ideal for position sharing where timeliness is less critical than coverage.

Team Sync — ATAK CoT / soRD Detections

GARN TOPO talks ATAK Cursor on Target (CoT) over UDP multicast both ways. Your team members appear on any ATAK device on the same LAN, and inbound detections from ATAK or the soRD SAR drone platform appear on your map as color-coded markers in real time.

Bidirectional behavior (since v0.2.4)

  • Outbound: when GARN is connected to atak_bridge.py, every GPS update is converted to a CoT 2.0 PLI event of type a-f-G-U-C-I (friendly · ground · unit · civilian · individual) and broadcast to 239.2.3.1:6969. Each peer gets a stable UID (GARN.<peerId>) so ATAK updates the same marker rather than spawning new ones, with a 60-second stale attribute so dropped peers auto-clear.
  • Inbound: incoming CoT events are parsed for position, callsign, remarks, thermal score (Thermal: 0.XX), ArcFace score (ArcFace: 0.XX), composite confidence (Conf: XX%), CoT type, color argb, and the original stale timestamp. All scores are displayed on the marker label and detection list. A sweeper auto-removes detections after their stale time elapses (default 120 s), matching ATAK's own behavior.

GeoChat & markers (NEW in 0.5.0)

  • GeoChat bridging (both ways): messages you send in Team Chat are emitted as ATAK GeoChat (b-t-f) CoT, so they land in ATAK's chat window; inbound ATAK GeoChat appears in GARN chat tagged (ATAK). Works even in an ATAK-only session with no P2P/LAN room. Echo of your own messages is suppressed via a BAO.F.GARN. source tag.
  • Affiliation classification: inbound events now carry an affiliation (friend / hostile / neutral / unknown) derived from the CoT type, so friendly units, neutral contacts and placed markers — not just hostile detections — are distinguished on the map.
  • Outbound markers: the bridge accepts cot_marker frames and emits them as CoT marker events, so a GARN-placed point can be pushed to ATAK.
  • TAK Protocol "Mesh SA" framing: the bridge unwraps the 0xbf TAK Protocol header. Version 0 (CoT XML) is decoded; version 1 (protobuf) is detected and skipped with a one-time notice — set ATAK to "CoT XML" output, or route protobuf mesh through a TAK server.

Detection confidence colors

  • Red circle — High confidence ≥85% hostile (a-h-*) — auto-alert
  • Orange circle — Review queue 60–85% hostile — manual verification needed
  • Blue circle — Friendly CoT type (a-f-*) — PLI / teammate position

Automatic connection

The app silently tries ws://localhost:4243 at startup; if the bridge is running on the same machine (or you enter its LAN address), it connects automatically and your team starts publishing CoT.

Manual setup

pip install --user --break-system-packages websockets
python3 bridge/atak_bridge.py --ws-port 4243 --mcast-group 239.2.3.1

In Team Sync, expand ATAK CoT Detections (soRD) and enter ws://<bridge-ip>:4243.

If you're using the LAN relay (relay.mjs) instead, CoT detections flow automatically as before — but outbound CoT requires atak_bridge.py.

soRD platform overview (v5)

soRD is a compact SAR drone system. The v5 ground station adds a multimodal detection pipeline and per-mission folder management:

  • AMG8833 thermal sensor → ESP32-S2 DAC → 5.8 GHz VTX video transmitter
  • ROTG02 USB capture → YOLOv5n CPU inference → ArcFace identity verification
  • Composite score (Conf: XX%), thermal score (Thermal: 0.XX), and ArcFace score (ArcFace: 0.XX) are all embedded in CoT remarks and displayed separately on GARN markers
  • Scores ≥0.85 trigger automatic ATAK CoT broadcasts to 239.2.3.1:6969 with a 120 s stale window
  • Scores 0.6–0.85 enter an operator review queue (also broadcast as CoT with lower score)
  • Per-mission folders: detections, crops, and thermal frames are saved under a timestamped mission directory; the operator review dialog lets you accept/reject each detection
  • WiFi latency ~260 ms; LoRa relay latency 3–30 s

Team Chat

Chat is available in any Team Sync session. Tap the chat bubble icon to open the chat panel. The input bar sits at the top of the panel so it remains visible when the keyboard is open on mobile.

  • Text messages — sent to all peers in the session
  • Voice messages — tap-and-hold the microphone button to record; release to send; tap to cancel
  • Chat history is stored locally for the current session
  • Unread message badge on the chat button
  • ATAK GeoChat bridge (NEW in 0.5.0) — when the ATAK bridge is connected, text chat is mirrored to/from ATAK's GeoChat, so GARN and ATAK users share one conversation.
GPS is automatically enabled (locked mode) when you join a Team Sync session, so your position broadcasts to teammates immediately without an extra tap.

SAR — Operational Periods

Open More → Operational Periods. Each period represents a planning cycle (typically 12 or 24 hours) in a search operation. Assign map objects to periods to keep your map organized and filterable.

  • Name, start date/time, and status (active/inactive)
  • Toggle visibility of all objects in a period with one switch
  • Export a period's objects as GeoJSON for incident documentation

SAR — Resources & Roles

Open More → Team Members. Each member has:

  • Name and role (team leader, member, medical, air, K9, logistics, etc.)
  • Contact info — radio channel, phone, callsign
  • Assignment — linked to an operational period

Import/export team rosters as JSON or CSV for briefings and ICS documentation.

SAR — Gear / Pack List

Open More → Gear. Build a complete inventory of field equipment with:

  • Item name, category, quantity, weight
  • Total pack weight summary
  • Category grouping (shelter, navigation, medical, comms, etc.)
  • Export as JSON or CSV for logistics planning
  • Import from a previous operation's export

SAR — Clue & Find Logging

Any marker can be flagged as a clue or find using its SAR properties panel:

  • Type: Physical clue, track, trail, scent, shelter, victim found, deceased
  • Description: Detailed notes
  • Time found: Timestamp recorded automatically
  • Assigned to OP: Links to an operational period
  • Photo: Attach a camera photo directly to the clue marker

All clue markers are also exported in GeoJSON with full SAR metadata intact.


Import & Export — GeoJSON

GeoJSON is GARN TOPO's primary exchange format. All map objects are serialized to standard GeoJSON with GARN-specific properties in the properties object.

Export

Open More → Export → GeoJSON. Choose to export all objects, a specific folder, or a specific operational period. The file saves to the device's Downloads folder or triggers a browser download.

Import

Open More → Import and select a .geojson or .json file. Objects are merged into the current map — existing objects with the same ID are updated; new IDs are created. Drag-and-drop import is supported in the web app.

Import & Export — KML / KMZ

Export to KML for use in Google Earth, CalTopo, or other GIS tools. KMZ (zipped KML) is also supported for import. Exported KML includes point placemarks, line strings, and polygon features with names and color styling.

Import & Export — GPX

GPX export is ideal for loading routes onto dedicated GPS devices (Garmin, Suunto, etc.). Waypoints export as <wpt> elements; recorded tracks export as <trk>. GPX import reads both <wpt> and <trk>/<rte> elements.

Import & Export — CSV

CSV export writes all marker coordinates and properties to a spreadsheet-compatible file — useful for importing into ArcGIS, QGIS, or sharing data with agencies that use spreadsheet-based tools. Team member rosters and gear lists also export/import as CSV.


Bridge Tools — LAN Relay (relay.mjs)

The LAN relay is a lightweight Node.js WebSocket server for offline LAN-only Team Sync. It requires no internet connection — just Node.js installed on any device on the network.

# Start on default port 8080
node bridge/relay.mjs

# Custom port
node bridge/relay.mjs 9000

The relay prints its WebSocket URLs on startup. Share one with teammates for the LAN Hub URL field in Team Sync. The relay also automatically listens on UDP multicast 239.2.3.1:6969 for ATAK CoT detection events and forwards them to all connected clients — no separate bridge needed.

Run the relay on a laptop acting as a mobile hotspot. Android devices can connect to the hotspot and sync maps without any internet service.

Bridge Tools — Reticulum / LXMF Bridge (reticulum_bridge.py)

Bridges the Reticulum mesh network to GARN's WebSocket protocol with full LXMF identity, addressing, and messaging — Sideband-compatible.

pip install --user --break-system-packages rns lxmf websockets msgpack
python3 bridge/reticulum_bridge.py --port 4242 [--channel garntopo]

Features:

  • Persistent identity stored at ~/.garntopo/bridge.identity — destination hash stays consistent across restarts
  • Auto re-announce every 60 s for both the GARN destination and the LXMF delivery destination
  • RNS announce handler with aspect_filter='lxmf.delivery' picks up Sideband / other GARN bridges automatically and forwards them as lxmf_announce frames
  • Persistent peer cache at ~/.garntopo/lxmf_peers.json — known devices survive restarts
  • Display-name decoding: msgpack [name, stamp_cost] (LXMF ≥ 0.5) with UTF-8 fallback for legacy clients
  • LXMF delivery callbacks → UI delivered/failed status

WebSocket protocol

The bridge exposes a JSON WS protocol on ws://0.0.0.0:4242 (configurable):

DirectionFramePurpose
→ client{type:'lxmf_ready', address, display_name, peers}Sent on every WS connect
→ client{type:'lxmf_announce', address, display_name, ts}Peer announced on the mesh
→ client{type:'lxmf_message', from, content, title, ts}Inbound LXMF message
→ client{type:'lxmf_delivery', to, state}Delivery status update
← client{type:'lxmf_get_state'}Request current address + peer list
← client{type:'lxmf_announce'}Manually re-announce on the mesh
← client{type:'lxmf_add_contact', address, display_name}Discover + remember a peer
← client{type:'lxmf_send', to, content, title}Send an LXMF message

Bridge Tools — ATAK CoT Bridge (atak_bridge.py)

A bidirectional CoT ↔ GARN bridge. Receives ATAK / soRD detections from UDP multicast and forwards them as JSON to the WS client; in the other direction, it accepts position frames from GARN over WS and re-broadcasts them as proper CoT XML so any ATAK device on the LAN sees your team.

pip install --user --break-system-packages websockets
python3 bridge/atak_bridge.py [--ws-port 4243] [--mcast-group 239.2.3.1] [--mcast-port 6969]

GARN TOPO automatically tries ws://localhost:4243 at startup, so if the bridge is running on the same machine it connects automatically.

Outbound CoT (GARN → ATAK)

Position frames received from GARN — in either {t:'pos', d:{name, lat, lng, …}} or {type:'cot_pos', …} shape — are converted to a CoT 2.0 PLI event and sent to 239.2.3.1:6969:

<event version="2.0" uid="GARN.<peerId>" type="a-f-G-U-C-I"
       how="m-g" time="..." start="..." stale="+60s (peers)">
  <point lat="..." lon="..." hae="..." ce="9999999" le="9999999"/>
  <detail>
    <contact callsign="..."/>
    <__group name="Cyan" role="Team Member"/>
    <precisionlocation altsrc="GPS" geopointsrc="GPS"/>
    <remarks>GARN TOPO</remarks>
  </detail>
</event>

The stable per-peer UID prevents marker duplication on the ATAK side; the 60-second stale means peers auto-clear when they go offline. Echoes of our own outbound frames are filtered on the inbound listener.

Inbound CoT (ATAK → GARN)

Incoming CoT XML is parsed for:

  • Event UID (prepended with soRD-) — stable across updates
  • Position from <point lat lon hae />
  • Callsign from <detail><contact callsign="…"/>
  • Remarks text — full string forwarded to client
  • Composite confidence from Conf: XX% in remarks
  • thermal_score — parsed from Thermal: 0.XX in remarks
  • rgb_score — parsed from ArcFace: 0.XX in remarks
  • color — from <color argb="…"/> element
  • cot_type, cot_time, stale, how — passed through to the client

The client honors the stale timestamp (soRD v5 detections use a 120 s window) and a sweeper auto-removes expired markers every 5 seconds. Marker icons use cot_type to set color: red/orange for hostile (a-h-*), blue for friendly (a-f-*).


Settings — Coordinate Formats

Open More → Settings → Coordinate Format and choose from DD, DMS, DDM, UTM, or MGRS. This affects all coordinate displays: status bar, point info panel, marker info, and CSV export.

Settings — Units & Display

  • Distance — Imperial (miles, feet) or Metric (km, meters)
  • Area — Acres or Hectares
  • Elevation — Feet or Meters
  • Speed — mph or km/h
  • Map title — appears on PDF prints
  • Default base layer — layer shown on first load (default: OpenFreeMap Liberty)
  • Terrain exaggeration — 3D mesh vertical scale (1.0–3.0×)

Automatic persistence

The following state is saved automatically and restored on every launch — no manual action required:

  • Base layer selection — the active basemap (raster, vector, or imported archive) is remembered; the same layer loads on the next launch.
  • Map position & zoom — the last map center and zoom level are saved (debounced 1.5 s after panning stops) and restored on launch.
  • Hillshade opacity — the global relief opacity slider value is preserved across sessions.
All settings are stored in IndexedDB via localForage under the key garntopo_settings. Clearing site data in your browser will reset them to defaults.

Settings — Permissions

On first launch all permissions are requested via native dialogs. If you denied a permission and need to re-enable it:

  • Android: Settings → Apps → GARN TOPO → Permissions
  • Web / Chrome: Click the lock icon in the address bar → Permissions

Permissions used by the app:

PermissionUsed forRequired
Location (Fine, foreground only)GPS tracking, position broadcastsFor GPS features
MicrophoneVoice messages in Team Chat (requested in-context)Optional
Storage (Android ≤9)Legacy file import/export (newer Android uses the system file picker, no permission)Optional
Photos are attached to waypoints through the system file / photo picker, so the app declares no camera permission. As of v0.5.1 the app no longer requests Camera, Notifications, or Bluetooth permissions.

Platforms — Android

Built with Capacitor 6. Minimum Android 7.0 (API 24). Tested on Android 13–15.

  • Install via Google Play
  • Location is used in the foreground only — GPS tracking runs while the app is open (no background location)
  • The keyboard uses adjustResize so the chat input bar stays visible when typing
  • Release builds are signed; debug builds use .debug app ID suffix

Platforms — Web / PWA

The web version runs in any modern browser (Chrome, Firefox, Edge, Safari) at garntopoapp.pages.dev. Install as a PWA for an app-like experience:

  • Chrome / Edge: Click the install icon in the address bar
  • Firefox: Menu → Install as App (Firefox 128+)
  • iOS Safari: Share → Add to Home Screen

The PWA uses a Service Worker for offline-first operation — all app code and viewed tiles are cached automatically. No separate install or download manager needed.

GPS tracking runs while the app is open and in the foreground. When the screen locks or the app is backgrounded, the browser/WebView pauses location updates — keep the screen on during active recording.