sensorml

package
v1.0.0-beta.74 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 16, 2026 License: MIT Imports: 5 Imported by: 0

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:

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

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

View Source
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.

View Source
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.

func (AggregateProcess) Type

func (AggregateProcess) Type() string

Type implements Process.

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

func NewAsset(entityID string, process Process) *Asset

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) EntityID

func (a *Asset) EntityID() string

EntityID implements [graph.Graphable].

func (*Asset) Triples

func (a *Asset) Triples() []message.Triple

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

type Connection struct {
	Source      string `json:"source"`
	Destination string `json:"destination"`
}

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

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.

func (PhysicalComponent) Type

func (PhysicalComponent) Type() string

Type implements Process.

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.

func (PhysicalSystem) Type

func (PhysicalSystem) Type() string

Type implements Process.

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

func UnmarshalProcess(data []byte) (Process, error)

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.

func (SimpleProcess) Type

func (SimpleProcess) Type() string

Type implements Process.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL