Examples · JSON

GeoJSON properties schema examples for floor plans

The geometry is the easy part. What makes a floor plan file usable is a predictable properties object. These examples show a consistent structure for rooms, floors, doors and desks, the keys FloorJSON exports, and a JSON Schema you can validate files against.

Updated 6 November 20267 min read

Five rules that save you a refactor later

  1. Put a stable ID on the Feature, not only in properties. GeoJSON allows a top-level "id". Libraries such as MapLibre use it for hover and selection state.
  2. Pick one naming style. snake_case is the most common in GeoJSON. Don't mix roomName, room-name and Room Name.
  3. Store numbers as numbers. "level": 1, not "level": "1", so filters and sorting work.
  4. Put units in key names: area_m2, height_m, capacity_people.
  5. Use null for "known to be empty" and leave the key out for "not collected". Readers can tell the difference.

Example 1: a room, before and after

The left version is typical of a hand-built file: mixed naming, numbers as strings, and the floor buried in a label. The right version holds the same information in keys you can filter on.

Ad hoc
{
  "type": "Feature",
  "properties": {
    "Room Name": "Meeting room 2 (1st floor)",
    "roomType": "meeting",
    "Area": "18.5 m2",
    "seats": "8",
    "color": "orange"
  },
  "geometry": { "type": "Polygon", "coordinates": [...] }
}
Consistent
{
  "type": "Feature",
  "id": "room-1-02",
  "properties": {
    "name": "Meeting room 2",
    "category": "room",
    "level": 1,
    "level_name": "First floor",
    "area_m2": 18.5,
    "capacity_people": 8,
    "fill": "#f0a020"
  },
  "geometry": { "type": "Polygon", "coordinates": [...] }
}

Example 2: a multi-level building

A FeatureCollection can't be nested, so the building's structure lives in properties. Each floor gets a footprint feature, and every room points to its floor with level_id.

building.geojson
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "id": "level-0",
      "properties": { "category": "level", "level": 0, "level_name": "Ground floor", "building": "A" },
      "geometry": { "type": "Polygon", "coordinates": [...] }
    },
    {
      "type": "Feature",
      "id": "level-1",
      "properties": { "category": "level", "level": 1, "level_name": "First floor", "building": "A" },
      "geometry": { "type": "Polygon", "coordinates": [...] }
    },
    {
      "type": "Feature",
      "id": "room-0-01",
      "properties": { "category": "room", "name": "Reception", "level": 0, "level_id": "level-0" },
      "geometry": { "type": "Polygon", "coordinates": [...] }
    },
    {
      "type": "Feature",
      "id": "room-1-02",
      "properties": { "category": "room", "name": "Meeting room 2", "level": 1, "level_id": "level-1" },
      "geometry": { "type": "Polygon", "coordinates": [...] }
    }
  ]
}

To show one floor, filter on level. Keeping both level (for filtering) and level_id (for joining) costs a few bytes and saves a lookup.

Example 3: doors, desks and points of interest

points.geojson
[
  {
    "type": "Feature",
    "id": "door-1-07",
    "properties": { "category": "door", "level": 1, "connects": ["room-1-02", "corridor-1"], "accessible": true },
    "geometry": { "type": "Point", "coordinates": [-0.0903871, 51.5052214] }
  },
  {
    "type": "Feature",
    "id": "desk-1-14",
    "properties": { "category": "desk", "level": 1, "room_id": "room-1-04", "bookable": true, "asset_tag": "D-114" },
    "geometry": { "type": "Polygon", "coordinates": [...] }
  }
]

Example 4: styling and labels

Colours aren't part of GeoJSON. The simplestyle-spec keys are the de facto convention: geojson.io and GitHub read them, and FloorJSON both reads and writes them.

Styled room
{
  "type": "Feature",
  "properties": {
    "name": "Kitchen",
    "stroke": "#ef4444",
    "stroke-width": 2,
    "fill": "#f0a020",
    "fill-opacity": 0.5,
    "label": "Kitchen",
    "label_size": "l",
    "label_color": "#ffffff"
  },
  "geometry": { "type": "Polygon", "coordinates": [...] }
}

