= 5.8 Point Observation Data =

Section 5 explains how to specify Coordinate Systems using coordinate variables and auxiliary coordinate variables. This section extends and modifies that framework for point observational data.

== Overview ==

A point observation is a data measurement at a specific time and location. Each kind of measured data is placed in a data variable. The time and location values are placed into coordinate variables and auxiliary coordinate variables.

Point data files contain collections of point observation data of one of the following feature types:

    * '''point''': one or more parameters measured at one point in time and space
    * '''stationTimeSeries''': a time-series of data points at the same location, with varying time
    * '''trajectory''': a connected set of data points along a 1D curve in time and space
    * '''profile''': a set of data points along a vertical line
    * '''stationProfileTimeSeries''': a time-series of profiles at a named location
    * '''section''':  a collection of profiles which originate along a trajectory.

You cannot mix multiple types of data in the same file. A global attribute CF:featureType indicates the feature type used in a particular file, and its value must be one of the above names in order to be correctly processed as point observation data.

All point data must have lat, lon and time coordinate or auxiliary coordinate variables, which are identified according to section 4 of this document. Some feature types allow the vertical coordinate to be optional, and some require it.

There are several ways to represent point data in the classic netCDF model:

    * multidimensional (rectangular array) representation
    * contiguous ragged array representation
    * non-contiguous ragged array representation

The following subsections detail each point feature types and show examples of the possible representations of each.

Some assumption are common to all representations:
  
    * lat, lon and time coordinates must always exist
    * index numbering, if used, is always 0 based
    * variables are associated together by having a common, outer dimension

== 5.8.1 Point Data ==

To represent data at scattered, unconnected points, both data and coordinates use the same, single dimension. The 'coordinates' attribute is used on the data variables to unambiguously identify the time, lat, lon and height auxiliary coordinate variables.

{{{
dimensions:
  obs = 1234 ;

variables:
  double time(obs) ;
    time:long_name = "time of measurement" ;
    time:units = "days since 1970-01-01 00:00:00" ;
  float lon(obs) ;
    lon:long_name = "longitude of the observation";
    lon:units = "degrees_east";
  float lat(obs) ;
    lat:long_name = "latitude of the observation" ;
    lat:units = "degrees_north" ;
  float alt(obs) ;
    alt:long_name = "altitude above MSL" ;
    alt:units = "m";
    alt:positive = "up";

  float humidity(obs) ;
    humidity:long_name = "specific humidity" ;
    humidity:coordinates = "time lat lon alt" ;
  float temp(obs) ;
    temp:long_name = "temperature" ;
    temp:units = "Celsius" ;
    temp:coordinates = "time lat lon alt" ;

attributes:
  :CF\:featureType = "point";
}}}

In this example, the humidity(i) and temp(i) data are associated with the coordinate values time(i), lat(i), lon(i), and optionally alt(i). The obs dimension may use the unlimited dimension or not. If the time coordinate is ordered, the obs dimension may be named time (making time a coordinate variable rather than an auxiliary variable).

== 5.8.2 Time series of Station Data ==

Point data may be taken at a set of named locations called stations. The set of observations at a particular station, if ordered by time, is a time series, and the file contains a collection of time series data at named locations called stations.

Some assumption are common to all station representations:

   * The outer dimension of the latitude and longitude coordinates (which must agree) is the 'station dimension'.
   * All variables that have the station dimension as their outer dimension are considered to be station information, and are called 'station variables'. 
   * There must always be a station variable (of any type) with standard_name attribute "'''station_id'''", whose values uniquely identify the station.

=== 5.8.2.1 Multidimensional representation ===

When the number of observations at each station is the same, one can use the multidimensional representation.

{{{
dimensions:
  station = UNLIMITED ;
  obs = 13 ;

variables:
  float lon(station) ;
    lon:long_name = "station longitude";
    lon:units = "degrees_east";
  float lat(station) ;
    lat:long_name = "station latitude" ;
    lat:units = "degrees_north" ;
  float alt(station) ;
    alt:long_name = "altitude above MSL" ;
    alt:units = "m" ;
    alt:positive= "up" ;
  char station_name(station, name_strlen) ;
    station_name:long_name = "station name" ;
    station_name:standard_name = "station_id";
  int station_info(station) ;
    station_info:long_name = "any kind of station info" ;

  double time(station, obs) ;
    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:long_name = "specific humidity" ;
    humidity:coordinates = "time lat lon alt" ;
    humidity:_FillValue = -999.9;
  float temp(station, obs) ;
    temp:long_name = "temperature" ;
    temp:units = "Celsius" ;
    temp:coordinates = "time lat lon alt" ;
    temp:_FillValue = -999.9;

attributes:
    :CF\:featureType = "stationTimeSeries";
}}}

