Loading...
CURRENT VERSION 1 — APRIL 2026

DMD HUB GPX Extension

Pre-rendered route data for instant GPX loading across all DMD platforms — turn-by-turn instructions, surface classification, regulations and integrity hash all embedded in standard GPX 1.1.

Namespace
https://dmdnavigation.com/ns/gpx/1

Overview

The DMD HUB GPX Extension (dmd: namespace) embeds pre-computed route data directly inside standard GPX 1.1 files. This eliminates the need for routing API calls, on-device surface extraction, and elevation computation when loading a file — routes display instantly.

The extension is placed inside the <extensions> block of <rte> or <trk> elements and is fully compliant with the GPX 1.1 specification. Applications that don't understand the dmd: namespace will simply ignore it.

Benefits

Instant Route Loading

No routing API calls needed. The full routed geometry is embedded in the file, ready to render immediately.

Turn-by-Turn Instructions

Navigation instructions are pre-computed and stored with road names, eliminating on-device processing.

Surface Classification

Paved/unpaved segments with OSM surface and highway tags — no map tile extraction required.

Integrity Verification

SHA-256 hash ensures data validity. If the file is edited externally, apps know to re-compute.

Schema Structure

Add the namespace declaration to your <gpx> root element:

<gpx version="1.1" creator="YourApp"
     xmlns="http://www.topografix.com/GPX/1/1"
     xmlns:dmd="https://dmdnavigation.com/ns/gpx/1">

The extension defines two root elements, each placed inside a different parent's <extensions> block:

  • <dmd:PreRendered> — goes inside <rte><extensions> or <trk><extensions>. Carries routed geometry, turn instructions, surface, timing, warnings, regulations, and aggregate stats.
  • <dmd:NavigationCard> — goes inside <wpt><extensions>. Carries a per-waypoint proximity-warning payload that overrides the consumer's global "waypoint warnings" preference. See Navigation Card (waypoints) below.

<dmd:PreRendered> example inside a route:

<rte>
  <name>My Route</name>
  <extensions>
    <dmd:PreRendered version="1" hash="sha256:abc123..." profile="road-fast-all">
      <dmd:CalculatedRoute>...</dmd:CalculatedRoute>
      <dmd:Instructions>...</dmd:Instructions>
      <dmd:Surface>...</dmd:Surface>
      <dmd:Warnings>...</dmd:Warnings>
      <dmd:Regulations>...</dmd:Regulations>
      <dmd:Stats .../>
    </dmd:PreRendered>
  </extensions>
  <rtept lat="..." lon="..."></rtept>
</rte>

<dmd:NavigationCard> example inside a waypoint:

<wpt lat="46.123456" lon="11.456789">
  <name>Sharp left turn</name>
  <sym>Slow Down</sym>
  <extensions>
    <dmd:NavigationCard>
      <dmd:show>true</dmd:show>
      <dmd:distance>1000</dmd:distance>
      <dmd:message>Slow to 30 km/h — gravel switchback ahead</dmd:message>
    </dmd:NavigationCard>
  </extensions>
</wpt>

Element Reference

<dmd:PreRendered>

Root container for all pre-rendered data.

AttributeTypeDescription
versionIntegerSchema version (currently 1). If a reader encounters a higher version, it should fall back to normal routing.
hashStringIntegrity hash. Format: sha256:<16 hex chars>
profileStringThe routing profile used to generate this data (e.g., road-fast-all, offroad-medium)

<dmd:CalculatedRoute>

Routes only (tracks already have <trkpt> geometry). Contains the full routed geometry as a semicolon-separated text node:

<dmd:CalculatedRoute>48.123456,16.456789,312.5;48.123470,16.456900,313.1;...</dmd:CalculatedRoute>

Each point is lat,lon,ele with 6 decimal places for coordinates and 1 for elevation (meters). Whitespace between points is allowed and should be stripped before parsing.

<dmd:Instructions>

Turn-by-turn navigation instructions enriched with OSM-derived data so consumers don't need offline tile lookups to render rich navigation UI:

<dmd:Instructions>
  <dmd:I lat="48.123" lon="16.456" type="LEFT" dist="1250"
         road="Hauptstrasse" ref="B17" lanes="left|through" stop="1" maxspeed="50"/>
  <dmd:I lat="48.200" lon="16.500" type="ROUNDABOUT" dist="3400"
         exit="3" rba="42.7" maxspeed="50"/>
  <dmd:I lat="48.250" lon="16.520" type="EXIT_RIGHT" dist="4200"
         jref="21A" dest="Wien Mitte" destref="A4" lanes="none|none|right" maxspeed="100"/>
  <dmd:I lat="48.300" lon="16.600" type="DESTINATION" dist="5200"/>
</dmd:Instructions>
AttributeDescription
lat, lonInstruction point coordinates (required)
typeInstruction type enum (see below) (required)
distCumulative distance from route start in meters (required)
roadOSM name tag of the road at this instruction (optional)
refOSM ref tag — road reference number, e.g. "A1", "M25" (optional)
destOSM destination tag — destination sign text, e.g. "Wien, Salzburg" (optional)
destrefOSM destination:ref tag — destination road codes, e.g. "A1;IC2" (optional)
jrefOSM junction:ref tag — highway exit number, e.g. "21A" (optional). Most useful on EXIT_RIGHT/EXIT_LEFT.
exitRoundabout exit number (1-based positive integer). Only present when type=ROUNDABOUT.
rbaRoundabout turn angle in degrees, signed: positive = right turn, negative = left turn (relative to entry direction). Computed from the route polyline. Only meaningful for type=ROUNDABOUT. Consumers can use it to draw an exit-direction icon that matches the geometry instead of a generic exit-number layout.
lanesOSM turn:lanes tokens, pipe-separated (e.g. "left|through|through;right"). Multi-direction lanes use ; as separator. Empty/unmarked lanes are written as "none" so the lane count round-trips. The producer is direction-aware: it prefers turn:lanes:forward/:backward based on the rider's bearing on the way.
stop"1" if a highway=stop node sits within ~35m of the instruction. Absent otherwise. Useful for showing a STOP badge on the navigation banner.
gw"1" if a highway=give_way node sits within ~35m. Absent otherwise. Same use case as stop.
pass"1" for synthesized passthrough instructions — emitted at multi-way junctions / motorway entries where the router gave a "continue" but lane / destination data is worth surfacing. Absent on real turn instructions.
maxspeedPosted speed limit on the road at this instruction, normalized to integer km/h. Producers convert before writing: bare numbers and "N km/h" stay as-is, "N mph" becomes round(N × 1.609344), "walk" becomes 7. Values that cannot be resolved confidently are omitted — including "none" (explicit no-limit) and country-implicit tags like "RU:urban" or "RO:trunk". Consumers should treat absence as "unknown" and either fall back to a local lookup or keep the device's last-known limit.

Backward compatibility: all attributes after dist are optional. Consumers reading older files (without exit/rba/destref/stop/gw/lanes/pass/maxspeed) should treat missing attributes as absent — no defaults beyond what the type implies.

Roundabout note: if exit is missing on a ROUNDABOUT instruction (legacy file), consumers should fall back to displaying a generic "Take exit" without a number rather than guessing.

Instruction Types:

LEFT
RIGHT
SLIGHT_LEFT
SLIGHT_RIGHT
SHARP_LEFT
SHARP_RIGHT
KEEP_LEFT
KEEP_RIGHT
U_TURN
ROUNDABOUT
STRAIGHT
EXIT_RIGHT
EXIT_LEFT
DESTINATION

<dmd:Surface>

Paved/unpaved classification segments:

<dmd:Surface>
  <dmd:S s="0" e="142" sf="asphalt" hw="secondary" p="1"/>
  <dmd:S s="143" e="380" sf="gravel" hw="track" p="0"/>
</dmd:Surface>
AttributeDescription
sStart point index (into CalculatedRoute or trkpt sequence)
eEnd point index (inclusive)
sfOSM surface tag value (e.g., asphalt, gravel, dirt)
hwOSM highway tag value (e.g., secondary, track, path)
pPaved flag: 1 = paved, 0 = unpaved

<dmd:Timing>

Per-segment travel time breakdown. Enables point-accurate ETA calculations to any waypoint along the route. The sum of all segment t values equals <dmd:Stats>'s time attribute.

<dmd:Timing>
  <dmd:T s="0" e="142" t="310" spd="52.1"/>
  <dmd:T s="143" e="380" t="480" spd="31.8"/>
