The CoverageJSON Format Specification

WORK-IN-PROGRESS

Authors Maik Riechert (University of Reading), Jon Blower (University of Reading)
Revision 0.2-draft
Date xx yy 2016
Abstract CoverageJSON is a geospatial coverage interchange format based on JavaScript Object Notation (JSON).
Copyright Copyright © 2015 by ... . This work is licensed under a Creative Commons Attribution 4.0 International License.

TODO

The following items are (major) outstanding issues to be resolved for the first version:

Contents

1. Introduction

CoverageJSON is a format for encoding coverage data like grids, time series, and vertical profiles, distinguished by the geometry of their spatiotemporal domain. A CoverageJSON object represents a domain, a range, a coverage, or a collection of coverages. A range in CoverageJSON represents coverage values. A coverage in CoverageJSON is the combination of a domain, parameters, ranges, and additional metadata. A coverage collection represents a list of coverages.

A complete CoverageJSON data structure is always an object (in JSON terms). In CoverageJSON, an object consists of a collection of name/value pairs – also called members. For each member, the name is always a string. Member values are either a string, number, object, array or one of the literals: true, false, and null. An array consists of elements where each element is a value as described above.

1.1. Example

A CoverageJSON grid coverage of global air temperature:

{
  "type" : "Coverage",
  "domain" : {
    "type": "Domain",
    "domainType": "Grid",
    "axes": {
      "x": { "start": -179.5, "stop": 179.5, "num": 360 },
      "y": { "start": -89.5, "stop": 89.5, "num": 180 },
      "t": { "values": ["2013-01-13T00:00:00Z"] }
    },
    "referencing": [{
      "coordinates": ["x","y"],
      "system": {
        "type": "GeographicCRS",
        "id": "http://www.opengis.net/def/crs/OGC/1.3/CRS84"        
      }
    }, {
      "coordinates": ["t"],
      "system": {
        "type": "TemporalRS",
        "calendar": "Gregorian"
      }
    }]
  },
  "parameters" : {
    "TEMP": {
      "type" : "Parameter",
      "description" : {
        "en": "The air temperature measured in degrees Celsius."
      },
      "unit" : {
        "label": {
          "en": "Degree Celsius"
        },
        "symbol": {
          "value": "Cel",
          "type": "http://www.opengis.net/def/uom/UCUM/"
        }
      },
      "observedProperty" : {
        "id" : "http://vocab.nerc.ac.uk/standard_name/air_temperature/",
        "label" : {
          "en": "Air temperature",
          "de": "Lufttemperatur"
        }
      }
    }
  },
  "ranges" : {
    "TEMP" : "http://example.com/coverages/123/TEMP"
  }
}

where "http://example.com/coverages/123/TEMP" points to the following document:

{
  "type" : "NdArray",
  "dataType": "float",
  "axisNames": ["t","y","x"],
  "shape": [1, 180, 360],
  "values" : [ 27.1, 24.1, null, 25.1, ... ]
}

Range data can also be directly embedded into the main CoverageJSON document, making it stand-alone.

1.2. Differences to OGC Coverage Implementation Schema (CIS)

The candidate OGC standard Coverage Implementation Schema 1.1 (short CIS) defines a coverage model targeted towards OGC service types like Web Coverage Service (WCS) and is the successor of the “GML 3.2.1 Application Schema – Coverages” version 1.0 (short GMLCOV).

The model of CoverageJSON can be seen as a mix of CIS and the data cube-based NetCDF file format.

The following lists some areas where the model used by CoverageJSON departs from CIS:

1.3. Definitions

2. i18n Objects

An i18n object represents a string in multiple languages where each key is a language tag as defined in BCP 47, and the value is the string in that language. The special language tag "und" can be used to identify a value whose language is unknown or undetermined.

Example:

{
  "en": "Temperature",
  "de": "Temperatur"
}

3. Parameter Objects

Parameter objects represent metadata about the values of the coverage in terms of the observed property (like water temperature), the units, and others.

Example for a continuous-data parameter:

{
  "type" : "Parameter",
  "description" : {
    "en": "The sea surface temperature in degrees Celsius."
  },
  "observedProperty" : {
    "id" : "http://vocab.nerc.ac.uk/standard_name/sea_surface_temperature/",
    "label" : {
      "en": "Sea Surface Temperature"
    },
    "description" : {
      "en": "The temperature of sea water near the surface (including the part under sea-ice, if any), and not the skin temperature."
    }
  },
  "unit" : {
    "label" : {
      "en": "Degree Celsius"
    },
    "symbol": {
      "value": "Cel",
      "type": "http://www.opengis.net/def/uom/UCUM/"
    }
  }
}

Example for a categorical-data parameter:

{
  "type" : "Parameter",
  "description" : {
    "en": "The land cover category."
  },
  "observedProperty" : {
    "id" : "http://example.com/land_cover",
    "label" : {
      "en": "Land Cover"
    },
    "description" : {
      "en": "longer description..."
    },
    "categories": [{
      "id": "http://example.com/land_cover/categories/grass",
      "label": {
        "en": "Grass"
      },
      "description": {
        "en": "Very green grass."
      }
    }, {
      "id": "http://example.com/land_cover/categories/forest",
      "label": {
        "en": "Forest"
      }
    }]
  },
  "categoryEncoding": {
    "http://example.com/land_cover/categories/grass": 1,
    "http://example.com/land_cover/categories/forest": [2,3]
  }
}

4. ParameterGroup Objects

Parameter group objects represent logical groups of parameters, for example vector quantities.

Example of a group describing a vector quantity:

{
  "type": "ParameterGroup",
  "observedProperty": {
    "label": {
      "en": "Wind velocity"
    }
  },
  "members": ["WIND_SPEED", "WIND_DIR"]
}

where "WIND_SPEED" and "WIND_DIR" reference existing parameters in a CoverageJSON coverage or collection object by their short identifiers.

Example of a group describing uncertainty of a parameter:

{
  "type": "ParameterGroup",
  "label": {
    "en": "Daily sea surface temperature with uncertainty information"
  },
  "observedProperty": {
    "id": "http://vocab.nerc.ac.uk/standard_name/sea_surface_temperature/",
    "label": {
      "en": "Sea surface temperature"
    }
  },
  "members": ["SST_mean", "SST_stddev"]
}

where "SST_mean" references the following parameter:

{
  "type" : "Parameter",
  "observedProperty" : {
    "label" : {
      "en": "Sea surface temperature daily mean"
    },
    "statisticalMeasure": "http://www.uncertml.org/statistics/mean",
    "statisticalPeriod": "P1D",
    "narrowerThan": ["http://vocab.nerc.ac.uk/standard_name/sea_surface_temperature/"]
  },
  "unit" : {
    "label": {
      "en": "Kelvin"
    },
    "symbol": {
      "value": "K",
      "type": "http://www.opengis.net/def/uom/UCUM/"
    }
  }
}

and "SST_stddev":

{
  "type" : "Parameter",
  "observedProperty" : {
    "label" : {
      "en": "Sea surface temperature standard deviation of daily mean"
    },
    "statisticalMeasure": "http://www.uncertml.org/statistics/standard-deviation",
    "narrowerThan": ["http://vocab.nerc.ac.uk/standard_name/sea_surface_temperature/"]
  },
  "unit" : {
    "label": {
      "en": "Kelvin"
    },
    "symbol": {
      "value": "K",
      "type": "http://www.opengis.net/def/uom/UCUM/"
    }
  }
}

5. Reference system objects

Reference system objects are used to provide information about how to interpret coordinate values within the domain. Coordinates are usually geospatial or temporal in nature, but may also be categorical (based on identifiers). All reference system objects MUST have a member "type", the possible values of which are given in the sections below. Custom values MAY be used as detailed in the “Extensions” section below.

5.1. Geospatial Coordinate Reference Systems

Geospatial coordinate reference systems (CRSs) link coordinate values to the Earth.

5.1.1 Geographic Coordinate Reference Systems

Geographic CRSs anchor coordinate values to an ellipsoidal approximation of the Earth. They have coordinate axes of geodetic longitude and geodetic latitude, and perhaps height above the ellipsoid (i.e. they can be two- or three-dimensional). The origin of the CRS is on the surface of the ellipsoid.

