A recent IFCJSON-Team discussion around the uses of JSON-LD Framing led me to make a pedagogical demo based on bot. I figured I’d post it in the open in case it helps others.

Modeling a Simple Two Storey Building

Let’s model a simple two storey building using bot and JSON-LD. We’ll assign two rooms to each storey.

{"@context":[{"bot":"https://w3id.org/bot#",
              "hasStorey":{"@type":"@id", "@id":"bot:hasStorey"},
              "hasSpace":{"@type":"@id", "@id":"bot:hasSpace"}},
             {"@base":"http://myproject.org#"}],

 "@graph":[
   {"@id":"mybuilding", "@type":"bot:Building",
       "hasStorey":["level-1", "level-2"]},

   {"@id":"level-1", "@type":"bot:Storey",
          "hasSpace":["room-1.1", "room-1.2"]},
   {"@id":"level-2", "@type":"bot:Storey",
          "hasSpace":["room-2.1", "room-2.2"]},

   {"@id":"room-1.1", "@type":"bot:Space"},
   {"@id":"room-1.2", "@type":"bot:Space"},

   {"@id":"room-2.1", "@type":"bot:Space"},
   {"@id":"room-2.2", "@type":"bot:Space"}
 ]
}

The graph we’ve defined is simple enough to be read and understood directly. Unfortunately, all buildings, storeys, and spaces are defined in a flat list, belying their hierarchical structure. It would be nice if our JSON-LD document could be reordered to reflect the containment relationships inherent in the model. This is a use case JSON-LD Framing solves well. Let’s see how.

Framing The Building Top-Down

Every JSON-LD Frame is a request, intended for a JSON-LD Frame Processor, that instructs it how we would like the underlying JSON-LD graph (here, our building model) presented. Every Frame defines the requested node types (e.g., bot:Building, bot:Storey, bot:Space), along with a description of the "shape" we’d like those types to take. In our case, we’d like to hoist the graph into a tree reflecting the hierarchical decomposition of the building. Here’s a Frame to do that:

{
  "@context": {
    "@vocab": "https://w3id.org/bot#",
    "@base": "http://myproject.org#"
  },
  "@type": "Building",
  "hasStorey": {
    "@type": "Storey",
    "hasSpace": {
      "@type": "Space"
    }
  }
}

Applying this Frame to the original graph returns a view onto the graph that reflects the intended containment relationships:

{
  "@context": {
    "@vocab": "https://w3id.org/bot#",
    "@base": "http://myproject.org#"
  },
  "@id": "mybuilding", "@type": "Building",
  "hasStorey": [
    {
      "@id": "level-1","@type": "Storey",
      "hasSpace": [
        {"@id": "room-1.1", "@type": "Space"},
        {"@id": "room-1.2","@type": "Space"}
    ]
    },
    {
      "@id": "level-2", "@type": "Storey",
      "hasSpace": [
        {"@id": "room-2.1", "@type": "Space"},
        {"@id": "room-2.2","@type": "Space"}
      ]
    }
  ]
}

We didn’t need to change any part of our building model to build this view. The frame just helped us highlight the relationships we care about - the spatial decomposition of our Building by Storey and Space.

Framing The Building Bottom-Up

Other frames can operate on the same graph model to highlight other node types or relationships. For example, let’s say we wanted a schedule of all the Spaces in our Buildings. Here’s one possible frame:

{
	"@context": {
		"@vocab": "https://w3id.org/bot#",
		"@base": "http://myproject.org#",
		"inStorey": {
			"@reverse": "hasSpace"
		},
		"inBuilding": {
			"@reverse": "hasStorey"
		}
	},
	"@type": "Space",
	"inStorey": {
		"@type": "Storey",
		"@embed": "@always",
		"@explicit": true
	},
	"inBuilding": {
		"@type": "Building",
		"@embed": "@always",
		"@explicit": true
	}
}
}

And the result when applied to the model:

{
  "@context": {
    "@vocab": "https://w3id.org/bot#",
    "@base": "http://myproject.org#",
    "inStorey": {
      "@reverse": "hasSpace"
    },
    "inBuilding": {
      "@reverse": "hasStorey"
    }
  },
  "@graph": [
    {
      "@id": "room-1.1",
      "inStorey": {
        "@id": "level-1",
        "inBuilding": {"@id": "mybuilding","@type": "Building"},
        "@type": "Storey"
      },
      "@type": "Space"
    },
    {
      "@id": "room-1.2",
      "inStorey": {
        "@id": "level-1",
        "inBuilding": {"@id": "mybuilding", "@type": "Building"},
        "@type": "Storey"
      },
      "@type": "Space"
    },
    {
      "@id": "room-2.1",
      "inStorey": {
        "@id": "level-2",
        "inBuilding": { "@id": "mybuilding", "@type": "Building"},
        "@type": "Storey"
      },
      "@type": "Space"
    },
    {
      "@id": "room-2.2",
      "inStorey": {
        "@id": "level-2",
        "inBuilding": { "@id": "mybuilding", "@type": "Building"},
        "@type": "Storey"
      },
      "@type": "Space"
    }
  ]
}

By changing the frame only, we’ve managed to flip the building containment hierarchy inside-out. Now we see a list of every Space, along with references to its containing Storey and Building. It’s straightforward to build asset-specific schedules from a format like this.

Wrap-Up

Intermediary frame shapes are possible too. The richer the building model, the more frames we can apply. Given the prevalance of breakdown structures within the construction industry, JSON-LD Frames offer a simple and flexible strategy for defining tailored "views" onto asset ontologies, digital twins, and building information models.