The humidity(s,i) and temp(s,i) data are associated with the coordinate values time(s,i), lat(s), lon(s), and optionally vertical(s). The station dimension may be the unlimited dimension or not. 

The time coordinate may use a missing value, which indicates that data is missing for that station and obs index. This allows one to have a variable number of observations at different stations, at the cost of some wasted space. The data variables may also use missing data values, to indicate that just that data variable is missing.

Note that this is a generalization of Example 5.4, which assumes that all the stations have observations with the same time coordinates.

=== 5.8.2.2 Ragged array (contiguous) representation ===

When the number of observations at each station vary, one can use the 'contiguous ragged array' representation if you are able to completely control the order in which the observations are written. Add a '''rowSize''' station variable specifying the number of observations for each station. The observations for each station are stored sequentially, first for the station with index = 0, then the station with index = 1, etc.

{{{
dimensions:
  station = 23 ;
  obs = 1234 ;

variables:
  float lon(station) ;
    lon:long_name = "station longitude";
    lon:units = "degrees_east";
  float lat(station) ;
    lat:long_name = "station latitude" ;
    lat:units = "degrees_north" ;
  float alt(station) ;
    alt:long_name = "altitude above MSL" ;
    alt:units = "m" ;
  char station_name(station, name_strlen) ;
    station_name:long_name = "station name" ;
    station_name:standard_name = "station_id";
  int station_info(station) ;
    station_info:long_name = "some kind of station info" ;
  int nobs(station) ;
    nobs:long_name = "number of observations for this station " ;
    nobs:standard_name = ragged_rowSize" ;

  double time(obs) ;
    time:long_name = "time of measurement" ;
    time:units = "days since 1970-01-01 00:00:00" ;
  float humidity(obs) ;
    humidity:long_name = "specific humidity" ;
    humidity:coordinates = "time lat lon alt" ;
    humidity:_FillValue = -999.9;
  float temp(obs) ;
    temp:long_name = "temperature" ;
    temp:units = "Celsius" ;
    temp:coordinates = "time lat lon alt" ;
    temp:_FillValue = -999.9;

attributes:
    :CF\:featureType = "stationTimeSeries";
}}}

Then for each station with index stn, its observations go from

{{{
  rowStart(stn) to rowStart(stn) + nobs(stn) - 1

where

  rowStart(stn) = 0 if stn = 0    
  rowStart(stn) = rowStart(stn-1) + nobs(stn-1) if i > 0

}}}

The rowSize variable contains the number of observations for each station, and is identified by having a standard_name of "'''ragged_rowSize'''". It must have the station dimension as its single dimension.

The outer dimension of the time coordinate is the obs dimension. All variables having the obs dimension as their outer dimension are observation variables. The obs dimension may use the unlimited dimension or not.

=== 5.8.2.3 Ragged array (non-contiguous) representation ===

When the number of observations at each station vary, and the observations cannot be written in order, one can use the 'non-contiguous ragged array' representation. Add a '''parentIndex''' observation variable specifying the station index that the observation belongs to:

{{{
dimensions:
  station = 23 ;
  obs = UNLIMITED ;

variables:
  float lon(station) ;
    lon:long_name = "station longitude";
    lon:units = "degrees_east";
  float lat(station) ;
    lat:long_name = "station latitude" ;
    lat:units = "degrees_north" ;
  float alt(station) ;
    alt:long_name = "altitude above MSL" ;
    alt:units = "m" ;
  char station_name(station, name_strlen) ;
    station_name:long_name = "station name" ;
    station_name:standard_name = "station_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:standard_name = "ragged_parentIndex" ;
  double time(obs) ;
    time:long_name = "time of measurement" ;
    time:units = "days since 1970-01-01 00:00:00" ;
  float humidity(obs) ;
    humidity:long_name = "specific humidity" ;
    humidity:coordinates = "time lat lon alt" ;
    humidity:_FillValue = -999.9;
  float temp(obs) ;
    temp:long_name = "temperature" ;
    temp:units = "Celsius" ;
    temp:coordinates = "time lat lon alt" ;
    temp:_FillValue = -999.9;

attributes:
    :CF\:featureType = "stationTimeSeries";
}}}

The humidity(i) and temp(i) data are associated with the coordinate values time(i), lat(s), lon(s), and optionally alt(s), where s = stationIndex(i). The stationIndex variable is identified by having a standard_name of "'''ragged_parentIndex'''".  It must have the obs dimension as its single dimension.

The outer dimension of the time coordinate is the obs dimension. All variables having the obs dimension as their outer dimension are observation variables. The obs dimension may use the unlimited dimension or not.

=== 5.8.2.4 Single station ===