</dmd:Timing>
AttributeUnitDescription
sindexStart point index (inclusive). Same index space as <dmd:Surface>
eindexEnd point index (inclusive)
tsecondsSegment travel time
spdkm/hSegment average speed (optional, informational)

<dmd:Warnings>

Pre-computed hazard warnings along the route:

<dmd:Warnings>
  <dmd:W type="SLOPE" lat="48.5" lon="16.8" dist="12500" val="18.5"/>
  <dmd:W type="UNPAVED" lat="48.6" lon="16.9" dist="15000" len="3200"/>
</dmd:Warnings>
AttributeDescription
typeSLOPE (gradient ≥ 15%) or UNPAVED (unpaved run ≥ 250m)
lat, lonWarning location
distCumulative distance from route start (meters)
valSlope percentage (SLOPE type only)
lenUnpaved section length in meters (UNPAVED type only)

<dmd:Regulations>

Regulatory road features encountered along the full route polyline — not just at turn instructions. Lets consumers (e.g., DMD2PlayGround's navigation card and SpeedLimitDetector) drive stop/yield heads-ups and a route-aware speed-limit display without falling back to offline OSM tile lookups.

<dmd:Regulations>
  <dmd:R type="MAXSPEED" lat="48.123" lon="16.456" dist="0"    val="50"/>
  <dmd:R type="STOP"     lat="48.130" lon="16.459" dist="820"/>
  <dmd:R type="MAXSPEED" lat="48.140" lon="16.470" dist="1480" val="90"/>
  <dmd:R type="GIVE_WAY" lat="48.150" lon="16.480" dist="2310"/>
</dmd:Regulations>
AttributeDescription
typeSTOP (highway=stop node), GIVE_WAY (highway=give_way node), or MAXSPEED (point where the posted speed limit changes)
lat, lonFeature location, snapped to the nearest polyline point
distCumulative distance from route start (meters). Entries are sorted by dist ascending.
valMAXSPEED only — integer km/h. Same normalization rules as dmd:I@maxspeed (mph → km/h, "walk" → 7, country-implicit tags omitted entirely).
Consumer rules
  • STOP / GIVE_WAY: independent of dmd:I@stop/@gw. Those flag stops at the turn; this section flags stops anywhere along the polyline, including between turns. Both should be surfaced.
  • MAXSPEED: the active limit at distance D is the entry with the largest dist ≤ D. Producers SHOULD emit a dist=0 entry when the route starts inside a speed-limited area so consumers don't display "unknown" until the first transition.
  • Failure-safe: the section is optional. Consumers that don't find dmd:Regulations should fall back to whatever local lookup they had before.

<dmd:Stats>

Aggregate statistics (self-closing element with attributes):

<dmd:Stats dist="185420" gain="2340" loss="2180" eleMin="120" eleMax="1845"
           time="14400" paved="72" maxSlope="22.5" minSlope="-18.3"/>
AttributeUnitDescription
distmetersTotal route distance
gainmetersTotal elevation gain
lossmetersTotal elevation loss
eleMinmetersMinimum elevation
eleMaxmetersMaximum elevation
timesecondsEstimated travel time
paved%Percentage of route that is paved (0–100)
maxSlope%Maximum uphill slope
minSlope%Maximum downhill slope (negative value)

Travel Time & ETAs

The extension provides travel-time data at two levels of granularity:

  • Aggregate<dmd:Stats>'s time attribute gives the total estimated travel time for the route/track. Sufficient for "ETA to end of route" displays.
  • Per-segment<dmd:Timing> breaks the total time into contiguous runs so a reader can compute the ETA to any arbitrary point along the route (tap-to-info, next-waypoint, etc.) without assuming a flat average speed.
Reader Fallback Order
  1. If <dmd:Timing> is present and indices are valid — sum segment times from the user's current index to the target index.
  2. Else, if <dmd:Stats>'s time is present — distribute proportionally by distance fraction.
  3. Else, fall back to a profile-based speed model (e.g., the profile specified on <dmd:PreRendered>) or the device's observed average speed.

<dmd:NavigationCard> is the per-waypoint companion to <dmd:PreRendered>. It attaches an authored proximity-warning payload to an individual <wpt> so that, on approach, the consumer surfaces a popup, voice line, LED blink, screen flash, and progress-card highlight — even if the rider has disabled their global "waypoint proximity warnings" preference. The file author's authoring intent overrides the device-side default.

<dmd:NavigationCard>

Container element. All three child elements are optional — absence equals the "feature off" defaults below. Wraps inside <wpt><extensions>:

<wpt lat="..." lon="...">
  <name>...</name>
  <sym>...</sym>
  <extensions>
    <dmd:NavigationCard>
      <dmd:show>true</dmd:show>
      <dmd:distance>1000</dmd:distance>
      <dmd:message>Sharp left turn ahead — slow to 30 km/h</dmd:message>
    </dmd:NavigationCard>
  </extensions>
</wpt>
Child elementTypeDescription
show Boolean string "true" / "false" (or "1" / "0"). When true, the warning chain fires regardless of the consumer's waypoint-proximity preference. Default false when the element is omitted — the entire NavigationCard block then has no runtime effect.
distance Non-negative integer (metres) Trigger radius. The warning fires when the rider is within this distance of the waypoint along the active line. Stored as metres regardless of the author's UI unit — planners that show km or mi in the editor convert at the boundary. Consumers that find this element missing or zero SHOULD fall back to 1000 m.
message String Free-form warning text shown verbatim in the proximity popup and spoken (when voice output is enabled) by TTS. Authoring tools MUST require a non-empty message when <show> is true — a blank message produces a useless empty popup. Both the DMD HUB Planner and the in-app waypoint editor enforce this at save time.
Consumer Behaviour

When a NavigationCard-bearing waypoint is the rider's next upcoming waypoint along the active line, consumers MUST:

  1. Compute distance-along-track from the rider's snapped position to the waypoint.
  2. When that distance ≤ <dmd:distance>, fire the warning chain:
    • Sliding popup carrying the <dmd:message> text. The display window SHOULD be longer than a regular notification toast (DMD2 / DMDPlayGround uses 15 s vs. the default 5 s).
    • Speedo / HUD indicator counting metres-to-waypoint.
    • Progress-card highlight on the next-waypoint row.
    • LED blink (orange, ~3 cycles) and screen flash — same primitives the regular proximity warning uses.
    • Voice line speaking the <dmd:message> verbatim (skip only if the rider's voice-output toggle is off).
  3. Hold all indicators visible until the rider exits the radius (distance > <dmd:distance>) or the next-waypoint id advances past this one.

Critically, the consumer MUST NOT gate any of the above on its global "waypoint proximity warnings" preference. That pref governs unauthored waypoints; a NavigationCard waypoint is an authored override.

Failure-Safe Defaults
  • A waypoint with no <extensions> block, or with an <extensions> block that doesn't contain <dmd:NavigationCard>, behaves exactly as a legacy GPX 1.1 waypoint — the rider's global pref controls whether warnings fire.
  • A NavigationCard with <dmd:show>false</dmd:show> (or no show child at all) is dormant. The block still round-trips through the planner / app session intact, but the warning chain does not fire.
  • A NavigationCard with show=true but a blank or missing <dmd:message> SHOULD be treated as malformed by readers — either suppress the warning entirely, or fall back to the waypoint's <name>. (Writers should not produce this combination; both reference writers refuse to save it.)
Integrity Hash

Unlike <dmd:PreRendered>, NavigationCard has no integrity hash. The payload is fully self-contained (a flag, a distance, a string) and doesn't depend on the waypoint's lat/lon staying put — if the author repositions the pin, the same warning still applies at the new position. Round-tripping through a third-party editor that preserves <extensions> blocks is safe.

Integrity Hash

The hash ensures that pre-rendered data matches the current route waypoints. If a file is edited in another application (waypoints moved/added/removed), the hash will not match and readers should discard the pre-rendered data and re-route.

Algorithm
  1. Build a canonical string from the source waypoints:
    lat1,lon1;lat2,lon2;...;latN,lonN;profile=road-fast-all
    • For <rte>: use all <rtept> coordinates
    • For <trk>: use all <trkpt> coordinates
    • Coordinates truncated to 6 decimal places
    • Append ;profile= followed by the routing profile string
  2. Compute SHA-256 of the canonical string (UTF-8 encoded)
  3. Take the first 16 hex characters of the hash
  4. Store as: sha256:<16 hex chars>
Reader Behavior
ScenarioAction
Hash matchesUse pre-rendered data directly. No routing needed.
Hash mismatchDiscard pre-rendered data. Route via normal method (DMD Router, etc.)
No hash presentTreat as no pre-rendered data.
Unknown versionFall back to normal routing.

Implementation Guide

For Readers (GPX Viewers)

  1. Parse the GPX file normally (tracks, routes, waypoints)
  2. For each <rte> or <trk>, check for <dmd:PreRendered> in <extensions>
  3. If found, compute the expected hash from the source coordinates + profile
  4. If hash matches: use <dmd:CalculatedRoute> for display geometry, <dmd:Stats> for metadata, etc.
  5. If hash doesn't match: ignore the extension and route normally

For Writers (Route Planners / Navigation Apps)

  1. After computing a route (via DMD Router or your routing engine), collect:
    • Full routed geometry (the dense point array)
    • Surface/highway classification per segment
    • Turn instructions (if your router provides them)
    • Elevation statistics
  2. Compute the hash from the source waypoints + routing profile
  3. Write the <dmd:PreRendered> block inside <extensions>
  4. Continue writing normal <rtept> / <trkpt> elements as usual

Full Example

<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="DMD HUB GPX Planner"
     xmlns="http://www.topografix.com/GPX/1/1"
     xmlns:dmd="https://dmdnavigation.com/ns/gpx/1">
  <metadata>
    <name>Mountain Loop</name>
  </metadata>
  <rte>
    <name>Route 1</name>
    <extensions>
      <dmd:PreRendered version="1" hash="sha256:f28a213d70082096" profile="offroad-medium">
        <dmd:CalculatedRoute>41.651310,-8.249183,114.5;41.651327,-8.249231,114.3;41.650449,-8.249211,110.8;41.649858,-8.247084,127.0;41.648490,-8.245994,134.5</dmd:CalculatedRoute>
        <dmd:Instructions>
          <dmd:I lat="41.651296" lon="-8.250214" type="SHARP_LEFT" dist="91" road="Rua das Camélias" maxspeed="50"/>
          <dmd:I lat="41.648015" lon="-8.245977" type="SLIGHT_RIGHT" dist="658" maxspeed="40"/>
          <dmd:I lat="41.632462" lon="-8.244647" type="DESTINATION" dist="3980"/>
        </dmd:Instructions>
        <dmd:Surface>
          <dmd:S s="0" e="2" sf="asphalt" hw="tertiary" p="1"/>
          <dmd:S s="3" e="4" sf="" hw="track" p="0"/>
        </dmd:Surface>
        <dmd:Timing>
          <dmd:T s="0" e="2" t="138" spd="52.5"/>
          <dmd:T s="3" e="4" t="220" spd="38.0"/>
        </dmd:Timing>
        <dmd:Warnings>
          <dmd:W type="SLOPE" lat="41.651274" lon="-8.250075" dist="79" val="15.4"/>
          <dmd:W type="UNPAVED" lat="41.648490" lon="-8.245994" dist="658" len="2364"/>
        </dmd:Warnings>
        <dmd:Regulations>
          <dmd:R type="MAXSPEED" lat="41.651310" lon="-8.249183" dist="0"   val="50"/>
          <dmd:R type="STOP"     lat="41.650449" lon="-8.249211" dist="142"/>
          <dmd:R type="MAXSPEED" lat="41.648490" lon="-8.245994" dist="658" val="40"/>
        </dmd:Regulations>
        <dmd:Stats dist="3980" gain="396" loss="48" eleMin="105.0" eleMax="465.0"
                 time="358" paved="51" maxSlope="33.8" minSlope="-24.9"/>
      </dmd:PreRendered>
    </extensions>
    <rtept lat="41.65131" lon="-8.249183"></rtept>
    <rtept lat="41.632462" lon="-8.244647"></rtept>
  </rte>
</gpx>

Compatibility

The extension is currently supported by:

  • DMD HUB GPX Planner (web) — writer + reader
  • DMD HUB GPX Browser (web) — reader
  • DMD HUB iOS App — reader
  • DMD2 Android App — writer + reader

Any GPX application can add support by following the implementation guide above. The extension does not interfere with standard GPX processing — non-supporting apps will simply ignore the dmd: namespace elements.

Top