| Internet-Draft | JSON Structure Semantic Annotations | August 2026 |
| Vasters | Expires 6 February 2027 | [Page] |
Data types describe representation, but they do not explain the semantic, temporal, spatial, and operational characteristics needed to interpret and compare data. This document defines optional JSON Structure annotations that bind schema nodes to terms in external vocabularies; annotations for observation results, observed properties, features of interest, procedures, time semantics, quality, derivation, and cadence; annotations for spatial referencing by coordinates, by vector and tensor reference frames, by transformations between frames, and along linear elements; and annotations for color spaces, audio channel layouts, spectral bands, code lists, and measurement conditioning.¶
The annotations make an incompatibility between two data sets detectable by machine; they do not resolve one. This document defines no conversion, and a processor that cannot resolve a referenced definition reports the check as indeterminate rather than assuming agreement. Correctly declining to combine two values is the outcome these annotations enable; transforming them so that they can be combined remains the work of a tool that holds the authoritative definitions.¶
The annotations provide progressively richer evidence. Their absence does not make a schema invalid, and the annotations do not define analytical procedures, expressions, causal inference, execution policy, or a lineage model. Several of them carry lineage facts; none of them chains one value to another, and a schema needing lineage in the modeled sense uses a provenance model beside them.¶
This note is to be removed before publishing as an RFC.¶
The latest revision of this draft can be found at https://json-structure.github.io/semantic-annotations/draft-vasters-json-structure-semantic-annotations.html. Status information for this document may be found at https://datatracker.ietf.org/doc/draft-vasters-json-structure-semantic-annotations/.¶
Source for this draft and an issue tracker can be found at https://github.com/json-structure/semantic-annotations.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 6 February 2027.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
A schema states how a value is written. It gives a type, and with JSON Structure
Units [JSTRUCT-UNITS] it gives a unit, so that a reader knows a member holds a
number of metres. It does not state what the number measures, what the metres
are measured from, or when the measurement applies. Two schemas can agree on
double and on m and still describe water level above a tide-gauge datum and
height above an ellipsoid, which are not the same quantity and must not be
compared. That difference is usually recorded in prose documentation, inferred
from a member name, or known only to the people who built the system.¶
This document is an extension to JSON Structure Core [JSTRUCT-CORE] that records it in the schema, by annotation, without changing what the schema validates. Its keywords let a schema author:¶
bind a type or member to a term in a published vocabulary, so that two systems naming a thing differently can establish that they mean the same thing;¶
declare what a record observes and which member carries the result, as distinct from the property observed, the feature it belongs to, the procedure that produced it, and the time it applies to;¶
name the reference system a value is expressed against, whether a temporal regime, a coordinate reference system, a vector or tensor reference frame, a transformation from one frame to another, or a linear reference system, so that a position, a direction, or an orientation can be interpreted and two of them compared; and¶
resolve the members that together carry one compound value onto the axes, channels, or bands that give them meaning, whether those of a color space, an audio channel layout, or a set of spectral bands.¶
The same concern applies to a value that stands alone. One keyword binds a coded value to the register that assigns the code its meaning, so that a number or a short string can be resolved rather than guessed at from a member name. Another records the frequency weighting, time weighting, and level reference that a conditioned measurement already carries, so that a sound level measured under one weighting is not silently compared with one measured under another.¶
Most of these are bindings to a definition maintained elsewhere. This document defines no vocabulary, no reference system, no color space, no channel layout, and no code list of its own. Established bodies publish them, and an annotation refers to one. What is defined here is the form of that reference, the roles a schema may assign to its own members, and the rules by which a processor can check that the two agree.¶
The roles are the one place where that account needs qualifying. semanticRole
and derivation do carry a vocabulary of observation concepts, and this document
does not pretend otherwise. What it declines to be is a normative encoding of the
observation model of [ISO19156]. It defines no observation as a type to
instantiate, no classes for procedures or features, and no relationships among
observation entities, and it requires no record to be shaped like an observation.
The roles describe what the members of a record already are, over a structure
someone else fixed. A record does not become an observation by carrying them, a
schema that carries none is not deficient for that reason, and a processor MUST NOT reconstruct an observation entity from the roles it finds.¶
Nor does this document define analytical procedures. Several keywords name an operation, and naming one is not specifying it. A schema that declares a value an hourly mean records an operation that has already been performed; it does not state what a mean computes, how gaps in the set were treated, whether the window was inclusive of its bounds, or whether a consumer may recompute the value. The annotations describe what was done, and a processor MUST NOT read an instruction out of them.¶
Nor does it define a lineage model, although several keywords carry lineage
facts: derivation says how a value was produced, observingProcedure
identifies what produced it, and ingestionTime says when a system received it.
[PROV-O] gives entities, activities, and agents identities and relates them by
derivation and attribution. What is here is flat and confined to one record,
with no identity for the act that produced a value and no way to chain a value
to the value it came from. The two do not conflict and a record can carry both;
the concepts example maps a publication instant onto prov:generatedAtTime
(Section 4.1).¶
The keywords defined here are chosen by one test. A quality of a value earns a keyword when a consumer must know it to decide whether two values may be combined or compared, and when it holds for the type rather than varying from one instance to the next. Axis order, frequency weighting, and the register a code is drawn from meet that test: get one of them wrong and an arithmetic result is wrong while every value still validates. Licensing, retention, and endpoint addressing do not meet it, because they do not change what may be computed from a value. A per-observation calibration record does not meet it, because it varies from one record to the next and belongs in the payload rather than in the schema. Geometry, provenance, and unit algebra do not meet it, because each is a model in its own right that another specification defines; this document cites those rather than restating them.¶
The annotations are optional and additive. A processor that does not implement them reads the schema exactly as JSON Structure Core defines it.¶
Most keywords defined here share one shape. Each is an object carrying a
reference property that identifies a definition and a kind property that
names the model the definition belongs to. This lets a reader know how to
interpret it and a processor know what can be checked.
concepts, observedProperty, temporalReferenceSystem,
coordinateReferenceSystem, linearReferenceSystem, codedValues, the entries
of vectorReferenceFrames, colorSpaces, audioChannels, and
spectralBands, and the frames named by tensorReferenceFrames and
frameTransforms all follow this shape.¶
A definition is ordinarily maintained outside the schema, and reference is
then an absolute URI [RFC3986]. Every one of these keywords except concepts
and observedProperty also admits a definition held in the schema itself,
carried by a shareable type that
Section 8 calls a meta-type; kind is then type and reference is a
type reference { "$ref": <JSON Pointer> } to that type. The kind determines
which form applies, and each keyword states the rule for its own values.¶
A kind names a definition model and not the format of the resource that
carries it. The enumerations are open so that an author whose model is not
already named can name it.¶
The annotations bind terms; they do not express statements, node identity, or entailment, and this document defines no prefix mechanism and no compact URI form.¶
A value is read against one temporal or coordinate reference system, quantifies
one phenomenon, and draws its code from one register, so those keywords,
observedProperty, and codedValues each take a single binding. Vocabularies
overlap by design, and the same notion is
deliberately given a term in several of them, so concepts takes a list. The
keywords that resolve components onto axes, channels, or bands take a list for a
different reason: one record can carry several such quantities at once, and one
set of numbers can be published in more than one frame or more than one color
space at once.¶
measurementConditioning stands apart from this shape. It identifies no
external definition, because what it records is not a system a value is read
against but a treatment the value has already undergone, and it states that
treatment directly.¶
Observation is one application of the general model in Section 1.1. An
observable property definition is the externally governed concept, such as
water level or bridge vibration, and this document defines no format for one.
An observed property declaration is the observedProperty annotation that
binds one record shape to one such definition.¶
An observation act is one concrete execution of an observing procedure for one declared observed property and feature context, producing one result value and optionally its qualifiers.¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
concepts MAY occur on a type definition and on a property, collection item,
map value, or choice member schema, subject to Section 4.¶
semanticRole, derivation, temporalReferenceSystem, and cadence MAY occur
directly on a property, collection item, map value, or choice member schema,
subject to Section 5. phenomenonTimeRelation and
supportPeriod MAY occur on a direct property. statistic MAY occur wherever
derivation occurs, subject to Section 5.5. temporalReferenceSystem MAY
also occur on an object or tuple that defines a temporal type, and binds an
existing member of it.¶
observedProperty MAY occur on an object or tuple intended to describe an
observation record, and on a member schema of one that carries a result,
subject to Section 5.1.
coordinateReferenceSystem, vectorReferenceFrames, tensorReferenceFrames,
frameTransforms, linearReferenceSystem, colorSpaces, audioChannels, and
spectralBands MAY occur on an object or tuple and bind existing properties.
codedValues and measurementConditioning MAY occur on a property, collection
item, map value, or choice member schema, subject to their sections.
referenceRole MAY occur on a member of a meta-type, subject to Section 8.¶
All keywords defined here are direct peer keywords, and no wrapper is implied. Every annotation is OPTIONAL, and a schema can use any subset, including none. Conformance constrains only annotations that are present; it never requires another annotation or an annotated property to exist.¶
Several keywords bind members of the annotated type by name. Such a name is the
name of the property as declared in the schema, and it is resolved against the
effective definition of the annotated type, which includes members contributed
by $extends and members of an imported or shadowing definition
[JSTRUCT-IMPORT]. A name that does not resolve to a direct member of that
effective definition is invalid. An alternate, localized, or otherwise
serialization-facing name assigned to a member by another extension, such as
JSON Structure Alternate Names [JSTRUCT-ALTNAMES], changes how the member
appears in an instance document and does not change the identity the annotation
binds; a processor MUST NOT resolve a member name stated in an annotation
against such a name, and MUST NOT treat the presence of one as altering the
mapping.¶
Every reference value that is a URI SHOULD be resolvable, and dereferencing it
SHOULD yield a definition of the identified term or system. A processor is not
required to dereference a reference, and an unresolved reference is
indeterminate rather than incorrect.¶
A reference identifies whatever its URI identifies, revision included, and
this document defines no version, epoch, or as-of member to stand beside it. A
body that revises definitions and means them to stay citable puts the revision
in the identifier, which is what the naming policy behind the identifiers used
throughout this document provides for: a definition is named
/def/{objectType}/{authority}/{version}/{code} [OGC-NAMES], so the 0 in
http://www.opengis.net/def/crs/EPSG/0/4326 occupies a version position that a
schema needing one edition rather than another fills in. A schema pins an
edition by writing the identifier the publisher supplies for it.¶
A separate member would state in a second place what the URI already carries,
and no processor could reconcile the two without resolving the reference, which
none is required to do. Where a publisher revises without giving each revision
an identifier, a schema SHOULD record that fact in description, which informs
a reader without inviting a processor to act on it.¶
The enumerations of this document are of two sorts, and one rule divides them. An enumeration is closed where its values select a behavior that this document itself defines, so that a value outside it would establish nothing for any processor. An enumeration is open where its values name a model, register, or definition that another body maintains, because this document cannot enumerate what others publish and a value it has not heard of may still be one that a reader knows.¶
Closed are semanticRole, derivation, statistic in both of its forms,
phenomenonTimeRelation, referenceRole, sortOrder, the kind of cadence,
the anchor of supportPeriod,
the variance of vectorReferenceFrames and tensorReferenceFrames, the
symmetry of tensorReferenceFrames, the encoding and rotationSequence of
frameTransforms, and the alphaMode and transfer of colorSpaces. A value
outside a closed enumeration is invalid.¶
Open are the kind of every reference-style keyword, the weighting and
timeWeighting of measurementConditioning, the levelReference of both
measurementConditioning and audioChannels, the calibration of
spectralBands, and the encoding of audioChannels. A value
outside an open enumeration is valid; a processor MUST preserve it and MUST NOT
reject a schema for carrying it.¶
Closure states where a value's meaning comes from, and is not a claim that a list is finished. A later version of this document may add values to a closed enumeration, and the versioned meta-schema URI a schema names is what tells a processor which set is in force (Section 14).¶
A value defined here is one a processor can act on, and this document states what each establishes. A value not defined here establishes nothing, and a processor MUST NOT infer a constraint from it. This document defines no registry of further values and no mechanism by which a private value acquires meaning for a processor that does not already know it.¶
Because the open enumerations are open, two authors may choose one token for two
unrelated things. kind classifies and reference identifies, and that
division bounds the consequence. A processor MUST NOT treat a kind value as
establishing the identity of a definition, and MUST NOT conclude from two
schemas carrying equal kind values that they draw on the same register, model,
or definition. Where kind agrees and reference does not, the references
govern. A processor meeting a kind it does not recognize reports the check
indeterminate (Section 13.1).¶
That bounds a collision rather than preventing one. A registry of kind values
is the remedy; a future revision of this document is expected to establish one,
and none exists at the time of writing. Until one does, a kind value outside
those defined here means what it means only to a processor that already knows
it. A schema SHOULD use a value defined here where one fits, and SHOULD NOT coin
a broad token such as sensor or registry for a private arrangement, since
the broadest tokens are the ones most likely to be coined twice.¶
| Keyword | Meaning |
|---|---|
concepts
|
Terms in external vocabularies that the annotated node corresponds to. |
semanticRole
|
Function of a result, temporal, quality, status, or operational value. |
observedProperty
|
Reference to an observable-property definition. |
phenomenonTimeRelation
|
Refinement of how a result relates to phenomenonTime. |
supportPeriod
|
Length of the phenomenon-time period a result characterizes, and the position anchoring it. |
derivation
|
Category describing how a result value was produced. |
statistic
|
Summary function that produced a result from a set of values. |
temporalReferenceSystem
|
Binding from a temporal-position encoding to its reference definition. |
cadence
|
Expected pattern of successive temporal positions. |
coordinateReferenceSystem
|
CRS and ordered properties forming a coordinate. |
vectorReferenceFrames
|
Frames and ordered properties forming the components of vector quantities. |
tensorReferenceFrames
|
Frames and indexed properties forming the components of tensor quantities. |
frameTransforms
|
Frames and properties forming a transformation from one frame to another. |
linearReferenceSystem
|
LRS and properties forming a location along a linear element. |
colorSpaces
|
Color spaces and properties forming the channels of color values. |
referenceRole
|
Function of a member within a reference-system meta-type. |
audioChannels
|
Audio channel layout, level reference, and encoding for channel values. |
spectralBands
|
Spectral bands and ordered properties forming the bands of a multiband value. |
codedValues
|
Binding from a coded property to an external code list. |
measurementConditioning
|
Frequency or time weighting and level reference a scalar measurement carries. |
Omission means undeclared unless stated otherwise. It never implies compatible, successful, or acceptable data.¶
concepts Keyword
The concepts keyword binds the annotated node to terms defined by external
vocabularies, following the model in Section 1.1. In this document a
concept is any term that a vocabulary defines, including a class, a property, or
a SKOS concept [SKOS]. The skos-concept kind names one such term type and
places no constraint on the others.¶
When present, concepts MUST be a non-empty array of objects. Each object MUST
have a REQUIRED reference string and a REQUIRED kind string. No other
properties are permitted.¶
The array is unordered and no entry is primary. Every entry holds
simultaneously: the annotated node corresponds to all of the terms listed, and a
reader does not select among them. Two entries MUST NOT carry the same
reference.¶
reference Property
reference MUST be an absolute URI [RFC3986] that identifies one term. The
URI is the identifier that the vocabulary assigns to the term. This document
defines no prefix mechanism, no compact form, and no resolution protocol.¶
kind Property
kind classifies which definition model the URI identifies. It is an open
enumeration. The following values are defined:¶
| Value | Referenced definition |
|---|---|
rdfs-class
|
A class in RDF Schema [RDF-SCHEMA]. |
rdf-property
|
An RDF property [RDF-CONCEPTS]. |
owl-class
|
A class in OWL 2 [OWL2]. |
owl-object-property
|
An OWL 2 object property. |
owl-datatype-property
|
An OWL 2 datatype property. |
skos-concept
|
A concept in a SKOS concept scheme [SKOS]. |
dcterms-property
|
A property in DCMI Metadata Terms [DCTERMS]. |
Other values MAY identify further definition models. Appendix A.1 lists namespace URIs for the vocabularies named above.¶
A kind denotes either a class or a property, and the two attach to different
schema nodes:¶
rdfs-class and owl-class denote a class, and the annotation MUST occur on
a type definition.¶
rdf-property, owl-object-property, owl-datatype-property, and
dcterms-property denote a property, and the annotation MUST occur on a
property, collection item, map value, or choice member schema.¶
skos-concept denotes neither, and the annotation MAY occur on either.¶
All entries of one concepts array MUST agree. An array MUST NOT combine an
entry whose kind denotes a class with an entry whose kind denotes a
property. A kind outside the values defined above establishes no constraint,
and a processor MUST NOT infer one.¶
The keywords defined in this document already state the part a node plays. A
concepts entry that names a term whose meaning is that same part carries no
information beyond the annotation it accompanies and SHOULD be omitted.¶
A node that carries observedProperty is an observation record by
Section 3, and SHOULD NOT also be bound to a general observation
class. A member that carries semanticRole SHOULD NOT also be bound to a
vocabulary property whose meaning is that same role, such as a term for the
result of an observation beside observationValue, or a term for the feature
that an observation is about beside featureOfInterest.¶
concepts is for meaning that the annotation model does not carry: the domain
class of a record, the catalogue or taxonomic term that a member names, or a
correspondence that a consumer needs in order to join the data to another
vocabulary. A redundant entry is not an error, and a processor MUST NOT reject a
document for carrying one.¶
observedProperty
concepts states which external terms the annotated node corresponds to.
observedProperty states which phenomenon a record quantifies. Where the term
is an observable-property definition, observedProperty carries it and
concepts MUST NOT name it. The same URI MUST NOT appear in both keywords on
one node.¶
Correspondences between one observable-property definition and terms in other
vocabularies belong to the definition and are recorded once there, as described
in Section 5.2. They are not repeated as concepts entries
in every schema that cites the definition.¶
A binding is a statement about meaning and not about resolution. A missing or unresolved term is indeterminate and MUST NOT be repaired from property names, descriptions, labels, or samples.¶
Example:¶
{
"name": "TideGaugeReading",
"type": "object",
"description": "One water-level reading from a coastal tide gauge.",
"concepts": [
{
"reference": "http://www.w3.org/ns/sosa/Observation",
"kind": "owl-class"
}
],
"observedProperty": {
"reference": "https://vocab.nerc.ac.uk/collection/P01/current/ASLVZZ01/",
"kind": "nerc-p01"
},
"properties": {
"waterLevel": {
"type": "double",
"unit": "m",
"description": "Height of the water surface above chart datum.",
"examples": [2.41],
"semanticRole": "observationValue",
"concepts": [
{
"reference": "http://www.w3.org/ns/sosa/hasSimpleResult",
"kind": "rdf-property"
}
]
},
"issued": {
"type": "datetime",
"description": "Instant at which the reading was published.",
"examples": ["2026-03-11T08:15:00Z"],
"concepts": [
{
"reference": "http://purl.org/dc/terms/issued",
"kind": "dcterms-property"
},
{
"reference": "http://www.w3.org/ns/prov#generatedAtTime",
"kind": "rdf-property"
}
]
}
}
}
¶
observedProperty Keyword
The observedProperty keyword identifies the observable-property definition
associated with an observation record, or with one result within it, as
introduced in Section 1.2.¶
When present, observedProperty MUST be an object with a REQUIRED reference
string and a REQUIRED kind string. No other properties are permitted.¶
reference Property
reference MUST be an absolute URI [RFC3986] that identifies one immutable
observable-property definition. Version identity, when used, is implied by the
URI itself, and a materially different concept MUST be identified by a
different URI. The URI SHOULD deep-link to one concrete definition entry in the
selected vocabulary. This document does not define a resolution protocol, URI
layout, storage model, or catalog serialization.¶
kind Property
kind classifies which definition model the URI identifies. It is an open
enumeration, and a value identifies the vocabulary or catalog type that
publishes the definition.¶
Examples of catalog types include:¶
cf-standard-name for URIs identifying entries from the CF Standard Name
Table, for example a URI identifying air_temperature;¶
nerc-p01 for entries from the NERC Vocabulary Server Parameter Usage
Vocabulary (P01) identified by dereferenceable concept URIs.¶
An organization that publishes its own catalog names its own model. The examples
in this document use example-catalog where the cited catalog is fictional.¶
Example:¶
{
"observedProperty": {
"reference": "https://vocab.nerc.ac.uk/collection/P01/current/CTMPZZ01/",
"kind": "nerc-p01"
}
}
¶
observedProperty MAY occur on an object or tuple that describes an observation
record, and on a member schema of that object or tuple that carries a result.¶
On a record it identifies the observable property of every result in that record that does not carry one of its own. On a result member it identifies the observable property of that result alone and takes precedence over the record's.¶
Every annotation identifies exactly one observable property for the node it is attached to. A missing or unresolved reference is indeterminate and MUST NOT be repaired from labels, mappings, result schemas, units, descriptions, property names, or samples.¶
The feature, procedure, and temporal roles of a record are shared by every
result in it. Where a record carries more than one result, a resultQuality on
the record qualifies all of them, and qualifying one result on its own requires
modelling that result as a nested object.¶
Example of a record with two results:¶
{
"name": "BuoySurfacePacket",
"type": "object",
"properties": {
"buoy_id": {
"type": "string",
"semanticRole": "featureOfInterest"
},
"measured_at": {
"type": "datetime",
"description": "Time both results occurred",
"examples": ["2026-07-27T12:00:00Z"],
"semanticRole": "phenomenonTime"
},
"sea_surface_temperature": {
"type": "double",
"unit": "Cel",
"examples": [18.4],
"semanticRole": "observationValue",
"observedProperty": {
"reference": "https://vocab.nerc.ac.uk/collection/P01/current/CTMPZZ01/",
"kind": "nerc-p01"
}
},
"practical_salinity": {
"type": "double",
"unit": "1",
"examples": [35.1],
"semanticRole": "observationValue",
"observedProperty": {
"reference": "https://vocab.nerc.ac.uk/collection/P01/current/PSLTZZ01/",
"kind": "nerc-p01"
}
}
},
"required": ["buoy_id", "measured_at", "sea_surface_temperature", "practical_salinity"],
"additionalProperties": false
}
¶
An authority MAY publish semantic mappings from an observable-property
definition to other identified concepts, using relation kinds such as
exactMatch, closeMatch, broader, narrower, related, and
quantityKind. Mapping targets MUST be absolute URIs, and a mapping SHOULD
carry a review state such as proposed, reviewed, or rejected.¶
Only a reviewed exactMatch can provide evidence that two distinct identifiers
denote the same observable property. closeMatch, hierarchy, relatedness,
label similarity, and quantity-kind classification do not establish
equivalence, and no mapping alone authorizes execution.¶
A quantityKind mapping can reference a QUDT QuantityKind [QUDT] as a
classification and compatibility hint. Other mappings can target CF Standard
Names, SOSA/SSN concepts, or agency vocabularies
[CF-STANDARD-NAMES] [SOSA-SSN].¶
An authority MAY identify an expected result schema. That schema and any quantity-kind mapping are hints; the actual result schema and JSON Structure Units annotations remain authoritative. An observable-property definition MUST NOT override unit semantics or duplicate authoritative dimensions, unit lists, conversion factors, or conversion formulas.¶
semanticRole Keyword
The semanticRole keyword identifies the observation or operational function of an
annotated value.¶
The value of semanticRole MUST be one of a closed set of permitted values
defined in this section. A semanticRole value is never a URI; terms drawn from
external vocabularies are carried by concepts instead.¶
semanticRole is scalar; therefore each annotated schema element can carry one
semanticRole value.¶
One containing type MUST NOT declare two direct members with the same value for
any of phenomenonTime, resultTime, effectiveTime, phenomenonTimeStart,
phenomenonTimeEnd, effectiveTimeStart, effectiveTimeEnd, ingestionTime,
scheduledTime, actualTime, or forecastIssueTime. Each of these roles
identifies one position, and every rule in this document that resolves such a
role to a member presumes one. A type declaring two leaves those rules
unresolvable rather than ambiguous.¶
The observation-result and feature roles carry no such restriction. Repetition is meaningful for them, and the sections defining them state what it projects.¶
A record using the roles of this concern, together with the feature and procedure roles defined below:¶
{
"name": "WaterLevelObservation",
"type": "object",
"observedProperty": {
"reference": "https://catalog.example.org/observable-properties/water-level/v1",
"kind": "example-catalog"
},
"properties": {
"station": {
"type": "string",
"examples": ["USGS-12149000"],
"semanticRole": "featureOfInterest"
},
"procedure": {
"type": "string",
"examples": ["Pressure transducer"],
"semanticRole": "observingProcedure"
},
"result": {
"type": "double",
"description": "Water level above datum",
"unit": "m",
"examples": [2.47],
"semanticRole": "observationValue"
},
"quality": {
"type": "string",
"description": "Quality classification for this result",
"examples": ["validated", "estimated"],
"semanticRole": "resultQuality"
}
},
"required": ["station", "procedure", "result", "quality"],
"additionalProperties": false
}
¶
observationValue
A property carrying the result of an observation act.¶
An observationValue is the outcome of one observation act, not the act
itself. Each act is represented by one complete value in one annotated
property. Structured or composite results can be represented with an object,
tuple, or another compatible compound type. Multiple observationValue
properties in the same containing type represent multiple results, not one
combined act.¶
resultQuality
One result-quality value associated with the observation, corresponding to ISO result semantics [ISO19156].¶
resultQuality qualifies the observationValue; it is not the result value
itself.¶
A single observation act can carry multiple quality qualifiers, and each direct
property with semanticRole: resultQuality projects one of them.¶
The value schema or external vocabulary defines the quality scale. This
specification defines no threshold, ordering, confidence model, or processing
effect. Omission does not imply acceptable quality. Procedure-level quality
metadata describes the measuring process in general and is distinct from
resultQuality, which describes one observation result.¶
A record using the roles of this concern:¶
{
"name": "RiverSampleObservation",
"type": "object",
"observedProperty": {
"reference": "https://catalog.example.org/observable-properties/dissolved-oxygen/v1",
"kind": "example-catalog"
},
"properties": {
"observationId": { "type": "uuid" },
"waterBody": {
"type": "string",
"description": "River water body ultimately of interest",
"examples": ["Rhine", "Niers", "Schwalm"],
"semanticRole": "ultimateFeatureOfInterest"
},
"sampleParcel": {
"type": "string",
"description": "Sampled water parcel directly involved in observing",
"examples": ["Surface sample at station 17"],
"semanticRole": "proximateFeatureOfInterest"
},
"sampler": {
"type": "uri",
"description": "Instrument identifier from a device catalogue",
"examples": ["https://vocab.nerc.ac.uk/collection/L22/current/TOOL1248/"],
"semanticRole": "observingProcedure"
},
"dissolvedOxygen": {
"type": "double",
"unit": "mg/L",
"semanticRole": "observationValue"
}
},
"required": ["observationId", "waterBody", "sampleParcel", "sampler", "dissolvedOxygen"],
"additionalProperties": false
}
¶
featureOfInterest
Value identifying or describing the feature whose property is observed: the
entity that is the subject of the observation. It is distinct from
observedProperty, which identifies which property is observed, from
observingProcedure, which identifies how the value is produced, and from
observationValue, which carries the result.¶
The annotated property's value can be a scalar, object, tuple, or collection. The property schema defines representation, cardinality, and requiredness.¶
Where the feature is a member of a collection held elsewhere in the document, JSON Structure Relations [JSTRUCT-RELATIONS] states the reference:¶
{
"definitions": {
"HydroGraph": {
"type": "object",
"name": "HydroGraph",
"properties": {
"riverReaches": {
"type": "array",
"items": { "$ref": "#/definitions/RiverReach" }
},
"observations": {
"type": "array",
"items": { "$ref": "#/definitions/RiverObservation" }
}
},
"required": ["riverReaches", "observations"],
"additionalProperties": false
},
"RiverReach": {
"type": "object",
"name": "RiverReach",
"identity": ["reachId"],
"properties": {
"reachId": { "type": "string" },
"riverName": { "type": "string" },
"fromNodeId": { "type": "string" },
"toNodeId": { "type": "string" },
"lengthMeters": { "type": "double", "unit": "m" }
},
"required": ["reachId", "riverName", "fromNodeId", "toNodeId"],
"additionalProperties": false
},
"RiverObservation": {
"type": "object",
"name": "RiverObservation",
"identity": ["observationId"],
"properties": {
"observationId": { "type": "uuid" },
"reachIdRef": {
"type": "string",
"description": "River reach identifier",
"semanticRole": "featureOfInterest"
},
"waterLevel": {
"type": "double",
"semanticRole": "observationValue",
"unit": "m"
}
},
"relations": {
"featureReachRef": {
"cardinality": "single",
"targettype": { "$ref": "#/definitions/RiverReach" },
"scope": "#/definitions/HydroGraph/properties/riverReaches"
}
},
"required": ["observationId", "reachIdRef", "waterLevel"],
"additionalProperties": false
}
}
}
¶
In an instance, reachIdRef carries the same identifier value used by
the relation target identity, for example "RR-1042".¶
proximateFeatureOfInterest
Value identifying or describing the feature directly involved in observing.¶
This role identifies the immediate feature participating in measurement context (for example a sampled parcel). Where the observation involves sampling, this is the feature that ISO 19156 [ISO19156] calls a sampling feature.¶
ultimateFeatureOfInterest
Value identifying or describing the feature ultimately of interest.¶
This role identifies the broader feature for which the observation is semantically interpreted.¶
Neither proximate nor ultimate feature is inferred from the other. When
featureOfInterest and specialized FoI roles coexist, processors MUST preserve
them as separate declarations and MUST NOT assume equivalence. Feature identity
MUST NOT be inferred from observation identity, location, property names, or
transport metadata.¶
observingProcedure
Value identifying or describing the procedure used for the observation act.¶
Procedure identity is comparability-critical: different procedures can yield different biases or meanings for the same property and feature. Equality is evidence for candidate grouping, not proof of statistical interchangeability. When a shared catalog is available, procedure identifiers SHOULD be expressed as URIs; a device or instrument registry serves where the procedure is effectively defined by the instrument or sampler used.¶
A record using the roles of this concern:¶
{
"name": "WaterLevelBulletin",
"type": "object",
"properties": {
"station_id": {
"type": "string",
"semanticRole": "featureOfInterest"
},
"observed_at": {
"type": "datetime",
"description": "Time when the water level applied at the station",
"examples": ["2026-07-27T12:00:00Z"],
"semanticRole": "phenomenonTime"
},
"published_at": {
"type": "datetime",
"description": "Time when the result became available",
"examples": ["2026-07-27T12:00:04Z"],
"semanticRole": "resultTime"
},
"in_force": {
"type": "object",
"description": "Period during which the bulletin is in force",
"semanticRole": "effectiveTime",
"properties": {
"start": { "type": "datetime" },
"end": { "type": "datetime" }
},
"required": ["start", "end"],
"additionalProperties": false
},
"water_level": {
"type": "double",
"unit": "m",
"semanticRole": "observationValue"
}
},
"required": ["station_id", "observed_at", "published_at", "in_force", "water_level"],
"additionalProperties": false
}
¶
phenomenonTime
Time during which the result applies to the observed property. It can be represented as an instant or period.¶
When used for an instant, phenomenonTime MUST annotate a value whose Core
type and reference binding together encode a temporal position. It MAY instead
annotate a named object or tuple representing a period.¶
resultTime
Temporal position at which the result became available.¶
resultTime MUST annotate a value whose Core type and reference binding
together encode a temporal position.¶
effectiveTime
Period during which the record is in force and its use is intended.¶
effectiveTime MAY annotate a named object or tuple representing a period.¶
effectiveTime qualifies the record and not the phenomenon. It states how long
a warning, advisory, or other issued statement is meant to be acted on, and it
gives no boundary to any observed property. A record that describes a period of
the world, including a forecast, states that period with phenomenonTime or
with phenomenonTimeStart and phenomenonTimeEnd. The two are independent: a
warning in force for twelve hours may concern a phenomenon lasting minutes.¶
This document defines no record-versioning axis. The role is named
effectiveTime rather than validTime because the latter names the
bitemporal valid time of ISO 19108 [ISO19108]. effectiveTime is not that
valid time, which pairs the period a fact is held true of the world with the
period a system recorded it, and a processor MUST NOT read it as one.¶
“Time” or “Duration” in any ISO, boundary, or operational role name defined by
this document does not require a Gregorian, ISO 8601, or RFC 3339 encoding. The
semanticRole states semantics; the Core type and any
temporalReferenceSystem state representation and reference semantics.¶
A record using the roles of this concern. The two pairs are independent axes: the phenomenon-time pair bounds what the result is about, and the effective-time pair bounds how long the record is in force.¶
{
"name": "AirQualityAdvisory",
"type": "object",
"properties": {
"site_id": {
"type": "string",
"semanticRole": "featureOfInterest"
},
"averaging_window_opens": {
"type": "datetime",
"examples": ["2026-07-27T12:00:00Z"],
"semanticRole": "phenomenonTimeStart"
},
"averaging_window_closes": {
"type": "datetime",
"examples": ["2026-07-27T13:00:00Z"],
"semanticRole": "phenomenonTimeEnd"
},
"advisory_effective_at": {
"type": "datetime",
"examples": ["2026-07-27T15:00:00Z"],
"semanticRole": "effectiveTimeStart"
},
"advisory_expires_at": {
"type": "datetime",
"examples": ["2026-07-28T03:00:00Z"],
"semanticRole": "effectiveTimeEnd"
},
"mean_pm25": {
"type": "double",
"unit": "ug/m3",
"semanticRole": "observationValue"
}
},
"required": [
"site_id",
"averaging_window_opens",
"averaging_window_closes",
"advisory_effective_at",
"advisory_expires_at",
"mean_pm25"
],
"additionalProperties": false
}
¶
phenomenonTimeStart
Temporal position encoding the start of the phenomenonTime period.¶
phenomenonTimeStart MUST annotate a value whose Core type and reference
binding together encode a temporal position.¶
phenomenonTimeEnd
Temporal position encoding the end of the phenomenonTime period.¶
phenomenonTimeEnd MUST annotate a value whose Core type and reference
binding together encode a temporal position.¶
effectiveTimeStart
Temporal position encoding the start of the effectiveTime period.¶
effectiveTimeStart MUST annotate a value whose Core type and reference
binding together encode a temporal position.¶
effectiveTimeEnd
Temporal position encoding the end of the effectiveTime period.¶
effectiveTimeEnd MUST annotate a value whose Core type and reference binding
together encode a temporal position.¶
A paired start and end projects one period, not two separate attributes. Period
closure is not supplied by these role names. This specification uses half-open
[start,end) periods only for phenomenonTimeRelation; another convention requires a
separate representation or profile.¶
A planned activity, its execution, and its acceptance by a receiving system:¶
{
"name": "SamplingRun",
"type": "object",
"properties": {
"run_id": { "type": "uuid" },
"scheduled_sample_time": {
"type": "datetime",
"description": "Planned time for sample collection",
"examples": ["2026-07-27T14:00:00Z"],
"semanticRole": "scheduledTime"
},
"actual_sample_time": {
"type": "datetime",
"description": "Time when sample collection actually occurred",
"examples": ["2026-07-27T14:07:12Z"],
"semanticRole": "actualTime"
},
"ingested_at": {
"type": "datetime",
"description": "Time when the receiving system accepted the record",
"examples": ["2026-07-27T14:09:30Z"],
"semanticRole": "ingestionTime"
},
"station_id": {
"type": "string",
"semanticRole": "featureOfInterest"
}
},
"required": [
"run_id",
"scheduled_sample_time",
"actual_sample_time",
"ingested_at",
"station_id"
],
"additionalProperties": false
}
¶
ingestionTime
Temporal position when a declared system accepted the record.¶
ingestionTime MUST annotate a value whose Core type and reference binding
together encode a temporal position.¶
scheduledTime
Planned temporal position for an activity.¶
scheduledTime MUST annotate a value whose Core type and reference binding
together encode a temporal position.¶
actualTime
Temporal position when the planned activity occurred.¶
actualTime MUST annotate a value whose Core type and reference binding
together encode a temporal position.¶
forecastIssueTime
Forecast-specific resultTime: the temporal position when a forecast product
was issued.¶
forecastIssueTime MUST annotate a value whose Core type and reference
binding together encode a temporal position.¶
A forecast record states the position or period it describes with
phenomenonTime, or with phenomenonTimeStart and phenomenonTimeEnd. A
forecast is an observation whose result time precedes its phenomenon time, and
it carries the same temporal roles as any other observation; nothing about the
phenomenon-time roles restricts them to positions that have already elapsed.¶
forecastLeadDuration
Duration between the forecast issue position and the phenomenon-time position the forecast describes.¶
forecastLeadDuration MUST annotate Core duration or a numeric value with a
temporal unit [JSTRUCT-UNITS].¶
Example:¶
{
"name": "RiverStageForecast",
"type": "object",
"properties": {
"station_id": {
"type": "string",
"semanticRole": "featureOfInterest"
},
"issued_at": {
"type": "datetime",
"description": "Time when the forecast bulletin was issued",
"examples": ["2026-07-27T09:00:00Z"],
"semanticRole": "forecastIssueTime"
},
"forecast_window": {
"type": "object",
"description": "Phenomenon-time period the forecast describes",
"semanticRole": "phenomenonTime",
"properties": {
"start": { "type": "datetime" },
"end": { "type": "datetime" }
},
"required": ["start", "end"],
"additionalProperties": false
},
"lead_time": {
"type": "duration",
"description": "Duration from forecast issue to the phenomenon time described",
"examples": ["PT6H"],
"semanticRole": "forecastLeadDuration"
},
"predicted_water_level": {
"type": "double",
"unit": "m",
"semanticRole": "observationValue"
}
},
"required": ["station_id", "issued_at", "forecast_window", "predicted_water_level"],
"additionalProperties": false
}
¶
These operational values describe the handling of the record. A processor MUST NOT read any of them as phenomenonTime, resultTime, observedProperty,
featureOfInterest, or observingProcedure.¶
status
State of the record itself, or of the feature it describes, such as whether a value is provisional, verified, superseded, or withdrawn.¶
status MUST annotate a value drawn from a fixed set of states, which is a Core
string or an integer type. The states are defined outside this document. The
annotated schema MUST either constrain them with enum [JSTRUCT-CORE] or
identify the set that defines them, which MAY be a vocabulary referenced via
concepts.¶
status qualifies the record rather than the phenomenon. A change of status
does not change what was observed, and a record MAY be reissued with a new
status and an unchanged result.¶
status and resultQuality are distinct. resultQuality states how good a
result is, on a scale the quality vocabulary defines; status states how the
record carrying it is to be treated. A provisional record and a low-quality
result are independent conditions, and a processor MUST NOT read either
annotation as the other.¶
Example:¶
{
"name": "WaterLevelRecord",
"type": "object",
"properties": {
"water_level": {
"type": "double",
"unit": "m",
"semanticRole": "observationValue"
},
"record_status": {
"type": "string",
"description": "Standing of this record in the publication lifecycle",
"enum": ["provisional", "verified", "superseded", "withdrawn"],
"examples": ["provisional"],
"semanticRole": "status"
}
},
"required": ["water_level", "record_status"],
"additionalProperties": false
}
¶
derivation Keyword
The derivation keyword classifies how a result value was produced.¶
When present, derivation MUST be one of:¶
| Derivation | Meaning |
|---|---|
measured
|
Produced directly by an observation procedure performing measurement. |
statistic
|
Produced by summarizing a set of values with one of the functions named by statistic (Section 5.5). |
calculated
|
Produced by a deterministic calculation that statistic does not name. |
estimated
|
Inferred from incomplete, indirect, or uncertain evidence. |
modeled
|
Produced by a model, simulation, or predictive procedure. |
Routine conversion, rounding, or serialization does not by itself change
measured to calculated. The category identifies no source, formula,
software, or detailed procedure, and it is not a lineage model: it does not
identify the act that produced the value or relate that value to the values it
was derived from ([PROV-O]).¶
statistic and calculated divide the calculations between them. Where the
result is one of the summaries this document names, the derivation is
statistic and the statistic keyword names which one, so a reader can tell an
hourly mean from an hourly maximum without reading prose. Every other
calculation is calculated, and the schema SHOULD explain the method in the
description of the annotated schema. This document defines no expression
language, and a processor MUST NOT parse a description or reproduce a
calculation from it.¶
The names alone do not divide the categories, and the tests below do. They are stated so that two authors describing the same value reach the same category.¶
The first division is between measured and the rest, and it is the one that
carries the most weight for a consumer. A value is measured where it is what
an observation procedure read, and it is not measured where any function, fit,
inference, or model stood between the procedure and the value. Unit conversion,
rounding, and serialization are not such functions, as stated above.¶
Determinism divides calculated from estimated and modeled. Where the same
inputs must yield the same output, and the function could be written down, the
value is calculated however elaborate the arithmetic and however many inputs
it consumes. A dew point obtained from a measured temperature and a measured
humidity by a published formula is calculated.¶
Dependence on unobserved state divides modeled from estimated. An
estimated value carries only what the observations carry, arranged under an
assumption about their error: an interpolated fill for a failed sensor, or a
strike position derived from arrival times at several detectors, is estimated,
because something was observed and the value is an inference from evidence that
does not determine it. A modeled value carries information the observations do
not contain, supplied by the model's own representation of the system, and the
procedure would produce a value for a place and time at which nothing was
observed at all. A forecast temperature is modeled. A value that a model
produced and that observations then corrected, as in a reanalysis or an
assimilated field, is modeled, because the model supplies the state and the
observations only constrain it.¶
Where the choice among calculated, estimated, and modeled is genuinely
unclear, the schema SHOULD state the method in description. An author MUST NOT
resolve such a case by choosing measured, and a processor MUST NOT infer a
formula, a model, an uncertainty, or a procedure from any of these values.¶
Example:¶
{
"name": "SeaStateReport",
"type": "object",
"properties": {
"sea_state_index": {
"type": "double",
"description": "Composite sea-state index derived from significant wave height, peak period, and wind speed by the regional forecast model",
"semanticRole": "observationValue",
"derivation": "modeled"
}
},
"required": ["sea_state_index"],
"additionalProperties": false
}
¶
statistic Keyword
The statistic keyword names the summary function that produced a result value
from a set of values.¶
Most summary functions are fully identified by their name. A few are not: a
percentile is not one function but a family, and naming the family without the
rank identifies nothing. statistic therefore takes two forms. A function that
takes no parameter is written as a string, and a function that takes one is
written as an object that names the function and carries the parameter.¶
When statistic is a string, it MUST be one of:¶
| Statistic | Meaning |
|---|---|
mean
|
Arithmetic mean of the set. |
median
|
Middle value of the ordered set. |
mode
|
Most frequent value of the set. |
minimum
|
Least value of the set. |
maximum
|
Greatest value of the set. |
sum
|
Total of the set. |
count
|
Number of values in the set. |
standardDeviation
|
Standard deviation of the set. |
variance
|
Variance of the set. |
range
|
Difference between the greatest and least value. |
When statistic is an object, it MUST carry a function member and the
parameter member that function requires, and no other members. function
MUST be one of:¶
| Function | Parameter | Meaning |
|---|---|---|
percentile
|
percentile
|
Value below which the stated percentage of the set falls. |
nthHighest
|
rank
|
Value at the stated position counting down from the greatest. |
nthLowest
|
rank
|
Value at the stated position counting up from the least. |
percentile MUST be a number greater than zero and less than one hundred, and
rank MUST be an integer of two or more. percentile MUST be present when
function is percentile and MUST NOT be present otherwise; rank MUST be
present when function is nthHighest or nthLowest and MUST NOT be present
otherwise.¶
Both enumerations are closed. A statistic that is a string outside the first
table, or an object whose function is outside the second, is invalid.¶
One meaning has one spelling. A function that takes no parameter MUST be written
in the string form, so { "function": "mean" } is invalid. A percentile of zero
or one hundred MUST be written as minimum or maximum, and a rank of one MUST
be written as maximum or minimum, which is why the ranges above exclude
them. A quantile is expressed as the equivalent percentile, so a quantile of
0.95 is written as a percentile of 95. Without these rules two schemas could
declare the same statistic in ways that no equality test would match.¶
A rank is not a percentile. The fourth-highest value of a set of three hundred
and sixty-five is the 99.18th percentile and of a set of ninety is the 96.7th,
so neither form can be rewritten as the other without knowing how many values
the set held, and statistic does not state that. Both forms are needed because
both are what definitions in force actually specify: an air quality limit is
commonly expressed as a rank, and a service level objective as a percentile.¶
statistic and the statistic derivation are one declaration in two parts. A
schema whose derivation is statistic MUST carry a statistic keyword, and a
schema carrying a statistic keyword MUST have a derivation of statistic.
The derivation says the value summarizes a set, and the keyword says how. Where
phenomenonTimeRelation is accumulation,
statistic MUST be the string sum.¶
A calculation that no value in either table names is calculated rather than
statistic, and Section 5.4 states what a schema does instead.¶
A vocabulary term names the phenomenon and frequently excludes the summary
function, so an hourly mean and an hourly maximum of one phenomenon carry the
same observedProperty and differ only here. Two results that carry the same
observable property and different statistics are not comparable as like
quantities. The parameter is part of the statistic: a 95th percentile and a 99th
percentile are different statistics, as are a fourth-highest and a
fifth-highest, and a processor MUST NOT treat two parameterized statistics as
alike unless both the function and the parameter agree.¶
The set that the statistic summarizes is the one the other annotations already
establish: the temporal roles give its extent in time, and the feature and
procedure roles give its subject. This document defines no other scoping, and
the only argument statistic takes is the one that identifies the function.
It does not state a window alignment, a weighting, a sample count, a treatment
of missing values, an interpolation method by which a percentile is obtained
from a finite set, or a computation, and a processor MUST NOT recompute a
result from it.¶
Example:¶
{
"name": "HourlyAirTemperatureSummary",
"type": "object",
"observedProperty": {
"reference": "https://cfconventions.org/Data/cf-standard-names/current/build/cf-standard-name-table.html#air_temperature",
"kind": "cf-standard-name"
},
"properties": {
"station": {
"type": "string",
"examples": ["DWD-10382"],
"semanticRole": "featureOfInterest"
},
"hour_start": {
"type": "datetime",
"examples": ["2026-07-27T12:00:00Z"],
"semanticRole": "phenomenonTimeStart"
},
"hour_end": {
"type": "datetime",
"examples": ["2026-07-27T13:00:00Z"],
"semanticRole": "phenomenonTimeEnd"
},
"temperature_mean": {
"type": "double",
"unit": "Cel",
"description": "Mean air temperature over the hour",
"examples": [21.4],
"semanticRole": "observationValue",
"derivation": "statistic",
"statistic": "mean"
},
"temperature_max": {
"type": "double",
"unit": "Cel",
"description": "Greatest air temperature over the hour",
"examples": [24.9],
"semanticRole": "observationValue",
"derivation": "statistic",
"statistic": "maximum"
}
},
"required": ["station", "hour_start", "hour_end", "temperature_mean", "temperature_max"],
"additionalProperties": false
}
¶
The record below carries two parameterized statistics of the same observable over the same interval. They differ only in the parameter, and nothing but the parameter distinguishes them.¶
{
"name": "RequestLatencySummary",
"type": "object",
"properties": {
"window_start": {
"type": "datetime",
"examples": ["2026-07-27T12:00:00Z"],
"semanticRole": "phenomenonTimeStart"
},
"window_end": {
"type": "datetime",
"examples": ["2026-07-27T12:05:00Z"],
"semanticRole": "phenomenonTimeEnd"
},
"latency_p95": {
"type": "double",
"unit": "ms",
"description": "Request latency below which 95 percent of requests in the window completed",
"examples": [128.4],
"semanticRole": "observationValue",
"derivation": "statistic",
"statistic": { "function": "percentile", "percentile": 95 }
},
"latency_p99": {
"type": "double",
"unit": "ms",
"description": "Request latency below which 99 percent of requests in the window completed",
"examples": [512.7],
"semanticRole": "observationValue",
"derivation": "statistic",
"statistic": { "function": "percentile", "percentile": 99 }
}
},
"required": ["window_start", "window_end", "latency_p95", "latency_p99"],
"additionalProperties": false
}
¶
The record below carries a rank. The fourth-highest daily maximum is the form in which an ozone air quality standard is stated, and it is not the same statistic as any percentile unless the number of days in the year is known.¶
{
"name": "AnnualOzoneSummary",
"type": "object",
"observedProperty": {
"reference": "https://cfconventions.org/Data/cf-standard-names/current/build/cf-standard-name-table.html#mole_fraction_of_ozone_in_air",
"kind": "cf-standard-name"
},
"properties": {
"site": {
"type": "string",
"examples": ["US-060370016"],
"semanticRole": "featureOfInterest"
},
"year_start": {
"type": "datetime",
"examples": ["2025-01-01T00:00:00Z"],
"semanticRole": "phenomenonTimeStart"
},
"year_end": {
"type": "datetime",
"examples": ["2026-01-01T00:00:00Z"],
"semanticRole": "phenomenonTimeEnd"
},
"fourth_highest_daily_max_8h": {
"type": "double",
"unit": "[ppb]",
"description": "Fourth-highest daily maximum eight-hour mean ozone mole fraction of the calendar year",
"examples": [68.0],
"semanticRole": "observationValue",
"derivation": "statistic",
"statistic": { "function": "nthHighest", "rank": 4 },
"phenomenonTimeRelation": "interval"
}
},
"required": ["site", "year_start", "year_end", "fourth_highest_daily_max_8h"],
"additionalProperties": false
}
¶
codedValues Keyword
The codedValues keyword binds the annotated property to a code list,
so that the coded value it carries can be resolved to the meaning the list
assigns it.¶
A coded value is a number or a short string that stands for a state in a
register maintained elsewhere: a present-weather code, an airport identifier, a
diagnosis code. The register gives each code a meaning, and the value alone does
not. This keyword differs from the reference-style keywords that bind components
to the axes of a space in that it binds a single value to a list of meanings, and
it differs from concepts in that concepts binds the node to one term while
codedValues binds the property to a whole enumeration that its instance values
draw from.¶
When present, codedValues MUST be an object with a REQUIRED reference and a
REQUIRED kind, and no other members.¶
reference and kind Properties
reference identifies one specific code list. It MUST be an absolute URI
[RFC3986], or, where kind is type, a type reference
{ "$ref": <JSON Pointer> } [JSTRUCT-CORE] to a shareable type definition that
enumerates the codes. kind does not name the list; it classifies the register
model the list belongs to, so that a processor knows how the list is organized
and how a value joins to an entry. Several lists of one model are distinguished
by reference, not by kind: the country, currency, and language tables are
three iso lists, and the location indicators and aircraft type designators are
two icao lists. kind is an open enumeration; the following values are
defined here:¶
| Kind | Kind of list |
|---|---|
wmo-codes
|
A register in the WMO Codes Registry [WMO-CODES]. |
iso
|
An ISO code table, such as the ISO 3166 country codes [ISO3166], the ISO 4217 currency codes [ISO4217], or the ISO 639 language codes [ISO639]. |
unlocode
|
The UN/LOCODE location code list [UNLOCODE]. |
icao
|
An ICAO code list, such as the location indicators of [ICAO7910] or the aircraft type designators of [ICAO8643]. |
iata
|
An IATA code directory [IATA-CODES]. |
iana
|
An IANA registry [IANA-PROTOCOLS], such as the Language Subtag Registry [IANA-LANGTAGS]. |
icd
|
A linearization of the WHO International Classification of Diseases [WHO-ICD]. |
snomed-ct
|
A SNOMED CT edition or reference set [SNOMED-CT]. |
loinc
|
The LOINC database [LOINC]. |
atc
|
The WHO Anatomical Therapeutic Chemical classification [WHO-ATC]. |
unspsc
|
The UNSPSC commodity code set [UNSPSC]. |
type
|
A meta-type in the annotated schema whose enumeration lists the codes, as Section 8 describes. |
The annotated value MUST be of a scalar type, a string or an integer, of the
kind the identified list uses for its notations. Where kind is type, the
identified type enumerates the code notations, and a value denotes the entry
whose notation it equals.¶
A value in an instance denotes the entry the register publishes for that value. How the value is joined to the entry, whether by appending it to the register URI or by another rule, is stated by the register and not by this document. Some registers, including [WMO-CODES], publish each entry at a dereferenceable URI; a reference to such a register identifies more often than it resolves, which is true of the other reference-style keywords as well.¶
A missing or unresolved register is indeterminate and MUST NOT be repaired from labels, property names, descriptions, or samples. A value that the register does not define is not made meaningful by this annotation.¶
A processor is not required to dereference the URI. This document does not define a resolution protocol, URI layout, storage model, or definition serialization.¶
The property below carries a present-weather code from the WMO register for present weather, whose entries are the integers zero through several hundred.¶
{
"name": "SurfaceObservation",
"type": "object",
"properties": {
"stationId": { "type": "string" },
"presentWeather": {
"type": "int32",
"codedValues": {
"reference": "http://codes.wmo.int/bufr4/codeflag/0-20-003",
"kind": "wmo-codes"
}
}
},
"required": ["stationId", "presentWeather"],
"additionalProperties": false
}
¶
measurementConditioning Keyword
The measurementConditioning keyword states the frequency weighting, time
weighting, and level reference that a scalar measurement carries, so that two values of
the same unit that were conditioned differently are not compared as like
quantities.¶
A weighted level is a single number, not a bundle of channels, and its unit does not record how it was made. An A-weighted sound level and a Z-weighted one are both in decibels, and comparing them is a mistake the numbers do not prevent. The weighting, the time constant, and the reference the level stands against are the hidden convention this keyword carries.¶
When present, measurementConditioning MUST be an object with an OPTIONAL
weighting, an OPTIONAL timeWeighting, and an OPTIONAL levelReference, and no
other members. At least one of the three MUST be present. The annotated value
MUST be of numeric type.¶
weighting Property
weighting, when present, names the frequency weighting the value carries. It is
an open enumeration; the values defined here are the frequency weightings of
[IEC61672-1]:¶
| Value | Meaning |
|---|---|
a
|
A-weighting. |
c
|
C-weighting. |
z
|
Zero-weighting, the flat response. |
timeWeighting Property
timeWeighting, when present, names the time weighting the value carries. It is
an open enumeration; the values defined here are the time weightings of
[IEC61672-1]:¶
| Value | Meaning |
|---|---|
fast
|
Fast time weighting. |
slow
|
Slow time weighting. |
The impulse time weighting of the superseded IEC 651 is not among these; where a legacy dataset needs it, it is carried as an open value.¶
levelReference Property
levelReference, when present, states what the level is relative to. This is one
property appearing in two keywords: the levelReference of audioChannels
(Section 12.1.3) takes the same values with the same meanings, since
both answer the same question about a quantity expressed on a logarithmic scale.¶
| Value | Meaning |
|---|---|
soundPressure
|
The reference is 20 micropascals [ISO1683], and a level in decibels is sound pressure level. |
fullScale
|
The reference is digital full scale, the greatest level the representation admits, and a level in decibels is dBFS. |
The enumeration is open. A value outside it MUST be an absolute URI [RFC3986] identifying another reference, because a level may be referred to a voltage, a power, or a quantity that the standard governing an instrument fixes, and this document does not enumerate what other bodies define. A processor that does not know a value MUST preserve it, MUST NOT reject the schema for carrying it, and MUST NOT compare a level against one carrying a different reference.¶
The property below carries an A-weighted, fast, sound-pressure level.¶
{
"name": "NoiseReading",
"type": "object",
"properties": {
"sensorId": { "type": "string" },
"soundLevel": {
"type": "double",
"unit": "dB",
"measurementConditioning": {
"weighting": "a",
"timeWeighting": "fast",
"levelReference": "soundPressure"
}
}
},
"required": ["sensorId", "soundLevel"],
"additionalProperties": false
}
¶
A reference system need not be published by an authority. Where the kind of a
reference-system keyword is type, reference is a type reference
{ "$ref": <JSON Pointer> } [JSTRUCT-CORE] to a shareable type definition, and
that type definition is the
definition of the system. Such a type is a meta-type. It is ordinarily
maintained in its own document and brought into definitions with $import
[JSTRUCT-IMPORT], so that one definition serves every schema that cites it.¶
A meta-type declares the members of the system, and the annotation maps the members of the annotated schema onto them. The two need not agree in member names, member order, or member count.¶
A meta-type is an ordinary type definition that a schema author writes, and it is unrelated to the extension meta-schema of Section 14, which is the schema of this specification.¶
referenceRole Keyword
referenceRole states the function of a member within a meta-type. It MAY occur
on a property, collection item, map value, or choice member schema of a type
that a reference identifies, and it establishes nothing elsewhere.¶
When present, referenceRole MUST be one of:¶
| Value | Function of the member |
|---|---|
position
|
Carries a temporal position, mapped by position (Section 9.3). |
linearElement
|
Identifies a linear element, mapped by linearElement (Section 10.5). |
measure
|
Carries a distance along a linear element, mapped by measure. |
direction
|
Qualifies direction of travel or orientation, mapped by direction. |
One meta-type MUST NOT declare two members with the same referenceRole. A
member without referenceRole is a component of the system that no annotation
maps, and an annotated schema MAY carry it, under any name, or omit it.¶
A mapping is established by referenceRole and never by a member name. A
processor MUST NOT infer a role from the name of a member, and a meta-type that
declares no member for a role a keyword requires is unusable by that keyword.¶
A coordinate reference system takes no roles, because its meta-type is a tuple
and the order of its elements establishes the axes (Section 10.1).
A vector reference frame takes no roles for the same reason
(Section 10.2).¶
The keywords in this section concern temporal positions, the values that place
an observation or an operational event on a time line.
phenomenonTimeRelation states how a result relates to the position it
accompanies, supportPeriod gives the length of the period a result
characterizes where the record bounds it at one end, temporalReferenceSystem
states how a position value is to be read, and cadence states how successive
positions are expected to recur.¶
phenomenonTimeRelation Keyword
The phenomenonTimeRelation keyword refines how a result value relates to
phenomenonTime. When semanticRole: observationValue is also present, it
describes the observation result. It is not a replacement for phenomenonTime.¶
When present, it MUST be one of:¶
| Value | Meaning |
|---|---|
instant
|
Result applies at the sibling temporal position having role phenomenonTime. |
untilNext
|
Result applies from that position until the next actual compatible observation. |
interval
|
Result characterizes a half-open phenomenon-time period the record encodes. |
accumulation
|
Result is accumulated over that half-open phenomenon-time period. |
instant and untilNext can be resolved only when a sibling
phenomenonTime annotation identifies a temporal position. interval and
accumulation can be resolved when sibling phenomenonTimeStart and
phenomenonTimeEnd annotations identify boundaries in a common reference regime
or through an authoritative conversion, and can be resolved when supportPeriod
states the length of the period and a sibling position anchors it
(Section 9.2). Otherwise the support is declared but its temporal extent
is indeterminate. Effective-time and operational roles do not supply
phenomenon-time boundaries.¶
These values state how a result relates to a phenomenon time and not how it was
produced; the summary function, where there is one, is carried by statistic
(Section 5.5). They do not authorize summation or prove complete coverage. For
untilNext, the successor is the next actual observation with compatible
resolved feature of interest, observed property, declared procedure, value
type, unit, temporal binding, and support. Cadence does not prove a successor
exists; without one the support end is unknown. Omission is not instant.¶
Example. air_temperature holds until the next compatible observation, so it
reads against the sibling phenomenonTime; rainfall is accumulated over the
period, so it reads against the sibling boundary pair:¶
{
"name": "WeatherReport",
"type": "object",
"properties": {
"observed_at": {
"type": "datetime",
"semanticRole": "phenomenonTime"
},
"window_opens": {
"type": "datetime",
"semanticRole": "phenomenonTimeStart"
},
"window_closes": {
"type": "datetime",
"semanticRole": "phenomenonTimeEnd"
},
"air_temperature": {
"type": "double",
"unit": "Cel",
"semanticRole": "observationValue",
"phenomenonTimeRelation": "untilNext"
},
"rainfall": {
"type": "double",
"unit": "mm",
"semanticRole": "observationValue",
"phenomenonTimeRelation": "accumulation"
}
},
"required": [
"observed_at",
"window_opens",
"window_closes",
"air_temperature",
"rainfall"
],
"additionalProperties": false
}
¶
supportPeriod Keyword
The supportPeriod keyword states the length of the phenomenon-time period a
result characterizes, for a period the record bounds at one end rather than two.
A mean wind speed over the ten minutes ending at the observation time, a
half-hourly settlement quantity stamped with the instant its period opens, and a
pressure change over the preceding three hours are all of that shape: the length
is fixed by the publishing arrangement, and one position in the record fixes
where the period sits. Such a feed carries no second boundary and gains nothing
from a schema that invents a member for one.¶
When present, supportPeriod MUST be an object with a REQUIRED length and a
REQUIRED anchor. No other properties are permitted.¶
length Property
length states the extent of the period and MUST express a positive interval in
the temporal reference system applicable to the anchoring position. For a Core
datetime, date, or time, it MUST be a positive Core duration. Another
temporal reference system MAY use a numeric, string, or structured interval
representation defined by that system, on the terms Section 9.4 states for
period.¶
anchor Property
anchor states which boundary of the period the anchoring position occupies and
MUST be one of:¶
| Value | Meaning |
|---|---|
start
|
The anchoring position opens the period, which runs forward from it. |
end
|
The anchoring position closes the period, which runs back to it. |
The anchoring position is the sibling annotated phenomenonTimeStart when
anchor is start, and the sibling annotated phenomenonTimeEnd when anchor
is end. Where the record carries no member in that role, the anchoring
position is the sibling annotated phenomenonTime. Where it carries neither,
the period has a length and no location, and the extent remains indeterminate.¶
The period is half-open on the terms Section 9.1 states. For an
anchoring position t, an anchor of end gives [t - length, t) and an
anchor of start gives [t, t + length).¶
supportPeriod MUST NOT be present unless phenomenonTimeRelation is
interval or accumulation, and MUST NOT be present where sibling
phenomenonTimeStart and phenomenonTimeEnd annotations both identify
boundaries, because the record then encodes the period and a stated length would
restate or contradict it.¶
The boundary roles do not subsume this keyword. phenomenonTimeStart and
phenomenonTimeEnd annotate members, so they state a period only where the
record carries a value at each end of it, and the resolution rules of this
document read one sibling in each role. Support is a property of a result
rather than of a record, and one record may carry results of differing extent:
a buoy report may close a twenty-minute wave summary and a three-hour pressure
change at a single observation time, leaving one place to put a boundary and
two periods to state. supportPeriod is carried by the result it describes,
so each result states its own extent, and a length fixed by the publishing
arrangement is stated once in the schema rather than transmitted in every
record.¶
A support period is a fact about one value and a cadence is a fact about a
producer. The two are often numerically equal and are never the same statement.
A station reporting hourly a mean taken over the last ten minutes of each hour
has a cadence of one hour and a support period of ten minutes, and a schema
declaring only the cadence would leave a reader free to treat fifty minutes of
every hour as observed. cadence does not bound a phenomenon time
(Section 9.4), and supportPeriod asserts nothing about whether a successor
record exists or when it arrives.¶
Where the length is not fixed by the schema, because it varies with the station,
the instrument, or the message, there is no length to state and a schema MUST NOT state a nominal one. The extent is then indeterminate, and a schema SHOULD
record in description what governs the length, so that a reader learns where
to obtain it rather than assuming a value.¶
Example. A surface report carries a mean wind speed over the ten minutes ending at the observation time and a rainfall total accumulated over the hour that opens at the stated instant. The record has one boundary member and two periods:¶
{
"name": "SurfaceReport",
"type": "object",
"properties": {
"observed_at": {
"type": "datetime",
"examples": ["2026-07-27T12:50:00Z"],
"semanticRole": "phenomenonTime"
},
"accumulation_opens": {
"type": "datetime",
"examples": ["2026-07-27T12:00:00Z"],
"semanticRole": "phenomenonTimeStart"
},
"wind_speed": {
"type": "double",
"unit": "m/s",
"semanticRole": "observationValue",
"derivation": "statistic",
"statistic": "mean",
"phenomenonTimeRelation": "interval",
"supportPeriod": { "length": "PT10M", "anchor": "end" }
},
"rainfall": {
"type": "double",
"unit": "mm",
"semanticRole": "observationValue",
"derivation": "measured",
"phenomenonTimeRelation": "accumulation",
"supportPeriod": { "length": "PT1H", "anchor": "start" }
}
},
"required": [
"observed_at",
"accumulation_opens",
"wind_speed",
"rainfall"
],
"additionalProperties": false
}
¶
The record carries no phenomenonTimeEnd, so wind_speed anchors on
observed_at and covers [12:40Z, 12:50Z). rainfall anchors on
accumulation_opens and covers [12:00Z, 13:00Z). The two periods overlap and
neither is the other, and a reader that took the observation instant for both
would attribute the hour's rain to ten minutes of it.¶
temporalReferenceSystem Keyword
The temporalReferenceSystem keyword identifies the temporal reference
definition needed to interpret an encoded temporal position or duration.¶
It attaches to a temporally typed property or to a type definition that serves
as one. Where that type is an object or tuple, position names the member that
carries the position value.¶
When present, temporalReferenceSystem MUST be an object with a REQUIRED
reference string, a REQUIRED kind string, an OPTIONAL position string, and
an OPTIONAL sortOrder string. No other properties are permitted.¶
Core temporal types need no annotation when their Core semantics are fully intended. A non-Core or ambiguous encoding is indeterminate without one.¶
reference Property
reference MUST identify one temporal reference definition. Where kind is
type it MUST be a type reference { "$ref": <JSON Pointer> } [JSTRUCT-CORE]
to a shareable type
definition, and otherwise it MUST be an absolute URI [RFC3986]. kind states
which definition model the reference identifies. This document does not define a
resolution protocol, URI layout, storage model, or definition serialization.¶
Where the identified definition has a domain of validity, an annotated position MUST lie in that domain.¶
kind Property
kind classifies which definition model the URI identifies. It is an open
enumeration. The following values are defined here:¶
| Kind | Referenced definition |
|---|---|
ogc-trs
|
A concept in the OGC temporal reference system register, whose entries follow ISO 19108 [ISO19108]. |
ogc-temporal-crs
|
A GML TemporalCRS served by the OGC definitions server, establishing a temporal datum, origin, and coordinate system [ISO19111] [OGC-TOPIC2]. |
type
|
A meta-type declaring a member whose referenceRole is position, alongside the components of the regime (Section 8). |
Other values MAY name further definition models. Appendix A lists resolvable URIs for the registered kinds.¶
Whichever model a kind names, the identified definition MUST establish the
components applicable to the encoding it governs. Where it defines a compound
regime that locates a position by scoped components [OGC-TOPIC25], it MUST
state component order, the scope and reset behavior of each component, and the
rules for comparing positions from different scopes.¶
A type reference carries a regime that no register holds. The referenced
meta-type MUST declare a member whose referenceRole is position, and the
type and unit of that member establish the encoding. The position property
maps the annotated member onto it. The remaining members of the meta-type are
components of the regime that the annotation does not map. What a type
definition cannot express, such as reset behavior and comparison across scopes,
is stated in its description.¶
position Property
position is REQUIRED when the annotation is attached to an object or tuple
and is prohibited otherwise. It MUST name a direct member of that object or
tuple, and that member MUST be REQUIRED.¶
The named member carries the temporal position. Its values MUST sort in the
direction given by sortOrder under the ordering defined for its own type,
which for a string is lexical order. A compound position achieves this by
rendering its components most significant first at fixed width. A processor can
therefore order and compare positions without implementing the referenced
definition.¶
The remaining members MAY hold the individual components, identifiers, or other detail. A processor is not required to interpret them.¶
sortOrder Property
sortOrder states how the ordering of the encoded value runs relative to
temporal order. When present, it MUST be one of:¶
| Value | Ordering |
|---|---|
forward
|
An increasing value is a later position. |
backward
|
An increasing value is an earlier position. |
When sortOrder is absent, the value is forward.¶
Most definitions count from an epoch toward the present and are therefore
forward. An annotation citing a definition whose values count away from a
datum into the past, such as years before present or a geologic time scale,
MUST declare sortOrder as backward.¶
sortOrder applies to the annotated value, or to the member named by position
where one is named. It states the direction of the ordering and nothing else,
and a definition whose values do not order under their own type at all is not
made orderable by declaring either value.¶
The referenced definition establishes an encoding, and the annotated schema MUST be able to carry it:¶
| Encoding established by the definition | Compatible schema |
|---|---|
| A date and time in a calendar, following [RFC3339] | Core datetime, date, time, or string
|
| A count of units elapsed from an epoch | a Core integer or number type |
| Any other encoding |
string, or an object or tuple carrying position
|
Where the definition establishes a unit for its axis, a numeric position MUST
carry a unit or ucumUnit annotation compatible with that unit. Epoch
definitions differ in unit, so a count of seconds and a count of milliseconds
from the same origin are distinct definitions rather than one definition with
two encodings.¶
How much of this a reader can check depends on kind. An ogc-temporal-crs
definition determines the encoding, since its axis states either a unit of
measure, which takes a numeric position, or a date and time, which takes a
string. A type definition declares the type of each component and likewise
determines it. An ogc-trs concept names a time scale and establishes no axis,
unit, or encoding, so the compatibility check is indeterminate, as it is for a
kind outside this enumeration.¶
The annotation does not change the JSON base type, turn a data value into an identifiable temporal object, or supply a conversion. A processor MUST compare, order, or combine positions only within the same binding or through an authoritative transformation. A position whose definition establishes only order MUST NOT be treated as a metric coordinate without additional authority. Property names alone establish none of these semantics.¶
Example. The clock is defined once as a meta-type and cited by reference, so
a record of another shape can name the same definition. The record names the
mapped member ordinal rather than clockPosition, and position establishes
the mapping; the remaining components are carried under the record's own names
and are not mapped. ordinal renders the components at fixed width, so a
processor can order two positions without implementing the definition.¶
{
"$schema": "https://json-structure.org/meta/semantic-annotations/v0/#",
"$id": "https://schemas.example.org/racing-speed-observation",
"name": "RacingSpeedObservation",
"type": "object",
"identity": ["observation_id"],
"observedProperty": {
"reference": "https://catalog.example.org/observable-properties/vehicle-speed/v1",
"kind": "example-catalog"
},
"properties": {
"observation_id": { "type": "uuid" },
"entry_id": {
"type": "string",
"semanticRole": "featureOfInterest"
},
"race_clock": {
"type": "object",
"semanticRole": "phenomenonTime",
"temporalReferenceSystem": {
"reference": { "$ref": "#/definitions/RaceClockPosition" },
"kind": "type",
"position": "ordinal"
},
"properties": {
"ordinal": {
"type": "string",
"description": "Clock position rendered at fixed width and ordered lexically",
"examples": ["2026-07-26/R/S03/L014/01250.5"]
},
"session": { "type": "string" },
"stint": { "type": "uint32" },
"lap": { "type": "uint32" },
"distance_driven": { "type": "double", "unit": "m" }
},
"required": ["ordinal", "session", "stint", "lap", "distance_driven"],
"additionalProperties": false
},
"speed": {
"type": "double",
"unit": "km/h",
"semanticRole": "observationValue",
"phenomenonTimeRelation": "instant",
"derivation": "measured"
}
},
"required": ["observation_id", "entry_id", "race_clock", "speed"],
"additionalProperties": false,
"definitions": {
"RaceClockPosition": {
"name": "RaceClockPosition",
"type": "object",
"description": "Motor-racing clock. A position is located by session, stint, lap, and distance driven within the lap. Stint numbering is entry-specific, and positions from different entries are comparable only within one session.",
"properties": {
"clockPosition": {
"type": "string",
"description": "Components rendered at fixed width, most significant first, so that positions sort lexically",
"referenceRole": "position"
},
"session": { "type": "string" },
"stint": { "type": "uint32" },
"lap": { "type": "uint32" },
"distanceDriven": { "type": "double", "unit": "m" }
},
"required": ["clockPosition"]
}
}
}
¶
The compound position is comparable only under the rules of the identified regime: equal stint, lap, and distance values do not imply equal positions across sessions or entries. Mapping this clock to UTC or elapsed session time requires an authoritative synchronization relation or transformation.¶
cadence Keyword
The cadence keyword describes expected producer behavior across successive
values of an annotated temporal position. A temporal role such as
phenomenonTime, resultTime, ingestionTime, or forecastIssueTime, when
also present, gives that sequence an observation or operational meaning.¶
When present, cadence MUST be an object with a REQUIRED kind string and an
OPTIONAL period. No other properties are permitted.¶
kind Property
kind states the expected recurrence pattern and MUST be one of:¶
| Kind | Meaning |
|---|---|
fixed
|
Observations are expected at a regular period. |
irregular
|
Observations occur without a regular period. |
onChange
|
Observations occur when represented state changes. |
period Property
period is REQUIRED when kind is fixed and is prohibited otherwise. It
MUST express a positive interval in the temporal reference system applicable to
the annotated temporal position. For a Core datetime, date, or time, it
MUST be a positive Core duration. Another temporal reference system MAY use a
numeric, string, or structured interval representation defined by that system.¶
That latitude is what makes a rapid cadence expressible, and a schema for one SHOULD use it rather than force the period into civil time. A duration is written in seconds and their decimal fractions, and many rates that are exact on their own clock have no exact expression there: audio sampled at 48 kHz advances one frame every 1/48000 of a second, so any duration written for it is rounded, and a consumer that places positions by accumulating it drifts further from the truth with every frame. Writing the period more exactly does not help, because civil time cannot express the instants either. A stream whose positions are counted is not a stream whose positions are seconds.¶
Where the values are counted on a clock of their own, the schema declares that
clock as a meta-type, names it in temporalReferenceSystem
(Section 9.3), and gives period as a count of that clock's
own units, which for a value recorded once per tick is the integer 1. The
cadence is then exact, because it is stated in the system the values are
expressed in.¶
The conversion to seconds is data rather than annotation. A rate varies from one
delivery to the next while the schema stays the same, so a member of the record
carries it, annotated with a unit [JSTRUCT-UNITS], and a consumer MUST NOT
assume a conventional value for it. Where a schema serves one rate and no other,
const or enum [JSTRUCT-CORE] pins the value on that member.¶
No keyword binds a clock to the member carrying its rate. A processor MUST NOT
take a member to be the rate of a clock because of the member's name or because
its unit is one of frequency, and MUST NOT convert a position counted on such a
clock to elapsed civil time unless a relation to civil time is established
outside these annotations. The meta-type declaring the clock states that
relation, or its absence, in its description (Section 9.3).¶
Cadence is not delivery time, a service-level objective, a completeness
assertion, or a phenomenon-time boundary. It does not assert that every
position has a record, that records arrive in order, or that an untilNext
successor exists.¶
Cadence is an expectation and not a constraint, and the distinction is normative. A schema that declares a cadence constrains no instance document. An instance whose values do not follow the declared cadence is not invalid for that reason, because a stream that misses a beat is late rather than malformed, and a processor MUST NOT reject an instance, a value, or a schema on the ground that observed timing departs from a declared cadence. A runtime service level belongs to an agreement between a producer and a consumer, not to a schema.¶
A consumer may still act on a cadence. A declared period sizes a window, sets a threshold beyond which a value is treated as stale, and makes an absent value detectable as a gap rather than absorbed silently. Each of those is a decision the consumer makes about its own processing. None of them changes the meaning of a value, and none licenses a value to be supplied where none was recorded (Section 15).¶
Example:¶
{
"name": "WindSpeedObservation",
"type": "object",
"properties": {
"measured_at": {
"type": "datetime",
"description": "Instant to which the wind speed applies",
"examples": ["2026-07-27T12:10:00Z"],
"semanticRole": "phenomenonTime",
"cadence": {
"kind": "fixed",
"period": "PT10M"
}
},
"wind_speed": {
"type": "double",
"unit": "m/s",
"semanticRole": "observationValue"
}
},
"required": ["measured_at", "wind_speed"],
"additionalProperties": false
}
¶
A cadence too rapid for civil time, stated on a clock of its own:¶
{
"name": "AudioSampleFrame",
"type": "object",
"properties": {
"frame_index": {
"type": "int64",
"description": "Position of this frame, counted in samples from the start of the delivery",
"semanticRole": "phenomenonTime",
"temporalReferenceSystem": {
"reference": { "$ref": "#/definitions/AudioSampleClock" },
"kind": "type",
"sortOrder": "forward"
},
"cadence": {
"kind": "fixed",
"period": 1
}
},
"sample_rate": {
"type": "int32",
"unit": "Hz",
"description": "Sample frames per second for this delivery"
},
"amplitude": {
"type": "double",
"semanticRole": "observationValue"
}
},
"required": ["frame_index", "sample_rate", "amplitude"],
"additionalProperties": false,
"definitions": {
"AudioSampleClock": {
"name": "AudioSampleClock",
"type": "object",
"description": "A position is a count of sample frames from the start of the delivery. The clock advances one unit per frame and does not reset within a delivery, so positions sort numerically and are comparable within one delivery and not across deliveries. Elapsed seconds are the count divided by the frame rate of the delivery; this meta-type does not supply that rate.",
"properties": {
"frame_count": {
"type": "int64",
"description": "Count of sample frames from the start of the delivery.",
"referenceRole": "position"
}
},
"required": ["frame_count"],
"additionalProperties": false
}
}
}
¶
The cadence here is one sample-clock unit and is exact. Written as a duration it
could only have been approximated: PT0.0000208333S is short of a
forty-eight-kilohertz frame by about thirty-three picoseconds, which is nothing
in one frame and a full sample every thirteen seconds of programme, growing
without bound for as long as the recording runs.¶
Each keyword in this section has two parts. reference and kind identify an
external definition of a reference system. The remaining properties bind the
components of that system to named members of the object or tuple carrying the
annotation. A position or a vector is held across several members, so these
keywords attach to a complex type and are not meaningful on a scalar.¶
coordinateReferenceSystem Keyword
The coordinateReferenceSystem keyword identifies the coordinate reference
system [ISO19111] under which coordinate values held in properties of an
object or tuple are to be interpreted.¶
When present, coordinateReferenceSystem MUST be an object with a REQUIRED
reference string, a REQUIRED kind string, and a REQUIRED coordinates
array. No other properties are permitted.¶
reference Property
reference MUST identify one coordinate reference system whose definition
establishes an ordered set of axes, each with an axis direction and unit of
measure. Where kind is type it MUST be a type reference
{ "$ref": <JSON Pointer> } [JSTRUCT-CORE] to a shareable type definition, and
otherwise it MUST be an absolute
URI [RFC3986]. kind states which definition model the reference identifies.¶
A processor is not required to dereference the URI, and a returned representation need not expose the axes. This document does not define a resolution protocol, URI layout, storage model, or definition serialization.¶
kind Property
kind classifies which definition model the URI identifies. It is an open
enumeration. The following values are defined here:¶
| Kind | Referenced definition |
|---|---|
ogc-crs
|
A GML CRS served by the OGC definitions server, named according to the OGC name type specification [OGC-NAMES]. |
epsg
|
A record in the EPSG Geodetic Parameter Dataset [EPSG]. |
type
|
A meta-type that is a tuple whose elements in order are the axes of an engineering or local system (Section 8). |
Other values MAY name further definition models.¶
A type reference carries an engineering or local system that no register
holds. The referenced meta-type MUST be a tuple, and the order given by its
tuple keyword is the axis order. coordinates maps the annotated properties
onto those elements by position, so the number of names in coordinates MUST
equal the number of elements and each element establishes the unit of its axis.
Axis direction, datum, and origin are stated in the description of the
meta-type or of its elements.¶
Schema authors SHOULD use a registered definition where one exists. Appendix A lists resolvable URIs for the registered kinds.¶
coordinates Property
coordinates MUST be a non-empty ordered array of distinct property names.
Every name MUST resolve to a direct property of the annotated object or tuple.
The property at array index zero supplies axis 1 of the referenced coordinate
system, the property at index one supplies axis 2, and so on. The number of
names MUST equal the dimension of that coordinate system.¶
This ordering is an assertion by the schema author. It is not inferred from
property names or from a representation returned by dereferencing reference.¶
A name in coordinates MAY resolve to a property whose type is array or
tuple. Where it does resolve to an array, that name MUST be the only entry
in coordinates, and the elements of the array instances MUST supply the axes
in order rather than the named property supplying one axis.¶
For a tuple, the number of elements MUST equal the dimension of the
referenced system. A schema using an array SHOULD constrain its length. The
axes of a coordinate reference system may carry differing units, which a
tuple can state per element and an array cannot.¶
Coordinate properties MUST have numeric types. When a coordinate property has a
unit or ucumUnit annotation, that unit MUST be compatible with the
corresponding axis. A processor MAY verify the asserted ordering, units, and
dimension using a trusted authority-specific CRS database. Without such a
definition source, it MUST preserve the declaration but treat those checks and
coordinate transformations as indeterminate.¶
Properties not named by coordinates are not part of the coordinate. The
annotation therefore applies safely to an existing object that also contains
identity, temporal, status, or other values.¶
An object or tuple MUST NOT carry more than one coordinateReferenceSystem
annotation. An object containing multiple coordinates SHOULD model each
coordinate as a nested object. This document does not define coordinate epochs
for dynamic coordinate reference systems.¶
The coordinate order is significant. OGC CRS84 uses longitude, latitude:¶
{
"name": "Crs84Position",
"type": "object",
"coordinateReferenceSystem": {
"reference": "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
"kind": "ogc-crs",
"coordinates": ["lon", "lat"]
},
"properties": {
"lat": {
"type": "double",
"unit": "deg"
},
"lon": {
"type": "double",
"unit": "deg"
}
},
"required": ["lat", "lon"],
"additionalProperties": false
}
¶
EPSG:4326 [EPSG] uses its authoritative latitude, longitude axis order:¶
{
"name": "Epsg4326Position",
"type": "object",
"coordinateReferenceSystem": {
"reference": "http://www.opengis.net/def/crs/EPSG/0/4326",
"kind": "ogc-crs",
"coordinates": ["lat", "lon"]
},
"properties": {
"lat": {
"type": "double",
"unit": "deg"
},
"lon": {
"type": "double",
"unit": "deg"
}
},
"required": ["lat", "lon"],
"additionalProperties": false
}
¶
A vertical coordinate reference system has one axis, and coordinates then
names one property. This is the binding that makes a height or a depth
interpretable, because the number and its unit do not state what the value is
measured from.¶
The axis direction comes from the referenced definition and not from the
annotation, so whether the axis is positive up or positive down is a fact about
the identified system. Where kind is type, the description of the
meta-type or of its elements states it.¶
The following excerpt binds a gauge reading to NAVD88 height:¶
{
"name": "GaugeHeightObservation",
"type": "object",
"coordinateReferenceSystem": {
"reference": "http://www.opengis.net/def/crs/EPSG/0/5703",
"kind": "ogc-crs",
"coordinates": ["water_level"]
},
"properties": {
"station": {
"type": "string",
"description": "Observed gauging station",
"examples": ["USGS-12149000"],
"semanticRole": "featureOfInterest"
},
"measured_at": {
"type": "datetime",
"examples": ["2026-07-27T12:00:00Z"],
"semanticRole": "phenomenonTime"
},
"water_level": {
"type": "double",
"unit": "m",
"description": "Water surface elevation",
"examples": [2.47],
"semanticRole": "observationValue"
}
},
"required": ["station", "measured_at", "water_level"],
"additionalProperties": false
}
¶
Where a horizontal position and a height belong to one compound system, that system has one definition and one set of axes, so a single annotation names all of them in order. The annotation of such an object reads:¶
{
"coordinateReferenceSystem": {
"reference": "http://www.opengis.net/def/crs/EPSG/0/6349",
"kind": "ogc-crs",
"coordinates": ["lat", "lon", "height"]
}
}
¶
Where the height belongs to a different system from the horizontal position, or
where it is a result rather than part of a position, the two are separate
bindings. Since an object carries at most one coordinateReferenceSystem, the
schema models one of them as a nested object:¶
{
"name": "StationWaterLevel",
"type": "object",
"coordinateReferenceSystem": {
"reference": "http://www.opengis.net/def/crs/EPSG/0/5703",
"kind": "ogc-crs",
"coordinates": ["water_level"]
},
"properties": {
"station_position": {
"type": "object",
"coordinateReferenceSystem": {
"reference": "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
"kind": "ogc-crs",
"coordinates": ["lon", "lat"]
},
"properties": {
"lat": { "type": "double", "unit": "deg" },
"lon": { "type": "double", "unit": "deg" }
},
"required": ["lat", "lon"],
"additionalProperties": false
},
"water_level": {
"type": "double",
"unit": "m",
"examples": [2.47],
"semanticRole": "observationValue"
}
},
"required": ["station_position", "water_level"],
"additionalProperties": false
}
¶
This specification does not define a CRS, datum, coordinate operation, or transformation. Those definitions and semantics come from ISO 19111 and the referenced authority.¶
vectorReferenceFrames Keyword
The vectorReferenceFrames keyword identifies the reference frames on whose
axes the components of vector quantities held in properties of an object or
tuple are resolved.¶
A vector quantity has a magnitude and a direction, and its components are its
projections onto the axes of a frame, meaningless apart from it. A frame is an
ordered set of axis directions and has no origin, because moving the origin does
not change a vector, whereas a coordinate reference system has one, fixed by its
datum. A record may carry both kinds of quantity: coordinateReferenceSystem
binds the members that give a position (Section 10.1), and
vectorReferenceFrames binds the members that give the components of a vector.¶
A frame contributes directions only, and so does not establish the units of the members it binds. The axes of a coordinate reference system may carry differing units, as in a geographic system with two angles and a height, but the components of a vector all carry the unit of the quantity. The units of the members named by one frame MUST be mutually convertible and SHOULD be identical.¶
When present, vectorReferenceFrames MUST be a non-empty array. Each element
MUST be an object with REQUIRED reference, kind, and components, an
OPTIONAL variance, and no other members. The keyword is an array because a
single record may carry more than one vector quantity, and each quantity is
resolved in its own frame.¶
reference and kind Properties
reference and kind have the same value space and the same meaning as in
coordinateReferenceSystem (Section 10.1), under one added
condition: the axes of the referenced system MUST be directions rather than
angles, since a component is a projection onto a direction.¶
A registered coordinate reference system establishes axis directions, so it may
serve as a frame wherever that condition holds. Registries exist to identify
positions, so most frames in which vectors are reported are not registered
anywhere. Such a frame is written as a tuple meta-type whose members, in the
order given by tuple, are the axes, and whose member description values
state the direction each axis points in. The meta-type establishes the axes and
their order, not the units of the annotated members.¶
components Property
components behaves as coordinates does. It MUST be a non-empty array of
names, and it takes one of two forms. In the first, every name is that of a
direct property of numeric type, and the names are mapped by position onto the
axes of the frame. In the second, the array holds exactly one name, that of a
direct property whose type is array or tuple and whose elements are of
numeric type, and the elements of that property MUST supply the axes in order
rather than the named property supplying one axis. Because the components of a
vector all carry one unit, an array serves here where it would not serve for a
coordinate.¶
Every name in components MUST resolve to a direct property of the annotated
object or tuple. The names within one components array MUST be distinct, since
one value cannot be the component along two axes of one frame. The number of
axes supplied, whether by the names of the first form or by the elements of the
single property of the second, MUST equal the number of axes the frame declares.
The ordering is an assertion by the schema author and is never inferred.¶
variance Property
variance states how the components respond to a change of frame. When present,
it MUST be one of:¶
| Value | Response |
|---|---|
contravariant
|
The components transform with the change of frame, as those of a displacement or a velocity do. |
covariant
|
The components transform with the inverse transpose of the change of frame, as those of a gradient or a surface normal do. |
Writing M for the matrix that carries coordinates in one frame into
coordinates in another, in the sense Section 10.4.1 fixes, a
contravariant triple v becomes M v and a covariant triple w becomes the
inverse transpose of M applied to w. The change of frame is the passive one:
the quantity does not move, the frame does.¶
When variance is absent, the value is contravariant. That is the default
because the quantities most often reported, among them displacements,
velocities, accelerations, and forces, are contravariant, and the covariant
ones, among them gradients and surface normals, are the ones a schema author is
likelier to be conscious of having.¶
The distinction is invisible where two frames differ by a rotation alone,
because the inverse transpose of a rotation is that rotation. It becomes visible
as soon as they differ by a scaling or a shear. A surface normal carried through
a non-uniform scaling by the rule that carries a displacement ceases to be
perpendicular to the surface it describes, and a pressure gradient carried the
same way reports the wrong rate of change. A processor that re-expresses
components without reading variance is right for rotations and wrong for
everything else, which is what this member exists to prevent.¶
tensorReferenceFrames Keyword
The tensorReferenceFrames keyword identifies the reference frames on whose
axes the components of tensor quantities held in properties of an object or
tuple are resolved. A tensor quantity is a grid of numbers that means nothing
without a frame to read it against. Its rank is how many axes it takes to pick
out one number of the grid: one for a vector, two for a stress or a rotation. An
engineer checking whether a bridge beam will crack uses nine numbers because the
beam can be squeezed along its length and sheared across it at the same time, and
no single number says both.¶
At rank 1 the frame's declared order states the binding, which is what
vectorReferenceFrames (Section 10.2) does and why frames
requires at least two entries. Above rank 1 the vector model no longer suffices
and member names are insufficient. vectorReferenceFrames and
tensorReferenceFrames coexist, because vectors are common and should be easy
to declare.¶
When present, tensorReferenceFrames MUST be a non-empty array. Each element
MUST be an object with a REQUIRED frames array, a REQUIRED components
member, and an OPTIONAL symmetry string. No other members are permitted. The
keyword is an array because one record may carry more than one tensor quantity.¶
frames Property
frames MUST be an array of at least two objects, each with REQUIRED
reference and kind members whose values are as defined for
vectorReferenceFrames (Section 10.2). The number of entries is
the rank of the tensor, and the index in position k ranges over the axes of the
frame given by entry k, in the order that frame declares.¶
One frame MAY be named by more than one entry, and is so named for a stress, a strain, or a seismic moment tensor. Where the two entries of a rank-2 tensor name different frames, the tensor carries a vector in the second frame to a vector in the first, as for a rotation matrix or a Jacobian.¶
Each entry MAY carry variance as defined in Section 10.2.3, stating how the
index in that position responds to a change of the frame it ranges over. The
entries of one tensor need not agree. A stress tensor is contravariant in both
indices, while a Jacobian is contravariant in the first and covariant in the
second, which is what makes it a map between frames rather than a quantity in
one.¶
Three entries give a rank-3 tensor, and one frame may fill them all. The piezoelectric strain coefficients of a crystal are one such tensor: an electric field applied along one axis strains the crystal across every pair of axes, so each coefficient is picked out by three axes of the same crystal frame.¶
{
"name": "PiezoelectricCoefficients",
"type": "object",
"tensorReferenceFrames": [
{
"frames": [
{ "reference": { "$ref": "#/definitions/CrystalAxes" }, "kind": "type" },
{ "reference": { "$ref": "#/definitions/CrystalAxes" }, "kind": "type" },
{ "reference": { "$ref": "#/definitions/CrystalAxes" }, "kind": "type" }
],
"components": "d"
}
],
"properties": {
"d": {
"type": "array",
"minItems": 3,
"maxItems": 3,
"items": {
"type": "array",
"minItems": 3,
"maxItems": 3,
"items": {
"type": "array",
"minItems": 3,
"maxItems": 3,
"items": { "type": "double", "ucumUnit": "C/N" }
}
}
}
},
"required": ["d"],
"additionalProperties": false,
"definitions": {
"CrystalAxes": {
"name": "CrystalAxes",
"type": "tuple",
"description": "Orthogonal frame the coefficients of this material are published against, fixed by the symmetry of the crystal.",
"properties": {
"x1": { "type": "double", "description": "First axis of that frame." },
"x2": { "type": "double", "description": "Second axis, at a right angle to x1." },
"x3": { "type": "double", "description": "Third axis, completing a right-handed set." }
},
"tuple": ["x1", "x2", "x3"]
}
}
}
¶
d is nested three deep because frames has three entries, and each level is
indexed by the three axes of CrystalAxes, so it holds twenty-seven values. The
outermost index is the axis of the applied field and the two inner ones are the
axes of the strain it produces. Each level constrains its length to three
[JSTRUCT-VALIDATION], which is how the shape the frames require is stated in
the schema rather than left to the instance.¶
symmetry MUST NOT be present unless frames has exactly two entries naming
the same frame and declaring the same variance, or omitting it in both, since
it speaks of an exchange of two indices ranging over one set of axes and a pair
of indices of differing variance does not survive the exchange under a change of
frame. It MUST be symmetric, skewSymmetric, or none, and it is
none where absent. Under symmetric the component at row i and column j
equals the component at row j and column i; under skewSymmetric it is the
negation of it and the diagonal is zero. The symmetries of tensors of higher
rank, such as the minor and major symmetries of an elastic stiffness tensor, are
not expressible here, and every component of such a tensor that is carried MUST
be named individually.¶
components Property
components MUST take one of two forms, each of which states the pairing of
index to value in the schema, and neither of which admits a layout, packing, or
multiplication convention as a separate declaration.¶
The first form is a string naming a direct property of the annotated object or
tuple whose type is an array or a tuple nested to exactly the depth given by
the number of entries in frames, the outermost level indexed by the axes of
the first entry and each further level by the next in turn, and whose innermost
items are of a numeric type. A nesting whose depth differs from the number of
entries in frames makes the annotation invalid. At every level, the number of
elements an instance carries MUST equal the number of axes declared by the frame
of the corresponding entry, so a tuple, or an array whose length is
constrained [JSTRUCT-VALIDATION], states the shape in the schema where a bare
array leaves it to the instance. The nesting carries the layout, so the
row-major and column-major
question does not arise, and a tensor declared symmetric or skewSymmetric is
written out in full in this form. Every position of the tensor is carried, so
the rule of the paragraph after the next does not apply to this form.¶
The second form is a non-empty array of objects, each with a REQUIRED index
and a REQUIRED property and no other members. index MUST be an array of
non-negative integers whose length equals the number of entries in frames, and
whose integer in position k MUST be less than the number of axes declared by the
frame of entry k. property MUST name a direct property of the annotated object
or tuple having a numeric type. Every component carried states its own index, so
no packing order is defined here and the Voigt-style orderings in circulation
need not be distinguished. Within one components array no two entries may
carry equal index values, and no two entries may name the same property.¶
In the second form, a position named by no entry is determined by symmetry
where symmetry is symmetric or skewSymmetric, and is otherwise undeclared;
under none every position MUST be named. Where symmetry is symmetric or
skewSymmetric, at least one of each mirrored pair of positions MUST be named,
and where both are named the instance values MUST stand in the relation that
symmetry declares. The units of the members named by one element MUST be
mutually convertible and SHOULD be identical.¶
The Global CMT catalogue publishes a source solution for every significant
earthquake as a fixed-column text record, and states the frame those solutions
are resolved on only in the document describing that format: six moment-tensor
elements against a spherical frame in which r is up, t is south, and p is east
[GCMT-NDK]. The tensor is symmetric, so those six determine all nine, and the
second form of components states the index of each without appeal to a packing
order. What a reader of the text file must look up, the schema carries.¶
{
"name": "MomentTensor",
"type": "object",
"tensorReferenceFrames": [
{
"frames": [
{ "reference": { "$ref": "#/definitions/UseFrame" }, "kind": "type" },
{ "reference": { "$ref": "#/definitions/UseFrame" }, "kind": "type" }
],
"symmetry": "symmetric",
"components": [
{ "index": [0, 0], "property": "mrr" },
{ "index": [1, 1], "property": "mtt" },
{ "index": [2, 2], "property": "mpp" },
{ "index": [0, 1], "property": "mrt" },
{ "index": [0, 2], "property": "mrp" },
{ "index": [1, 2], "property": "mtp" }
]
}
],
"coordinateReferenceSystem": {
"reference": "http://www.opengis.net/def/crs/EPSG/0/4326",
"kind": "ogc-crs",
"coordinates": ["lat", "lon"]
},
"properties": {
"eventName": { "type": "string" },
"centroidTime": { "type": "datetime" },
"lat": { "type": "double", "unit": "deg" },
"lon": { "type": "double", "unit": "deg" },
"depth": { "type": "double", "ucumUnit": "km" },
"mrr": { "type": "double", "ucumUnit": "dyn.cm" },
"mtt": { "type": "double", "ucumUnit": "dyn.cm" },
"mpp": { "type": "double", "ucumUnit": "dyn.cm" },
"mrt": { "type": "double", "ucumUnit": "dyn.cm" },
"mrp": { "type": "double", "ucumUnit": "dyn.cm" },
"mtp": { "type": "double", "ucumUnit": "dyn.cm" }
},
"required": [
"eventName", "centroidTime", "lat", "lon", "depth",
"mrr", "mtt", "mpp", "mrt", "mrp", "mtp"
],
"additionalProperties": false,
"definitions": {
"UseFrame": {
"name": "UseFrame",
"type": "tuple",
"description": "Spherical frame of the Global CMT catalogue, oriented at the centroid position this record carries under lat and lon.",
"properties": {
"r": { "type": "double", "description": "Up." },
"t": { "type": "double", "description": "South." },
"p": { "type": "double", "description": "East." }
},
"tuple": ["r", "t", "p"]
}
}
}
¶
The following is the sample record printed in that same document: an earthquake beneath El Salvador on 1 January 2005, whose six elements give the orientation and the size of the movement on the fault, in dyne-centimetres. The catalogue prints them scaled by a power of ten carried on a line of its own, and the instance carries the values themselves, that is, with the exponent applied.¶
{
"eventName": "C200501010120A",
"centroidTime": "2005-01-01T01:20:05.1Z",
"lat": 13.76,
"lon": -89.08,
"depth": 162.8,
"mrr": 0.838e23,
"mtt": -0.005e23,
"mpp": -0.833e23,
"mrt": 1.050e23,
"mrp": -0.369e23,
"mtp": 0.044e23
}
¶
The value under mtp sits at row 1 and column 2, and the declaration of
symmetric puts the same value at row 2 and column 1. Nothing in the instance
says so: the six numbers alone determine nine components only once the schema
has stated the frame, the index of each, and the symmetry.¶
UseFrame is a local frame, and up, south, and east are directions only once a
point on the Earth is given. The point is the one the coordinateReferenceSystem
of the same object binds, and the description of the meta-type is where a
schema says so. This document defines no member that binds a frame to the
position that orients it, and a processor MUST NOT infer such a binding from the
presence of both keywords on one type.¶
A rank-2 quantity whose two entries name different frames is a map from one
frame to the other, and an attitude matrix, a Jacobian, and a sensor alignment
are all of that shape. tensorReferenceFrames describes such a quantity
correctly, but it says only what the indices range over. It does not say that
the numbers are a transformation, and it cannot describe the quaternion, the
axis and angle, or the three Euler angles that the same transformation is more
often published as. Where the annotated properties carry a transformation,
frameTransforms (Section 10.4) SHOULD be used instead.¶
frameTransforms Keyword
The frameTransforms keyword identifies properties of an object or tuple that
carry a transformation from one reference frame to another.¶
The keywords before it bind quantities. coordinateReferenceSystem binds a
position, vectorReferenceFrames binds a direction, and tensorReferenceFrames
binds a quantity whose components take more than one axis index. A
transformation is none of those. It is the map between two frames, and the
numbers carrying it mean nothing until the sense of the map, the handedness of
its angles, and the arrangement of its components are all settled.¶
Common practice settles none of them. NASA's Navigation and Ancillary Information Facility, which maintains the toolkit most planetary missions navigate with, states the position plainly: "there are no standards defining construction of quaternions, the underlying associated mathematics, or the connection to rotations. In the absence of such standards, different organizations adopt disparate definitions, which makes communication difficult" [NAIF-QUAT]. The same paper enumerates what a reader must resolve before a rotation can be applied: whether a positive angle turns in the right-hand sense, whether the rotation turns a vector or turns the frame, whether the matrix multiplies from the left or the right, whether the sense runs from the base frame or towards it, and, for a quaternion, where the scalar sits and what sign its multiplication rule carries. Six independent choices, of which, as that paper observes, "the composition of several misunderstandings can lead to correct results in one context, incorrect in another, and frustration in general". The Robot Operating System reaches the same conclusion about Euler angles, which it discourages "due to having 24 'valid' conventions with different domains using different conventions by default" [REP-103].¶
When present, frameTransforms MUST be a non-empty array. Each element MUST be
an object with REQUIRED from, to, encoding, and components, OPTIONAL
rotationSequence and translation, and no other members. The keyword is an
array because one record may carry more than one transformation, as an attitude
message does when it gives the orientation of a single instrument against two
different reference frames.¶
Every frame named by from or to MUST be right-handed and MUST declare
exactly three axes, and a positive angle MUST be read as a rotation in the
right-hand sense about the axis it is stated against. Every encoding this
keyword defines presupposes three dimensions, so a frame of any other arity
cannot be named by either member.¶
A transformation carries coordinates expressed in the from frame into
coordinates expressed in the to frame. Writing x for the coordinates of a
vector in the from frame and x' for the coordinates of the same vector in
the to frame, the transformation is the M for which x' = M x, and
translation, where present, adds to that as x' = M x + t. This is the
frame-transformation sense rather than the vector-rotation sense: the quantity
does not move, the frame does.¶
The Consultative Committee for Space Data Systems states that same sense for its
attitude messages. The quaternion "from frame A to frame B" is "the quaternion
of the rotation that transforms the basis vectors of frame A into the basis
vectors of frame B", and the matrix it corresponds to is defined by
XB = MBA * XA [CCSDS-ADM].¶
This document fixes these choices rather than offering them as members to be
declared, and that committee is the precedent. Its first issue made both the
direction and the placement of the quaternion scalar into fields a producer
filled in, ATTITUDE_DIR taking A2B or B2A and QUATERNION_TYPE taking
FIRST or LAST [CCSDS-ADM1]. Its second issue deleted both, recording the
rationale as "Simplicity of the standard" and, for the wider set of changes that
pinned the meanings down, "To avoid misuse of exchange data" [CCSDS-ADM].
Fixing them cost that standard the ability to carry records laid out the other
way, because its records are positional. It costs this document nothing, because
components names members rather than positions. A schema whose quaternion is
stored scalar-last and one whose quaternion is stored scalar-first carry the
same annotation, with the names written in the order fixed here.¶
from and to Properties
from and to MUST each be an object with a REQUIRED reference and a
REQUIRED kind, whose value spaces and meanings are those they have in
vectorReferenceFrames (Section 10.2). The axes of both frames
MUST be directions rather than angles. variance MUST NOT appear in either,
since a frame named by a transformation is not an index position of a quantity.¶
from and to MAY cite the same reference. A transformation between two
realizations of one frame, or between one frame at two epochs, is written that
way.¶
encoding Property
encoding states which arrangement of numbers carries the transformation. It is
a closed enumeration, and a value outside it is invalid, because each value
carries an arithmetic meaning that this document defines.¶
| Encoding | Carried by |
|---|---|
quaternion
|
Four members: the scalar, then the three components of the vector part. |
axisAngle
|
Four members: the angle, then the three components of the rotation axis. |
eulerAngles
|
Three members: the angles of three successive intrinsic rotations. |
rotationMatrix
|
Nine values arranged three by three. |
homogeneousMatrix
|
Sixteen values arranged four by four, acting on augmented coordinates. |
Every encoding but the last carries a rotation and nothing else, and an offset
between the origins of the two frames is then carried by translation
(Section 10.4.6) or not at all. homogeneousMatrix carries the
rotation and the offset together in one matrix M4, acting on augmented
coordinates as [x', 1] = M4 [x, 1] written as column vectors. Its first three
rows and first three columns are the rotation M, its fourth column holds in
its first three entries the same three values translation would carry and in
its fourth entry a one, and its fourth row is three zeroes and a one.¶
The nine values of a rotationMatrix, and the first three rows and columns of a
homogeneousMatrix, SHOULD be orthonormal with determinant positive one, within
a tolerance appropriate to the numeric type. A processor is not required to
verify this, and this document defines no tolerance. The remaining entries of a
homogeneousMatrix MUST be as the previous paragraph states, so a projective
matrix, a matrix carrying a scaling or a shear, and a matrix whose fourth row is
anything other than three zeroes and a one are all outside this encoding.¶
The first three entries of the fourth column of a homogeneousMatrix carry a
length and the rest of the matrix is dimensionless, so those three entries are
subject to the same unit rule as translation (Section 10.4.6): their
units MUST be mutually convertible and SHOULD be identical. A single nested
property cannot state a unit for one position and not another, so a schema
carrying a homogeneousMatrix in that form MUST establish those units in the
referenced definition or in the indexed form of components, which names a
separate property for each position.¶
components Property
components names the members that carry the transformation, and its form
follows encoding.¶
For quaternion, axisAngle, and eulerAngles, components MUST be an array
of names of direct properties of the annotated object or tuple, all of numeric
type and all distinct, of the length the encoding requires. Order within that
array is fixed by this document. It is never inferred from the order in which
the properties are declared, from their names, or from their position in a
tuple.¶
For rotationMatrix and homogeneousMatrix, components MUST be either the
name of one property whose type is an array or a tuple nested twice, the
outer index selecting the row, or an array of indexed components in the form
tensorReferenceFrames uses (Section 10.3), each stating its own
index of two non-negative integers, row first. Neither form leaves a row-major
or column-major reading open, which a flat array of nine or sixteen values
would.¶
In the nested form, each instance level MUST carry three elements for
rotationMatrix and four for homogeneousMatrix. In the indexed form, every
position MUST be named, nine of them for rotationMatrix and sixteen for
homogeneousMatrix, and each integer of an index MUST be less than three,
respectively four. symmetry has no counterpart here, so no position is
determined by any other and none may be left undeclared.¶
The four names given for quaternion are, in order, the member carrying the
scalar and the members carrying the three components of the vector part, in
the axis order the frames declare. Writing a for the angle of the rotation
that carries the from frame into the to frame and e for its unit axis, the
scalar is cos(a/2) and each of the other three is sin(a/2) times the
corresponding component of e. The axis is unchanged by its own rotation, so
its components are the same in both frames and the question of which frame they
are resolved in does not arise. Which physical direction the second, third, and
fourth names stand for therefore follows from the axis order the frames declare,
not from any convention of this document.¶
The four values taken together SHOULD have unit norm, and the scalar SHOULD be non-negative, which confines the rotation angle to a half turn either way [CCSDS-ADM1].¶
A worked case fixes the sense. Let the to frame be the from frame turned a
quarter turn in the right-hand sense about the third axis, so that the first
axis of the to frame has the coordinates 0, 1, 0 in the from frame. The
quaternion is then the scalar 0.7071 with axis components 0, 0, 0.7071, and a
vector whose coordinates in the from frame are 1, 0, 0 has the coordinates 0,
-1, 0 in the to frame. Those are the values [CCSDS-ADM] prints for the same
case.¶
One other convention is in wide enough use to name. The SPICE toolkit places the scalar first, as this document does, and measures its angle as the rotation of the coordinate system from the base frame in the right-hand sense, as this document does. It nevertheless carries the negation of the vector part this document carries, so for the case above it gives the scalar 0.7071 with vector components 0, 0, -0.7071. The cause is not the parameterization but the multiplication rule: NAIF records that its own product takes the cross-product term positive where the other convention in circulation takes it negative, that this "has the effect of inverting the sense of the rotation", and that converting a quaternion of the other convention into a SPICE one is done by moving the scalar to the front and negating the remaining three [NAIF-QUAT]. The two therefore differ in the sign of the vector part while agreeing on everything a reader is likely to check, and nothing in the four numbers reveals which was meant. That is the ambiguity this keyword removes.¶
The example below annotates an attitude carried as four separate properties. The
record stores the scalar last, as the current attitude message standard requires
of its own records, and components names it first, as this document requires
of the annotation.¶
{
"name": "SpacecraftAttitude",
"type": "object",
"frameTransforms": [
{
"from": { "reference": { "$ref": "#/definitions/BodyFrame" }, "kind": "type" },
"to": {
"reference": "http://www.opengis.net/def/crs/EPSG/0/4978",
"kind": "ogc-crs"
},
"encoding": "quaternion",
"components": ["qc", "q1", "q2", "q3"]
}
],
"properties": {
"epoch": { "type": "datetime" },
"q1": { "type": "double" },
"q2": { "type": "double" },
"q3": { "type": "double" },
"qc": { "type": "double" }
},
"required": ["epoch", "q1", "q2", "q3", "qc"],
"additionalProperties": false,
"definitions": {
"BodyFrame": {
"name": "BodyFrame",
"type": "tuple",
"description": "Spacecraft body frame.",
"properties": {
"x": { "type": "double", "description": "Along the instrument boresight." },
"y": { "type": "double", "description": "Completes a right-handed set." },
"z": { "type": "double", "description": "Towards the solar array hinge." }
},
"tuple": ["x", "y", "z"]
}
}
}
¶
An instance carrying the quarter turn worked above is the following.¶
{
"epoch": "2003-09-30T14:28:15.1172Z",
"q1": 0.0,
"q2": 0.0,
"q3": 0.7071068,
"qc": 0.7071068
}
¶
The four names given for axisAngle are, in order, the member carrying the
angle and the members carrying the three components of the axis. The angle is
that of the rotation carrying the from frame into the to frame, measured in
the right-hand sense about the axis, and the axis components SHOULD have unit
norm. This is the one rotation-only encoding whose members do not all carry one
unit: the angle MUST carry a unit or ucumUnit annotation of angle through
[JSTRUCT-UNITS], since radians and degrees are not distinguishable in the
value, and the axis components carry none. homogeneousMatrix likewise mixes
units, its rotation entries carrying none and its offset entries carrying a
length.¶
eulerAngles requires rotationSequence, and the three names in components
are the angles of the first, second, and third rotation in the order that
sequence gives.¶
The rotations are intrinsic. The first turns the from frame about the axis the
first letter names, the second turns the frame the first produced about the axis
the second letter names, and the third turns the frame the second produced, the
result being the to frame. Under the extrinsic reading, where every rotation
is about an axis of the original frame, the same three angles against the same
sequence describe a different transformation. Nothing is lost by fixing the
intrinsic reading, because every extrinsic sequence has an intrinsic equal that
Section 10.4.5 gives; degenerate cases, such as all three angles being
zero, agree under both readings and need no conversion.¶
That this needs saying is shown by the standard that most needs it. The current attitude message standard states the composition is intrinsic exactly once, in an annex marked informative [CCSDS-ADM], and the issue that preceded it for sixteen years never stated it at all [CCSDS-ADM1].¶
rotationSequence Property
rotationSequence MUST be present when encoding is eulerAngles and MUST NOT
be present otherwise. Its value MUST be three characters drawn from X, Y,
and Z, no two adjacent characters being equal, which admits twelve values:
XYX, XYZ, XZX, XZY, YXY, YXZ, YZX, YZY, ZXY, ZXZ, ZYX, and
ZYZ. The value is case-sensitive, and only these twelve upper-case strings are
valid. The leftmost character names the axis of the first rotation.¶
X names the first axis of the frame being turned, Y the second, and Z the
third, in the order that frame declares its axes. The letters are positional and
carry no direction of their own, whatever the frame calls its axes: in a frame
declaring north, east, and down, X is north, Y is east, and Z is down.¶
Twelve values are enough for every rotation, including every rotation a source states extrinsically, and an annotator working from such a source converts rather than looks for a value that is not there. A rotation stated extrinsically as a sequence of three axes with three angles is the same rotation as the intrinsic sequence whose axis letters are those three in reverse, taking the same three angle values in reverse order. Both reverse. Reversing the letters while leaving the angles in place, or the reverse of that, gives a different rotation, and is the error this paragraph exists to prevent.¶
The twelve conversions are these. In every row the angles reverse, so
components names the member holding the source's third angle first and the
member holding its first angle last.¶
| Extrinsic sequence | rotationSequence value | components order |
|---|---|---|
XYX
|
XYX
|
third, second, first |
XYZ
|
ZYX
|
third, second, first |
XZX
|
XZX
|
third, second, first |
XZY
|
YZX
|
third, second, first |
YXY
|
YXY
|
third, second, first |
YXZ
|
ZXY
|
third, second, first |
YZX
|
XZY
|
third, second, first |
YZY
|
YZY
|
third, second, first |
ZXY
|
YXZ
|
third, second, first |
ZXZ
|
ZXZ
|
third, second, first |
ZYX
|
XYZ
|
third, second, first |
ZYZ
|
ZYZ
|
third, second, first |
The six sequences whose first and third letters are equal reverse to themselves,
and those rows are the ones to read carefully: the value written is the same
string the source gives, which makes it easy to conclude that nothing needs
doing. Something does. The angles still reverse, and an extrinsic ZXZ recorded
as an intrinsic ZXZ with the angles left in source order is a different
rotation from the one the source states.¶
A source giving an extrinsic X-then-Y-then-Z rotation of 10, 20, and 30 degrees
is therefore annotated with a rotationSequence of ZYX, and components
naming the member holding 30 degrees first, then the member holding 20, then the
member holding 10. The members themselves do not move and their values are not
rewritten; only the order in which components names them changes. Because
nothing in the annotation records that the source was extrinsic, a schema that
converts SHOULD say so in description, so that a later reader comparing the
schema against the source document does not read the reversal as a mistake.
An extrinsic reading MUST NOT be recorded by inventing a thirteenth value, a
lower-case spelling, or any other marking.¶
The six values whose first and third characters are equal are permitted, though the standard that first enumerated them discouraged them "as their use can cause confusion" [CCSDS-ADM1].¶
translation Property
translation, when present, MUST be an array of exactly three names of direct
properties of the annotated object or tuple, all of numeric type and all
distinct, giving the coordinates of the origin of the from frame expressed in
the to frame, on the axes of the to frame in the order that frame declares
them. That is the t of x' = M x + t (Section 10.4.1), and it is
the offset from the origin of the to frame to the origin of the from frame,
not the other way about. Their units MUST be mutually convertible and SHOULD be
identical.¶
translation MUST NOT be present when encoding is homogeneousMatrix, whose
fourth column carries the same three values in the same sense. Where
translation is absent and encoding is not homogeneousMatrix, the element
declares a rotation and says nothing about the origins, which is the correct
reading for an attitude and the wrong one for a sensor alignment.¶
The KITTI dataset publishes the alignment between its laser scanner and its reference camera in a calibration file holding nine numbers on one line and three on another, and states in its paper that the rotation and the translation run from the laser to the camera [KITTI]. Neither the file nor the paper says whether the nine numbers are rows or columns; the developer kit for one of its benchmarks states that its matrices are stored row-major, and the raw calibration file carries no such statement. The two readings are transposes of one another, and for a rotation matrix both are valid rotations, so nothing in the numbers rules either out. Only the physical arrangement of the sensors does. The schema below states the reading, and the axis directions the file leaves to the reader, as declarations.¶
{
"name": "SensorAlignment",
"type": "object",
"frameTransforms": [
{
"from": { "reference": { "$ref": "#/definitions/LaserFrame" }, "kind": "type" },
"to": { "reference": { "$ref": "#/definitions/CameraFrame" }, "kind": "type" },
"encoding": "rotationMatrix",
"components": "rotation",
"translation": ["tx", "ty", "tz"]
}
],
"properties": {
"calibrationTime": { "type": "datetime" },
"rotation": {
"type": "array",
"minItems": 3,
"maxItems": 3,
"items": {
"type": "array",
"minItems": 3,
"maxItems": 3,
"items": { "type": "double" }
}
},
"tx": { "type": "double", "ucumUnit": "m" },
"ty": { "type": "double", "ucumUnit": "m" },
"tz": { "type": "double", "ucumUnit": "m" }
},
"required": ["calibrationTime", "rotation", "tx", "ty", "tz"],
"additionalProperties": false,
"definitions": {
"LaserFrame": {
"name": "LaserFrame",
"type": "tuple",
"description": "Frame of the rotating laser scanner.",
"properties": {
"x": { "type": "double", "description": "Forward along the vehicle." },
"y": { "type": "double", "description": "To the left of the vehicle." },
"z": { "type": "double", "description": "Up." }
},
"tuple": ["x", "y", "z"]
},
"CameraFrame": {
"name": "CameraFrame",
"type": "tuple",
"description": "Frame of the reference camera.",
"properties": {
"x": { "type": "double", "description": "To the right in the image." },
"y": { "type": "double", "description": "Down in the image." },
"z": { "type": "double", "description": "Along the optical axis, away from the camera." }
},
"tuple": ["x", "y", "z"]
}
}
}
¶
The instance below carries the values that dataset publishes for one recording day.¶
{
"calibrationTime": "2012-03-15T11:37:16Z",
"rotation": [
[0.007533745, -0.9999714, -0.000616602],
[0.01480249, 0.0007280733, -0.9998902],
[0.9998621, 0.00752379, 0.01480755]
],
"tx": -0.004069766,
"ty": -0.07631618,
"tz": -0.2717806
}
¶
Read as rows, the first inner array gives the first axis of the camera frame in the components of the laser frame, and the matrix maps the forward axis of the laser onto the optical axis of the camera, which is the arrangement the vehicle has. Read as columns, it maps them the other way. The file name carries the direction and nothing carries the layout, which is the division of labor this keyword ends.¶
linearReferenceSystem Keyword
The linearReferenceSystem keyword identifies the linear reference system
[ISO19148] under which a location held in properties of an object or tuple is
to be interpreted.¶
When present, linearReferenceSystem MUST be an object with REQUIRED
reference, kind, linearElement, and measure strings and OPTIONAL
measureEnd and direction strings. No other properties are permitted.¶
reference Property
reference MUST identify one linear reference system. Where kind is type it
MUST be a type reference { "$ref": <JSON Pointer> } [JSTRUCT-CORE] to a
shareable type
definition, and otherwise it MUST be an absolute URI [RFC3986]. The identified
definition MUST establish the linear referencing method, the measure origin, the
increasing-measure direction, the measure unit, and the linear-element
namespace. kind states which definition model the reference identifies.¶
A processor is not required to dereference the URI. This document does not define a resolution protocol, URI layout, storage model, or definition serialization.¶
kind Property
kind classifies which definition model the URI identifies. It is an open
enumeration. The following values are defined here:¶
| Kind | Referenced definition |
|---|---|
lrs-network
|
A network published by a geospatial feature service whose layer and metadata resources establish the linear elements and the measure, such as the WSDOT State Route system [WSDOT-LRS]. |
type
|
A meta-type declaring members whose referenceRole is linearElement and measure, and optionally direction (Section 8). |
Other values MAY name further definition models. Appendix A discusses the availability of registered definitions.¶
Since the identified definition MUST establish the measure unit, the
increasing-measure direction, and the linear-element namespace, a feature
service layer that carries measure values on its vertices and a route
identifier field without stating those is a rendering of positions in a
reference system rather than a definition of one, and it does not qualify as an
lrs-network.¶
A type reference carries a system that no authority publishes, such as one
internal to a plant, a terminal, or a private network. The referenced meta-type
MUST declare a member whose referenceRole is linearElement and a member
whose referenceRole is measure, and MAY declare one whose referenceRole is
direction. The linearElement, measure, and direction properties of the
annotation map the annotated properties onto those members, so the type of the
element identifier and the unit of the measure are checkable. Where the
annotation names a measureEnd, that property maps onto the same member of the
meta-type as measure, because both carry a distance in the same system. The
referencing method, measure origin, increasing-measure direction, and
linear-element namespace are stated in the description of the meta-type or of
its members.¶
linearElement Property
linearElement MUST name a direct property of the annotated object or tuple.
The property value identifies the road, railway, waterway, route, or other
linear element within the linear-element namespace established by the
identified system.¶
measure Property
measure MUST name a direct numeric property, distinct from linearElement.
The property gives the
distance from the measure origin along the identified linear element. It MUST
have a unit or ucumUnit annotation compatible with the measure unit
established by the identified system.¶
measureEnd Property
measureEnd, when present, MUST name another distinct direct numeric property
whose type and unit are those required of measure.¶
Where measureEnd is absent, the annotation locates a point on the linear
element at measure. Where it is present, the annotation locates the span of
that element between measure and measureEnd, and measure is the start of
the span. Both ends lie on the one element that linearElement identifies, and
this document defines no span crossing two elements.¶
The span is closed at both ends, which differs from the half-open convention
this document uses for temporal intervals. Two spans that share an end therefore
share the point at that end, and a consumer counting over abutting sections MUST
account for that shared point. A span whose ends are equal is the point at that
measure. This
document does not require that measureEnd exceed measure, since a system
whose increasing-measure direction opposes the direction of travel encodes a
forward span with a decreasing pair.¶
direction Property
direction, when present, MUST name another distinct direct property. Its
value qualifies the direction of travel or orientation using the vocabulary
established by the identified system. It does not alter the increasing-measure
direction.¶
Properties not named by linearElement, measure, measureEnd, or direction
are not part of the linearly referenced location.¶
This binding describes a location along one identified linear element, either a point or a span. This document does not define offsets, referent-relative addressing, interpolative methods, transformations between linear reference systems, or network topology.¶
The following excerpt locates a point on a Washington State route, where arm
is the accumulated route mile measured from the route origin. The cited service
qualifies as an lrs-network because the layer it names publishes the route
identifier syntax and the increasing-measure direction in its own metadata
rather than leaving them to a reader of the geometry: it decomposes the route
identifier into its parts and states that "a directional indicator has been added
to the RouteIdentifier to distinguish between the increasing and decreasing
direction of mileposting" [WSDOT-LRS]. It does not state the unit of the
measure values, which is why the measure rule requires the annotated property
to carry one:¶
{
"name": "WsdotStateRouteLocation",
"type": "object",
"linearReferenceSystem": {
"reference": "https://data.wsdot.wa.gov/arcgis/rest/services/Shared/LRSData/FeatureServer/9",
"kind": "lrs-network",
"linearElement": "route_identifier",
"measure": "arm",
"direction": "inventory_direction"
},
"properties": {
"route_identifier": {
"type": "string"
},
"arm": {
"type": "double",
"unit": "mi",
"ucumUnit": "[mi_i]"
},
"inventory_direction": {
"type": "string"
}
},
"required": ["route_identifier", "arm", "inventory_direction"],
"additionalProperties": false
}
¶
Color is the worked instance of a larger family: components resolved onto the channels of a named space, where the space fixes the basis those numbers weigh, the reference they stand relative to, and the encoding they carry. Section 10 gives that treatment to positions and directions, and this section gives it to color, because color is the perceptual signal most often carried in general-purpose JSON — design tokens, stylesheets, image metadata — and the one most often carried with all of that left silent. The same family continues past color in Section 12, where audio channel layout and multiband imaging resolve channel numbers onto a named space as color does. A coded value bound to an external list (Section 6) and the weighting a scalar measurement carries (Section 7) are relatives of a different shape, each treated in its own section.¶
colorSpaces Keyword
The colorSpaces keyword identifies the color spaces in which channel values
held in properties of an object or tuple are to be interpreted.¶
A color value is a measurement. Three numbers are not a color until something states which primaries they weigh, what white they are relative to, what range they run over, and whether they are proportional to light or to the signal that drives a display. Most of those are almost never carried with the numbers, and the ones that are are carried by conventions that differ between formats.¶
The keywords of Section 10 lean on registers. A
position has the EPSG dataset and the OGC definitions server behind it, and a
schema names a system by a URI that resolves to a definition of it. Color has no
equivalent. The International Color Consortium publishes registries of RGB color
spaces and of print characterization data, but those are pages written for a
reader rather than identifiers minted for a machine [ICC-REGISTRY]. What
exists in place of a register is of two other sorts. A standards document may
assign a code point, as [ITU-H273] assigns combinations of primaries, transfer
function, matrix coefficients, and signal range to four integers. Or the
definition travels with the data as a profile, identified by a checksum over its
own contents [ICC-SPEC]. Neither is a URI and neither dereferences. A
reference in this keyword identifies more often than it resolves, which is
true of the other reference-style keywords as well but is true of this one
always.¶
When present, colorSpaces MUST be a non-empty array. Each element MUST be an
object with REQUIRED reference, kind, and channels, OPTIONAL codePoints,
packing, alpha, alphaMode, transfer, illuminant, and observer, and no
other members. The keyword is an array because one record may carry a color in more
than one space, as a characterization dataset does when it gives the device
values that were printed alongside the color that was measured off the result.¶
These members can disagree, and one principle settles every case in which they
do. reference identifies a definition that describes a class of data, while
codePoints, transfer, illuminant, and observer state what is true of
this data. The narrower statement prevails, because a schema author declaring
one is recording a fact about the values in hand and a definition cannot be. The
specific rules follow from that and are stated where each member is defined
(Section 11.1.2, Section 11.1.6, Section 11.1.7); no
rule lets a definition override a member, and none of them makes a schema
invalid, since a processor that cannot resolve the definition cannot detect the
disagreement in the first place.¶
reference and kind Properties
reference MUST identify one color space or one set of device control values.
Where kind is type it MUST be a type reference { "$ref": <JSON Pointer> }
[JSTRUCT-CORE] to a shareable type definition. Where kind is icc-profile it MUST be a URI
[RFC3986] identifying the profile; the profile identifier [ICC-SPEC]
computes over the profile contents is not itself a URI, and a schema carrying
only that identifier MUST express it as one, under a scheme of the schema
author's choosing that states which digest the remainder is. Otherwise it MUST
be an absolute URI [RFC3986].¶
What the identified definition MUST establish follows from what is being identified.¶
| Identified | What the definition establishes |
|---|---|
| A space whose channels are additive primaries | The primaries, the white point, and the transfer function. |
| A space whose channels are tristimulus quantities, or are computed from them | The reference white, and the illuminant and the standard colorimetric observer, or else the schema declares them under illuminant and observer. |
| A set of device control values | The device, medium, and process the values drive, and the measurement conditions under which the result was characterized. |
A set of device control values is not a color space in the colorimetric sense,
and the third row exists because such values are carried alongside colors often
enough that excluding them would push the commonest characterization record out
of this keyword. channels, alpha, and alphaMode apply to it unchanged;
codePoints, transfer, illuminant, and observer do not, and MUST NOT be
present on an element identifying one.¶
kind classifies which definition model the reference identifies. It is an open
enumeration. The following values are defined here:¶
| Kind | Referenced definition |
|---|---|
itu
|
A Recommendation of the International Telecommunication Union. |
iec
|
A standard of the International Electrotechnical Commission. |
cie
|
A publication of the International Commission on Illumination [CIE015]. |
icc-profile
|
An ICC profile, identified as [ICC-SPEC] identifies one. |
icc-registry
|
An entry in a registry the International Color Consortium publishes [ICC-REGISTRY]. |
type
|
A meta-type in the annotated schema, as Section 8 describes. |
A processor is not required to dereference the URI. This document does not
define a resolution protocol, URI layout, storage model, or definition
serialization. Where an element carries codePoints, transfer, illuminant,
or observer alongside reference, those members prevail over the identified
definition where the two disagree (Section 11.1.2, Section 11.1.6,
Section 11.1.7).¶
codePoints Property
codePoints, when present, MUST be an array of exactly four non-negative
integers, being the color primaries, transfer characteristics, matrix
coefficients, and video full range flag that [ITU-H273] defines, in that
order.¶
These four integers are the only identifier of a color space that is both
machine-readable and widely deployed. The PNG specification carries the same
four in a chunk of its own, gives that chunk precedence over every other color
declaration a file may hold, and requires the chunk carrying mastering display
metadata to be accompanied by it, on the ground that such metadata means nothing
without the space it is relative to [PNG3]. Where the identified definition
and the code points disagree, the code points are the narrower statement and a
processor MUST prefer them, so that two processors reading one schema do not
part company. transfer is narrower still: where transfer is linear, it
overrides the transfer characteristics code point, and where transfer is
asDefined or absent, that code point governs.¶
channels Property
channels MUST be a non-empty array of names of direct properties of the
annotated object or tuple, all distinct, mapped by position onto the channels of
the space in the order the identified definition declares them. Except where
packing is present, the number of channels supplied MUST equal the number the
identified space defines, and an opacity channel is not one of them. As with
coordinates and components, the ordering is an assertion by the schema author
and is never inferred from property order, property names, or position in a
tuple.¶
Where packing is absent, either every name is that of a property of numeric
type, or the array holds exactly one name, that of a property whose type is
array or tuple and whose elements are of numeric type, in which case the
elements of that property supply the channels in order. Where packing is
present, the array holds exactly one name, that of a property of type string,
and the channels are read out of that one value as Section 11.1.4 states.¶
This document does not fix the range or the unit of a channel value. Where the
identified definition does not establish them, as it does not for a device
control value, the schema SHOULD declare them, by a unit annotation, by a
numeric range, or in the description of the property. The channels of one
element SHOULD share one range convention. A packing establishes both, and a
schema carrying one declares neither.¶
packing Property
A color is often carried not as a set of numbers but as one string that has all of them inside it. The hexadecimal notation is the commonest such string in the world, and it asserts four things at once that the numbers alone do not: that the order is red, green, blue; that each channel is eight bits; that the range runs from zero to two hundred and fifty-five; and, in the CSS reading, that the space is sRGB with its transfer function applied. A reader who knows the convention supplies all four. This keyword exists so that they need not be known.¶
packing, when present, states that one string property carries every channel of
the element. Its value MUST be hexRgb or hexRgba.¶
| Value | Meaning |
|---|---|
hexRgb
|
Three channels in three or six hexadecimal digits. |
hexRgba
|
Three channels and an opacity in four or eight hexadecimal digits. |
The value of the named property MUST consist of hexadecimal digits, optionally
preceded by a single #, and MUST hold three or six digits under hexRgb and
four or eight under hexRgba. The letters are case-insensitive, as [CSS-COLOR-4]
says in as many words: "the case of the letters doesn't matter - #00ff00 is
identical to #00FF00". The # is part of the CSS token rather than of the
notation, and a schema requiring one form or forbidding the other states so by a
pattern [JSTRUCT-VALIDATION]; CSS itself accepts the bare form under the
quirks mode of [CSS-COLOR-4].¶
In the six-digit and eight-digit forms each successive pair of digits is one
channel, read as an integer from zero to two hundred and fifty-five, where zero is
the minimum of that channel and two hundred and fifty-five is its maximum. In the
three-digit and four-digit forms each single digit stands for the pair obtained by
writing it twice, so that #123 is #112233. The order is fixed by this document
and is red, green, blue, and, under hexRgba, opacity. It is not taken from the
order the identified definition declares, and an element whose identified space
declares its channels in any other order MUST NOT use a packing.¶
The identified space MUST define exactly three channels, and they MUST be
additive primaries. alpha MUST NOT be present where packing is hexRgba,
whose fourth channel is the opacity; alphaMode MAY be present there and applies
to it. alpha MAY be present where packing is hexRgb, which carries none.¶
A packing carries eight bits for each channel, which is not enough resolution for
values proportional to light, so transfer SHOULD be asDefined or absent where
packing is present.¶
The space is still named by reference, and this document does not assume one. A
bare hexadecimal string in a document that says nothing further is sRGB only
because CSS says so, and CSS says so of the CSS notation and not of the digits:
hex colors are among those that "resolve to sRGB" [CSS-COLOR-4]. A record
carrying the same six digits for a wide-gamut display means a different color, and
the difference is invisible in the value.¶
The following excerpt is a design token whose color is written the way a stylesheet would write it.¶
{
"name": "BrandColorToken",
"type": "object",
"colorSpaces": [
{
"reference": "https://www.w3.org/TR/css-color-4/#predefined-sRGB",
"kind": "iec",
"channels": ["value"],
"packing": "hexRgba"
}
],
"properties": {
"token": {
"type": "string"
},
"value": {
"type": "string",
"pattern": "^#[0-9a-fA-F]{8}$"
}
},
"required": ["token", "value"],
"additionalProperties": false
}
¶
alpha and alphaMode Properties
alpha, when present, MUST name a direct property of numeric type carrying
opacity, distinct from every name in channels. Its value is a dimensionless
fraction from zero to one unless the property declares otherwise by a unit
annotation or a numeric range, and where it declares otherwise a processor MUST
reduce it to that fraction before applying alphaMode. alphaMode MUST NOT be
present when both alpha is absent and packing is not hexRgba, and states how
the channel values stand to the opacity, wherever that opacity is carried. When
present, its value MUST be straight or premultiplied.¶
| Value | Meaning |
|---|---|
straight
|
The channel values are independent of the opacity value. |
premultiplied
|
The channel values have already been multiplied by the opacity value. |
Where an opacity is carried and alphaMode is absent, the value is straight.
That is also the reading a packed hexadecimal opacity carries on its own, since
the notation of [CSS-COLOR-4] does not premultiply.¶
The two are not distinguishable by inspection, except at an opacity of zero, where the premultiplied channels are all zero and the color is gone. The PNG specification states its own choice in as many words: "The color values in a pixel are not premultiplied by the alpha value assigned to the pixel. This rule is sometimes called 'unassociated' or 'non-premultiplied' alpha", and, flatly, "PNG does not use premultiplied alpha" [PNG3]. Formats that do premultiply exist, and values carried from one to the other without the conversion are wrong everywhere the opacity is neither zero nor one.¶
transfer Property
transfer, when present, states whether the channel values carry the transfer
function of the identified space or are proportional to light. Its value MUST be
asDefined or linear.¶
| Value | Meaning |
|---|---|
asDefined
|
The values carry the transfer function the identified definition establishes. |
linear
|
The values are proportional to radiometric quantity, taking the primaries and white point of the identified space but not its transfer function. |
When absent, the value is asDefined.¶
linear is what a renderer means when it calls a color energy-linear. A space
and its linear counterpart are commonly given the same name, and the two are far
apart: under the transfer function the ICC registry publishes for sRGB, a
channel value of 0.5 stands for approximately 0.214 of the light that a channel
value of 1.0 stands for [ICC-REGISTRY]. Arithmetic on color is defined
on the linear values, and the PNG specification requires compositing to be
performed on "intensity samples (not gamma-encoded samples)" [PNG3].¶
illuminant and observer Properties
illuminant and observer, when present, state the conditions the values are
relative to. illuminant names a standard illuminant, and SHOULD use a
designation of [CIE015]. observer names a standard colorimetric observer; it
is an open enumeration, and the values defined here are cie-1931-2 for the
1931 standard colorimetric observer and cie-1964-10 for the 1964
supplementary standard colorimetric observer.¶
A space whose values are tristimulus quantities, or are computed from them,
does not by itself establish either. The definition of the 1976 L*a*b* space
says as much: it "is applicable to tristimulus values calculated using
colour-matching functions of the CIE 1931 standard colorimetric system or the
CIE 1964 standard colorimetric system" [ISO11664-4]. Values computed under the
two observers from one sample differ, and nothing in the values records which
was used. Where the identified definition does not establish the illuminant and
the observer, a schema SHOULD declare illuminant and observer. Where the
definition does establish them and the declared members disagree with it, the
declared members prevail and a processor MUST prefer them, for the reason that
the schema author is stating the condition these values were computed under and
the definition is stating the condition its class of values is customarily
computed under. A processor SHOULD report the disagreement, and MUST NOT treat
it as making the schema invalid.¶
Published measurement data shows both practices. The characterization datasets of the International Color Consortium carry their conditions in the file itself. One widely used print dataset opens by stating its instrument geometry as "D50, 2 degree, geometry 45/0, no polarisation filter, white backing, according to ISO 13655:2009 M1", then repeats the illuminant and the observer angle in fields of their own and records the national laboratory its measurements are traceable to [ICC-REGISTRY]. Reference data for a widely used color target, published by the maker of that target, states the measurement condition and the filter and names neither an illuminant nor an observer anywhere in the file. The second is not usable as reference data without information from outside it, and a schema is a place to put that information.¶
An identifier of a color space is also an identifier of an edition of it. The reference values for that same color target were revised, and for the white patch alone the lightness moved by more than one unit and the yellow-blue coordinate by nearly two, under an unchanged product name. A schema naming the space but not the edition describes two different sets of numbers.¶
A characterization dataset pairs the device values that were sent to a press with the color that was measured off the printed result. The two are in different spaces, which is why the keyword is an array, and only one of them is a color space in the colorimetric sense at all; the other is a set of device control values whose meaning is precisely the measurement it is paired with.¶
{
"name": "CharacterizationPatch",
"type": "object",
"colorSpaces": [
{
"reference": "https://registry.color.org/cmyk-registry/fogra51",
"kind": "icc-registry",
"channels": ["c", "m", "y", "k"]
},
{
"reference": "https://cie.co.at/publications/colorimetry-part-4-cie-1976-lab-colour-space-1",
"kind": "cie",
"channels": ["lStar", "aStar", "bStar"],
"illuminant": "D50",
"observer": "cie-1931-2"
}
],
"properties": {
"sampleId": { "type": "int32" },
"c": { "type": "double", "unit": "%" },
"m": { "type": "double", "unit": "%" },
"y": { "type": "double", "unit": "%" },
"k": { "type": "double", "unit": "%" },
"lStar": { "type": "double" },
"aStar": { "type": "double" },
"bStar": { "type": "double" }
},
"required": [
"sampleId", "c", "m", "y", "k", "lStar", "aStar", "bStar"
],
"additionalProperties": false
}
¶
The instance below is one patch of that dataset, the solid of the third ink.¶
{
"sampleId": 649,
"c": 0.0,
"m": 0.0,
"y": 100.0,
"k": 0.0,
"lStar": 88.94,
"aStar": -4.04,
"bStar": 92.37
}
¶
The three measured values are a position in a space that is undefined until the illuminant and the observer are known, and the file the numbers came from is one of the ones that says so. The annotation carries what that file carries, in a place a processor can read.¶
The keywords of this section resolve components onto the channels of a named
space, as Section 11 does for color. A signal is
a bundle of channel numbers that means nothing until something states which
channel is which, what the numbers stand relative to, and how they are encoded.
Color carries that in colorSpaces; audio and multiband imaging carry it here.¶
audioChannels Keyword
The audioChannels keyword identifies the channel layout in which audio sample
values held in properties of an object or tuple are to be interpreted, the level
the samples are relative to, and the encoding they carry.¶
An audio sample is a measurement of a signal, and a set of samples is not sound until something states which loudspeaker or which role each channel drives, what the numbers stand relative to, and whether they are amplitudes or a companded encoding of them. A bare array carries none of that, and the conventions that carry it differ between formats.¶
When present, audioChannels MUST be a non-empty array. Each element MUST be an
object with a REQUIRED reference, a REQUIRED kind, a REQUIRED channels, an
OPTIONAL levelReference, and an OPTIONAL encoding, and no other members. The
keyword is an array because one record may carry more than one channel group, as
a programme does when a commentary track accompanies a music-and-effects mix.¶
reference and kind Properties
reference MUST identify one channel layout whose definition establishes an
ordered set of channels, each with a loudspeaker position or a role. Where
kind is type it MUST be a type reference { "$ref": <JSON Pointer> }
[JSTRUCT-CORE] to a shareable type definition, and otherwise it MUST be an
absolute URI [RFC3986].
kind classifies which definition model the reference identifies. It is an open
enumeration; the following values are defined here:¶
| Kind | Referenced definition |
|---|---|
itu
|
A Recommendation of the International Telecommunication Union, such as the loudspeaker configurations of [ITU-BS2051]. |
type
|
A meta-type in the annotated schema, as Section 8 describes. |
A layout recommendation such as [ITU-BS2051] defines several configurations,
and a bare reference to it names the recommendation rather than one of them; a
schema that must pin one configuration deep-links to it or carries it as a
type. Where the recommendation mints no per-configuration URI, the deep link is
a fragment that names the configuration in the recommendation's own notation, as
#0+5+0 names its five-loudspeaker system with a low-frequency-effects channel;
the fragment identifies the configuration whether or not the recommendation
serves it. A processor is not required to dereference the URI. This document does
not define a resolution protocol, URI layout, storage model, or definition
serialization.¶
channels Property
channels MUST be a non-empty array of names of direct properties of the
annotated object or tuple, all distinct, mapped by position onto the channels of
the layout in the order the identified definition declares them. The number of
channels supplied MUST equal the number the identified layout defines. As with
coordinates and the channels of colorSpaces, the ordering is an assertion by
the schema author and is never inferred from property order, property names, or
position in a tuple. A low-frequency-effects channel is one of the channels,
in the position the layout assigns it, and is not inferred from a property name.
Either every name is that of a property of numeric type, or the array holds
exactly one name, that of a property whose type is array or tuple and whose
elements are of numeric type, in which case the elements of that property supply
the channels in order.¶
levelReference Property
levelReference, when present, states what the sample amplitudes are relative
to. Its values, their meanings, and its openness are those of the
levelReference of measurementConditioning (Section 7.1.3), which
defines them. Under fullScale the numeric range of the samples is stated by
the property carrying them and not by this value.¶
The default differs from that of measurementConditioning. Here, absence means
fullScale, because digital audio samples are referred to full scale unless
something says otherwise. Under measurementConditioning, absence means that no
level reference is stated, because a conditioned measurement has no comparable
default.¶
Programme loudness is not a per-sample reference: it is an integrated, gated, frequency-weighted measure of a whole programme, defined by [ITU-BS1770] and given a target by [EBU-R128], and a schema that carries it carries it as its own annotated value, not as the level reference of the channels.¶
encoding Property
encoding, when present, states how the stored numbers stand to amplitude. Its
value MUST be one of the values below or an absolute URI identifying another
encoding.¶
| Value | Meaning |
|---|---|
linear
|
The numbers are proportional to amplitude, and full scale is the range the annotated member's declared type permits. |
float
|
The numbers are proportional to amplitude and full scale is unit magnitude, so a sample of 1.0 is at full scale. A magnitude greater than one is legal and is a level above full scale. |
aLaw
|
Each channel value is the eight-bit A-law code word that [ITU-G711] defines. |
muLaw
|
Each channel value is the eight-bit mu-law code word that [ITU-G711] defines. |
Where absent, the encoding is linear.¶
The division between linear and float is the scale, not the storage type. A
member of floating-point type whose samples run to the range of an integer
quantization is linear; a member whose samples are normalized to unit
magnitude is float, whatever its declared type. The distinction matters
because every value validates under either and only one of them makes a
decibel level or a sum of channels come out right, and because a float
member may legitimately carry a magnitude greater than one that a reader
must not treat as an error.¶
Under aLaw or muLaw each channel value is
an integer from zero to two hundred and fifty-five, and a
reader restores amplitude by the inverse companding before any arithmetic.¶
The enumeration is open because a sample encoding is a definition maintained elsewhere, as G.711 companding is. A value that is a URI SHOULD identify a definition of the encoding. A processor that does not know a value MUST preserve it, MUST NOT reject the schema for carrying it, and MUST NOT assume that the numbers are proportional to amplitude; the check is indeterminate rather than incorrect. A compressed bitstream is not a set of channel members and is outside this keyword whatever its encoding is called.¶
Object-based and scene-based audio, in which a sample is not one loudspeaker,
are not channel layouts in this sense; they are described by the Audio
Definition Model [ITU-BS2076] and are outside this keyword. The time axis of
audio, the sample rate, is a regular temporal cadence and is carried by
temporalReferenceSystem and cadence (Section 9.4), not here.¶
The record below is one multichannel sample frame of a five-channel-with-LFE
programme, whose layout is the 0+5+0 system of [ITU-BS2051]. The six property
names are the schema author's; the order of channels is what binds them to the
layout.¶
{
"name": "SurroundSampleFrame",
"type": "object",
"audioChannels": [
{
"reference": "https://www.itu.int/rec/R-REC-BS.2051/en#0+5+0",
"kind": "itu",
"channels": ["l", "r", "c", "lfe", "ls", "rs"],
"levelReference": "fullScale",
"encoding": "linear"
}
],
"properties": {
"frameIndex": { "type": "int64" },
"l": { "type": "double" },
"r": { "type": "double" },
"c": { "type": "double" },
"lfe": { "type": "double" },
"ls": { "type": "double" },
"rs": { "type": "double" }
},
"required": ["frameIndex", "l", "r", "c", "lfe", "ls", "rs"],
"additionalProperties": false
}
¶
spectralBands Keyword
The spectralBands keyword identifies the spectral bands onto which the
components of a multiband value held in properties of an object or tuple are
resolved, in the order a named sensor or band set declares them.¶
A multiband pixel is a row of numbers, one per band, and it means nothing until something states which wavelengths each band covers and what physical quantity the number is. The bands of one sensor are not the bands of another even where they are given the same colour name, and a raw count is not a radiance until the sensor's calibration is applied.¶
When present, spectralBands MUST be a non-empty array. Each element MUST be an
object with a REQUIRED reference, a REQUIRED kind, a REQUIRED bands, and an
OPTIONAL calibration, and no other members. The keyword is an array because one
record may resolve its components onto more than one band set, as a fused product
does when it carries bands from two instruments.¶
reference and kind Properties
reference MUST identify one band set whose definition establishes an ordered
set of bands, each with a wavelength range. Where kind is type it MUST be a
type reference { "$ref": <JSON Pointer> } [JSTRUCT-CORE] to a shareable type
definition, and
otherwise it MUST be an absolute URI [RFC3986]. kind classifies which
definition model the reference identifies. It is an open enumeration; the
following values are defined here:¶
| Kind | Referenced definition |
|---|---|
sensor
|
A band set published by the operator of an instrument, such as the Operational Land Imager bands of [USGS-LANDSAT]. |
type
|
A meta-type in the annotated schema, as Section 8 describes. |
A publication that lists the band sets of several instruments names one of them
by a deep link, a fragment naming the instrument where the publisher mints no
per-instrument URI, as #landsat-8-9-oli names the Operational Land Imager; the
fragment identifies the band set whether or not the publication serves it. A
processor is not required to dereference the URI. This document does not
define a resolution protocol, URI layout, storage model, or definition
serialization.¶
bands Property
bands MUST be a non-empty array of names of direct properties of the annotated
object or tuple, all distinct, mapped by position onto the bands of the set in
the order the identified definition declares them. The number of bands supplied
MUST equal the number the identified set defines. As with the channels of
colorSpaces, the ordering is an assertion by the schema author and is never
inferred from property order, property names, or position in a tuple. Either
every name is that of a property of numeric type, or the array holds exactly one
name, that of a property whose type is array or tuple and whose elements are
of numeric type, in which case the elements of that property supply the bands in
order.¶
calibration Property
calibration, when present, states what physical quantity each band value is.
It is an open enumeration; the values defined here are:¶
| Value | Meaning |
|---|---|
digitalNumber
|
The values are quantized, calibrated, scaled digital numbers in the identified product encoding, which become a physical quantity only when the per-band coefficients of the acquisition are applied. |
radiance
|
The values are spectral radiance, and their unit is stated by JSON Structure Units [JSTRUCT-UNITS]. |
reflectance
|
The values are reflectance, a dimensionless fraction, of the kind the identified product defines, such as top-of-atmosphere planetary reflectance with or without a solar-angle correction; where they are stored scaled, the property states the scale. |
Where absent, the calibration is not established by this annotation and the identified definition or the unit of the property governs. The rescaling from a digital number to radiance or reflectance is per-band; for the Landsat imagers it is a multiplicative and an additive coefficient for each band, delivered in the product metadata [USGS-LANDSAT-L1], and a schema that holds digital numbers holds those coefficients as their own values. A quantity the values defined here do not name, such as brightness temperature, is carried as an open value.¶
The record below is one pixel of a nine-band Operational Land Imager scene, whose bands are those of [USGS-LANDSAT], carried as reflectance.¶
{
"name": "OliPixel",
"type": "object",
"spectralBands": [
{
"reference": "https://www.usgs.gov/faqs/what-are-band-designations-landsat-satellites#landsat-8-9-oli",
"kind": "sensor",
"bands": [
"coastalAerosol", "blue", "green", "red",
"nir", "swir1", "swir2", "pan", "cirrus"
],
"calibration": "reflectance"
}
],
"properties": {
"column": { "type": "int32" },
"row": { "type": "int32" },
"coastalAerosol": { "type": "double" },
"blue": { "type": "double" },
"green": { "type": "double" },
"red": { "type": "double" },
"nir": { "type": "double" },
"swir1": { "type": "double" },
"swir2": { "type": "double" },
"pan": { "type": "double" },
"cirrus": { "type": "double" }
},
"required": [
"column", "row", "coastalAerosol", "blue", "green", "red",
"nir", "swir1", "swir2", "pan", "cirrus"
],
"additionalProperties": false
}
¶
A check defined by this document has one of three outcomes. It is valid when a processor evaluated it and it held, invalid when a processor evaluated it and it did not hold, and indeterminate when a processor did not evaluate it because a definition it depends on was not resolved.¶
A processor MUST report the three outcomes distinctly. It MUST NOT report an indeterminate check as valid, MUST NOT reject a schema solely because a check was indeterminate, and MUST NOT act on an annotation as though an indeterminate check had held.¶
A rule whose subject lies within the schema is always evaluable, and a check of such a rule is never indeterminate. A rule whose subject is an external definition is indeterminate for as long as that definition is unresolved.¶
Which definitions a processor holds is a deployment matter, so two conforming processors MAY reach different outcomes for one schema. They MUST differ only in that one returns indeterminate where the other returns valid or invalid. A processor MUST NOT return valid where a processor holding the definition would return invalid.¶
A profile, a deployment, or an agreement between parties MAY require that named checks be evaluated rather than left indeterminate. This document requires it of no check.¶
A conforming schema selects the versioned extension meta-schema URI. It MAY use any subset of the annotations defined by this document, including none, and it MUST NOT be rejected merely because an annotation or annotated property is absent.¶
Every annotation that is present:¶
MUST occur at an attachment point permitted by this document;¶
MUST have the defined value shape and use an allowed kind, semanticRole,
or referenceRole value;¶
MUST NOT repeat, within one containing type, a semanticRole that
Section 5.3 states identifies one position;¶
MUST be compatible with the Core type of the annotated schema; and¶
MUST satisfy the applicable rules of JSON Structure Units [JSTRUCT-UNITS].¶
Validation MUST reject malformed annotations and invalid identifiers or values within annotations, and those checks are never indeterminate. External resolution, domain-of-validity, mapping-review, and transformation checks are indeterminate while the definition they depend on is unresolved, and Section 13.1 states how a processor reports them.¶
A conforming processor MUST preserve declarations, externally resolved facts, and inferences separately. It MUST preserve the differences among an observation act, its result, an observable-property identity, a definition URI, an identifiable temporal object, and a temporal-position value.¶
A processor MUST NOT infer:¶
any semanticRole, observed-property annotation, concept binding, derivation,
statistic, or cadence from a name, label, description, type, unit, position,
or sample;¶
a referenceRole, or the member of a meta-type that a mapping property names,
from a member name;¶
graph structure, node identity, statements, or entailment from a concept binding, or a term of one vocabulary from a term of another;¶
identity or semantic equivalence from labels, closeMatch, hierarchy,
relatedness, or QuantityKind classification;¶
a feature, procedure, or identity absent from the corresponding
semanticRole, or a proximate feature from an ultimate feature or conversely;¶
a temporal reference regime from a non-Core or ambiguous encoding;¶
metric intervals from ordinal positions or an untilNext end from cadence;¶
complete coverage from interval or accumulation;¶
that absent quality means acceptable quality;¶
a coordinate, vector-frame, or linear reference binding from names or samples;¶
the direction, sense, or component order of a frame transformation, or the variance of a vector or tensor quantity, from member names, from the values themselves, or from the frames the transformation runs between;¶
a color space, an illuminant, an observer, a transfer function, or an alpha mode from channel names, from the range the channel values fall in, or from the number of channels present;¶
an audio channel layout, level reference, or encoding; a spectral band set or calibration; a code-list binding; or a measurement weighting, time weighting, or level reference, from names, samples, units, or the number of members present;¶
that members sharing a name prefix, a unit, or an observed property are the components of one vector quantity; or¶
permission to aggregate, convert, transform, reject outliers, or infer causality.¶
A processor that selects one temporal position as the event-time axis of a
record, for windowing, ordering, or watermarking, SHOULD select the member
annotated phenomenonTime, or, where the record bounds the period at both ends,
the member annotated phenomenonTimeStart. It SHOULD NOT select a member
annotated resultTime, ingestionTime, scheduledTime, or actualTime for
that purpose. Those roles state the handling of the record and not the time of
the phenomenon, and a window built on them reports the behaviour of the pipeline
rather than of the world.¶
Where a processor needs both axes and its execution model admits only one, the phenomenon-time member is the recommended axis, and the operational position is carried as an ordinary value. A processor that departs from this SHOULD record the position it selected, so that a reader of the output is not left to infer which axis produced it.¶
A record annotating no phenomenon-time role supplies no phenomenon-time axis. A processor MUST NOT construct one from a member name, and MUST NOT read an operational position as one. It MAY still window on an operational position, which states when the record was handled.¶
A processor MAY ignore this extension. A processor claiming support MUST treat unresolved identifiers, domains, mappings, or conversions as indeterminate rather than compatible.¶
An annotation on an inherited property or type remains part of the effective schema. Core inheritance does not define a local override of an inherited property. Cross-property rules MUST be checked against the effective inherited type.¶
JSON Structure Import copies complete definitions, including annotations [JSTRUCT-IMPORT]. Shadowing replaces the complete imported definition; it does not merge individual annotations.¶
Member names stated by an annotation are resolved as Section 3 requires: against the effective definition of the annotated type, and against declared property names rather than serialized ones.¶
An annotation belongs to the definition it is written on, so annotations are part of what a type means rather than of how one schema uses it. A schema that needs different annotations for the same structure shadows the definition and restates them, or defines a distinct type. This document defines no overlay by which a schema attaches annotations to a definition it does not own.¶
Where a type would inherit the same keyword from more than one base, the derived definition MUST state that keyword itself, and the stated value is the effective one. A definition that leaves two inherited values of one keyword in force is not conforming, and a processor MUST NOT select between them.¶
The extension meta-schema will be published at:¶
https://json-structure.org/meta/semantic-annotations/v0/#¶
It offers one feature, JSONStructureSemanticAnnotations, whose add-ins contribute
the keywords defined here to the Core property, object, tuple, array, set, map,
and choice definitions. A schema activates this specification by selecting that
URI and naming the feature in $uses. The meta-schema enables JSON Structure
Units, Import, Conditional Composition, and Validation. The annotations carry no
profile or version member; the versioned meta-schema URI is the version
identifier.¶
The meta-schema validates the shape of each annotation defined here: the
presence and form of its members, the enumerations that Section 3
states are closed, and the form required of a reference for the accompanying
kind. It does not express a rule
whose subject lies outside the annotated node. Such rules, including every rule
about a property that an annotation names and every rule about a definition that
a reference identifies, are checked against the effective schema rather than by
the meta-schema alone. No companion reference type or import is required.¶
Incorrect or malicious catalog entries, labels, mappings, feature identities, or procedure identities can cause results from different subjects, acts, or concepts to be combined. Implementations MUST preserve catalog-URI identity, MUST NOT infer equivalence from discovery labels, and MUST NOT treat an unreviewed or non-exact mapping as approval. Catalog write access and mapping review therefore require authentication, authorization, audit, and provenance. Deprecation alternatives are migration advice, not automatic substitutions.¶
No annotation is checked against the data it describes. Validation confirms that
a named member exists, that a closed enumeration holds, and that components and
units agree in number and kind; it cannot confirm that a reference still
identifies the definition the values are actually expressed against. An
annotation is therefore a claim that can be true when written and false later
without anything failing: an instrument is recalibrated, a station is resurveyed
onto a new datum, a producer reorders channels or changes a rate, a code list is
superseded. The schema continues to validate and the annotation continues to
read as authoritative.¶
Stale annotation is for that reason more dangerous than absent annotation. The prohibitions in Section 13.3 guard the absent case, where a processor is required to decline rather than guess; they do not guard the stale case, where a processor holds an explicit statement, has no ground to doubt it, and proceeds with a combination it would otherwise have refused. A schema author MUST revise the annotations of a schema in the same change that alters what the schema describes, and a consumer MUST NOT treat an annotation as evidence more current than the schema revision carrying it.¶
The same property makes annotations a target. Modifying a schema changes what
data means without touching the data, without failing validation, and without
any signal to a consumer: a substituted CRS reference relocates every
coordinate, an alphaMode moved between straight and premultiplied alters
every composite, an altered levelReference shifts every level by an amount
large enough to matter and plausible enough to pass review. Schema distribution
therefore needs the integrity protection the data needs, and a change to an
annotation warrants the review a change to a type warrants.¶
Incorrect temporal roles, boundaries, reference systems, transformations, or
domains of validity can reorder positions or create false coverage. Cadence MUST NOT synthesize a missing untilNext successor. Incorrect CRS, axis order, LRS,
measure origin, unit, or direction can place a feature incorrectly. Processors
MUST NOT perform temporal, coordinate, linear, or unit transformations without
validating authoritative definitions.¶
Catalog labels and mappings, procedure and feature identities, locations, times, statuses, and quality can reveal sensitive operations or subjects. Hidden labels are not an access-control mechanism. This specification grants no access and does not replace minimization, privacy review, retention, or export controls.¶
Remote registries, schemas, vocabularies, procedures, mapping targets, and reference systems are untrusted input. Implementations SHOULD use HTTPS where available, bounded retrieval, caching with version awareness, allow-lists where appropriate, cycle detection, and explicit trust decisions. Dereferencing can disclose processor interest.¶
This document has no IANA actions.¶
The URIs below illustrate the kind values defined in this document. None of
these lists is exhaustive, and a publisher can supersede any definition.¶
A reference identifies a term, not the namespace of the model that defines it;
Section 4.1 names the model behind each kind. Vocabularies that define terms
in those models and that are widely used with observation data include the
Semantic Sensor Network ontology [SOSA-SSN], which publishes
http://www.w3.org/ns/sosa/ and http://www.w3.org/ns/ssn/, and the Provenance
Ontology [PROV-O], which publishes http://www.w3.org/ns/prov#.¶
The URIs in this section resolve at the OGC definitions server.¶
Time-scale concepts, for kind ogc-trs:¶
http://www.opengis.net/def/trs/BIPM/0/UTC, Coordinated Universal Time;¶
http://www.opengis.net/def/trs/BIPM/0/TAI, International Atomic Time;¶
http://www.opengis.net/def/trs/IERS/0/UT1, Universal Time UT1; and¶
http://www.opengis.net/def/trs/USNO/0/GPS, GPS Time.¶
None of these constrains the type of the annotated value. Each names a time scale and establishes no axis, unit, or encoding, so any of them can accompany a Core temporal type, a string, a numeric epoch count, or a compound position.¶
Temporal coordinate reference systems, for kind ogc-temporal-crs. Each
establishes an origin and an axis, and the axis constrains the annotated type:¶
http://www.opengis.net/def/crs/OGC/0/GregorianDateTime, a date and time in
the Gregorian calendar. Its axis carries no unit, and it takes a Core
datetime, date, or time, or a string in the same form.¶
http://www.opengis.net/def/crs/OGC/0/UnixTime, seconds elapsed from
1970-01-01T00:00:00Z. It takes an integer or number carrying UCUM s.¶
http://www.opengis.net/def/crs/OGC/0/AnsiDate, days elapsed from
1601-01-01T00:00:00Z. It takes an integer or number carrying UCUM d.¶
http://www.opengis.net/def/crs/OGC/0/JulianDate, days elapsed from the
Julian period origin. It takes a number carrying UCUM d, since its origin
falls at noon and positions are ordinarily fractional.¶
http://www.opengis.net/def/crs/OGC/0/TruncatedJulianDate, days elapsed
from 1968-05-24T00:00:00Z. It takes a number carrying UCUM d.¶
http://www.opengis.net/def/crs/OGC/0/BeforePresentTime, years counted
backwards from 1950. It takes a number carrying UCUM a.¶
http://www.opengis.net/def/crs/OGC/0/ChronometricGeologicTime, millions of
years counted backwards from year zero. It takes a number carrying UCUM Ma.¶
None of the numeric definitions takes a string, and GregorianDateTime does
not take a number. The last two count backwards, so a larger value is an
earlier position and sortOrder is backward.¶
The register also serves a parameterized definition taking an origin and a unit, which covers epoch counts for which no named definition exists. A count of milliseconds from the Unix origin is identified by:¶
http://www.opengis.net/def/crs/OGC/0/Temporal ?epoch=%221970-01-01T00:00:00Z%22&uom=%22ms%22¶
Parameter values are quoted, and the URI is one line. The constraint follows
the uom parameter: this URI takes an integer or number carrying UCUM ms.
A count of seconds from the same origin is a different definition, not the same
definition read at a different scale.¶
For kind ogc-crs:¶
http://www.opengis.net/def/crs/OGC/1.3/CRS84, WGS 84 with axes longitude,
latitude;¶
http://www.opengis.net/def/crs/OGC/0/CRS84h, WGS 84 with axes longitude,
latitude, ellipsoidal height;¶
http://www.opengis.net/def/crs/EPSG/0/4326, WGS 84 with axes latitude,
longitude;¶
http://www.opengis.net/def/crs/EPSG/0/4979, WGS 84 with axes latitude,
longitude, ellipsoidal height.¶
Axis order differs among these definitions, and the definition establishes it. The first two and the last two describe the same datum in opposite axis order.¶
For kind epsg, the EPSG Geodetic Parameter Dataset serves its own records,
for example https://apps.epsg.org/api/v1/CoordRefSystem/4326 [EPSG]. A
definition served at an OGC URI under an EPSG code, as in the list above, is
ogc-crs rather than epsg.¶
No register of vector reference frames corresponds to the temporal and
coordinate registers. A registered coordinate reference system whose axes are
directions may be cited with ogc-crs or epsg, and the URIs listed in
Appendix A.3 apply unchanged. Most frames in which vector
quantities are reported are not registered at all.¶
Some communities publish a closed list of frame names without serving a URI
per frame. The SPASE data model [SPASE] is the case in point: its
CoordinateSystemName enumeration fixes tokens such as GSE, GSM, GEI,
SM, RTN, HEE, MFA, and SensorCoordinates, and defines what each one
means, but the register itself carries no per-token reference URI. A token from
such a list is not a reference value. Where the community that publishes the
list also serves a resolvable definition, that URI is the reference and the
token is not; where it does not, the frame is written as a tuple meta-type in
the schema document and cited with kind type, as in
Section 10.2.¶
No register of linear reference systems corresponds to the temporal and
coordinate registers. ISO 19148 [ISO19148] specifies the conceptual schema,
in which a location is a measurement along a linear element and optionally an
offset from it, so that overlapping attributes can be carried against one
geometry without fragmenting it. It defines no identifiers for individual
systems. A linear reference system is therefore published by the authority that
maintains the network, which is why lrs-network is the value defined here for
a published system, and why a system published in another form is identified by
a value naming the model that publishes it. A system that no authority publishes
is defined as a meta-type in the schema and cited with type.¶
In the United States, the FHWA ARNOLD directive [FHWA-ARNOLD] requires each
state department of transportation to maintain one linear reference system
covering all public roads, modelled as the HPMS Field Manual prescribes
[FHWA-HPMS]: routes carrying measure values on their vertices, with
attributes held in event tables that cite a route and a measure rather than
segmenting the underlying geometry. A service published under that directive is
an lrs-network, and its layer and metadata resources establish the linear
elements and the measure. The Washington state route network used in
Section 10.5 is served at
https://data.wsdot.wa.gov/arcgis/rest/services/Shared/LRSData/FeatureServer/9
[WSDOT-LRS].¶
A reference identifies one specific code list, and kind classifies the
register model that list belongs to (Section 6.1). The registers fall into
two tiers by how a reference behaves.¶
Some publish a resolvable URI for the list, or for each entry, so that a
reference both identifies and dereferences:¶
wmo-codes: a register in the WMO Codes Registry, such as
http://codes.wmo.int/bufr4/codeflag/0-20-003, the present-weather register,
whose entries are served beneath it [WMO-CODES].¶
iana: a registry served under https://www.iana.org/assignments/, such as
https://www.iana.org/assignments/media-types/media-types.xhtml for media
types, or https://www.iana.org/assignments/language-subtag-registry for the
BCP 47 language subtags [IANA-PROTOCOLS] [IANA-LANGTAGS].¶
snomed-ct: an edition under the SNOMED CT URI scheme, http://snomed.info/sct
for the International Edition, with a module or version appended for a specific
release [SNOMED-CT].¶
icd: a WHO linearization, http://id.who.int/icd/release/11/mms for the
ICD-11 mortality and morbidity statistics [WHO-ICD].¶
The rest publish their lists in standards or directories that mint no per-list
machine URI. A reference to one identifies more often than it resolves, and
names the standard's identifier or landing page:¶
iso: an ISO code table, named by its catalogue page such as
https://www.iso.org/iso-3166-country-codes.html, or by a standard URN such as
urn:iso:std:iso:3166:-1 [ISO3166] [ISO4217] [ISO639].¶
unlocode:
https://unece.org/trade/cefact/unlocode-code-list-country-and-territory
[UNLOCODE].¶
icao: the location indicators of Doc 7910,
https://store.icao.int/en/location-indicators-doc-7910, or the aircraft type
designators of Doc 8643,
https://www.icao.int/operational-safety/doc-8643-aircraft-type-designators
[ICAO7910] [ICAO8643].¶
iata: https://www.iata.org/en/publications/directories/code-search/
[IATA-CODES].¶
A processor is not required to dereference a reference of either tier, and an
unresolved reference is indeterminate rather than incorrect. Under kind
type the list is a meta-type in the schema and reference is the type
reference { "$ref": <JSON Pointer> }, not a URI.¶
Initial version.¶
The author thanks the JSON Structure community for review and feedback.¶