Building Blocks for HTTP APIs C. Vasters Internet-Draft Microsoft Corporation Intended status: Standards Track 5 August 2026 Expires: 6 February 2027 JSON Structure: Semantic and Reference-System Annotations draft-vasters-json-structure-semantic-annotations-latest Abstract 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. About This Document 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. Status of This Memo 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 Notice 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. Table of Contents 1. Introduction 1.1. Semantic Binding and External Definitions 1.2. Observable and Observed Property Concepts 2. Conventions 3. Annotation Model 4. Vocabulary Annotations 4.1. The concepts Keyword 4.1.1. The reference Property 4.1.2. The kind Property 4.1.3. Type Compatibility 4.1.4. Relationship to the Annotation Model 4.1.5. Relationship to observedProperty 5. Observation Annotations 5.1. The observedProperty Keyword 5.1.1. The reference Property 5.1.2. The kind Property 5.1.3. Attachment and Scope 5.2. Semantic Mappings and Result Hints 5.3. The semanticRole Keyword 5.3.1. Observation Result Concern 5.3.2. Feature and Procedure Concern 5.3.3. Temporal Concern (Observation Time) 5.3.4. Temporal Concern (Flattened Period Boundaries) 5.3.5. Temporal Concern (Operational Event Time) 5.3.6. Status Concern 5.4. The derivation Keyword 5.5. The statistic Keyword 6. Coded Value Annotations 6.1. The codedValues Keyword 6.1.1. The reference and kind Properties 6.1.2. Example 7. Measurement Conditioning Annotations 7.1. The measurementConditioning Keyword 7.1.1. The weighting Property 7.1.2. The timeWeighting Property 7.1.3. The levelReference Property 7.1.4. Example 8. Reference System Meta-Types 8.1. The referenceRole Keyword 9. Temporal Reference Annotations 9.1. The phenomenonTimeRelation Keyword 9.2. The supportPeriod Keyword 9.2.1. The length Property 9.2.2. The anchor Property 9.3. The temporalReferenceSystem Keyword 9.3.1. The reference Property 9.3.2. The kind Property 9.3.3. The position Property 9.3.4. The sortOrder Property 9.3.5. Type Compatibility 9.4. The cadence Keyword 9.4.1. The kind Property 9.4.2. The period Property 10. Spatial Reference Annotations 10.1. The coordinateReferenceSystem Keyword 10.1.1. The reference Property 10.1.2. The kind Property 10.1.3. The coordinates Property 10.1.4. Vertical and Compound Systems 10.2. The vectorReferenceFrames Keyword 10.2.1. The reference and kind Properties 10.2.2. The components Property 10.2.3. The variance Property 10.2.4. Multiple Frames and Shared Components 10.3. The tensorReferenceFrames Keyword 10.3.1. The frames Property 10.3.2. The components Property 10.4. The frameTransforms Keyword 10.4.1. The Conventions This Document Fixes 10.4.2. The from and to Properties 10.4.3. The encoding Property 10.4.4. The components Property 10.4.5. The rotationSequence Property 10.4.6. The translation Property 10.5. The linearReferenceSystem Keyword 10.5.1. The reference Property 10.5.2. The kind Property 10.5.3. The linearElement Property 10.5.4. The measure Property 10.5.5. The measureEnd Property 10.5.6. The direction Property 11. Colorimetric Reference Annotations 11.1. The colorSpaces Keyword 11.1.1. The reference and kind Properties 11.1.2. The codePoints Property 11.1.3. The channels Property 11.1.4. The packing Property 11.1.5. The alpha and alphaMode Properties 11.1.6. The transfer Property 11.1.7. The illuminant and observer Properties 11.1.8. Example 12. Signal Channel Annotations 12.1. The audioChannels Keyword 12.1.1. The reference and kind Properties 12.1.2. The channels Property 12.1.3. The levelReference Property 12.1.4. The encoding Property 12.1.5. Example 12.2. The spectralBands Keyword 12.2.1. The reference and kind Properties 12.2.2. The bands Property 12.2.3. The calibration Property 12.2.4. Example 13. Conformance 13.1. Check Outcomes 13.2. Schema Conformance 13.3. Processing Conformance 13.4. Inheritance and Imports 14. Extension Meta-Schema 15. Security and Privacy Considerations 16. IANA Considerations 17. References 17.1. Normative References 17.2. Informative References Appendix A. Reference URIs (Informative) A.1. Vocabularies A.2. Temporal Reference Systems A.3. Coordinate Reference Systems A.4. Vector Reference Frames A.5. Linear Reference Systems A.6. Code Lists Changes from draft-vasters-json-structure-semantic-annotations-00 Acknowledgments Author's Address 1. Introduction 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. 1.1. Semantic Binding and External Definitions 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": } 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. 1.2. Observable and Observed Property Concepts 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. 2. Conventions 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. 3. Annotation Model 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. | +---------------------------+-------------------------------------+ Table 1 Omission means undeclared unless stated otherwise. It never implies compatible, successful, or acceptable data. 4. Vocabulary Annotations 4.1. The 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. 4.1.1. The 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. 4.1.2. The 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]. | +-----------------------+-------------------------------------+ Table 2 Other values MAY identify further definition models. Appendix A.1 lists namespace URIs for the vocabularies named above. 4.1.3. Type Compatibility 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. 4.1.4. Relationship to the Annotation Model 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. 4.1.5. Relationship to 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" } ] } } } 5. Observation Annotations 5.1. The 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. 5.1.1. The 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. 5.1.2. The 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" } } 5.1.3. Attachment and Scope 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 } 5.2. Semantic Mappings and Result Hints 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. 5.3. The 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. 5.3.1. Observation Result Concern 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 } 5.3.1.1. 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. 5.3.1.2. 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. 5.3.2. Feature and Procedure Concern 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 } 5.3.2.1. 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". 5.3.2.2. 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. 5.3.2.3. 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. 5.3.2.4. 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. 5.3.3. Temporal Concern (Observation Time) 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 } 5.3.3.1. 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. 5.3.3.2. 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. 5.3.3.3. 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. 5.3.4. Temporal Concern (Flattened Period Boundaries) 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 } 5.3.4.1. 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. 5.3.4.2. 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. 5.3.4.3. 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. 5.3.4.4. 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. 5.3.5. Temporal Concern (Operational Event Time) 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 } 5.3.5.1. 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. 5.3.5.2. scheduledTime Planned temporal position for an activity. scheduledTime MUST annotate a value whose Core type and reference binding together encode a temporal position. 5.3.5.3. actualTime Temporal position when the planned activity occurred. actualTime MUST annotate a value whose Core type and reference binding together encode a temporal position. 5.3.5.4. 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. 5.3.5.5. 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. 5.3.6. Status Concern 5.3.6.1. 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 } 5.4. The 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. | +------------+----------------------------------------------------+ Table 3 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 } 5.5. The 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. | +-------------------+----------------------------------+ Table 4 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. | +------------+------------+----------------------------------+ Table 5 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 } 6. Coded Value Annotations 6.1. The 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. 6.1.1. The 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": } [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. | +-----------+----------------------------------------------------+ Table 6 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. 6.1.2. Example 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 } 7. Measurement Conditioning Annotations 7.1. The 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. 7.1.1. The 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. | +-------+------------------------------------+ Table 7 7.1.2. The 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. | +-------+----------------------+ Table 8 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. 7.1.3. The 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. | +---------------+-------------------------------------------------+ Table 9 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. 7.1.4. Example 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 } 8. Reference System Meta-Types 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": } [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. 8.1. The 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. | +---------------+-------------------------------------+ Table 10 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). 9. Temporal Reference Annotations 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. 9.1. The 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. | +--------------+--------------------------------------------+ Table 11 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 } 9.2. The 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. 9.2.1. The 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. 9.2.2. The 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. | +-------+-------------------------------------+ Table 12 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. 9.3. The 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. 9.3.1. The reference Property reference MUST identify one temporal reference definition. Where kind is type it MUST be a type reference { "$ref": } [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. 9.3.2. The 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). | +------------------+----------------------------------------+ Table 13 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. 9.3.3. The 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. 9.3.4. The 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. | +----------+---------------------------------------------+ Table 14 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. 9.3.5. Type Compatibility 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, | Core datetime, date, | | following [RFC3339] | 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 | +----------------------------------------+-------------------------+ Table 15 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. 9.4. The 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. 9.4.1. The 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. | +-----------+----------------------------------------------------+ Table 16 9.4.2. The 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. 10. Spatial Reference Annotations 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. 10.1. The 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. 10.1.1. The 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": } [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. 10.1.2. The 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). | +---------+------------------------------------------+ Table 17 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. 10.1.3. The 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 } 10.1.4. Vertical and Compound Systems 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. 10.2. The 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. 10.2.1. The 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. 10.2.2. The 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. 10.2.3. The 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. | +---------------+--------------------------------------------+ Table 18 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. 10.2.4. Multiple Frames and Shared Components Two elements MAY cite the same reference, which is how a record reporting two distinct vector quantities in one frame is written. A property MAY be named by more than one element, which asserts that the frames share that axis and that the value is the same component in both, and is how one quantity expressed in two frames is written. The assertion is the schema author's, and a processor MUST NOT infer a shared axis from a shared property, from the names of the frames, or from equal values in samples. Two elements that name one property MUST declare the same variance, or omit it in both. Components of differing variance along one shared axis agree under a rotation and part company under any other change of frame, so one value cannot stand for both. A property MAY also be named both by vectorReferenceFrames and by the coordinateReferenceSystem of the same object or tuple, as a position vector is. Where it is, the unit established by the coordinate axis and the unit borne by the components of the vector MUST be mutually convertible. vectorReferenceFrames binds vector quantities and nothing else. A rotation, a stress, a strain, or any other quantity whose components take more than one axis index is bound by tensorReferenceFrames (Section 10.3), since the declared order of a single frame cannot state which axis each component belongs to. A quaternion carrying a rotation is bound by neither keyword: three of its four members lie along the axes of a frame and the fourth does not, so no ordered list of components states its binding. A reference that does not resolve leaves the frame indeterminate. It does not make the annotation incorrect. In the following example a satellite navigation receiver reports a position and a velocity. EPSG:4979 fixes the position; two of its three axes are angles, so it cannot serve as a vector frame, and the velocity cites EPSG:4978, whose axes are geocentric X, Y, and Z. Re- expressing the record in another system would move the position by the whole of the transformation and would turn the velocity by its rotation alone, which is the distinction the two keywords carry. { "name": "NavigationFix", "type": "object", "coordinateReferenceSystem": { "reference": "http://www.opengis.net/def/crs/EPSG/0/4979", "kind": "ogc-crs", "coordinates": ["lat", "lon", "height"] }, "vectorReferenceFrames": [ { "reference": "http://www.opengis.net/def/crs/EPSG/0/4978", "kind": "ogc-crs", "components": ["vel_x", "vel_y", "vel_z"] } ], "properties": { "lat": { "type": "double", "unit": "deg" }, "lon": { "type": "double", "unit": "deg" }, "height": { "type": "double", "unit": "m" }, "vel_x": { "type": "double", "unit": "m/s" }, "vel_y": { "type": "double", "unit": "m/s" }, "vel_z": { "type": "double", "unit": "m/s" } }, "required": ["lat", "lon", "height", "vel_x", "vel_y", "vel_z"], "additionalProperties": false } Most frames have no registered identifier. In the next example a magnetic field is reported in two such frames, each written as a meta-type. The frames share their first axis, so the schema reports that component once, under bx, and names it in both elements. The remaining components differ between the frames and are carried separately. { "name": "SolarWindSample", "type": "object", "vectorReferenceFrames": [ { "reference": { "$ref": "#/definitions/GseFrame" }, "kind": "type", "components": ["bx", "by_gse", "bz_gse"] }, { "reference": { "$ref": "#/definitions/GsmFrame" }, "kind": "type", "components": ["bx", "by_gsm", "bz_gsm"] } ], "properties": { "bx": { "type": "double", "unit": "nT" }, "by_gse": { "type": "double", "unit": "nT" }, "bz_gse": { "type": "double", "unit": "nT" }, "by_gsm": { "type": "double", "unit": "nT" }, "bz_gsm": { "type": "double", "unit": "nT" } }, "required": ["bx", "by_gse", "bz_gse", "by_gsm", "bz_gsm"], "additionalProperties": false, "definitions": { "GseFrame": { "name": "GseFrame", "type": "tuple", "description": "Geocentric Solar Ecliptic frame.", "properties": { "x": { "type": "double", "description": "Earth towards the Sun." }, "y": { "type": "double", "description": "In the ecliptic plane, towards dusk, completing a right-handed set." }, "z": { "type": "double", "description": "Parallel to the ecliptic pole, positive north." } }, "tuple": ["x", "y", "z"] }, "GsmFrame": { "name": "GsmFrame", "type": "tuple", "description": "Geocentric Solar Magnetospheric frame.", "properties": { "x": { "type": "double", "description": "Earth towards the Sun." }, "y": { "type": "double", "description": "Perpendicular to the geomagnetic dipole axis, completing a right-handed set." }, "z": { "type": "double", "description": "In the plane of x and the geomagnetic dipole axis, positive towards the northern magnetic pole." } }, "tuple": ["x", "y", "z"] } } } The frames are written separately because they differ, and they share their first axis because both take x from the Earth towards the Sun. The axis directions are those the NASA Satellite Situation Center states for these two frames [SSC-COORDS]. This specification does not define a frame, an epoch, or a transformation between frames. Those definitions come from the referenced authority or from the meta-type the schema itself supplies. 10.3. The 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. 10.3.1. The 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. 10.3.2. The 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. 10.4. The 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. 10.4.1. The Conventions This Document Fixes 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. 10.4.2. The 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. 10.4.3. The 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. | +-------------------+----------------------------------------+ Table 19 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. 10.4.4. The 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. 10.4.4.1. Quaternions 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 } 10.4.4.2. Axis and Angle 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. 10.4.4.3. Euler Angles 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]. 10.4.5. The 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 | +--------------------+------------------------+------------------+ Table 20 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]. 10.4.6. The 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. 10.5. The 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. 10.5.1. The reference Property reference MUST identify one linear reference system. Where kind is type it MUST be a type reference { "$ref": } [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. 10.5.2. The 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). | +-------------+---------------------------------------------------+ Table 21 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. 10.5.3. The 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. 10.5.4. The 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. 10.5.5. The 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. 10.5.6. The 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 } 11. Colorimetric Reference Annotations 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. 11.1. The 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. 11.1.1. The 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": } [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 | The primaries, the white point, and the | | are additive primaries | transfer function. | +------------------------+-----------------------------------------+ | A space whose channels | The reference white, and the illuminant | | are tristimulus | and the standard colorimetric observer, | | quantities, or are | or else the schema declares them under | | computed from them | illuminant and observer. | +------------------------+-----------------------------------------+ | A set of device | The device, medium, and process the | | control values | values drive, and the measurement | | | conditions under which the result was | | | characterized. | +------------------------+-----------------------------------------+ Table 22 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. | +--------------+--------------------------------------------+ Table 23 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). 11.1.2. The 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. 11.1.3. The 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. 11.1.4. The 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. | +---------+----------------------------------------------------+ Table 24 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 } 11.1.5. The 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. | +---------------+--------------------------------------+ Table 25 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. 11.1.6. The 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. | +-----------+-----------------------------------------------------+ Table 26 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]. 11.1.7. The 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. 11.1.8. Example 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. 12. Signal Channel Annotations 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. 12.1. The 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. 12.1.1. The 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": } [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. | +------+---------------------------------------------+ Table 27 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. 12.1.2. The 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. 12.1.3. The 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. 12.1.4. The 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. | +--------+-------------------------------------------------+ Table 28 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. 12.1.5. Example 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 } 12.2. The 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. 12.2.1. The 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": } [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. | +--------+-----------------------------------------+ Table 29 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. 12.2.2. The 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. 12.2.3. The 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. | +---------------+----------------------------------------------+ Table 30 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. 12.2.4. Example 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 } 13. Conformance 13.1. Check Outcomes 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. 13.2. Schema Conformance 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. 13.3. Processing Conformance 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. 13.4. Inheritance and Imports 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. 14. Extension Meta-Schema 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. 15. Security and Privacy Considerations 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. 16. IANA Considerations This document has no IANA actions. 17. References 17.1. Normative References [ISO19108] International Organization for Standardization, "ISO 19108:2002 Geographic information - Temporal schema", 2002, . [ISO19111] International Organization for Standardization, "ISO 19111:2019 Geographic information - Referencing by coordinates", 2019, . [ISO19148] International Organization for Standardization, "ISO 19148:2021 Geographic information - Linear referencing", 2021, . [ISO19156] International Organization for Standardization, "ISO 19156:2023 Geographic information - Observations, measurements and samples", 2023, . [JSTRUCT-CORE] Vasters, C., "JSON Structure Core", n.d., . [JSTRUCT-UNITS] Vasters, C., "JSON Structure: Symbols, Scientific Units, and Currencies", n.d., . [JSTRUCT-VALIDATION] Vasters, C., "JSON Structure: Validation", n.d., . [OGC-NAMES] Open Geospatial Consortium, "OGC Name Type Specification - definitions - part 1 - basic name", n.d., . [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, . [RFC3339] Klyne, G. and C. Newman, "Date and Time on the Internet: Timestamps", RFC 3339, DOI 10.17487/RFC3339, July 2002, . [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform Resource Identifier (URI): Generic Syntax", STD 66, RFC 3986, DOI 10.17487/RFC3986, January 2005, . [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, . 17.2. Informative References [CCSDS-ADM] Consultative Committee for Space Data Systems, "Attitude Data Messages, Recommended Standard, CCSDS 504.0-B-2, Blue Book", January 2024, . [CCSDS-ADM1] Consultative Committee for Space Data Systems, "Attitude Data Messages, Recommended Standard, CCSDS 504.0-B-1, Blue Book, including Technical Corrigendum 1 (July 2015)", May 2008, . [CF-STANDARD-NAMES] CF Conventions, "CF Standard Name Table", n.d., . [CIE015] International Commission on Illumination, "CIE 015:2018 Colorimetry, 4th Edition", 2018, . [CSS-COLOR-4] World Wide Web Consortium, "CSS Color Module Level 4", n.d., . [DCTERMS] Dublin Core Metadata Initiative, "DCMI Metadata Terms", n.d., . [EBU-R128] European Broadcasting Union, "EBU R 128: Loudness normalisation and permitted maximum level of audio signals", November 2023, . [EPSG] International Association of Oil and Gas Producers, "EPSG Geodetic Parameter Dataset", n.d., . [FHWA-ARNOLD] Federal Highway Administration, "All Road Network of Linear Referenced Data", n.d., . [FHWA-HPMS] Federal Highway Administration, "Highway Performance Monitoring System Field Manual", n.d., . [GCMT-NDK] Global CMT Project, "Explanation of the ndk file format used for the Global Centroid-Moment-Tensor catalog", 2006, . [IANA-LANGTAGS] Internet Assigned Numbers Authority, "Language Subtag Registry", n.d., . [IANA-PROTOCOLS] Internet Assigned Numbers Authority, "Protocol Registries", n.d., . [IATA-CODES] International Air Transport Association, "Airline and Airport Code Search", n.d., . [ICAO7910] International Civil Aviation Organization, "Location Indicators (Doc 7910)", n.d., . [ICAO8643] International Civil Aviation Organization, "Aircraft Type Designators (Doc 8643)", n.d., . [ICC-REGISTRY] International Color Consortium, "ICC Characterization Data Registry", n.d., . [ICC-SPEC] International Color Consortium, "Image technology colour management - Architecture, profile format, and data structure (ISO 15076-1:2025, ICC.1:2022, profile version 4.4)", 2022, . [IEC61672-1] International Electrotechnical Commission, "IEC 61672-1:2013 Electroacoustics - Sound level meters - Part 1: Specifications", 2013, . [ISO639] International Organization for Standardization, "ISO 639: Code for individual languages and language groups", n.d., . [ISO1683] International Organization for Standardization, "ISO 1683:2015 Acoustics - Preferred reference values for acoustical and vibratory levels", 2015, . [ISO3166] International Organization for Standardization, "ISO 3166: Codes for the representation of names of countries and their subdivisions", n.d., . [ISO4217] International Organization for Standardization, "ISO 4217: Codes for the representation of currencies", n.d., . [ISO11664-4] International Commission on Illumination, "Colorimetry - Part 4: CIE 1976 L*a*b* colour space (ISO/CIE 11664-4:2019)", 2019, . [ITU-BS1770] International Telecommunication Union, "Recommendation ITU-R BS.1770-5: Algorithms to measure audio programme loudness and true-peak audio level", November 2023, . [ITU-BS2051] International Telecommunication Union, "Recommendation ITU-R BS.2051-3: Advanced sound system for programme production", May 2022, . [ITU-BS2076] International Telecommunication Union, "Recommendation ITU-R BS.2076: Audio Definition Model", n.d., . [ITU-G711] International Telecommunication Union, "Recommendation ITU-T G.711: Pulse code modulation (PCM) of voice frequencies", November 1988, . [ITU-H273] International Telecommunication Union, "Recommendation ITU-T H.273: Coding-independent code points for video signal type identification", July 2024, . [JSTRUCT-ALTNAMES] Vasters, C., "JSON Structure: Alternate Names and Descriptions", n.d., . [JSTRUCT-IMPORT] Vasters, C., "JSON Structure: Import", n.d., . [JSTRUCT-RELATIONS] Vasters, C., "JSON Structure: Relations", n.d., . [KITTI] Geiger, A., Lenz, P., Stiller, C., and R. Urtasun, "Vision meets Robotics: The KITTI Dataset", 2013, . [LOINC] Regenstrief Institute, "LOINC", n.d., . [NAIF-QUAT] NASA Jet Propulsion Laboratory, Navigation and Ancillary Information Facility, "Quaternions White Paper", 30 November 2003, . [OGC-TOPIC2] Open Geospatial Consortium, "OGC Abstract Specification Topic 2: Referencing by coordinates", 2019, . [OGC-TOPIC25] Open Geospatial Consortium, "OGC Abstract Specification Topic 25: Abstract Conceptual Model for Time", n.d., . [OWL2] World Wide Web Consortium, "OWL 2 Web Ontology Language Document Overview", n.d., . [PNG3] World Wide Web Consortium, "Portable Network Graphics (PNG) Specification (Third Edition)", 24 June 2025, . [PROV-O] World Wide Web Consortium, "PROV-O: The PROV Ontology", n.d., . [QUDT] QUDT.org, "QUDT Ontologies", n.d., . [RDF-CONCEPTS] World Wide Web Consortium, "RDF 1.1 Concepts and Abstract Syntax", n.d., . [RDF-SCHEMA] World Wide Web Consortium, "RDF Schema 1.1", n.d., . [REP-103] Foote, T. and M. Purvis, "REP 103: Standard Units of Measure and Coordinate Conventions", 7 October 2010, . [SKOS] World Wide Web Consortium, "SKOS Simple Knowledge Organization System Reference", n.d., . [SNOMED-CT] SNOMED International, "SNOMED CT", n.d., . [SOSA-SSN] World Wide Web Consortium, "Semantic Sensor Network Ontology", n.d., . [SPASE] SPASE Consortium, "Space Physics Archive Search and Extract (SPASE) Data Model, Version 2.7.2", 2026, . [SSC-COORDS] NASA Goddard Space Flight Center, "Satellite Situation Center Users Guide, Appendix C: Description of Selected Coordinate Systems", n.d., . [UNLOCODE] United Nations Economic Commission for Europe, "UN/LOCODE: United Nations Code for Trade and Transport Locations", n.d., . [UNSPSC] United Nations Global Marketplace, "United Nations Standard Products and Services Code (UNSPSC)", n.d., . [USGS-LANDSAT] United States Geological Survey, "What are the band designations for the Landsat satellites?", n.d., . [USGS-LANDSAT-L1] United States Geological Survey, "Using the USGS Landsat Level-1 Data Product", n.d., . [WHO-ATC] WHO Collaborating Centre for Drug Statistics Methodology, "ATC/DDD Index", n.d., . [WHO-ICD] World Health Organization, "International Classification of Diseases (ICD)", n.d., . [WMO-CODES] World Meteorological Organization, "WMO Codes Registry (WMO No. 306 Manual on Codes)", n.d., . [WSDOT-LRS] Washington State Department of Transportation, "State Route Linear Referencing System", n.d., . Appendix A. Reference URIs (Informative) 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.1. Vocabularies 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#. A.2. Temporal Reference Systems 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. A.3. Coordinate Reference Systems 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. A.4. Vector Reference Frames 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. A.5. Linear Reference Systems 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.6. Code Lists 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]. * loinc: the LOINC code system, http://loinc.org [LOINC]. * 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]. * atc: https://atcddd.fhi.no/atc_ddd_index/ [WHO-ATC]. * unspsc: https://www.ungm.org/Public/UNSPSC [UNSPSC]. 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": }, not a URI. Changes from draft-vasters-json-structure-semantic-annotations-00 * Initial version. Acknowledgments The author thanks the JSON Structure community for review and feedback. Author's Address Clemens Vasters Microsoft Corporation Email: clemensv@microsoft.com