IMDF vs. IndoorGML for Indoor Map Interchange
Part of Indoor Map Data Standards: the two standards that were actually designed for indoor navigation, what each does better, and why the better-designed one is usually not the one to publish.
What Each Standard Is For
IMDF was designed to get indoor maps into a consumer mapping application. That purpose shows everywhere in it: GeoJSON encoding because every client already parses it, WGS84 because that is what a world map uses, closed category enumerations because a renderer needs to know what to draw, and no explicit graph because Apple’s router builds its own.
IndoorGML was designed to model indoor space for navigation research and analysis. Its central
idea is the separation of primal space — cells and their boundaries — from dual space, a graph
of State nodes and Transition edges. That separation is genuinely better modelling, and it
buys a capability nothing else has: multiple graph layers over the same cells, one per travel mode
or accessibility profile, related to each other through the MultiLayeredGraph construct.
If you were designing an internal model from scratch, IndoorGML’s structure is the one to borrow. If you are choosing what to publish, the question is entirely who reads it.
Choosing by Consumer
Two practical considerations sit alongside the consumer question.
Size. GML is XML and IMDF is GeoJSON, and the ratio is roughly 3.7× for the same venue. For a 32-level tower that is 98 MB against 26 MB, which matters for submission workflows, for storage, and for any consumer fetching it over a network.
Tooling. IMDF has a published validator, a reference implementation and a submission process with feedback. IndoorGML has a schema and considerably less around it, which means more of the validation burden falls on the emitter.
Minimal Working Example
The same three rooms, emitted both ways, is the clearest illustration of the difference.
import json
import logging
import uuid
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)
NS = uuid.UUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
def as_imdf_unit(feature: dict, level_id: str) -> dict:
"""One room as an IMDF unit Feature. The graph is left for the consumer to derive."""
props = feature["properties"]
try:
category = {"room": "room", "corridor": "walkway"}[props["space_class"]]
except KeyError:
raise ValueError(f"no IMDF category for {props['space_class']!r}")
return {
"id": str(uuid.uuid5(NS, f"unit:{props['feature_id']}")),
"type": "Feature",
"feature_type": "unit",
"geometry": feature["geometry"],
"properties": {"category": category, "level_id": level_id,
"name": {"en": props.get("name")} if props.get("name") else None,
"display_point": None},
}
def as_indoorgml_cell(feature: dict) -> dict:
"""One room as an IndoorGML CellSpace, plus the dual State it corresponds to."""
props = feature["properties"]
cell_id = f"c-{props['feature_id']}"
return {
"CellSpace": {"gml:id": cell_id, "geometry": feature["geometry"]},
# The dual node exists explicitly and references its cell — this is the part
# IMDF has no equivalent for.
"State": {"gml:id": f"s-{props['feature_id']}", "duality": cell_id},
}
The asymmetry in those two functions is the whole comparison. The IMDF function emits geometry and a category; the IndoorGML function emits geometry, a category and the node in the navigation graph that corresponds to it. A consumer of the first has to reconstruct the graph — the work routing graph construction describes — while a consumer of the second is handed it.
Comparison Reference
| Dimension | IMDF | IndoorGML |
|---|---|---|
| Encoding | GeoJSON in a zip archive | GML 3.2 (XML) |
| Coordinates | WGS84 only | any CRS, declared |
| Room | unit Feature |
CellSpace |
| Doorway | opening LineString |
CellSpaceBoundary |
| Graph | derived by consumer | explicit State / Transition |
| Multi-modal graphs | not expressible | MultiLayeredGraph |
| Levels | level with signed ordinal |
layers with Cell membership |
| Validator | published, maintained | schema only |
| Consumer reach | Apple Maps, many SDKs | transit, research |
| Size (8 levels) | ~6.6 MB | ~24.4 MB |
| Effort to emit | moderate | high |
The MultiLayeredGraph row is the one worth understanding even if you never emit IndoorGML,
because it names a modelling idea most indoor stacks eventually rediscover: the same physical cells
can carry more than one navigation graph, and the relationship between those graphs is itself data.
That is exactly what
accessible routing profiles
implements as weight vectors over one topology — a pragmatic version of the same insight.
Common Errors & Fixes
IMDF submission rejected for a category value. The internal space_class was passed through
instead of mapped. IMDF’s enumerations are closed; every internal value needs an explicit mapping
and an unmapped one must raise at build time.
IndoorGML consumers report a disconnected graph. Transition edges were emitted without their
connects references resolving to State ids, usually because the dual nodes were generated after
the transitions. Emit states first, then transitions referencing them, and validate that every
transition’s endpoints exist.
Coordinates are right in IMDF and wrong in IndoorGML. IndoorGML permits any CRS and requires it
to be declared; omitting srsName leaves the consumer guessing, and the usual guess is WGS84
applied to local metres. Declare the CRS explicitly on every geometry container.
Both archives disagree about level ordinals. IMDF ordinals are signed with 0 at the ground floor, which matches the convention level mapping establishes. IndoorGML layers have no inherent ordering, so the ordinal has to be carried as an attribute — and if it is derived independently in the two converters, they will eventually differ. Derive it once, internally, and pass it to both.
Integration Point
Both converters read the same internal envelope and run at the publishing boundary described in Indoor Map Data Standards. Neither is a storage format, and neither should be read back into the pipeline.
The IndoorGML converter has one additional input the IMDF converter does not need: the routing graph itself, from routing graph construction. That is the point of the standard — it publishes the graph rather than making the consumer rebuild it — and it means the IndoorGML publication is downstream of graph construction where the IMDF publication is not.
Frequently Asked Questions
Can one archive satisfy both kinds of consumer?
No, but one internal model can produce both, which is the arrangement that actually works. The formats are incompatible at the encoding level — a zip of GeoJSON files versus a GML document — so there is no single artefact that satisfies an Apple Maps submission and a transit operator’s IndoorGML importer. What is shared is everything upstream: the same envelope, the same reprojection, the same level ordinals and the same stable identifiers feed both converters, so the second target costs a mapping table and a serialiser rather than a parallel pipeline.
Is IndoorGML worth emitting for internal use?
Not as a storage format, and occasionally as an export for analysis. Its verbosity and XML encoding make it a poor working format compared with the internal envelope, and nothing in the pipeline reads GML. Where it earns its keep is when a specialist tool — an evacuation simulator, a pedestrian flow model, an academic collaborator’s software — consumes it natively, because handing over a document that already contains the navigation graph avoids a round of reconstruction and the disagreements that come with it.
Which standard handles accessibility better?
IndoorGML, structurally, and IMDF, practically. IndoorGML can express a step-free graph as a distinct layer over the same cells, which is the cleanest possible model of the problem. IMDF instead carries accessibility as attributes on units and openings, leaving the consumer to apply them — which is less elegant and is what the consumers that exist actually implement. If you publish IMDF, the important thing is to populate those accessibility attributes fully rather than leaving them null, because a consumer cannot infer step-free routing from geometry alone.
Related
- Indoor Map Data Standards — the publishing boundary both converters sit at.
- Converting GeoJSON Envelopes to IndoorGML — emitting primal and dual space from the internal model.
- Validating IMDF Archives in Python — the checks that catch a rejection before submission.
This page is a companion to Indoor Map Data Standards, part of the Indoor Mapping Architecture & Standards section.