The keys FloorJSON exports

Every FloorJSON export uses the same keys, so code written against one file works on the next. Keys only appear when they apply to that feature.

KeyTypeMeaning
namestringRoom or shape name, from the SVG or typed in the editor
groupstring | nullGroup the shape belongs to, typically a floor or zone
shape_typestringWhere the shape came from: rect, path, polygon, circle, pen, point, radius, merged…
sourcestring"drawn" for shapes drawn in the editor; absent for shapes converted from the SVG
fill, stroke, stroke-width, fill-opacitystring / numbersimplestyle-spec colours and line width
marker-colorstringPoint and icon colour (simplestyle-spec)
label, label_size, label_colorstringText shown inside the shape; size is s, m, l or xl
radius_m, centernumber, [lng, lat]For circles, which are exported as 64-point polygons
icon_type, icon_name, icon_sizestring, string, numberFor animated icons: builtin, lottie or svg, and size in pixels
centroid_lng, centroid_latnumberCentre point of shapes converted from the SVG
layer_rotation, layer_opacitynumberHow the SVG was rotated and displayed when aligned
Your own attributesstringAnything you add in the shape inspector, such as level or department

The collection also carries a metadata object naming the generator and the CC BY 4.0 licence.

Example 5: a JSON Schema to validate against

A schema turns your conventions into something a build step can check. This one validates the room and door properties used above. It uses JSON Schema 2020-12 and works with validators such as Ajv.

floorplan-feature.schema.json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.com/schemas/floorplan-feature.json",
  "title": "Floor plan feature properties",
  "type": "object",
  "required": ["category", "level"],
  "properties": {
    "name":        { "type": "string", "minLength": 1 },
    "category":    { "enum": ["level", "room", "corridor", "desk", "door", "stair", "poi"] },
    "level":       { "type": "integer", "minimum": -10, "maximum": 200 },
    "level_name":  { "type": "string" },
    "level_id":    { "type": "string" },
    "room_id":     { "type": "string" },
    "area_m2":     { "type": "number", "exclusiveMinimum": 0 },
    "capacity_people": { "type": "integer", "minimum": 0 },
    "connects":    { "type": "array", "items": { "type": "string" }, "minItems": 2 },
    "fill":        { "type": "string", "pattern": "^#[0-9a-fA-F]{6}$" },
    "stroke":      { "type": "string", "pattern": "^#[0-9a-fA-F]{6}$" },
    "fill-opacity":{ "type": "number", "minimum": 0, "maximum": 1 }
  },
  "allOf": [
    { "if": { "properties": { "category": { "const": "door" } } },
      "then": { "required": ["connects"] } }
  ]
}
validate.mjs
import Ajv from "ajv/dist/2020.js";
import fs from "node:fs";

const schema = JSON.parse(fs.readFileSync("floorplan-feature.schema.json", "utf8"));
const data = JSON.parse(fs.readFileSync("building.geojson", "utf8"));
const validate = new Ajv({ allErrors: true }).compile(schema);

for (const f of data.features) {
  if (!validate(f.properties)) {
    console.error(f.id ?? f.properties?.name, validate.errors);
  }
}

FAQ

Can I put any custom fields in GeoJSON properties?Yes. The spec allows any JSON values in properties. The value of a schema is consistency: the same key, type and meaning in every file.
Where should the feature ID go?On the Feature itself as "id", which GeoJSON allows. Copy it into properties only if a tool you use can't read the top-level id.
How should I structure a multi-level floor plan in JSON?Keep one flat FeatureCollection. Add a level number to every feature and, if you need joins, a level_id that points to a level footprint feature.
Does FloorJSON add a level property?It exports a group per shape, which most people use for floors. If you need a numeric level, add it as a custom attribute in the shape inspector and it is exported with each feature.

Generate this structure instead of typing it

Draw or upload your plan in FloorJSON and every feature is exported with the same keys: name, group, type, colours and labels, plus any custom attributes you add.

Open FloorJSON