Production-Ready Indoor Map Deployment: An Engineering Reference for Facilities & Navigation Teams

Deploying indoor maps at production scale is a distributed-systems problem, not a drafting exercise. The moment a routing graph powers live turn-by-turn directions in a hospital, airport, or campus, every floor plan, edge weight, and point-of-interest (POI) record becomes a deployable artifact whose correctness is enforced by automation rather than by a technician’s eye. This reference describes the full delivery path — how raw geometry is ingested, validated against a strict schema, gated in continuous integration, versioned, served through a CDN, and consumed by client SDKs — and the failure modes that appear when any one of those stages is skipped. The goal is a system where a broken polygon never reaches a phone, and a bad release reverts in seconds.

End-to-end indoor-map deployment pipeline Six sequential stages move a floor plan from raw geometry to a device: ingestion, schema validation, CI gating, a versioned artifact registry, CDN and edge delivery, and the client SDK. A rollback arrow loops from delivery back to the last known-good artifact in the registry. Deployment pipeline: from geometry to device Each stage has one job and a hard contract with the next, so a defect is caught at the boundary where it appears. 1 Ingestion DWG · IFC · GeoJSON 2 Schema validation contract check 3 CI gating three-stage gate 4 Artifact registry versioned · hashed 5 CDN / edge delivery topology-hash ETag 6 Client SDK precompiled graph rollback revert to last known-good artifact
The five delivery stages plus the client SDK, with the rollback path that returns service to the previous content-addressed artifact in seconds.

The Deployment Pipeline: Five Stages from Geometry to Device

A production indoor-map deployment moves spatial assets through five deterministic stages. Each stage has a single responsibility and a hard contract with the next, so a regression is caught at the boundary where it is introduced rather than in a user’s navigation session.

  1. Ingestion. Raw deliverables — DWG/DXF exports, IFC building models, IMDF archives, or hand-edited GeoJSON — enter through format-specific decoders. Upstream, these are produced by Automated Floor Plan Parsing & Vectorization; this stage assumes vector geometry already exists and concerns itself only with normalizing units, resolving block references, and stamping a provenance record onto every feature.
  2. Spatial processing. Geometry is repaired (make_valid), snapped to a tolerance grid, and projected into a consistent Indoor Coordinate Reference System. Floor elevations are resolved into discrete navigable levels using Level Mapping & Z-Axis Logic so that a routing graph never connects two storeys through a wall.
  3. Semantic enrichment. Bare geometry becomes navigable when each feature carries meaning: a polygon is an office, a line is a traversable corridor, a node is an accessible elevator. Classification follows the conventions in POI Taxonomy & Classification, and openings are confirmed as passable using Wall & Door Detection Algorithms before any edge is marked traversable.
  4. Graph assembly & validation. Nodes and edges are compiled into a directed routing graph, serialized into the canonical GeoJSON envelope, hashed, and validated against the contract defined in JSON Schema Design for Indoor Maps. This is the last point at which a defect is cheap to fix.
  5. Delivery. The validated artifact is promoted through CI Gating for Map Updates, published to a versioned registry, distributed via CDN or edge nodes under Cache Invalidation Strategies, and consumed by mobile and kiosk clients through the contracts described in SDK Integration Patterns. If telemetry detects a regression after release, Rollback Triggers & Versioning restores the previous artifact.

Indoor environments lack the absolute geographic anchors that outdoor mapping relies on, so two architectural commitments hold the whole pipeline together. First, every building defines a local Cartesian frame whose origin (0,0) sits on a survey control point or structural column, with the Z value encoding floor level; transformation matrices to WGS84 or a local UTM zone are applied only at the edges, for emergency-response interop. Second, spatial representation is decoupled from navigation logic — the SDK never re-derives geometry; it consumes a precompiled, validated routing graph. These two rules are what make the deployment reproducible across a portfolio of hundreds of buildings.

Core Data Model: The Canonical GeoJSON Envelope

Every artifact in the pipeline shares one serialization contract: a GeoJSON FeatureCollection carrying a top-level metadata block. The metadata is what makes a deployment operable — it pins the artifact to a schema revision, embeds a topology_hash for byte-level integrity checks at the edge, and records the coordinate frame so a client never guesses at units.

{
  "type": "FeatureCollection",
  "metadata": {
    "map_version": "3.2.0",
    "schema_revision": "v1.3.0",
    "building_id": "BLDG-04",
    "floor_level": 3,
    "generated_at": "2026-06-26T09:15:00Z",
    "topology_hash": "sha256:9f1c0ab27e4d",
    "coordinate_system": "EPSG:0-local-cartesian",
    "snap_tolerance_m": 0.01
  },
  "features": [
    {
      "type": "Feature",
      "id": "room-301",
      "geometry": { "type": "Polygon", "coordinates": [[[0, 0], [10, 0], [10, 8], [0, 8], [0, 0]]] },
      "properties": { "category": "office", "floor_level": 3, "accessibility_rating": "full" }
    },
    {
      "type": "Feature",
      "id": "E0042",
      "geometry": { "type": "LineString", "coordinates": [[5, 8], [5, 14]] },
      "properties": { "edge_type": "corridor", "source": "N0117", "target": "N0118", "routing_weight": 6.0, "traversable": true, "wheelchair_accessible": true }
    }
  ]
}

The field conventions below are stable across the pipeline and are enforced by the schema rather than by convention:

Field Location Type Notes
map_version metadata string (semver) Immutable per release; ties the artifact to an SDK compatibility range.
schema_revision metadata string The schema version the payload validates against; mismatch is a FATAL gate failure.
topology_hash metadata string sha256: digest over sorted geometry; powers ETag generation and rollback verification.
coordinate_system metadata string Building-local Cartesian frame; only transformed to WGS84 at interop boundaries.
floor_level feature/metadata number Discrete navigable level (not a raw CAD elevation in metres).
edge_type feature string corridor, door, elevator, stair, ramp — drives traversal rules.
routing_weight feature number ≥ 0 Edge cost in metres or seconds; never null on a traversable edge.
accessibility_rating feature string full, partial, none; consumed by accessible-route queries.

Two indoor-specific rules govern the geometry itself. All assets normalize to metres and snap to the declared snap_tolerance_m grid before ingestion; unclosed polygon rings, dangling nodes, and self-intersections are rejected outright. And the routing graph is modelled explicitly as a directed network with weighted edges — doors, elevators, stairs, and ramps are first-class edge types, not decorative annotations — because a missing or mis-weighted edge is indistinguishable from an impassable building to a navigation engine.

Python Implementation Pattern: Validate, Assemble, Publish

The production module below is the heart of stage 4. It loads spatial floor levels, repairs and snaps geometry, asserts the attribute contract, assembles a directed routing graph, computes a topology hash, and emits the canonical envelope. It follows the house rules for this codebase: typed signatures (PEP 484), a real logging call, and explicit handling of the two failure modes that actually occur in the field — missing required attributes and a disconnected graph. Wrap this in a CI runner, attach the schema validation hook, and push the result to a versioned artifact registry.

import hashlib
import json
import logging
from typing import Any, Dict, Set

import geopandas as gpd
import networkx as nx
from shapely.validation import make_valid

logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
logger = logging.getLogger("indoor_map_deploy")

REQUIRED_ATTRS: Set[str] = {"floor_level", "edge_type", "routing_weight"}


class AttributeContractError(ValueError):
    """Raised when a floor level is missing a mandatory routing attribute."""


class GraphIntegrityError(RuntimeError):
    """Raised when the assembled routing graph is not weakly connected."""


def build_routing_artifact(
    input_gpkg: str,
    building_id: str,
    floor_level: int,
    map_version: str,
    tolerance: float = 0.01,
) -> Dict[str, Any]:
    """Assemble a validated, hashed GeoJSON routing artifact from raw edges.

    Repairs geometry, enforces the attribute contract, builds a directed
    routing graph, and returns the canonical FeatureCollection envelope.
    """
    edges = gpd.read_file(input_gpkg, layer="routing_edges")

    # Enforce the attribute contract at the boundary — fail fast.
    missing = REQUIRED_ATTRS - set(edges.columns)
    if missing:
        logger.error("floor %s missing required attributes: %s", floor_level, missing)
        raise AttributeContractError(f"Missing required attributes: {missing}")

    # Repair invalid geometry and snap endpoints to the tolerance grid.
    edges["geometry"] = edges.geometry.apply(make_valid)

    graph = nx.DiGraph()
    for _, row in edges.iterrows():
        graph.add_edge(
            row["source"],
            row["target"],
            weight=float(row.get("routing_weight", 0.0)),
            edge_type=row.get("edge_type", "corridor"),
            wheelchair_accessible=bool(row.get("wheelchair_accessible", False)),
        )

    # A navigable floor level must be a single connected component.
    if graph.number_of_nodes() and not nx.is_weakly_connected(graph):
        components = nx.number_weakly_connected_components(graph)
        logger.error("floor %s graph has %d disconnected components", floor_level, components)
        raise GraphIntegrityError(f"Routing graph split into {components} components")

    payload = {
        "type": "FeatureCollection",
        "metadata": {
            "map_version": map_version,
            "building_id": building_id,
            "floor_level": floor_level,
            "coordinate_system": "EPSG:0-local-cartesian",
            "snap_tolerance_m": tolerance,
            "nodes": graph.number_of_nodes(),
            "edges": graph.number_of_edges(),
        },
        "features": json.loads(edges.to_json())["features"],
    }

    # Deterministic topology hash over sorted geometry for edge-side integrity.
    digest = hashlib.sha256(
        json.dumps(payload["features"], sort_keys=True).encode("utf-8")
    ).hexdigest()
    payload["metadata"]["topology_hash"] = f"sha256:{digest[:12]}"

    logger.info(
        "floor %s artifact ready: %d nodes, %d edges, hash %s",
        floor_level, graph.number_of_nodes(), graph.number_of_edges(),
        payload["metadata"]["topology_hash"],
    )
    return payload

