Designing JSON Schemas for Indoor Map APIs: Production-Ready Deployment & Validation
Indoor mapping payloads require strict structural guarantees before they reach navigation SDKs or facility management dashboards. Unlike outdoor geospatial data, indoor environments operate on localized Cartesian grids, hierarchical floor models, and directed wayfinding graphs. The JSON schema must enforce spatial precision, prevent topological breaks, and guarantee deterministic parsing across heterogeneous client environments. Facilities technicians and GIS developers should treat the schema as a contract that blocks malformed geometry at ingestion rather than relying on downstream error handling. This architectural discipline is foundational to any Production-Ready Indoor Map Deployment where schema drift directly correlates with routing failures, SDK crashes, and costly facility downtime.
Declaring Coordinate Reference Systems and Floor Anchors
Indoor coordinates rarely align with WGS84. Most deployments use arbitrary local grids or transformed EPSG:3857 projections anchored to building survey control points. The schema must explicitly declare the coordinate system at the root level and enforce strict numeric precision to prevent rendering drift.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "IndoorMapPayload",
"type": "object",
"required": ["crs", "floors", "metadata"],
"properties": {
"crs": {
"type": "object",
"required": ["type", "origin", "units", "scale_factor"],
"properties": {
"type": { "type": "string", "enum": ["local_cartesian", "epsg:3857_transformed", "wgs84_local"] },
"origin": {
"type": "array",
"items": { "type": "number" },
"minItems": 2,
"maxItems": 2,
"description": "Survey control point [x, y] in declared units"
},
"units": { "type": "string", "enum": ["meters", "feet"] },
"scale_factor": { "type": "number", "minimum": 0.999, "maximum": 1.001 }
}
},
"floors": {
"type": "array",
"items": { "$ref": "#/$defs/floor" },
"minItems": 1
},
"metadata": { "$ref": "#/$defs/metadata" }
},
"$defs": {
"floor": {
"type": "object",
"required": ["id", "level", "elevation", "geometry"],
"properties": {
"id": { "type": "string", "pattern": "^floor_[a-z0-9]{4,8}$" },
"level": { "type": "integer" },
"elevation": { "type": "number" },
"geometry": { "$ref": "#/$defs/polygon_collection" }
}
},
"polygon_collection": {
"type": "object",
"required": ["type", "coordinates"],
"properties": {
"type": { "const": "PolygonCollection" },
"coordinates": {
"type": "array",
"items": {
"type": "array",
"items": {
"type": "array",
"items": { "type": "number" },
"minItems": 2,
"maxItems": 2
},
"minItems": 4
}
}
}
},
"metadata": {
"type": "object",
"required": ["version", "hash", "last_modified"],
"properties": {
"version": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+$" },
"hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
"last_modified": { "type": "string", "format": "date-time" }
}
}
}
}
When Python automation builders ingest survey data, they must validate the origin array against known control points before schema serialization. Use pyproj to verify that any transformed coordinates maintain sub-centimeter tolerance relative to the declared origin. Mismatched CRS declarations are the primary cause of SDK rendering offsets and should be caught during the initial payload assembly phase. For teams standardizing this workflow, the JSON Schema Design for Indoor Maps specification provides the baseline validation rules required for cross-platform compatibility.
Enforcing Topological Consistency in Wayfinding Graphs
Navigation engines rely on directed graphs where nodes represent decision points and edges represent traversable paths. The schema must guarantee graph integrity by enforcing unique identifiers, valid source/target references, and explicit traversal constraints. Facilities teams frequently introduce schema drift when adding temporary closures or ADA-compliant routes, which breaks routing algorithms if not strictly typed.
{
"$defs": {
"node": {
"type": "object",
"required": ["id", "coordinates", "floor_id", "type"],
"properties": {
"id": { "type": "string", "pattern": "^node_[a-z0-9]{8}$" },
"coordinates": { "type": "array", "items": { "type": "number" }, "minItems": 2, "maxItems": 2 },
"floor_id": { "type": "string" },
"type": { "type": "string", "enum": ["corridor", "intersection", "elevator", "stairwell", "door"] }
}
},
"edge": {
"type": "object",
"required": ["id", "source", "target", "weight", "constraints"],
"properties": {
"id": { "type": "string", "pattern": "^edge_[a-z0-9]{8}$" },
"source": { "type": "string", "pattern": "^node_[a-z0-9]{8}$" },
"target": { "type": "string", "pattern": "^node_[a-z0-9]{8}$" },
"weight": { "type": "number", "minimum": 0.1 },
"constraints": {
"type": "object",
"properties": {
"traversable": { "type": "boolean" },
"accessibility": { "type": "array", "items": { "type": "string", "enum": ["wheelchair", "stroller", "standard"] } },
"direction": { "type": "string", "enum": ["bidirectional", "forward", "reverse"] }
},
"required": ["traversable", "accessibility", "direction"]
}
}
},
"graph": {
"type": "object",
"required": ["nodes", "edges"],
"properties": {
"nodes": { "type": "array", "items": { "$ref": "#/$defs/node" } },
"edges": { "type": "array", "items": { "$ref": "#/$defs/edge" } }
}
}
}
}
Graph validation must extend beyond JSON Schema’s native capabilities. While the schema enforces type safety and pattern matching, it cannot natively verify that every source and target ID exists in the nodes array, or that the graph contains no isolated subgraphs. Facilities teams must implement post-schema validation to catch dangling references before they propagate to routing engines.
CI Gating and Automated Validation Pipelines
Schema validation cannot be relegated to manual review. Production pipelines require automated gating that rejects payloads violating spatial or topological constraints. The following Python automation script demonstrates a production-ready validation pipeline that combines jsonschema for structural enforcement with networkx for graph topology verification.
import json
import hashlib
import jsonschema
import networkx as nx
from typing import Dict, List, Tuple
from jsonschema import Draft202012Validator
from jsonschema.exceptions import ValidationError
# Load schema and payload
with open("indoor_map_schema.json", "r") as f:
schema = json.load(f)
def compute_content_hash(payload: Dict) -> str:
"""Deterministic SHA-256 hash for cache invalidation."""
normalized = json.dumps(payload, sort_keys=True, separators=(",", ":"))
return hashlib.sha256(normalized.encode("utf-8")).hexdigest()
def validate_graph_topology(payload: Dict) -> List[str]:
"""Verify directed graph connectivity and reference integrity."""
errors = []
nodes = {n["id"] for n in payload.get("graph", {}).get("nodes", [])}
edges = payload.get("graph", {}).get("edges", [])
G = nx.DiGraph()
G.add_nodes_from(nodes)
for edge in edges:
if edge["source"] not in nodes:
errors.append(f"Dangling source reference: {edge['source']}")
if edge["target"] not in nodes:
errors.append(f"Dangling target reference: {edge['target']}")
G.add_edge(edge["source"], edge["target"], weight=edge["weight"])
# Check for isolated components
if not nx.is_weakly_connected(G) and len(G.nodes) > 0:
components = list(nx.weakly_connected_components(G))
errors.append(f"Graph contains {len(components)} disconnected components. Expected 1.")
return errors
def validate_indoor_payload(raw_json: str) -> Tuple[bool, List[str]]:
try:
payload = json.loads(raw_json)
except json.JSONDecodeError as e:
return False, [f"JSON parse error: {str(e)}"]
# Structural validation
try:
Draft202012Validator.check_schema(schema)
Draft202012Validator(schema).validate(payload)
except ValidationError as e:
return False, [f"Schema violation: {e.message} at path {list(e.absolute_path)}"]
# Topological validation
topo_errors = validate_graph_topology(payload)
if topo_errors:
return False, topo_errors
# Inject deterministic hash if missing
payload.setdefault("metadata", {})["hash"] = compute_content_hash(payload)
return True, []
# Usage in CI pipeline
if __name__ == "__main__":
with open("map_payload.json", "r") as f:
raw = f.read()
is_valid, diagnostics = validate_indoor_payload(raw)
if not is_valid:
print("CI GATE FAILED:")
for diag in diagnostics:
print(f" ❌ {diag}")
exit(1)
print("✅ Payload validated and ready for deployment.")
This pipeline blocks malformed geometry at ingestion, aligning with the Production-Ready Indoor Map Deployment methodology where automated gating replaces manual QA. Facilities teams should integrate this script into GitHub Actions, GitLab CI, or Jenkins pipelines, failing merges on any ValidationError or topological break.
Cache Invalidation and Versioning Strategies
Map updates trigger downstream cache invalidation across mobile SDKs and web dashboards. The schema’s metadata block must drive deterministic cache busting. Relying on timestamps alone introduces race conditions across distributed edge networks. Instead, implement content-addressable hashing:
- Normalize JSON: Strip whitespace, sort keys, and standardize float precision to 4 decimal places before hashing.
- Generate SHA-256: Compute the hash of the normalized payload and store it in
metadata.hash. - Semantic Versioning: Increment
metadata.versionusing SemVer (MAJOR.MINOR.PATCH).MAJORfor CRS changes,MINORfor floor/graph topology updates,PATCHfor attribute/metadata adjustments. - Delta Invalidation: SDKs should compare local
hashagainst the API response. If mismatched, fetch only the changed floor or graph subset usingETagheaders and conditionalGETrequests.
This strategy prevents stale routing data from persisting on user devices during emergency evacuations or facility reconfigurations. The JSON Schema Design for Indoor Maps framework explicitly mandates hash-based validation to guarantee cache consistency across multi-tenant deployments.
SDK Integration Patterns and Deterministic Parsing
Deterministic parsing requires strict typing and predictable null handling. SDKs should deserialize payloads using generated model classes rather than dynamic dictionaries. When mapping the JSON schema to typed models (e.g., Pydantic in Python, Swift Codable, or Kotlin data class), enforce the following patterns:
- Coordinate Precision Loss Mitigation: JSON floats lose precision during serialization. Store coordinates as strings in transit if sub-millimeter accuracy is required, or use fixed-point integers scaled by 10,000.
- Fallback Routing: When
constraints.traversableisfalse, the SDK must dynamically recompute paths using Dijkstra or A* without blocking the UI thread. Precompute adjacency matrices during ingestion to accelerate runtime queries. - Graceful Degradation: If a floor’s
geometryfails validation, the SDK should render a bounding box placeholder and disable indoor positioning until the payload is corrected.
Integration should follow the OGC IndoorGML specification for interoperability, ensuring that proprietary indoor map formats can be translated into standardized JSON payloads without losing topological fidelity. Refer to the OGC IndoorGML Standard for alignment on spatial feature classification and hierarchical floor modeling.
Conclusion
Designing JSON schemas for indoor map APIs is an infrastructure discipline, not a documentation exercise. Facilities technicians, GIS developers, and Python automation builders must treat schema validation as the first line of defense against routing failures, rendering drift, and cache corruption. By enforcing strict CRS declarations, validating graph topology programmatically, implementing content-addressed cache invalidation, and generating deterministic SDK models, teams can deploy indoor navigation systems that scale reliably across complex facility portfolios. The schema is the contract; the pipeline is the enforcement mechanism. Build it rigidly, validate it continuously, and deploy with confidence.