Note that sometimes (e.g. for numerical model data) the exact CRS may not be known or may be undefined. In this case the "id" may be omitted, but the "type" still indicates that this is a geographic CRS. Therefore clients can still use geodetic longitude, geodetic latitude (and maybe height) axes, even if they can’t accurately georeference the information.

If a Coverage conforms to one of the defined domain types then the coordinate identifier "x" is used to denote geodetic longitude, "y" is used for geodetic latitude and z for ellipsoidal height.

Example of a two-dimensional geographic CRS (longitude-latitude):

{
  "type": "GeographicCRS",
  "id": "http://www.opengis.net/def/crs/OGC/1.3/CRS84"
}

Example of a three-dimensional geographic CRS (latitude-longitude-height):

{
  "type": "GeographicCRS",
  "id": "http://www.opengis.net/def/crs/EPSG/0/4979"
}

5.1.2 Projected Coordinate Reference Systems

Projected CRSs use two coordinates to denote positions on a Cartesian plane, which is derived from projecting the ellipsoid according to some defined transformation.

If a Coverage conforms to one of the defined domain types then the coordinate identifier "x" is used to denote easting and "y" is used for northing.

Example of a projected CRS (here British National Grid):

{
  "type": "ProjectedCRS",
  "id": "http://www.opengis.net/def/crs/EPSG/0/27700"
}

5.1.3 Vertical Coordinate Reference Systems

Vertical CRSs use a single coordinate to denote some measure of height or depth, usually approximately oriented with gravity.

Example of a vertical CRS, here representing height above the NAV88 datum:

{
  "type": "VerticalCRS",
  "id": "http://www.opengis.net/def/crs/EPSG/0/5703"
}

5.1.4 Providing inline definitions of CRSs

Sometimes there may be no well-known identifier for a geospatial CRS. Or the data provider may wish to make the CoverageJSON file more self-contained by avoiding external lookups. In this case a full inline definition of the CRS in JSON (instead of, or in addition to the "id"). This has not yet been fully defined in this specification, but we recommend following the OGC Well-Known Text (WKT) structure, for example:

{
  "type": "VerticalCRS",
  "id": "http://www.opengis.net/def/crs/EPSG/0/5703",
  "datum": {
    "id": "http://www.opengis.net/def/datum/EPSG/0/5103",
    "label": {
      "en": "North American Vertical Datum 1988"
    }
  },
  "cs": {
    "id": "http://www.opengis.net/def/cs/EPSG/0/6499",
    "csAxes": [{
      "id": "http://www.opengis.net/def/axis/EPSG/0/114",
      "name": {
        "en": "Gravity-related height"
      },
      "direction": "up",
      "unit": {
        "symbol": "m"
      }
    }]
  }
}

In future work, a mapping from OGC WKT2 to JSON may be defined, and may be adopted into the CoverageJSON specification.

5.2. Temporal Reference Systems

Time is referenced by a temporal reference system (temporal RS). In this specification, only a string-based notation for time values is defined.

Example:

{
  "type": "TemporalRS",
  "calendar": "Gregorian"
}

5.3. Identifier-based Reference Systems

Identifier-based reference systems (identifier RS) .

Example of a geographic identifier reference system:

{
  "type": "IdentifierRS",
  "id": "https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2",
  "label": { "en": "ISO 3166-1 alpha-2 codes" },
  "targetConcept": {
    "id": "http://dbpedia.org/resource/Country",
    "label": {"en": "Country", "de": "Land" }
  },
  "identifiers": {
    "de": {
      "id": "http://dbpedia.org/resource/Germany",
      "label": { "de": "Deutschland", "en": "Germany" }
    },
    "gb": {
      "id": "http://dbpedia.org/resource/United_Kingdom",
      "label": { "de": "Vereinigtes Königreich", "en": "United Kingdom" }
    }
  }
}

The domain values in the above example would be "de" and "gb".

6. CoverageJSON Objects

CoverageJSON documents always consist of a single object. This object (referred to as the CoverageJSON object below) represents a domain, range, coverage, or collection of coverages.

