Sub-Space Mapping for Small Compartments

This document extends the Spatial Relationships System and the Container Pattern to support identifying and managing tiny compartments (e.g. dividers in a screw organiser) when individual QR/NFC tags are impractical.


1 Problem Statement

Certain containers—tackle boxes, pill organisers, hardware assortments—contain many small sub-containers. These slots are too small for their own physical identifiers, yet users still need to:

  1. Locate the slot that holds a specific ObjectClass.
  2. Track implicit quantities per slot (“25 screws left”).
  3. Instantiate or remove explicit ObjectInstances from a slot.

2 Solution Overview

  1. Overview Image – A photograph (or schematic) of the parent container is stored once.
  2. Sub-Space Nodes – Each compartment is modelled as a node with a polygon (normalised coordinates) that points to a region on the image.
  3. Fixed Spatial Anchor – A new relationship (:SubSpace)-[:FIXED_IN]->(:ObjectInstance {id:"Box-001"}) asserts the polygon is rigidly attached to the container.
  4. Implicit Quantity – Each SubSpace carries its own implicit_quantity counter for bulk items stored inside.

3 Data Model Additions

3.1 Node: :SubSpace

Property Type Description
name String Human-friendly label (e.g. “Slot C3”)
polygon List<List<Float>> Normalised 0-1 coordinate pairs [[x,y]…]
expected_class_id String (FK) Default ObjectClass stored here
implicit_quantity Integer Count of un-instantiated units remaining

3.2 Relationship: FIXED_IN

Start Type End Semantics
:SubSpace FIXED_IN :ObjectInstance Polygon is physically part of the container

FIXED_IN behaves like PART_OF but is 2-D image-centric: moving the parent moves all its sub-spaces.


4 Workflow (UX)

  1. Capture – User scans the parent container’s QR → selects “Add compartment map” → uploads / snaps photo.
  2. Annotate – In-app polygon editor lets user draw slots, assign ObjectClass & starting quantity.
  3. Persist – Frontend sends addSubSpace mutations; backend writes nodes & edges.
  4. Daily Ops – Tapping a slot opens quick actions: Remove N, Instantiate items, Add items back.
  5. Aggregate View – Container detail page sums implicit_quantity of its slots plus explicit descendants.

5 GraphQL API Sketch

type SubSpace {
  id: ID!
  name: String!
  polygon: [[Float!]!]!
  expectedClass: ObjectClass!
  implicitQuantity: Int!
  parentContainer: ObjectInstance!
}

input SubSpaceInput {
  name: String!
  polygon: [[Float!]!]!
  expectedClassId: ID!
  implicitQuantity: Int!
}

extend type Mutation {
  addSubSpace(containerId: ID!, input: SubSpaceInput!): SubSpace!
  updateSubSpaceQuantity(subSpaceId: ID!, delta: Int!): SubSpace!
  deleteSubSpace(id: ID!): Boolean!  # fails if quantity > 0 or has explicit children
}

6 Integrity & Safeguards

  • Polygon overlap – API validates new polygon does not intersect existing ones (optional tolerance).
  • Normalised Coordinates – Stored 0-1 so replacing the background image (higher resolution) does not break mapping.
  • Deletion Guard – Cannot delete a SubSpace with non-zero implicit_quantity or with child objects linked via REMOVED_FROM.

7 Storage Notes

  • Overview image stored per Image Management rules; mark with purpose = "layout".
  • polygon JSON typically < 1 KB; negligible storage.
  • implicit_quantity updates write a single Postgres row, supporting optimistic UI counters.

8 Compatibility with Existing Patterns

Requirement Covered By
Bulk ownership Container pattern (implicit)
Partial instantiation REMOVED_FROM edges from :SubSpace
Spatial queries Treat SubSpace as any ObjectInstance in IN chains
Analytics & alerts Same aggregation queries; just include SubSpace

No breaking changes to the core schema are introduced; merely a new label + edge type fully aligned with the extensible spatial system.