Data may be taken over periods of time at a set of discrete point, spatial locations called stations (see also discussion in 9.1). The set of elements at a particular station is referred to as a timeSeries feature and a data variable may contain a collection of such features. The instance dimension in the case of timeSeries specifies the number of time series in the collection and is also referred to as the station dimension. The instance variables, which have just this dimension, including latitude and longitude for example, are also referred to as station variables and are considered to contain information describing the stations. The station variables may contain missing values, allowing one to reserve space for additional stations that may be added at a later time, as discussed in section 9.6. In addition,
It is strongly recommended that there should be a station variable (which may be of any type) with the attribute cf_role=”timeseries_id”
, whose values uniquely identify the stations.
It is recommended that there should be station variables with standard_name attributes "platform_name
", "surface_altitude
" and “platform_id
” when applicable.
All the representations described in section 9.3 can be used for time series. The global attribute featureType=”timeSeries”
(case-insensitive) must be included.
If the time series instances have the same number of elements and the time values are identical for all instances, you may use the orthogonal multidimensional array representation. This has either a one-dimensional coordinate variable, time(time), provided the time values are ordered monotonically, or a one-dimensional auxiliary coordinate variable, time(o), where o is the element dimension. In the former case, listing the time variable in the coordinates
attributes of the data variables is optional.
Example H.2. Timeseries with common element times in a time coordinate variable using the orthogonal multidimensional array representation.
dimensions: station = 10 ; // measurement locations time = UNLIMITED ; variables: float humidity(station,time) ; humidity:standard_name = "specific humidity" ; humidity:coordinates = "lat lon alt" ; double time(time) ; time:standard_name = "time"; time:long_name = "time of measurement" ; time:units = "days since 1970-01-01 00:00:00" ; float lon(station) ; lon:standard_name = "longitude"; lon:long_name = "station longitude"; lon:units = "degrees_east"; float lat(station) ; lat:standard_name = "latitude"; lat:long_name = "station latitude" ; lat:units = "degrees_north" ; float alt(station) ; alt:long_name = "vertical distance above the surface" ; alt:standard_name = "height" ; alt:units = "m"; alt:positive = "up"; alt:axis = "Z"; char station_name(station, name_strlen) ; station_name:long_name = "station name" ; station_name:cf_role = "timeseries_id"; attributes: :featureType = "timeSeries";
In this example, humidity(i,o)
is element o of time series i, and associated with the coordinate values time(o)
, lat(i)
, and lon(i)
. Either the instance (station) or the element (time) dimension may optionally be the netCDF unlimited dimension.
Much of the simplicity of the orthogonal multidimensional representation can be preserved even in cases where individual time series have different time coordinate values. All time series must be allocated the amount of staorage needed by the longest, so the use of this representation will trade off simplicity against storage space in some cases.
Example H.3. Timeseries of station data in the incomplete multidimensional array representation.
dimensions: station = UNLIMITED ; obs = 13 ; variables: float lon(station) ; lon:standard_name = "longitude"; lon:long_name = "station longitude"; lon:units = "degrees_east"; float lat(station) ; lat:standard_name = "latitude"; lat:long_name = "station latitude" ; lat:units = "degrees_north" ; float alt(station) ; alt:long_name = "vertical distance above the surface" ; alt:standard_name = "height" ; alt:units = "m"; alt:positive = "up"; alt:axis = "Z"; char station_name(station, name_strlen) ; station_name:long_name = "station name" ; station_name:cf_role = "timeseries_id"; int station_info(station) ; station_info:long_name = "any kind of station info" ; float station_elevation(station) ; station_elevationalt:long_name = "height above the geoid" ; station_elevationalt:standard_name = "surface_altitude" ; station_elevationalt:units = "m"; double time(station, obs) ; time:standard_name = "time"; time:long_name = "time of measurement" ; time:units = "days since 1970-01-01 00:00:00" ; time:missing_value = -999.9; float humidity(station, obs) ; humidity:standard_name = “specific_humidity” ; humidity:coordinates = "time lat lon alt" ; humidity:_FillValue = -999.9; float temp(station, obs) ; temp:standard_name = “air_temperature” ; temp:units = "Celsius" ; temp:coordinates = "time lat lon alt" ; temp:_FillValue = -999.9; attributes: :featureType = "timeSeries";
In this example, the humidity(i,o) and temp(i,o) data for element o of time series i are associated with the coordinate values time(i,o), lat(i), lon(i) and alt(i). Either the instance (station) dimension or the element (obs) dimension could be the unlimited dimension of a netCDF file. Any unused elements of the data and auxiliary coordinate variables must contain the missing data flag value(section 9.6).
When the intention of a data variable is to contain only a single time series, the preferred encoding is a special case of the multidimensional array representation.
Example H.4. A single timeseries.
dimensions: time = 100233 ; name_strlen = 23 ; variables: float lon ; lon:standard_name = "longitude"; lon:long_name = "station longitude"; lon:units = "degrees_east"; float lat ; lat:standard_name = "latitude"; lat:long_name = "station latitude" ; lat:units = "degrees_north" ; float alt ; alt:long_name = "vertical distance above the surface" ; alt:standard_name = "height" ; alt:units = "m"; alt:positive = "up"; alt:axis = "Z"; char station_name(name_strlen) ; station_name:long_name = "station name" ; station_name:cf_role = "timeseries_id"; double time(time) ; time:standard_name = "time"; time:long_name = "time of measurement" ; time:units = "days since 1970-01-01 00:00:00" ; time:missing_value = -999.9; float humidity(time) ; humidity:standard_name = “specific_humidity” ; humidity:coordinates = "time lat lon alt" ; humidity:_FillValue = -999.9; float temp(time) ; temp:standard_name = “air_temperature” ; temp:units = "Celsius" ; temp:coordinates = "time lat lon alt" ; temp:_FillValue = -999.9; attributes: :featureType = "timeSeries";
While an idealized time series is defined at a single, stable point location, there are examples of time series, such as cabled ocean surface mooring measurements, in which the precise position of the observations varies slightly from a nominal fixed point. In the following example we show how the spatial positions of such a time series should be encoded in CF. Note that although this example shows only a single time series, the technique is applicable to all of the representations.
Example H.5. A single timeseries with time-varying deviations from a nominal point spatial location
dimensions: time = 100233 ; name_strlen = 23 ; variables: float lon ; lon:standard_name = "longitude"; lon:long_name = "station longitude"; lon:units = "degrees_east"; lon:axis = “X”; float lat ; lat:standard_name = "latitude"; lat:long_name = "station latitude" ; lat:units = "degrees_north" ; lat: axis = “Y” ; float precise_lon (time); precise_lon:standard_name = "longitude"; precise_lon:long_name = "station longitude"; precise_lon:units = "degrees_east"; float precise_lat (time); precise_lat:standard_name = "latitude"; precise_lat:long_name = "station latitude" ; precise_lat:units = "degrees_north" ; float alt ; alt:long_name = "vertical distance above the surface" ; alt:standard_name = "height" ; alt:units = "m"; alt:positive = "up"; alt:axis = "Z"; char station_name(name_strlen) ; station_name:long_name = "station name" ; station_name:cf_role = "timeseries_id"; double time(time) ; time:standard_name = "time"; time:long_name = "time of measurement" ; time:units = "days since 1970-01-01 00:00:00" ; time:missing_value = -999.9; float humidity(time) ; humidity:standard_name = “specific_humidity” ; humidity:coordinates = "time lat lon alt precise_lon precise_lat" ; humidity:_FillValue = -999.9; float temp(time) ; temp:standard_name = “air_temperature” ; temp:units = "Celsius" ; temp:coordinates = "time lat lon alt precise_lon precise_lat " ; temp:_FillValue = -999.9; attributes: :featureType = "timeSeries";
When the time series have different lengths and the data values for entire time series are available to be written in a single operation, the contiguous ragged array representation is efficient.
Example H.6. Timeseries of station data in the contiguous ragged array representation.
dimensions: station = 23 ; obs = 1234 ; variables: float lon(station) ; lon:standard_name = "longitude"; lon:long_name = "station longitude"; lon:units = "degrees_east"; float lat(station) ; lat:standard_name = "latitude"; lat:long_name = "station latitude" ; lat:units = "degrees_north" ; float alt(station) ; alt:long_name = "vertical distance above the surface" ; alt:standard_name = "height" ; alt:units = "m"; alt:positive = "up"; alt:axis = "Z"; char station_name(station, name_strlen) ; station_name:long_name = "station name" ; station_name:cf_role = "timeseries_id"; int station_info(station) ; station_info:long_name = "some kind of station info" ; int row_size(station) ; row_size:long_name = "number of observations for this station " ; row_size:sample_dimension = "obs" ; double time(obs) ; time:standard_name = "time"; time:long_name = "time of measurement" ; time:units = "days since 1970-01-01 00:00:00" ; float humidity(obs) ; humidity:standard_name = “specific_humidity” ; humidity:coordinates = "time lat lon alt" ; humidity:_FillValue = -999.9; float temp(obs) ; temp:standard_name = “air_temperature” ; temp:units = "Celsius" ; temp:coordinates = "time lat lon alt" ; temp:_FillValue = -999.9; attributes: :featureType = "timeSeries";
The data humidity(o) and temp(o) are associated with the coordinate values time(o), lat(i), lon(i), and alt(i), where i indicates which time series. Time series i comprises the data elements from
rowStart(i) to rowStart(i) + row_size(i) - 1
where
rowStart(i) = 0 if i = 0 rowStart(i) = rowStart(i-1) + row_size(i-1) if i > 0
The variable, row_size
, is the count variable containing the length of each time series feature. It is identified by having an attribute with name sample_dimension
whose value is name of the sample dimension (obs
in this example). The sample dimension could optionally be the netCDF unlimited dimension. The variable bearing the sample_dimension
attribute must have the instance dimension (station
in this example) as its single dimension, and must be of type integer. This variable implicitly partitions into individual instances all variables that have the sample dimension. The auxiliary coordinate variables lat
, lon
, alt
and station_name
are station variables.
When time series with different lengths are written incrementally, the indexed ragged array representation is efficient.
Example H.7. Timeseries of station data in the indexed ragged array representation.
dimensions: station = 23 ; obs = UNLIMITED ; variables: float lon(station) ; lon:standard_name = "longitude"; lon:long_name = "station longitude"; lon:units = "degrees_east"; float lat(station) ; lat:standard_name = "latitude"; lat:long_name = "station latitude" ; lat:units = "degrees_north" ; float alt(station) ; alt:long_name = "vertical distance above the surface" ; alt:standard_name = "height" ; alt:units = "m"; alt:positive = "up"; alt:axis = "Z"; char station_name(station, name_strlen) ; station_name:long_name = "station name" ; station_name:cf_role = "timeseries_id"; int station_info(station) ; station_info:long_name = "some kind of station info" ; int stationIndex(obs) ; stationIndex:long_name = "which station this obs is for" ; stationIndex:instance_dimension= "station" ; double time(obs) ; time:standard_name = "time"; time:long_name = "time of measurement" ; time:units = "days since 1970-01-01 00:00:00" ; float humidity(obs) ; humidity:standard_name = “specific_humidity” ; humidity:coordinates = "time lat lon alt" ; humidity:_FillValue = -999.9; float temp(obs) ; temp:standard_name = “air_temperature” ; temp:units = "Celsius" ; temp:coordinates = "time lat lon alt" ; temp:_FillValue = -999.9; attributes: :featureType = "timeSeries";
The humidity(o) and temp(o) data are associated with the coordinate values time(o), lat(i), lon(i), and alt(i), where i = stationIndex(o) is a zero-based index indicating which time series. Thus, time(0), humidity(0) and temp(0) belong to the element of the station
dimension that is indicated by stationIndex(0)
; time(1), humidity(1) and temp(1) belong to element stationIndex(1)
of the station
dimension, etc.
The variable, stationIndex
, is identified as the index variable by having an attribute with name of instance_dimension
whose value is the instance dimension (station
in this example). The variable bearing the instance_dimension
attribute must have the sample dimension (obs
in this example) as its single dimension, and must be type integer. This variable implicitly assigns the station to each value of any variable having the sample dimension. The sample dimension need not be the netCDF unlimited dimension, though it commonly is.