6.1. Domain Objects

A domain object is a CoverageJSON object which defines a set of positions and their extent in one or more referencing systems. Its general structure is:

{
  "type": "Domain",
  "domainType": "...",
  "axes": { ... },
  "referencing": [...]
}

6.1.1. Axis Objects

Example of an axis object with bounds:

{
  "values": [20,21],
  "bounds": [19.5,20.5,
             20.5,21.5]
}

Example of an axis object with regular axis encoding:

{
  "start": 0,
  "stop": 5,
  "num": 6
}

The axis values in the above example are equal to "values": [0,1,2,3,4,5].

Example of an axis object with tuple values:

{
  "dataType": "tuple",
  "coordinates": ["t","x","y"],  
  "values": [
    ["2008-01-01T04:00:00Z",1,20],
    ["2008-01-01T04:30:00Z",2,21]
  ]
}

Example of an axis object with Polygon values:

{
  "dataType": "polygon",
  "coordinates": ["x","y"],
  "values": [
    [ [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ]  ]
  ]
}

6.1.2. Reference System Connection Objects

A reference system connection object creates a link between values within domain axes and a reference system to be able to interpret those values, e.g. as coordinates in a certain coordinate reference system.

Example of a reference system connection object:

{
  "coordinates": ["y","x","z"],
  "system": {
    "type": "GeographicCRS",
    "id": "http://www.opengis.net/def/crs/EPSG/0/4979"
  }
}

6.1.3. Examples

Example of a domain object with "Grid" domain type:

{
  "type": "Domain",
  "domainType": "Grid",
  "axes": {
    "x": { "values": [1,2,3] },
    "y": { "values": [20,21] },
    "z": { "values": [1] },
    "t": { "values": ["2008-01-01T04:00:00Z"] }
  },
  "referencing": [{
    "coordinates": ["t"],
    "system": {
      "type": "TemporalRS",
      "calendar": "Gregorian"
    }
  }, {
    "coordinates": ["y","x","z"],
    "system": {
      "type": "GeographicCRS",
      "id": "http://www.opengis.net/def/crs/EPSG/0/4979"
    }
  }]
}

Example of a domain object with "Trajectory" domain type:

{
  "type": "Domain",
  "domainType": "Trajectory",
  "axes": {
    "composite": {
      "dataType": "tuple",
      "coordinates": ["t","x","y"],
      "values": [
        ["2008-01-01T04:00:00Z", 1, 20],
        ["2008-01-01T04:30:00Z", 2, 21]
      ]
    }
  },
  "referencing": [{
    "coordinates": ["t"],
    "system": {
      "type": "TemporalRS",
      "calendar": "Gregorian"
    }
  }, {
    "coordinates": ["x","y"],
    "system": {
      "type": "GeographicCRS",
      "id": "http://www.opengis.net/def/crs/OGC/1.3/CRS84"
    }
  }]
}

6.2. NdArray Objects

A CoverageJSON object with the type "NdArray" is an NdArray object. It represents a multidimensional (>= 0D) array with named axes, encoded as a flat one-dimensional array in row-major order.

Example:

{
  "type": "NdArray",
  "dataType": "float",
  "shape": [4, 2],
  "axisNames": ["y", "x"],
  "values": [
    12.3, 12.5, 11.5, 23.1,
    null, null, 10.1, 9.1
  ]  
}

6.3. TiledNdArray Objects

A CoverageJSON object with the type "TiledNdArray" is a TiledNdArray object. It represents a multidimensional (>= 1D) array with named axes that is split up into sets of linked NdArray documents. Each tileset typically covers a specific data access scenario, for example, loading a single time slice of a grid vs. loading a time series of a spatial subset of a grid.

Example:

{
  "type" : "TiledNdArray",
  "dataType": "integer",
  "axisNames": ["t", "y", "x"],
  "shape": [2, 5, 10],
  "tileSets": [{
    "tileShape": [null, null, null],
    "urlTemplate": "http://example.com/a/all.covjson"
  }, {
    "tileShape": [1, null, null],
    "urlTemplate": "http://example.com/b/{t}.covjson"
  }, {
    "tileShape": [null, 2, 3],
    "urlTemplate": "http://example.com/c/{y}-{x}.covjson"
  }]
}