When there is a single station in the file, one can can use any of the above representations with number of stations = 1. One can also use scalar coordinates. This case is identified when the lat and lon coordinates are scalar. In this case, no connecting variable between station and observations is required.

{{{
dimensions:
  obs = 1233 ;
  name_strlen = 23

variables:
  float lon ;
    lon:long_name = "station longitude";
    lon:units = "degrees_east";
  float lat ;
    lat:long_name = "station latitude" ;
    lat:units = "degrees_north" ;
  float alt ;
    alt:long_name = "altitude above MSL" ;
    alt:units = "m" ;
  char station_name(name_strlen) ;
    station_name:long_name = "station name" ;
    station_name:standard_name = "station_id";

  double time(obs) ;
    time:long_name = "time of measurement" ;
    time:units = "days since 1970-01-01 00:00:00" ;
    time:missing_value = -999.9;
  float humidity(obs) ;
    humidity:long_name = "specific humidity" ;
    humidity:coordinates = "time lat lon alt" ;
    humidity:_FillValue = -999.9;
  float temp(obs) ;
    temp:long_name = "temperature" ;
    temp:units = "Celsius" ;
    temp:coordinates = "time lat lon alt" ;
    temp:_FillValue = -999.9;

attributes:
    :CF\:featureType = "stationTimeSeries";
}}}

=== 5.8.2.5 Flattened representation ===

When factoring out the station information is not needed, one may use a 'flattened representation', in which the station information is repeated for each observation. A station_id variable is required to associate the observations into a time series.

{{{
dimensions:
  obs = UNLIMITED ;

variables:
  float lon(obs) ;
    lon:long_name = "station longitude";
    lon:units = "degrees_east";
  float lat(obs) ;
    lat:long_name = "station latitude" ;
    lat:units = "degrees_north" ;
  float alt(obs) ;
    alt:long_name = "altitude above MSL" ;
    alt:units = "m" ;
  int station_id(obs) ;
    station_name:long_name = "station" ;
    station_name:standard_name = "station_id";

  double time(obs) ;
    time:long_name = "time of measurement" ;
    time:units = "days since 1970-01-01 00:00:00" ;
  float humidity(obs) ;
    humidity:long_name = "specific humidity" ;
    humidity:coordinates = "time lat lon alt" ;
    humidity:_FillValue = -999.9;
  float temp(obs) ;
    temp:long_name = "temperature" ;
    temp:units = "Celsius" ;
    temp:coordinates = "time lat lon alt" ;
    temp:_FillValue = -999.9;

attributes:
    :CF\:featureType = "stationTimeSeries";
}}}


The humidity(i) and temp(i) data are associated with the coordinate values time(i), lat(i), lon(i), and optionally alt(i). There must be a variable (of any type) with the observation dimension as its outer dimension, with a standard_name attribute "'''station_id'''", whose values uniquely identify the station. All observations with the same station_id are assumed to belong to that station.

In some observational networks, station location may change. However, for station feature types this should be infrequent and not overly consequential. In principle, a new station identifier should be assigned. In practice, occasional and small adjustments to station location may not matter for typical processing of data, and generic clients may not detect these changes, eg they may assume that the first location encountered is valid for all other observations at the same station. Specialized clients, of course, may be more careful in examining station location data, and nothing prevents data providers from using a factored representation as in 5.8.2.1, 5.8.2.2, and 5.8.2.3, and also putting location information into the observation record, as in the flattened representation in 5.8.2.5.

== 5.8.3 Trajectory Data ==

Point data may be taken along a flight path or ship path, constituting a connected set of points called a trajectory.

=== 5.8.3.1 Single Trajectory ===

When a single trajectory is stored in a file, the representation is straightforward:


{{{
dimensions:
  time = 1000 ;

variables:
  double time(time) ;
    time:long_name = "time" ;
    time:units = "days since 1970-01-01 00:00:00" ;
  float lon(time) ;
    lon:long_name = "longitude" ;
    lon:units = "degrees_east" ;
  float lat(time) ;
    lat:long_name = "latitude" ;
    lat:units = "degrees_north" ;
  float z(time) ;
    z:long_name = "height above mean sea level" ;
    z:units = "km" ;
    z:positive = "up" ; 

  float O3(time) ;
    O3:long_name = "ozone concentration" ;
    O3:units = "1e-9" ;
    O3:coordinates = "time lon lat z" ;

  float NO3(time) ;
    NO3:long_name = "NO3 concentration" ;
    NO3:units = "1e-9" ;
    NO3:coordinates = "time lon lat z" ;

attributes:
  :CF\:featureType = "trajectory";
}}}


The NO3(n) and O3(n) data is associated with the coordinate values time(n), z(n), lat(n), and lon(n). The time coordinate should be ordered, so it is appropriate to use a coordinate variable for time. The time dimension may be unlimited or not.