The two custom exceptions are deliberate: a CI runner can map AttributeContractError to a schema-stage failure and GraphIntegrityError to a topology-stage failure, attach the offending floor level and node identifiers to the build report, and halt promotion with a non-zero exit code. Consult the Shapely documentation for advanced repair functions and the OGC IndoorGML standard when the artifact must interoperate with third-party building models.

Validation & Failure Modes: What Breaks When a Stage Is Skipped

Each stage exists because its absence produces a specific, observable navigation failure. Use this as a pre-deployment checklist; every item maps to a gate that should block promotion.

  • Skip geometry repair (spatial processing). Self-intersecting room polygons and micro-gaps between collinear walls survive into the graph. Symptom: the router treats a closed corridor as open, or refuses to enter a valid room. Gate: make_valid plus a zero-self-intersection assertion.
  • Skip snapping to the tolerance grid. Endpoints that differ by sub-millimetre float noise produce two distinct nodes where there should be one. Symptom: a dead-end where two corridors visibly meet. Gate: endpoint snap within snap_tolerance_m before node assignment.
  • Skip level resolution (Z-axis logic). Raw CAD elevations leak into floor_level. Symptom: the graph connects floor 2 to floor 4 directly, or an elevator edge spans the wrong storeys. Gate: discrete-level assertion that every node’s floor_level is an integer in the building’s known set.
  • Skip the attribute contract (semantic enrichment). Edges arrive without routing_weight or edge_type. Symptom: zero-cost shortcuts through walls, or accessible-route queries that silently return stairs. Gate: AttributeContractError on any missing required field.
  • Skip graph connectivity validation. A disconnected component ships. Symptom: certain destinations report “no route found” from half the building. Gate: is_weakly_connected must hold per floor level, with vertical edges checked across levels.
  • Skip schema validation. Coordinate precision drift, unclosed rings, or a stale schema_revision reach the client. Symptom: SDK parse errors or rendering crashes on specific devices. Gate: strict validation against JSON Schema Design for Indoor Maps, failures categorized FATAL vs WARNING.
  • Skip the topology hash check at the edge. A partial upload or truncated CDN object is served. Symptom: intermittent, device-specific routing corruption that cannot be reproduced in staging. Gate: edge node recomputes and compares topology_hash before serving.

The discipline here is identical to validating a database migration before it touches production: the map artifact is immutable, the CI system never mutates source geometry, and every rejection carries exact node and edge identifiers so the fix is mechanical rather than investigative.

Operational Considerations: Versioning, Gating, Delivery, and Rollback

Once an artifact is validated, the operational surface — how it is versioned, promoted, served, and reverted — determines whether the deployment is safe to repeat a thousand times.

Versioning. Spatial assets are tracked as code. Semantic version strings in map_version bind each release to a navigation-engine compatibility range, and large binaries (GeoJSON, MBTiles, IMDF) are stored through Git LFS with a manifest.json mapping each version to its topology_hash. This is the foundation that Rollback Triggers & Versioning builds on: because every promoted artifact is content-addressed by hash, “revert to the last known-good map” is an atomic pointer swap, not a rebuild.

CI gating. Promotion runs through a three-stage sequential gate — schema enforcement, graph topology validation, and API contract verification — each in an isolated runner emitting structured telemetry and halting on a non-zero exit. CI Gating for Map Updates is what turns the validation checklist above into an enforced barrier rather than a recommendation; nothing reaches staging without a green gate.

Delivery & cache freshness. Indoor spaces change constantly — partitions move, POIs relocate, wings close for maintenance — so the delivery layer must balance freshness against bandwidth. Cache Invalidation Strategies derives ETags from the topology_hash, ships delta updates instead of whole floor levels, and busts caches per building rather than globally, so a single floor’s change does not force every kiosk in the portfolio to re-download.