http://example.com/a/all.covjson:

{
  "type": "NdArray",
  "dataType": "integer",
  "axisNames": ["t", "y", "x"],
  "shape": [2, 5, 10],
  "values": [
     1,  2,  3,  4,  5,  6,  7,  8,  9, 10,
    11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
    21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
    31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50,

    51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
    61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
    71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
    81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
    91, 92, 93, 94, 95, 96, 97, 98, 99, 100
  ]
}

http://example.com/b/0.covjson:

{
  "type": "NdArray",
  "dataType": "integer",
  "axisNames": ["t", "y", "x"],
  "shape": [1, 5, 10],
  "values": [
     1,  2,  3,  4,  5,  6,  7,  8,  9, 10,
    11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
    21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
    31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50
  ]
}

http://example.com/c/0-0.covjson:

{
  "type": "NdArray",
  "dataType": "integer",
  "axisNames": ["t", "y", "x"],
  "shape": [2, 2, 3],
  "values": [
     1,  2,  3,
    11, 12, 13,

    51, 52, 53,
    61, 62, 63
  ]
}

http://example.com/c/0-3.covjson:

{
  "type": "NdArray",
  "dataType": "integer",
  "axisNames": ["t", "y", "x"],
  "shape": [2, 2, 1],
  "values": [
    10,
    20,

    60,
    70
  ]
}

6.4. Coverage Objects

A CoverageJSON object with the type "Coverage" is a coverage object.

Example:

See the Vertcal Profile Coverage Example

6.5. Coverage Collection Objects

A CoverageJSON object with the type "CoverageCollection" is a coverage collection object.

Example:

See the Coverage Collection Example

7. Extensions

A CoverageJSON document can be extended with custom members and types in a robust and interoperable way. For that, it makes use of absolute URIs and compact URIs (prefix:suffix) in order to avoid conflicts with other extensions and future versions of the format. A central registry of compact URI prefixes is provided which anyone can extend and which is a simple mapping from compact URI prefix to namespace URI in order to avoid collisions with other extensions that are based on compact URIs as well. Extensions that do not follow this approach MAY use simple names instead of absolute or compact URIs but have to accept the consequence of the document being less interoperable and future-proof. In certain use cases this is not an issue and may be a preferred solution for simplicity reasons, for example, if such CoverageJSON documents are only used internally and are not meant to be shared to a wider audience.

7.1. Custom members

If a custom member is added to a CoverageJSON document, its name SHOULD be a compact URIs of the form "prefix:suffix".

Example:

{
  "type" : "Coverage",
  "dct:license": "https://creativecommons.org/licenses/by/4.0/",
  ...
}

The prefix SHOULD be registered at https://covjson.org/prefixes/ which in the example above would be dct = http://purl.org/dc/terms/.

If the value of a custom member can have multiple structures, for example a string or an object, then a client should ignore the member if it does not understand the structure that is used.

Example of a different value structure:

{
  "type" : "Coverage",
  "dct:license": {
    "id": "https://creativecommons.org/licenses/by/4.0/",
    "label": {
      "en": "Creative Commons Attribution 4.0 International License"
    }
  },
  ...
}

7.2. Custom types

Custom types MAY be used with the following members:

The custom value of those members SHOULD be either an absolute URI or a compact URI. If a compact URI is used, then the prefix SHOULD be registered at https://covjson.org/prefixes/.

Example of a custom unit symbol type using an absolute URI:

{
  "type" : "Parameter",
  "unit" : {
    "symbol": {
      "value": "degreeC",
      "type": "http://www.opengis.net/def/uom/UDUNITS/"
    }
  },
  "observedProperty" : {
    "label" : {
      "en": "Air temperature"
    }
  }
}

Example of a custom reference system type using a compact URI:

{
  "type": "uor:HEALPixRS",
  "uor:h": 3,
  "uor:k": 3,
  "uor:ordering": "nested"
}

8. JSON-LD