Note that structurally this looks like unconnected point data as in example 5.8.1. The presence of the :CF\:featureType = "trajectory" global attribute indicates that in fact the points are connected along a trajectory.

Note that this is the same as Example 5.5.

=== 5.8.3.2 Multidimensional representation ===

When storing multiple trajectories in the same file, and the number of observations in each trajectory is the same, one can use the multidimensional representation.


{{{
dimensions:
  obs = 1000 ;
  trajectory = 77 ;

variables:
  char trajectory(trajectory, name_strlen) ;
    trajectory:long_name = "trajectory name" ;
  int trajectory_info(trajectory) ;
    trajectory_name:long_name = "some kind of trajectory info" 

  double time(trajectory, obs) ;
    time:long_name = "time" ;
    time:units = "days since 1970-01-01 00:00:00" ;
  float lon(trajectory, obs) ;
    lon:long_name = "longitude" ;
    lon:units = "degrees_east" ;
  float lat(trajectory, obs) ;
    lat:long_name = "latitude" ;
    lat:units = "degrees_north" ;

  float z(trajectory, obs) ;
    z:long_name = "height above mean sea level" ;
    z:units = "km" ;
    z:positive = "up" ; 

  float O3(trajectory, obs) ;
    O3:long_name = "ozone concentration" ;
    O3:units = "1e-9" ;
    O3:coordinates = "time lon lat z" ;

  float NO3(trajectory, obs) ;
    NO3:long_name = "NO3 concentration" ;
    NO3:units = "1e-9" ;
    NO3:coordinates = "time lon lat z" ;

attributes:
  :CF\:featureType = "trajectory";
}}}


The NO3(t,i) and O3(t,i) data are associated with the coordinate values time(t,i), lat(t,i), lon(t,i), and alt(t,i). The trajectory dimension may be the unlimited dimension or not. All variables that have trajectory as their only dimension are considered to be information about that trajectory.

The time coordinate may use a missing value, which indicates that data is missing for that trajectory and obs index. This allows one to have a variable number of observations for different trajectories, at the cost of some wasted space. The data variables may also use missing data values.

=== 5.8.3.3 Ragged array (contiguous) representation ===

When the number of observations for each trajectory varies, one can use the contiguous ragged array representation. One stores the set of observation for each trajectory contiguously along the obs dimension, and adds a rowSize variable specifying the number of observations for each trajectory:


{{{
dimensions:
  obs = UNLIMITED ;
  trajectory = 77 ;

variables:
  char trajectory(trajectory, name_strlen) ;
    trajectory:long_name = "trajectory name" ;
  int trajectory_info(trajectory) ;
    trajectory_name:long_name = "some kind of trajectory info" ;

  int rowSize(trajectory) ;
    rowSize:long_name = "number of obs for this trajectory " ;
    rowSize:standard_name = "ragged_rowSize" ;
  double time(obs) ;
    time:long_name = "time" ;
    time:units = "days since 1970-01-01 00:00:00" ;
  float lon(obs) ;
    lon:long_name = "longitude" ;
    lon:units = "degrees_east" ;
  float lat(obs) ;
    lat:long_name = "latitude" ;
    lat:units = "degrees_north" ;
  float z(obs) ;
    z:long_name = "height above mean sea level" ;
    z:units = "km" ;
    z:positive = "up" ; 

  float O3(obs) ;
    O3:long_name = "ozone concentration" ;
    O3:units = "1e-9" ;
    O3:coordinates = "time lon lat z" ;

  float NO3(obs) ;
    NO3:long_name = "NO3 concentration" ;
    NO3:units = "1e-9" ;
    NO3:coordinates = "time lon lat z" ;

attributes:
  :CF\:featureType = "trajectory";
}}}


The O3(i) and NO3(i) data are associated with the coordinate values time(i), lat(i), lon(i), and alt(i). All observations for one trajectory are contiguous along the obs dimension, and should be time ordered. All variables that have trajectory as their single dimension are considered to be information about that trajectory. The obs dimension may use the unlimited dimension or not.

The rowSize variable contains the number of observations for each trajectory, and is identified by having a standard_name of "ragged_rowSize". It must have the trajectory dimension as its single dimension.

=== 5.8.3.4 Ragged array (non-contiguous) representation ===

When the number of observations at each trajectory vary, and the observations cannot be written in order, one can use the non-contiguous ragged array representation. Add a parentIndex field specifying the trajectory index that the observation belongs to:


{{{
dimensions:
  obs = UNLIMITED ;
  trajectory = 77 ;

variables:
  char trajectory(trajectory, name_strlen) ;
    trajectory:long_name = "trajectory name" ;
  int trajectory_info(trajectory) ;
    trajectory_name:long_name = "some kind of trajectory info" ;

  int trajectory_index(obs) ;
    trajectory_index:long_name = "index of trajectory this obs belongs to " ;
    trajectory_index:standard_name = "ragged_parentIndex" ;
  double time(obs) ;
    time:long_name = "time" ;
    time:units = "days since 1970-01-01 00:00:00" ;
  float lon(obs) ;
    lon:long_name = "longitude" ;
    lon:units = "degrees_east" ;
  float lat(obs) ;
    lat:long_name = "latitude" ;
    lat:units = "degrees_north" ;
  float z(obs) ;
    z:long_name = "height above mean sea level" ;
    z:units = "km" ;
    z:positive = "up" ; 

  float O3(obs) ;
    O3:long_name = "ozone concentration" ;
    O3:units = "1e-9" ;
    O3:coordinates = "time lon lat z" ;

  float NO3(obs) ;
    NO3:long_name = "NO3 concentration" ;
    NO3:units = "1e-9" ;
    NO3:coordinates = "time lon lat z" ;

attributes:
  :CF\:featureType = "trajectory";
}}}


The O3(i) and NO3(i) data are associated with the coordinate values time(i), lat(i), lon(i), and alt(i). All observations for one trajectory will have the same trajectory index value, and should be time ordered. All variables that have trajectory as their single dimension are considered to be information about that trajectory. The obs dimension may use the unlimited dimension or not.

The parentIndex variable is identified by having a standard_name of "ragged_parentIndex". It must have the obs dimension as its single dimension.

 
== 5.8.4 Profile Data ==

A series of connected observations along a vertical line, like an atmospheric or ocean sounding, is called a profile.

=== 5.8.4.1 Single Profile ===

When a single profile is stored in a file, the representation is straightforward:


{{{
dimensions:
  z = 42 ;

variables:
  float z(z) ;
    z:long_name = "height above mean sea level" ;
    z:units = "km" ;
    z:positive = "up" ; 

  double time;
    time:long_name = "time" ;
    time:units = "days since 1970-01-01 00:00:00" ;
  float lon;
    lon:long_name = "longitude" ;
    lon:units = "degrees_east" ;
  float lat;
    lat:long_name = "latitude" ;
    lat:units = "degrees_north" ;

  float pressure(z) ;
    pressure:long_name = "pressure level" ;
    pressure:units = "hPa" ;
    pressure:coordinates = "time lon lat z" ;

  float temperature(z) ;
    temperature:long_name = "skin temperature" ;
    temperature:units = "Celsius" ;
    temperature:coordinates = "time lon lat z" ;

  float humidity(z) ;
    humidity:long_name = "relative humidity" ;
    humidity:units = "%" ;
    humidity:coordinates = "time lon lat z" ;

attributes:
  :CF\:featureType = "profile";
}}}


The pressure(i), temperature(i), and humidity(i) data is associated with the coordinate values time, z(i), lat, and lon. The z coordinate must be ordered. The time coordinate may depend on z also, eg may be time(z).

=== 5.8.4.2 Multidimensional representation ===

When storing multiple profiles in the same file, and the vertical levels in each profile are the same, one can use the multidimensional representation:


{{{
dimensions:
  z = 42 ;
  profile = 142 ;

variables:
  float z(z) ;
    z:long_name = "height above mean sea level" ;
    z:units = "km" ;
    z:positive = "up" ; 

  double time(profile);
    time:long_name = "time" ;
    time:units = "days since 1970-01-01 00:00:00" ;
  float lon(profile);
    lon:long_name = "longitude" ;
    lon:units = "degrees_east" ;
  float lat(profile);
    lat:long_name = "latitude" ;
    lat:units = "degrees_north" ;

  float pressure(profile, z) ;
    pressure:long_name = "pressure level" ;
    pressure:units = "hPa" ;
    pressure:coordinates = "time lon lat z" ;

  float temperature(profile, z) ;
    temperature:long_name = "skin temperature" ;
    temperature:units = "Celsius" ;
    temperature:coordinates = "time lon lat z" ;

  float humidity(profile, z) ;
    humidity:long_name = "relative humidity" ;
    humidity:units = "%" ;
    humidity:coordinates = "time lon lat z" ;

attributes:
  :CF\:featureType = "profile";
}}}


The pressure(p,i), temperature(p,i), and humidity(p,i) data is associated with the coordinate values time(p), z(i), lat(p), and lon(p). If the vertical coordinates differ for each profile, but the number of levels are the same, one can use z(profile, z), but the z coordinate must be ordered for each profile.

The profile dimension may be the unlimited dimension or not. All variables that have profile as their only dimension are considered to be profile information.