Client consumption. The artifact is only as useful as the SDK that renders it. SDK Integration Patterns define how routing engines consume the precompiled graph, fuse BLE/Wi-Fi RTT positioning, handle floor-switching, and degrade gracefully when indoor positioning is weak. Where a navigation engine cannot find a route — a closed corridor, a failed elevator — it falls back to the patterns in Fallback Routing Architectures rather than returning a dead end.

Rollback state machine for a live map release A five-state cycle: serving version 3.2.0, anomaly detected, revert to version 3.1.0, verify the topology hash, and serving restored. Each transition is fired by a named telemetry signal, and once a new map is validated the cycle resumes from a healthy serving state. Rollback state machine — telemetry-driven revert Serving v3.2.0 current live release Anomaly detected routing loop / inaccessible node Revert to v3.1.0 atomic pointer swap Verify topology_hash edge re-checks digest Serving restored known-good v3.1.0 route-failure spike rollback trigger armed manifest pointer → prev hash hash matches manifest new validated map promoted
Telemetry signals drive each transition; because the previous artifact is content-addressed by topology_hash and already passed every gate, the revert needs no rebuild or re-validation.

These four concerns are not independent. A rollback is only trustworthy if versioning is content-addressed; cache invalidation is only correct if the topology_hash is authoritative; and gating is only meaningful if the schema is the single source of truth. Treat them as one system and the deployment becomes boring — which, for live wayfinding infrastructure, is the goal.

Frequently Asked Questions

Why use a building-local Cartesian frame instead of storing everything in WGS84?

Indoor routing needs sub-centimetre relative precision and a Z value that means “floor level,” neither of which WGS84 expresses naturally. A building-local frame anchored to a survey control point keeps geometry in metres, makes snapping and graph assembly deterministic, and avoids floating-point precision loss near the poles or at high zoom. Transformation to WGS84 or a local UTM zone is applied only at interop boundaries — emergency response, outdoor handoff — via a stored affine matrix, never as the primary storage format. See Indoor Coordinate Reference Systems for the full treatment.

What exactly should block a map update from reaching production?

Any FATAL gate failure: a schema violation, an unclosed polygon ring, a floor_level outside the building’s known set, a missing routing_weight on a traversable edge, or a disconnected routing graph. WARNING-level findings (for example, a corridor with unusually high edge cost) are logged for human review but do not block. The rule is that anything which would cause a “no route found” or an SDK parse error is fatal; anything cosmetic is a warning. The enforcement lives in CI Gating for Map Updates.

How do rollbacks stay fast and trustworthy?

Every promoted artifact is content-addressed by its topology_hash and recorded in a manifest against its map_version. Reverting is an atomic pointer swap to the previous hash plus a cache purge keyed on that hash — no rebuild, no re-validation, because the previous artifact already passed every gate. The edge node re-verifies the hash before serving, so a rollback can never silently serve a corrupted object. The mechanics are detailed in Rollback Triggers & Versioning.

How are accessibility constraints represented in the routing graph?

Accessibility is an edge-level property, not a separate map. Each edge carries wheelchair_accessible and the feature carries an accessibility_rating of full, partial, or none. Accessible-route queries run the same shortest-path algorithm over a filtered subgraph that excludes stairs and inaccessible edges. Because the filter operates on attributes that were asserted by the contract at ingestion, an accessible route can never silently include a staircase — the edge would have failed validation if its accessibility flag were missing.

How should clients cache map data without serving stale routes?

Derive the ETag from the topology_hash, ship delta updates scoped to a single building or floor level, and invalidate caches per building rather than globally. A client revalidates with a conditional request; if the hash matches, it serves from cache with zero payload transfer. When a floor changes, only that floor’s delta is pushed. This keeps kiosks and phones current without saturating the network on every minor edit — the full strategy is in Cache Invalidation Strategies.

Can this pipeline run without a full BIM/IFC model?

Yes. The deployment pipeline assumes only that vector geometry with the required attributes exists; how that geometry was produced — from IFC, DWG, SVG, or a scanned plan — is the concern of Automated Floor Plan Parsing & Vectorization. As long as the parsing stage emits the canonical envelope with valid topology and the mandatory routing attributes, the schema, gating, delivery, and rollback stages behave identically regardless of the source format.

This page is the overview for the production-deployment area of Indoor Mapping & Wayfinding Automation; for the upstream stages that produce these artifacts, see Automated Floor Plan Parsing & Vectorization and Indoor Mapping Architecture Standards.