If no JSON-LD context is given, then the default context https://covjson.org/context.jsonld SHALL be assumed. Note that this context includes registered namespace prefixes and MAY be updated in a backwards-compatible way as the format evolves.

Additional semantics not provided by the default context MAY be provided by specifying an explicit "@context" member in the root of a CoverageJSON document. The value of that member MUST be an array where the first element is the default context URL. Any additional context definitions SHALL NOT override definitions of the default context, except when the definition is identical.

Providing an explicit context is especially useful for extensions. A recommended practice is to include any used namespace prefixes, even if registered, in the explicit context. This provides additional clarity and helps humans understand the document more quickly.

It is NOT RECOMMENDED to use the explicit JSON-LD context to map simple names, for example, "license": "dct:license". On one side, this would hinder interoperability for generic non-JSON-LD clients, as they generally rely on absolute URIs or registered prefixes of compact URIs. On the other side, it would make documents less future-proof as there may be name collisions with future versions of the format where semantics of that name may be defined differently. It is therefore RECOMMENDED to use compact or absolute URIs if an explicit JSON-LD context is included.

Note that domain axis values and range values SHOULD NOT be exposed as linked data via the JSON-LD context since they are not suitable for such representation.

Example:

{
  "@context": [
    "https://covjson.org/context.jsonld",
    {
      "dct": "http://purl.org/dc/terms/",
      "dct:license": { "@type": "@id" }
    }
  ],
  "type" : "Coverage",
  "dct:license": "https://creativecommons.org/licenses/by/4.0/",
   ...
}

In this example, additional semantics for the registered dct prefix are provided by stating that the "dct:license" member value in this document is an identifier and not just an unstructured string.

9. Resolving domain and range URLs

If a domain or range is referenced by a URL in a CoverageJSON document, then the client should, whenever is appropriate, load the data from the given URL and treat the loaded data as if it was directly embedded in place of the URL. When sending HTTP requests, the Accept header SHOULD be set appropriately to the CoverageJSON media type.

10. Media Type and File Extension

The CoverageJSON media type SHALL be application/prs.coverage+json with an optional parameter profile which is a non-empty list of space-separated URIs identifying specific constraints or conventions that apply to a CoverageJSON document according to RFC6906. The only profile URI defined in this document is https://covjson.org/def/core#standalone which asserts that all domain and range objects are directly embedded in a CoverageJSON document and not referenced by URLs. There is no charset parameter and CoverageJSON documents MUST be serialized using the UTF-8 character encoding.

The file extension SHALL be covjson.

Appendix A. Coverage Examples

Vertical Profile Coverage

{
  "type" : "Coverage",
  "domain" : {
    "type" : "Domain",
    "domainType" : "VerticalProfile",
    "axes": {
      "x" : { "values": [-10.1] },
      "y" : { "values": [ -40.2] },
      "z" : { "values": [
              5.4562, 8.9282, 14.8802, 20.8320, 26.7836, 32.7350,
              38.6863, 44.6374, 50.5883, 56.5391, 62.4897, 68.4401,
              74.3903, 80.3404, 86.2902, 92.2400, 98.1895, 104.1389,
              110.0881, 116.0371, 121.9859 ] },
      "t" : { "values": ["2013-01-13T11:12:20Z"] }
    },
    "referencing": [{
      "coordinates": ["x","y"],
      "system": {
        "type": "GeographicCRS",
        "id": "http://www.opengis.net/def/crs/OGC/1.3/CRS84"
      }
    }, {
      "coordinates": ["z"],
      "system": {
        "type": "VerticalCRS",
        "cs": {
          "csAxes": [{
            "name": {
              "en": "Pressure"
            },
            "direction": "down",
            "unit": {
              "symbol": "Pa"
            }
          }]
        }
      }
    }, {
      "coordinates": ["t"],
      "system": {
        "type": "TemporalRS",
        "calendar": "Gregorian"
      }
    }]
  },
  "parameters" : {
    "PSAL": {
      "type" : "Parameter",
      "description" : {
        "en": "The measured salinity, in practical salinity units (psu) of the sea water "
      },
      "unit" : {
        "symbol" : "psu"
      },
      "observedProperty" : {
        "id" : "http://vocab.nerc.ac.uk/standard_name/sea_water_salinity/",
        "label" : {
          "en": "Sea Water Salinity"
        }
      }
    },
    "POTM": {
      "type" : "Parameter",
      "description" : {
        "en": "The potential temperature, in degrees celcius, of the sea water"
      },
      "unit" : {
        "symbol" : "°C"
      },
      "observedProperty" : {
        "id" : "http://vocab.nerc.ac.uk/standard_name/sea_water_potential_temperature/",
        "label" : {
          "en": "Sea Water Potential Temperature"
        }
      }
    }
  },
  "ranges" : {
    "PSAL" : {
      "type" : "NdArray",
      "dataType": "float",
      "shape": [21],
      "axisNames": ["z"],
      "values" : [ 43.9599, 43.9599, 43.9640, 43.9640, 43.9679, 43.9879, 44.0040,
                   44.0120, 44.0120, 44.0159, 44.0320, 44.0320, 44.0480, 44.0559,
                   44.0559, 44.0579, 44.0680, 44.0740, 44.0779, 44.0880, 44.0940 ]
    },
    "POTM" : {
      "type" : "NdArray",
      "dataType": "float",
      "shape": [21],
      "axisNames": ["z"],
      "values" : [ 23.8, 23.7, 23.5, 23.4, 23.2, 22.4, 21.8,
                   21.7, 21.5, 21.3, 21.0, 20.6, 20.1, 19.7,
                   19.4, 19.1, 18.9, 18.8, 18.7, 18.6, 18.5 ]
    }
  }
}