The time coordinate may depend on z also, eg time(p,z). The time coordinate may use a missing value, which indicates that data is missing for that profile and obs index. This allows one to have a variable number of observations for different profiles, at the cost of some wasted space. The data variables may also use missing data values.

=== 5.8.4.3 Ragged array (contiguous) representation ===

When the number of vertical levels for each profile varies, one can use the contiguous ragged array representation. One stores the set of observation for each profile contiguously along the obs dimension, and adds a rowSize variable specifying the number of observations for each profile:


{{{
dimensions:
  obs = UNLIMITED ;
  profiles = 142 ;

variables:
  double time(profile);
    time:long_name = "time" ;
    time:units = "days since 1970-01-01 00:00:00" ;
  float lon(profile);
    lon:long_name = "longitude" ;
    lon:units = "degrees_east" ;
  float lat(profile);
    lat:long_name = "latitude" ;
    lat:units = "degrees_north" ; 
  int rowSize(profile) ;
    rowSize:long_name = "number of obs for this profile " ;
    rowSize:standard_name = "ragged_rowSize" ;

  float z(obs) ;
    z:long_name = "height above mean sea level" ;
    z:units = "km" ;
    z:positive = "up" ;

  float pressure(obs) ;
    pressure:long_name = "pressure level" ;
    pressure:units = "hPa" ;
    pressure:coordinates = "time lon lat z" ;

  float temperature(obs) ;
    temperature:long_name = "skin temperature" ;
    temperature:units = "Celsius" ;
    temperature:coordinates = "time lon lat z" ;

  float humidity(obs) ;
    humidity:long_name = "relative humidity" ;
    humidity:units = "%" ;
    humidity:coordinates = "time lon lat z" ;

attributes:
  :CF\:featureType = "profile";
}}}


The pressure(i), temperature(i), and humidity(i) data is associated with the coordinate values time(p), z(i), lat(p), and lon(p), where p=profile_index(i). The time coordinate may depend on z also, eg time(p,z).

=== 5.8.4.4 Ragged array (non-contiguous) representation ===

When the number of vertical levels for each profile varies, and one cant write them contiguously, one can use the non-contiguous ragged array representation. Add a parentIndex field specifying the profile index that the observation belongs to:


{{{
dimensions:
  obs = UNLIMITED ;
  profiles = 142 ;

variables:
  double time(profile);
    time:long_name = "time" ;
    time:units = "days since 1970-01-01 00:00:00" ;
  float lon(profile);
    lon:long_name = "longitude" ;
    lon:units = "degrees_east" ;
  float lat(profile);
    lat:long_name = "latitude" ;
    lat:units = "degrees_north" ; 
  int parentIndex(obs) ;
    parentIndex:long_name = "index of profile " ;
    parentIndex:standard_name = "ragged_parentIndex" ;

  float z(obs) ;
    z:long_name = "height above mean sea level" ;
    z:units = "km" ;
    z:positive = "up" ;

  float pressure(obs) ;
    pressure:long_name = "pressure level" ;
    pressure:units = "hPa" ;
    pressure:coordinates = "time lon lat z" ;

  float temperature(obs) ;
    temperature:long_name = "skin temperature" ;
    temperature:units = "Celsius" ;
    temperature:coordinates = "time lon lat z" ;

  float humidity(obs) ;
    humidity:long_name = "relative humidity" ;
    humidity:units = "%" ;
    humidity:coordinates = "time lon lat z" ;

attributes:
  :CF\:featureType = "profile";
}}}


The pressure(i), temperature(i), and humidity(i) data is associated with the coordinate values time(p), z(i), lat(p), and lon(p), where p=parentIndex(i). The time coordinate may depend on z also, eg time(p,z).

== 5.8.5 Station Profile Data ==

When profiles are taken at a set of stations, one gets a time series of profiles at each station, called a stationProfileTimeSeries.

=== 5.8.5.1 Profile time series at a single station ===

If there is only one station in a file, and the vertical levels are the same for each profile, one can use a representation that is a variant of Example 5.8.4.2:


{{{
dimensions:
  z = 42 ;
  time = UNLIMITED ;

variables:
  float z(z) ;
    z:long_name = "height above mean sea level" ;
    z:units = "km" ;
    z:positive = "up" ; 

  double time(time);
    time:long_name = "time" ;
    time:units = "days since 1970-01-01 00:00:00" ;
  float lon;
    lon:long_name = "longitude" ;
    lon:units = "degrees_east" ;
  float lat;
    lat:long_name = "latitude" ;
    lat:units = "degrees_north" ;

  float pressure(time, z) ;
    pressure:long_name = "pressure level" ;
    pressure:units = "hPa" ;
    pressure:coordinates = "time lon lat z" ;

  float temperature(time, z) ;
    temperature:long_name = "skin temperature" ;
    temperature:units = "Celsius" ;
    temperature:coordinates = "time lon lat z" ;

  float humidity(time, z) ;
    humidity:long_name = "relative humidity" ;
    humidity:units = "%" ;
    humidity:coordinates = "time lon lat z" ;

attributes:
  :CF\:featureType = "stationProfileTimeSeries";
}}}


