Documentation
¶
Overview ¶
Package sensorml provides a Go parser, emitter, and Graphable adapter for the OGC SensorML JSON encoding bundled with the OGC API Connected Systems v1.0 standard.
SensorML (OGC 12-000r2) is the canonical XML/JSON schema for describing observing systems, components, processes, and the hardware/software platforms hosting them. The JSON encoding shipped alongside CS API v1.0 is what every Connected Systems server, client, or processor exchanges to declare what a system IS — its identity, capabilities, mode structure, and the procedures it executes.
This package is the framework-side adapter that bridges SensorML descriptions and SemStreams' Graphable / KV / triple model. It is one of the load-bearing pieces of ADR-044 Phase 5.
Scope ¶
Pinned to the OGC CS API v1.0 SensorML JSON bundle. Coverage is the load-bearing subset every CS API consumer needs:
- PhysicalSystem — composite physical assembly with children and connections (the canonical "system" record).
- PhysicalComponent — leaf physical observing unit.
- SimpleProcess — leaf algorithmic / procedural unit.
- AggregateProcess — composite of child processes.
- AbstractProcess — shared fields embedded by all four.
SensorML has roughly 500 schema classes; the four above cover the path from "CS API GET /systems/{id}" → Graphable triples → downstream graph processors. Less common types (Mode/ModeChoice, Algorithm, Configuration, DeployedSystem, Position, Time) are intentionally deferred — they extend the schema's coverage but are not on the CS API v1.0 critical path. Add them in follow-up tags when concrete consumers surface a need.
Graphable bridge ¶
Parsed SensorML values do NOT directly implement [graph.Graphable] because SensorML's local id field is not a 6-part SemStreams entity ID. The pairing happens via Asset:
process, err := sensorml.UnmarshalProcess(data)
asset := sensorml.NewAsset("acme.ops.robotics.gcs.drone.001", process)
// asset.EntityID() → "acme.ops.robotics.gcs.drone.001"
// asset.Triples() → SOSA/SSN-aligned triples
Each Asset emits triples using dotted predicate names registered by this package against the corresponding SOSA/SSN IRIs (see predicates.go). Importing this package runs the registration at init time, so RDF/Turtle export through vocabulary/export produces compacted sosa:/ssn: forms.
Standards-at-work, not OGC hell ¶
This package adopts the SensorML JSON encoding as first-class Go structs. No code-generation from upstream schemas: the surface area is small enough to hand-write, and the cost of a build-time codegen dependency is not paid by current consumers. Auto-generation may make sense once coverage broadens past Phase 5; defer until that pain materializes.
External references ¶
- OGC 12-000r2 (SensorML): https://www.ogc.org/standard/sensorml/
- CS API v1.0 SensorML JSON encoding: https://docs.ogc.org/DRAFTS/23-001r0.html
See [ADR-044] for the framework/sister-repo split rationale and the dependency chain that places this package in Phase 5.
[graph.Graphable]: ../../graph vocabulary/export: ../../vocabulary/export [ADR-044]: ../../docs/adr/044-ogc-connected-systems-framework-split.md
Index ¶
- Constants
- type AbstractProcess
- type AggregateProcess
- type Asset
- type CapabilityList
- type CharacteristicList
- type ClassifierList
- type Connection
- type ConnectionList
- type DataComponent
- type DataComponentList
- type IdentifierList
- type PhysicalComponent
- type PhysicalSystem
- type Process
- type Reference
- type SimpleProcess
- type Term
Constants ¶
const ( // PredType is the RDF type assertion (sosa class IRI). PredType = "sensorml.process.type" // PredLabel pairs to dc:title for the SensorML label field. PredLabel = "sensorml.process.label" // PredDescription pairs to dc:description. PredDescription = "sensorml.process.description" // PredDefinition pairs to the entity's SOSA-aligned class IRI. PredDefinition = "sensorml.process.definition" // PredHosts maps PhysicalSystem → child PhysicalComponent // (sosa:hosts). PredHosts = "sensorml.system.hosts" // PredIsHostedBy is the inverse of PredHosts. PredIsHostedBy = "sensorml.component.isHostedBy" // PredHasSubSystem maps an AggregateProcess to its child // processes (ssn:hasSubSystem). PredHasSubSystem = "sensorml.process.hasSubSystem" // PredUsedProcedure maps a PhysicalComponent / SimpleProcess // to the method reference (sosa:usedProcedure). PredUsedProcedure = "sensorml.process.usedProcedure" // PredAttachedTo records the SensorML attachedTo reference — // "I am physically mounted on this thing". Maps to // sosa:isHostedBy (the inverse of sosa:hosts), NOT // ssn:hasDeployment — deployment is a temporal-spatial- // purpose context, while attachedTo is a hardware mounting // link. Operators modeling explicit deployment contexts // should introduce a separate predicate targeting // ssn:hasDeployment when that capability lands. PredAttachedTo = "sensorml.process.attachedTo" // PredIdentifierValue carries a flat identifier value // (typically a serial number, registration ID, or callsign). // Maps to skos:notation. PredIdentifierValue = "sensorml.identifier.value" // PredCapabilityValue carries a capability-list entry's value // (operating range, performance bound). PredCapabilityValue = "sensorml.capability.value" // PredCharacteristicValue carries a characteristic-list // entry's value (physical property of the device). PredCharacteristicValue = "sensorml.characteristic.value" )
Predicate name constants for the dotted-name SemStreams convention. Each is registered to its SOSA/SSN IRI in init() so that RDF/Turtle export through vocabulary/export emits the compacted sosa:/ssn: forms automatically.
const ( TypePhysicalSystem = "PhysicalSystem" TypePhysicalComponent = "PhysicalComponent" TypeSimpleProcess = "SimpleProcess" TypeAggregateProcess = "AggregateProcess" )
Type discriminator strings per OGC SensorML JSON encoding (CS API v1.0 bundle).
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AbstractProcess ¶
type AbstractProcess struct {
ID string `json:"id,omitempty"`
Label string `json:"label,omitempty"`
Description string `json:"description,omitempty"`
Definition string `json:"definition,omitempty"`
UniqueID string `json:"uniqueId,omitempty"`
Identifiers IdentifierList `json:"identifiers,omitempty"`
Classifiers ClassifierList `json:"classifiers,omitempty"`
Characteristics CharacteristicList `json:"characteristics,omitempty"`
Capabilities CapabilityList `json:"capabilities,omitempty"`
Keywords []string `json:"keywords,omitempty"`
Inputs DataComponentList `json:"inputs,omitempty"`
Outputs DataComponentList `json:"outputs,omitempty"`
Parameters DataComponentList `json:"parameters,omitempty"`
TypeOf *Reference `json:"typeOf,omitempty"`
}
AbstractProcess holds the fields shared by every concrete SensorML process kind per OGC 12-000r2. Concrete types embed this struct by value.
Field names mirror the SensorML JSON property names; pointer fields are optional. The struct is a hand-written subset of the full schema — fields outside CS API v1.0's critical path are intentionally omitted (see doc.go's Scope section).
type AggregateProcess ¶
type AggregateProcess struct {
AbstractProcess
Components []Process `json:"components,omitempty"`
Connections ConnectionList `json:"connections,omitempty"`
}
AggregateProcess is a SensorML concrete composite process — a container that orchestrates child processes via the Components list and the Connections among them.
func (*AggregateProcess) Base ¶
func (a *AggregateProcess) Base() *AbstractProcess
Base implements Process.
func (*AggregateProcess) LocalID ¶
func (a *AggregateProcess) LocalID() string
LocalID implements Process.
func (*AggregateProcess) MarshalJSON ¶
func (a *AggregateProcess) MarshalJSON() ([]byte, error)
MarshalJSON for AggregateProcess.
type Asset ¶
type Asset struct {
Process Process
// ChildIDFn maps a child component's local id (the inner
// SensorML id field, e.g. "battery") to its 6-part SemStreams
// entity ID. When nil, child triples reference the bare
// local id — fine for in-document graph queries but a
// downgrade for cross-system entity resolution.
ChildIDFn func(localID string) string
// contains filtered or unexported fields
}
Asset pairs a parsed SensorML Process with the 6-part SemStreams entity ID assigned by the operator. Asset implements the [graph.Graphable] contract; the underlying Process does not, because SensorML's document-local id is not a SemStreams entity ID.
Multiple Assets may share the same underlying Process value (e.g. when ingestion deduplicates structurally identical system descriptions across deployments). EntityID is the stable handle; pair construction is the operator's responsibility.
func NewAsset ¶
NewAsset wraps a parsed Process with a 6-part SemStreams entity ID. The 6-part ID is opaque to this package — callers own the org.platform.domain.system.type.instance convention.
func (*Asset) Triples ¶
Triples implements [graph.Graphable]. Emits an rdf:type triple, label / description / definition where present, every identifier value as a separate triple, and the structural relationships to children (sosa:hosts for PhysicalSystem, ssn:hasSubSystem for AggregateProcess) plus the procedure reference (sosa:usedProcedure) for leaf physical / simple processes.
type CapabilityList ¶
type CapabilityList []Term
CapabilityList groups capability Terms (operating range, performance bounds).
type CharacteristicList ¶
type CharacteristicList []Term
CharacteristicList groups characteristic Terms (physical properties of the procedure / device).
type ClassifierList ¶
type ClassifierList []Term
ClassifierList groups classifier Terms (taxonomies, types).
type Connection ¶
Connection wires together two PhysicalSystem components via their data ports. The source / destination fields are dotted-path references into the embedded components' data dictionaries (e.g. "battery/output/voltage" → "controller/input/sensorReading").
type ConnectionList ¶
type ConnectionList []Connection
ConnectionList groups Connection entries inside a PhysicalSystem.
type DataComponent ¶
type DataComponent struct {
Name string `json:"name"`
Type string `json:"type,omitempty"`
Definition string `json:"definition,omitempty"`
Label string `json:"label,omitempty"`
UoM string `json:"uom,omitempty"`
}
DataComponent is the SensorML reference to a SWE Common typed field used as an input, output, or parameter slot. We intentionally model only the cross-cutting fields rather than the full SWE Common type hierarchy — the SWE IRI roster ships in vocabulary/swe and the parser's job is to surface the type reference, not to revalidate the SWE Common payload shape.
type DataComponentList ¶
type DataComponentList []DataComponent
DataComponentList groups DataComponents under a SensorML inputs / outputs / parameters slot.
type IdentifierList ¶
type IdentifierList []Term
IdentifierList groups identifier Terms. SensorML uses lists instead of bare arrays so the list itself can carry a label / definition; we collapse to a flat slice for the common case.
type PhysicalComponent ¶
type PhysicalComponent struct {
AbstractProcess
Method *Reference `json:"method,omitempty"`
AttachedTo *Reference `json:"attachedTo,omitempty"`
}
PhysicalComponent is a SensorML concrete leaf physical process — a single sensor / actuator / sampler unit. The Method reference points to the procedure the component executes (sosa:Procedure semantics).
Physical-process types extend AbstractProcess with an AttachedTo field (the physical thing the component is mounted on). Per OGC 12-000r2 §6.4.
func (*PhysicalComponent) Base ¶
func (p *PhysicalComponent) Base() *AbstractProcess
Base implements Process.
func (*PhysicalComponent) LocalID ¶
func (p *PhysicalComponent) LocalID() string
LocalID implements Process.
func (*PhysicalComponent) MarshalJSON ¶
func (p *PhysicalComponent) MarshalJSON() ([]byte, error)
MarshalJSON for PhysicalComponent.
type PhysicalSystem ¶
type PhysicalSystem struct {
AbstractProcess
AttachedTo *Reference `json:"attachedTo,omitempty"`
Components []Process `json:"components,omitempty"`
Connections ConnectionList `json:"connections,omitempty"`
}
PhysicalSystem is a SensorML concrete composite physical process — a hardware assembly containing PhysicalComponent children (sensors, actuators, samplers) and Connection entries describing how their data ports link.
In OGC API Connected Systems terms, a PhysicalSystem record IS a SOSA/SSN System, and its Components are typically SOSA Sensors / Actuators hosted on the System's Platform.
func (*PhysicalSystem) Base ¶
func (p *PhysicalSystem) Base() *AbstractProcess
Base implements Process.
func (*PhysicalSystem) LocalID ¶
func (p *PhysicalSystem) LocalID() string
LocalID implements Process.
func (*PhysicalSystem) MarshalJSON ¶
func (p *PhysicalSystem) MarshalJSON() ([]byte, error)
MarshalJSON for PhysicalSystem: writes the type discriminator up-front and emits children through the polymorphic Process marshalers so each child's own type is preserved.
type Process ¶
type Process interface {
// Type returns the SensorML JSON type-discriminator string
// ("PhysicalSystem", "PhysicalComponent", "SimpleProcess",
// "AggregateProcess").
Type() string
// Base returns the embedded AbstractProcess block carrying
// the fields shared by every SensorML process kind. Never
// nil for a well-formed value (the embedded struct is
// value-typed, not pointer-typed).
Base() *AbstractProcess
// LocalID returns the document-local id field. Local IDs are
// scoped to the SensorML document — they are NOT 6-part
// SemStreams entity IDs. Pair through [NewAsset] before
// feeding to the graph layer.
LocalID() string
}
Process is the SensorML JSON polymorphic root. Every concrete process type (PhysicalSystem, PhysicalComponent, SimpleProcess, AggregateProcess) implements Process by reporting its type-discriminator string and exposing the shared AbstractProcess block.
Operate on the concrete types when you need type-specific fields (e.g. PhysicalSystem.Components); operate through Process when you only need the shared fields or are walking a heterogeneous list.
func UnmarshalProcess ¶
UnmarshalProcess decodes a SensorML JSON object into the appropriate concrete Process type, switching on the type discriminator per OGC 12-000r2. Returns the concrete value (not a pointer) so callers can type-switch directly:
process, err := sensorml.UnmarshalProcess(data)
switch p := process.(type) {
case *sensorml.PhysicalSystem: …
case *sensorml.PhysicalComponent: …
}
Returns an error if the JSON is malformed or if the type field names a kind this package does not handle.
The function returns a Process interface value rather than concrete types because composite kinds (PhysicalSystem, AggregateProcess) carry children of arbitrary process kinds — their JSON shape is heterogeneous and must dispatch through this same function.
type Reference ¶
type Reference struct {
Href string `json:"href,omitempty"`
Title string `json:"title,omitempty"`
Role string `json:"role,omitempty"`
}
Reference is a SensorML JSON typed cross-reference. It carries at minimum an href (the target IRI / URI); other fields are optional and used for display or content-negotiation hints.
References show up everywhere — procedure links, typeOf, featureOfInterest, configuration overlays. A bare string in JSON is also accepted by UnmarshalJSON for callers that emit the IRI directly without the full envelope.
type SimpleProcess ¶
type SimpleProcess struct {
AbstractProcess
Method *Reference `json:"method,omitempty"`
}
SimpleProcess is a SensorML concrete leaf process — an algorithmic / procedural unit with no sub-processes. The Method reference points to the procedure that defines what the process does.
func (*SimpleProcess) Base ¶
func (s *SimpleProcess) Base() *AbstractProcess
Base implements Process.
func (*SimpleProcess) LocalID ¶
func (s *SimpleProcess) LocalID() string
LocalID implements Process.
func (*SimpleProcess) MarshalJSON ¶
func (s *SimpleProcess) MarshalJSON() ([]byte, error)
MarshalJSON for SimpleProcess.
type Term ¶
type Term struct {
Definition string `json:"definition,omitempty"`
Label string `json:"label,omitempty"`
Value any `json:"value,omitempty"`
UoM string `json:"uom,omitempty"`
SweTypeOf string `json:"sweType,omitempty"`
}
Term is the SensorML "Term" structure used for identifiers, classifiers, characteristics, and capabilities. It carries a definition IRI (the semantic kind of the term), a human- readable label, and the value the term holds.
SensorML 2.x JSON encoding represents Term values as a single JSON object even when the underlying SWE Common shape would be a typed AbstractDataComponent. We surface only the load-bearing fields; the SWE typing is preserved as a raw IRI in SweTypeOf so downstream consumers can resolve via vocabulary/swe when needed.