Coverage Collection

{
  "type" : "CoverageCollection",
  "domainType" : "VerticalProfile",
  "parameters" : {
    "PSAL": {
      "type" : "Parameter",
      "description" : {
        "en": "The measured salinity, in practical salinity units (psu) of the sea water"
      },
      "unit" : {
        "symbol" : "psu"
      },
      "observedProperty" : {
        "id": "http://vocab.nerc.ac.uk/standard_name/sea_water_salinity/",
        "label" : {
          "en": "Sea Water Salinity"
        }
      }
    }
  },
  "referencing": [{
    "coordinates": ["x","y"],
    "system": {
      "type": "GeographicCRS",
      "id": "http://www.opengis.net/def/crs/OGC/1.3/CRS84"
    }
  }, {
    "coordinates": ["z"],
    "system": {
      "type": "VerticalCRS",
      "cs": {
        "csAxes": [{
          "name": {
            "en": "Pressure"
          },
          "direction": "down",
          "unit": {
            "symbol": "Pa"
          }
        }]
      }
    }
  }, {
    "coordinates": ["t"],
    "system": {
      "type": "TemporalRS",
      "calendar": "Gregorian"
    }
  }],
  "coverages": [
    {
      "type" : "Coverage",
      "domain" : {
        "type": "Domain",
        "axes": {
          "x": { "values": [-10.1] },
          "y": { "values": [-40.2] },
          "z": { "values": [ 5, 8, 14 ] },
          "t": { "values": ["2013-01-13T11:12:20Z"] }
        }
      },
      "ranges" : {
        "PSAL" : {
          "type" : "NdArray",
          "dataType": "float",
          "shape": [3],
          "axisNames": ["z"],
          "values" : [ 43.7, 43.8, 43.9 ]
        }
      }
    }, {
      "type" : "Coverage",
      "domain" : {
        "type": "Domain",
        "axes": {
          "x": { "values": [-11.1] },
          "y": { "values": [-45.2] },
          "z": { "values": [ 4, 7, 9 ] },
          "t": { "values": ["2013-01-13T12:12:20Z"] }
        }
      },
      "ranges" : {
        "PSAL" : {
          "type" : "NdArray",
          "dataType": "float",
          "shape": [3],
          "axisNames": ["z"],
          "values" : [ 42.7, 41.8, 40.9 ]
        }
      }
    }]
}

Attribution

This specification uses ideas and sentence structures from the GeoJSON specification which is licensed under a Creative Commons Attribution 3.0 United States License.

Acknowledgements

This work was inspired by a demonstration of the concept by Joan Masó of CREAF.