The pressure(i,j), temperature(i,j), and humidity(i,j) data are associated with the coordinate values time(i), z(j), lat, and lon.

=== 5.8.5.2 Multidimensional representation ===

When storing time series of profiles at multiple stations in the same file, if there are the same number of time points and vertical levels for every profile, one can use the multidimensional representation.


{{{
dimensions:
  station = 22 ;
  time = 30 ;
  z = 42 ;

variables:
  float lon(station) ;
    lon:long_name = "station longitude";
    lon:units = "degrees_east";
  float lat(station) ;
    lat:long_name = "station latitude" ;
    lat:units = "degrees_north" ;
  char station_name(station, name_strlen) ;
    station_name:long_name = "station name" ;
  int station_info(station) ;
    station_name:long_name = "some kind of station info" ;

  float z(z) ;
    z:long_name = "height above mean sea level" ;
    z:units = "km" ;
    z:positive = "up" ; 

  double time(station, time) ;
    time:long_name = "time of measurement" ;
    time:units = "days since 1970-01-01 00:00:00" ;
    time:missing_value = -999.9;

  float pressure(station, time, z) ;
    pressure:long_name = "pressure level" ;
    pressure:units = "hPa" ;
    pressure:coordinates = "time lon lat z" ;

  float temperature(station, time, z) ;
    temperature:long_name = "skin temperature" ;
    temperature:units = "Celsius" ;
    temperature:coordinates = "time lon lat z" ;

  float humidity(station, time, z) ;
    humidity:long_name = "relative humidity" ;
    humidity:units = "%" ;
    humidity:coordinates = "time lon lat z" ;

attributes:
 :CF\:featureType = "stationProfileTimeSeries";
}}}


The pressure(s,p,i), temperature(s,p,i), and humidity(s,p,i) data is associated with the coordinate values time(s,p), z(i), lat(s), and lon(s).

=== 5.8.5.3 Ragged array of multidimensional profiles ===

When the number of profiles for each station varies, one can use (continguous or non-contiguous) ragged arrays for the time series. If each profile has the same number of vertical levels, a multidimensional profile representation can be usedd. Here is the contiguous form:


{{{
dimensions:
  station = 22 ;
  profile = UNLIMITED ;
  z = 42 ;

variables:
  float z(z) ;
    z:long_name = "height above mean sea level" ;
    z:units = "km" ;
    z:positive = "up" ; 

  float lon(station) ;
    lon:long_name = "station longitude";
    lon:units = "degrees_east";
  float lat(station) ;
    lat:long_name = "station latitude" ;
    lat:units = "degrees_north" ;
  char station_name(station, name_strlen) ;
    station_name:long_name = "station name" ;
  int station_info(station) ;
    station_name:long_name = "some kind of station info" ;
  int rowSize(station) ;
    rowSize:long_name = "number of profiles for this station " ;
    rowSize:standard_name = "ragged_rowSize" ;
  double time(profile);
    time:long_name = "time" ;
    time:units = "days since 1970-01-01 00:00:00" ; 
  float pressure(profile, z) ;
    pressure:long_name = "pressure level" ;
    pressure:units = "hPa" ;
    pressure:coordinates = "time lon lat z" ;

  float temperature(profile, z) ;
    temperature:long_name = "skin temperature" ;
    temperature:units = "Celsius" ;
    temperature:coordinates = "time lon lat z" ;

  float humidity(profile, z) ;
    humidity:long_name = "relative humidity" ;
    humidity:units = "%" ;
    humidity:coordinates = "time lon lat z" ;

attributes:
  :CF\:featureType = "stationProfileTimeSeries";
}}}


The pressure(p,z), temperature(p,z), and humidity(p,z) data is associated with the coordinate values time(p), z(z), lat(s), and lon(s).

=== 5.8.5.4 Ragged array of ragged array ===

If the number of vertical levels for the profiles also varies significantly, one can also turn the profiles into a ragged array. Here we show the non-contiguous form:


{{{
dimensions:
  obs = UNLIMITED ;
  station = 142 ;

variables:
  float lon(station) ;
    lon:long_name = "station longitude";
    lon:units = "degrees_east";
  float lat(station) ;
    lat:long_name = "station latitude" ;
    lat:units = "degrees_north" ;
  float alt(station) ;
    alt:long_name = "altitude above MSL" ;
    alt:units = "m" ;
  char station_name(station, name_strlen) ;
    station_name:long_name = "station name" ;
  int station_info(station) ;
    station_name:long_name = "some kind of station info" ;
  int stationIndex(profile) ;
    stationIndex:long_name = "station index for this profile" ;
    stationIndex:standard_name = "ragged_parentIndex" ;
  double time(profile);
    time:long_name = "time" ;
    time:units = "days since 1970-01-01 00:00:00" ; 
  int profileIndex(obs) ;
    profileIndex:long_name = "profile index for this level" ;
    profileIndex:standard_name = "ragged_parentIndex" ;

  float z(obs) ;
    z:long_name = "height above mean sea level" ;
    z:units = "km" ;
    z:positive = "up" ;

  float pressure(obs) ;
    pressure:long_name = "pressure level" ;
    pressure:units = "hPa" ;
    pressure:coordinates = "time lon lat z" ;

  float temperature(obs) ;
    temperature:long_name = "skin temperature" ;
    temperature:units = "Celsius" ;
    temperature:coordinates = "time lon lat z" ;

  float humidity(obs) ;
    humidity:long_name = "relative humidity" ;
    humidity:units = "%" ;
    humidity:coordinates = "time lon lat z" ;

attributes:
  :CF\:featureType = "stationProfileTimeSeries";
}}}


The pressure(i), temperature(i), and humidity(i) data is associated with the coordinate values time(p), z(i), lat(s), and lon(s), where p = profileIndex(i), and s = stationIndex(p).

== 5.8.6 Section Profile Data ==

When profiles are taken along a trajectory, one gets a time series of profiles called a section. This looks like a collection of profiles (see 5.8.4.2), except that the profile location are a connected set of points along a trajectory.

=== 5.8.6.1 Multidimensional representation ===

If the vertical levels are the same for each profile, one can use a multidimensional representation:


{{{
dimensions:
  z = 42 ;
  time = UNLIMITED ;

variables:
  float z(z) ;
    z:long_name = "height above mean sea level" ;
    z:units = "km" ;
    z:positive = "up" ; 

  double time(time);
    time:long_name = "time" ;
    time:units = "days since 1970-01-01 00:00:00" ;
  float lon(time);
    lon:long_name = "longitude" ;
    lon:units = "degrees_east" ;
  float lat(time);
    lat:long_name = "latitude" ;
    lat:units = "degrees_north" ;

  float pressure(time, z) ;
    pressure:long_name = "pressure level" ;
    pressure:units = "hPa" ;
    pressure:coordinates = "time lon lat z" ;

  float temperature(time, z) ;
    temperature:long_name = "skin temperature" ;
    temperature:units = "Celsius" ;
    temperature:coordinates = "time lon lat z" ;

  float humidity(time, z) ;
    humidity:long_name = "relative humidity" ;
    humidity:units = "%" ;
    humidity:coordinates = "time lon lat z" ;

attributes:
  :CF\:featureType = "section";
}}}


The pressure(i,j), temperature(i,j), and humidity(i,j) data is associated with the coordinate values time(i), z(j), lat(i), and lon(i).

=== 5.8.6.2 Ragged Array ===

When the vertical levels vary across profiles, one can use(continguous or non-contiguous) ragged arrays to connect the observations to the profiles. Here we show the non-contiguous case:


{{{
dimensions:
  obs = UNLIMITED ;
  profile = 123 

variables:
  double time(profile);
    time:long_name = "time" ;
    time:units = "days since 1970-01-01 00:00:00" ;
  float lon(profile);
    lon:long_name = "longitude" ;
    lon:units = "degrees_east" ;
  float lat(profile);
    lat:long_name = "latitude" ;
    lat:units = "degrees_north" ;
  int profileIndex(obs) ;
    profileIndex:long_name = "profile index for this level" ;
    profileIndex:standard_name = "ragged_parentIndex" ;

  float z(obs) ;
    z:long_name = "height above mean sea level" ;
    z:units = "km" ;
    z:positive = "up" ;

  float pressure(obs) ;
    pressure:long_name = "pressure level" ;
    pressure:units = "hPa" ;
    pressure:coordinates = "time lon lat z" ;

  float temperature(obs) ;
    temperature:long_name = "skin temperature" ;
    temperature:units = "Celsius" ;
    temperature:coordinates = "time lon lat z" ;

  float humidity(obs) ;
    humidity:long_name = "relative humidity" ;
    humidity:units = "%" ;
    humidity:coordinates = "time lon lat z" ;

attributes:
  :CF\:featureType = "profileSection";
}}}


The pressure(i), temperature(i), and humidity(i) data is associated with the coordinate values time(p), z(i), lat(p), and lon(p), where p = profile_index(i).