Skip to content

Latest commit

 

History

History
1155 lines (913 loc) · 40.4 KB

swath.adoc

File metadata and controls

1155 lines (913 loc) · 40.4 KB

Encoding of Swath Data in the Climate and Forecast Convention

1. Introduction

This is a proposal for an extension of the Climate and Forecast metadata convention (CF) to standardize the encodings of Earth Science swath data in the original instrument viewing geometry. The aim is to have some form of this document added to a future version of the CF convention. The proposal is based on the CF version 1.7 although backward compatibility is not necessarily an objective.

The work is done under the U.S. National Science Foundation award 1541031: "EarthCube IA: Advancing netCDF-CF for the Geoscience Community".

The community-provided examples were valuable resource in preparing this proposal. These examples are collected in a GitHub repository at https://github.com/diwg/diwg.

Encoding examples here are expressed using the NetCDF Common Data Language (CDL) with only the relevant file content to illustrate the concept. They should not be construed as complete and used as templates.

2. Swath Data Encodings

Earth Science swath data originates as electromagnetic radiation collected from a specific direction into a solid angle and then measured at a number of intervals of the electromagnetic spectrum, called bands or channels. The combination of the direction, the solid angle, and the instrument data acquisition settings defines one observation or measurement. At any given instant an instrument sweeps over an area of the Earth while its platform (an object carrying such instrument) moves. Successive observations are usually combined to cover a larger portion of the Earth, sometimes even the entire Earth. When these successive observations are plotted on maps they appear to cover a swath on the Earth’s surface, hence the name for this type of data.

Radiometric swath data are further processed to retrieve geophysical parameters. In the satellite remote sensing context, the former are commonly referred to as Level 1 while the latter are called Level 2 swath data. Thus there are two distinctive swath data domains for which we define encodings: radiometric and geophysical. The chief differences between these two domains are:

  • Spectral (band, channel) coordinates must be defined for all radiometric swath data.

  • The geospatial extent for radiometric swath data is always two-dimensional (area), while for geophysical swath data it can also be three-dimensional.

  • Geophysical swath data may also include coordinates that represent altitude.

The proposed encodings are independent from the observation method and are applicable to swath data acquired by instruments on either satellites, airplanes, or unmanned aerial systems. The swath_data variable in the examples is a generic representation of any parameter that can be stored in swath data files. The order of its dimensions as shown in the examples is not the only one possible or allowed. It should be tailored to expected data access patterns in actual files.

2.1. Geospatial Coordinates

Geospatial coordinates for swath data are comprised of a horizontal and, in some cases, a vertical component. The horizontal component reflects the instrument scanning and stepping in terms of the latitude and longitude for each observation. Because of this the rank of the latitude and longitude coordinates is at least two, but can be larger, with the slowest varying dimension representing forward (along-track) movement of the platform. The reference of latitude-longitude coordinates is a geodetic datum (planetodetic for other planetary bodies). The following example demonstrates a typical case of swath data latitude and longitude coordinates on the WGS-84 geoid.

Example 1: Latitude and longitude coordinates on the WGS-84 geoid for a scanning instrument.
dimensions:
  atrack = 78 ; (1)
  xtrack = 2048 ; (2)

variables:
  float lat(atrack, xtrack) ;
    lat:units = "degrees_north" ; (3)
    lat:standard_name = "latitude" ; (4)

  float lon(atrack, xtrack) ;
    lon:units = "degrees_east" ; (3)
    lon:standard_name = "longitude" ; (4)

  int crs ; (5)
    crs:grid_mapping_name = "latitude_longitude"; (6)
    crs:longitude_of_prime_meridian = 0.0 ; (7)
    crs:semi_major_axis = 6378137.0 ; (7)
    crs:inverse_flattening = 298.257223563 ; (7)
  1. The dimension representing along-track platform movement.

  2. The dimension representing data collected across-track to platform movement.

  3. Required use of the units attribute and its values for the latitude and longitude coordinates.

  4. Required use of the standard_name attribute, identifying the latitude and longitude coordinates.

  5. The grid mapping variable. The term "grid" is for historical reasons only. It’s name and data type are arbitrary.

  6. The value is an identifier that can describe any combination of a geoid, coordinate reference system, and map projection. In this case it is the latitude-longitude reference system.

  7. The attributes related to describing the WGS-84 geoid datum.

As an alternative to having latitude and longitude coordinates, projection coordinates (e.g., fixed grid over a map projection) may be employed instead. When used, there has to be a grid mapping variable describing the map projection. The example below shows the projection coordinates for swath data collected by a geostationary instrument.

Example 2: Projection coordinates for a geostationary scanning instrument.
dimensions:
  y = 1500 ;
  x = 2500 ;

variables:
  short y(y) ;
    y:units = "radian" ;
    y:axis = "Y" ;
    y:standard_name = "projection_y_coordinate" ; (1)

  short x(x) ;
    x:units = "radian" ;
    x:axis = "X" ;
    x:standard_name = "projection_x_coordinate" ; (1)

  int goes_imager_projection ; (2)
    goes_imager_projection:grid_mapping_name = "geostationary" ;
    goes_imager_projection:perspective_point_height = 35786023. ;
    goes_imager_projection:semi_major_axis = 6378137. ;
    goes_imager_projection:semi_minor_axis = 6356752.31414 ;
    goes_imager_projection:inverse_flattening = 298.2572221 ;
    goes_imager_projection:latitude_of_projection_origin = 0. ;
    goes_imager_projection:longitude_of_projection_origin = -89.5 ;
    goes_imager_projection:sweep_angle_axis = "x" ;
  1. Required use of the standard_name attribute with values that identify the projection coordinates.

  2. The grid mapping variable with the attributes describing the Geostationary projection.

Note

It is important to mention here that the requirements to provide geospatial coordinate data for every observation, or include latitude and longitude coordinates in addition to projection coordinates, does preclude the possibility of CF compliance for many existing archives of satellite swath data. For example:

  • The MODIS Level 1B data (see MODIS: HDF-EOS Format) stores latitude and longitude for every 5th scan (atrack) line and across-track observation (xtrack), beginning at position (atrack = 3, xtrack = 3).

  • The GOES-16 Advanced Baseline Imager (ABI) data is reprojected to a fixed grid, which is a normalized geostationary projection. The projection coordinates of the fixed grid are stored rather than latitude/longitude coordinates.

The vertical component of geospatial coordinates represents the altitude associated with swath data. Any of the supported vertical coordinate types are acceptable.

2.2. Time Coordinates

Specifying time coordinates for swath data follows the pertinent CF convention rules. The rank of time coordinates can range from one (one-dimensional) up to the rank of the swath data variable to which they apply (2, 3, …​, n-dimensional). The slowest varying coordinate dimension must represent forward (along-track) movement of the platform.

Example 3: Time coordinate (one- and two-dimensional).

One dimensional time coordinate:

dimensions:
  time = 10 ; (1)

variables:
  double time(time) ;
    time:standard_name = "time" ;
    time:units = "<units> since <datetime string>" ;
    time:calendar = "gregorian" ;
  1. The dimension represents forward (along track) platform movement. It can also be an unlimited dimension (time = UNLIMITED).

Two dimensional time coordinate:

dimensions:
  atrack = 78 ;
  xtrack = 2048 ;

variables:
  double time(atrack, xtrack) ; (1)
    time:standard_name = "time" ;
    time:units = "<units> since <datetime string>" ;
    time:calendar = "gregorian" ;
  1. Time coordinates with the rank greater than one must be listed in the coordinates attribute of their swath data variables.

Note
Some satellite swath data use the International Atomic Time (TAI) which currently is not supported by the CF convention (see this thread on the CF mailing list for background). The encoding examples in this proposal use the Gregorian calendar.

2.3. Spectral Coordinates

The information about the electromagnetic radiation intervals (bands, channels) at which swath data were acquired is encoded as spectral coordinates. The coordinate values can either be stored as physical properties (wavelength, wavenumber, frequency) or alphanumeric strings.

Example 4: Numerical spectral coordinate.

For monotonically increasing or decreasing band data:

dimensions:
  band = 5 ; (1)

variables:
  float band(band) ; (2)
    band:standard_name = "sensor_band_central_radiation_wavelength" ; (3)
    band:units = "μm" ;
  1. Number of bands.

  2. The band variable is a coordinate variable.

  3. One of the standard names specifically for numerical band data. The other two are sensor_band_central_radiation_wavenumber and sensor_band_central_radiation_frequency. More generic names radiation_frequency and radiation_wavelength can also be used when appropriate.

For non-monotonic band data:

dimensions:
  num_band = 5 ; (1)

variables:
  double band(num_band) ; (2)
    band:standard_name = "sensor_band_central_radiation_wavenumber" ;
    band:units = "cm-1" ;
  1. Number of bands.

  2. The band variable must be listed in the coordinates attribute of its swath data variable.

Alphanumeric spectral coordinates are applicable to cases where bands cannot be differentiated solely numerically, for example, when polarization together with electromagnetic spectrum interval information has to be combined.

Example 5: Alphanumerical band coordinate.

For the netCDF extended model (strongly recommended to use):

dimensions:
  num_band = 5 ;

variables:
  string band(num_band) ;
    band:standard_name = "sensor_band_identifier" ;

For the netCDF classic model:

dimensions:
  num_band = 5 ;
  band_strlen = 10 ; (1)

variables:
  char band(num_band, band_strlen) ;
    band:standard_name = "sensor_band_identifier" ; (2)
  1. Number of characters of the longest band string identifier.

  2. The standard name specifically for band string identifiers.

2.4. Radiometric Swath Data Encodings

2.4.1. Multiband

Multiband swath data are very common, collected over an area on the Earth as successive across-track observations at a number of different spectral bands.

Example 6: Multiband data with numerical spectral coordinate.
dimensions:
  time = 120 ;
  scan = 512 ;
  band = 8 ;

variables:
  float band(band) ;
    band:standard_name = "sensor_band_central_radiation_wavelength" ;
    band:units = "μm" ;

  float lat(time, scan) ;
    lat:standard_name = "latitude" ;
    lat:units = "degrees_north" ;

  float lon(time, scan) ;
    lon:standard_name = "longitude" ;
    lon:units = "degrees_east" ;

  double time(time) ;
    time:standard_name = "time" ;
    time:units = "<units> since <datetime string>" ;
    time:calendar = "gregorian" ;

  float swath_data(time, scan, band) ;
    swath_data:coordinates = "lon lat" ;

Another example of the Multiband Swath encoding with alphanumeric-valued band coordinate because numerical information may not always be sufficient to distinguish between spectral bands.

Example 7: Multiband data with alphanumeric spectral coordinate.
dimensions:
  time = UNLIMITED ;
  scan = 1024 ;
  num_band = 8 ;

variables:
  string band(num_band) ;
    band:standard_name = "sensor_band_identifier" ;

  float lat(time, scan) ;
    lat:standard_name = "latitude" ;
    lat:units = "degrees_north" ;

  float lon(time, scan) ;
    lon:standard_name = "longitude" ;
    lon:units = "degrees_east" ;

  double time(time) ;
    time:standard_name = "time" ;
    time:units = "<units> since <datetime string>" ;
    time:calendar = "gregorian" ;

  float swath_data(time, scan, num_band) ;
    swath_data:coordinates = "lon lat band" ;

2.4.2. Multiband Image

The previous encoding is for swath data where a single time instance applies to all across-track observations belonging to one along-track scanning cycle. Single time instance can also apply to a number of successive along-track scanning cycles, for example, when two-dimensional imaging sensors acquire swath data as a multiband image.

Example 8: Multiband Image data.
dimensions:
  time = 1 ;
  nrows = 2048 ; (1)
  ncols = 2048 ; (2)
  band = 10 ;

variables:
  float band(band) ;
    band:standard_name = "sensor_band_central_radiation_wavelength" ;
    band:units = "μm" ;

  float lat(time, nrows, ncols) ;
    lat:standard_name = "latitude" ;
    lat:units = "degrees_north" ;

  float lon(time, nrows, ncols) ;
    lon:standard_name = "longitude" ;
    lon:units = "degrees_east" ;

  double time(time) ;
    time:standard_name = "time" ;
    time:units = "<units> since <datetime string>" ;
    time:calendar = "gregorian" ;

  float swath_data(time, nrows, ncols, band) ;
    swath_data:coordinates = "lon lat" ;
  1. Number of rows of the two-dimensional imaging sensor.

  2. Number of columns of the two-dimensional imaging sensor.

The same encoding is applicable to swath data from hyperspectral sounding instruments which make observations arranged into a grid, typically 2-by-2 or 3-by-3 (see Figure 1). Such grouping of the observations comprises one field-of-regard (FOR).

hypersounder
Figure 1. Cross-track Infrared Sounder (CrIS) instrument’s footprint for the field-of-regard (FOR) typical of one scan. (From Han, Y. et al. (2013), doi:10.1002/2013JD020344.) The field-of-regard contains nine fields-of-view arranged in a 3-by-3 grid, numbered 1 through 9. Observations are made from all fields-of-view independently. Depending on the viewing geometry for each FOR the geospatial extent and relative position varies for every field-of-view.

The following example is identical to the previous one except for two dimensions with changed names.

Example 9: Multiband Image data for each field-of-regard/observation.
dimensions:
  time = 50 ;
  FOR = 45 ; (1)
  obs = 9 ; (2)
  band = 1305 ;

variables:
  float band(band) ;
    band:standard_name = "sensor_band_central_radiation_wavelength" ;
    band:units = "μm" ;

  float lat(time, FOR, obs) ;
    lat:standard_name = "latitude" ;
    lat:units = "degrees_north" ;

  float lon(time, FOR, obs) ;
    lon:standard_name = "longitude" ;
    lon:units = "degrees_east" ;

  double time(time) ;
    time:standard_name = "time" ;
    time:units = "<units> since <datetime string>" ;
    time:calendar = "gregorian" ;

  float swath_data(time, FOR, obs, band) ;
    swath_data:coordinates = "lon lat" ;
  1. The number of fields-of-regard in one across-track scan.

  2. The number of observations within one field-of-regard.

An interesting variation in the above encoding would be if the FOR and obs dimensions exchanged places in the ordering, e.g. (time, FOR, obs, …​)(time, obs, FOR, …​). The latter ordering corresponds to the spatial arrangement of the fields-of-regard and their observations: FOR match the columns and obs match the rows of a two-dimensional imaging sensor. In other words: FORncols and obsnrows.

2.5. Geophysical Swath Data Encodings

2.5.1. Swath

Very common encoding used for storing 2D geophysical parameters.

Example 10: Swath data.
dimensions:
  time = 512 ;
  scan = 1024 ;

variables:
  double time(time) ;
    time:standard_name = "time" ;
    time:units = "<units> since <datetime string>" ;
    time:calendar = "gregorian" ;

  float lat(time, scan) ;
    lat:standard_name = "latitude" ;
    lat:units = "degrees_north" ;

  float lon(time, scan) ;
    lon:standard_name = "longitude" ;
    lon:units = "degrees_east" ;

  float swath_data(time, scan) ;
    swath_data:coordinates = "lon lat" ;

For completeness, the example below uses along- and across-track dimensions instead.

Example 11: Swath data utilizing along- and across-track dimensions.
dimensions:
  atrack = 512 ;
  xtrack = 1024 ;

variables:
  double time(atrack) ;
    time:standard_name = "time" ;
    time:units = "<units> since <datetime string>" ;
    time:calendar = "gregorian" ;

  float lat(atrack, xtrack) ;
    lat:standard_name = "latitude" ;
    lat:units = "degrees_north" ;

  float lon(atrack, xtrack) ;
    lon:standard_name = "longitude" ;
    lon:units = "degrees_east" ;

  float swath_data(atrack, xtrack) ;
    swath_data:coordinates = "time lon lat" ; (1)
  1. time is an auxiliary coordinate (because it is defined with the atrack dimension) and must be listed in the coordinates attribute.

2.5.2. Image Swath

This encoding is the geophysical analog of the Multiband Image encoding.

Example 12: Image Swath data.
dimensions:
  time = 1 ;
  nrows = 1024 ;
  ncols = 3600 ;

variables:
  float lat(time, nrows, ncols) ;
    lat:standard_name = "latitude" ;
    lat:units = "degrees_north" ;

  float lon(time, nrows, ncols) ;
    lon:standard_name = "longitude" ;
    lon:units = "degrees_east" ;

  double time(time) ;
    time:standard_name = "time" ;
    time:units = "<units> since <datetime string>" ;
    time:calendar = "gregorian" ;

  float swath_data(time, nrows, ncols) ;
    swath_data:coordinates = "lon lat" ;
Example 13: The version of Image Swath data for each field-of-regard/observation.
dimensions:
  time = UNLIMITED ;
  FOR = 30 ;
  obs = 9 ;

variables:
  double time(time) ;
    time:standard_name = "time" ;
    time:units = "<units> since <datetime string>" ;
    time:calendar = "gregorian" ;

  float lat(time, FOR, obs) ;
    lat:standard_name = "latitude" ;
    lat:units = "degrees_north" ;

  float lon(time, FOR, obs) ;
    lon:standard_name = "longitude" ;
    lon:units = "degrees_east" ;

  float swath_data(time, FOR, obs) ;
    swath_data:coordinates = "lon lat" ;

2.5.3. Profile

This encoding consists of the Swath encoding with a vertical coordinate. All types of vertical coordinates are allowed.

Example 14: Profile data.
dimensions:
  time = UNLIMITED ;
  scan = 512 ;
  press = 15 ;

variables:
  float press(press) ;
    press:standard_name = "air_pressure" ;
    press:units = "Pa" ;
    press:positive = "up" ;

  float lat(time, scan) ;
    lat:standard_name = "latitude" ;
    lat:units = "degrees_north" ;

  float lon(time, scan) ;
    lon:standard_name = "longitude" ;
    lon:units = "degrees_east" ;

  double time(time) ;
    time:standard_name = "time" ;
    time:units = "<units> since <datetime string>" ;
    time:calendar = "gregorian" ;

  float swath_data(time, scan, press) ;
    swath_data:coordinates = "lon lat" ;
Example 15: Profile data utilizing along- and across-track dimensions.
dimensions:
  atrack = 512 ;
  xtrack = 1024 ;
  press = 15 ;

variables:
  float press(press) ;
    press:standard_name = "air_pressure" ;
    press:units = "Pa" ;
    press:positive = "up" ;

  double time(atrack) ;
    time:standard_name = "time" ;
    time:units = "<units> since <datetime string>" ;
    time:calendar = "gregorian" ;

  float lat(atrack, xtrack) ;
    lat:standard_name = "latitude" ;
    lat:units = "degrees_north" ;

  float lon(atrack, xtrack) ;
    lon:standard_name = "longitude" ;
    lon:units = "degrees_east" ;

  float swath_data(atrack, xtrack, press) ;
    swath_data:coordinates = "time lon lat" ;

2.5.4. Multiband Profile

An interesting trait of this encoding is the presence of a spectral coordinate when storing geophysical parameters.

Example 16: Multiband Profile data.
dimensions:
  time = UNLIMITED ;
  scan = 512 ;
  band = 5 ;
  press = 15 ;

variables:
  float band(band) ;
    band:standard_name = "sensor_band_central_radiation_wavenumber" ;
    band:units = "cm-1" ;

  float press(press) ;
    press:standard_name = "air_pressure" ;
    press:units = "Pa" ;
    press:positive = "up" ;

  float lat(time, scan) ;
    lat:standard_name = "latitude" ;
    lat:units = "degrees_north" ;

  float lon(time, scan) ;
    lon:standard_name = "longitude" ;
    lon:units = "degrees_east" ;

  double time(time) ;
    time:standard_name = "time" ;
    time:units = "<units> since <datetime string>" ;
    time:calendar = "gregorian" ;

  float swath_data(time, scan, press, band) ;
    swath_data:coordinates = "lon lat" ;

2.5.5. Image Profile

This encoding consists of the the Image Swath with an altitude coordinate.

Example 17: Image Profile data.
dimensions:
  time = 1 ;
  nrows = 1024 ;
  ncols = 3600 ;
  press = 100 ;

variables:
  float press(press) ;
    press:standard_name = "air_pressure" ;
    press:units = "Pa" ;
    press:positive = "up" ;

  float lat(time, nrows, ncols) ;
    lat:standard_name = "latitude" ;
    lat:units = "degrees_north" ;

  float lon(time, nrows, ncols) ;
    lon:standard_name = "longitude" ;
    lon:units = "degrees_east" ;

  double time(time) ;
    time:standard_name = "time" ;
    time:units = "<units> since <datetime string>" ;
    time:calendar = "gregorian" ;

  float swath_data(time, nrows, ncols, press) ;
    swath_data:coordinates = "lon lat" ;

As in the few previous encoding examples, assuming that nrows represents fields-of-regard (FOR) and ncols represents observations (obs), the above encoding can also serve for storing profiles from hyperspectral sounders. The following example illustrates this:

Example 18: Image Profile data for fields-of-regard (FOR) with their observations.
dimensions:
  time = 1 ;
  FOR = 1024 ;
  obs = 3600 ;
  press = 100 ;

variables:
  float press(press) ;
    press:standard_name = "air_pressure" ;
    press:units = "Pa" ;
    press:positive = "up" ;

  float lat(time, FOR, obs) ;
    lat:standard_name = "latitude" ;
    lat:units = "degrees_north" ;

  float lon(time, FOR, obs) ;
    lon:standard_name = "longitude" ;
    lon:units = "degrees_east" ;

  double time(time) ;
    time:standard_name = "time" ;
    time:units = "<units> since <datetime string>" ;
    time:calendar = "gregorian" ;

  float swath_data(time, FOR, obs, press) ;
    swath_data:coordinates = "lon lat" ;

2.5.6. Field-of-Regard Profile

There are two more ways to encode swath data with fields-of-regard and their observations. The differences between these encodings are:

  • How much latitude/longitude data are provided: for all observations in each field-of-regard, or just for each field-of-regard as a whole.

  • A two-dimensional layout of observations within the field-of-regard.

Example 19: Field-of-Regard Profile data with latitude and longitude coordinates for each observation in every field-of-regard.
dimensions:
  time = 10 ;
  FOR = 30 ; (1)
  press = 15 ;
  obs_atrack = 3 ; (2)
  obs_xtrack = 3 ; (3)

variables:
  float press(press) ;
    press:standard_name = "air_pressure" ;
    press:units = "Pa" ;
    press:positive = "up" ;

  float lat(time, FOR, obs_atrack, obs_xtrack) ; (4)
    lat:standard_name = "latitude" ;
    lat:units = "degrees_north" ;

  float lon(time, FOR, obs_atrack, obs_xtrack) ; (4)
    lon:standard_name = "longitude" ;
    lon:units = "degrees_east" ;

  double time(time) ;
    time:standard_name = "time" ;
    time:units = "<units> since <datetime string>" ;
    time:calendar = "gregorian" ;

  float swath_data(time, FOR, obs_atrack, obs_xtrack, press) ;
    swath_data:coordinates = "lon lat" ;
  1. Number of fields-of-regard in one across-track scan.

  2. Along-track (row) dimension of the observations matrix.

  3. Across-track (column) dimension of the observations matrix.

  4. Latitude and longitude for all observations in every field-of-regard.

Example 20: Field-of-Regard Profile data with latitude and longitude coordinates for each field-of-regard as a whole.
dimensions:
  time = 10 ;
  FOR = 30 ;
  press = 15 ;
  obs_atrack = 3 ;
  obs_xtrack = 3 ;

variables:
  short obs_atrack(obs_atrack): (1)
    obs_atrack:long_name = "Along-track observation ordinal number" ;

  short obs_xtrack(obs_xtrack): (1)
    obs_xtrack:long_name = "Across-track observation ordinal number" ;

  float press(press) ;
    press:standard_name = "air_pressure" ;
    press:units = "Pa" ;
    press:positive = "up" ;

  float lat(time, FOR) ;
    lat:standard_name = "latitude" ;
    lat:units = "degrees_north" ;

  float lon(time, FOR) ;
    lon:standard_name = "longitude" ;
    lon:units = "degrees_east" ;

  double time(time) ;
    time:standard_name = "time" ;
    time:units = "<units> since <datetime string>" ;
    time:calendar = "gregorian" ;

  float swath_data(time, FOR, obs_atrack, obs_xtrack, press) ;
    swath_data:coordinates = "lon lat" ;
  1. obs_atrack and obs_xtrack are coordinates now compared to the previous example because the lat and lon auxiliary coordinates do not depend on them.

2.6. Encoding Geospatial Extent of Observations

The encodings so far represented observations as geolocated points. Points in mathematics represent geometric entities with no length, area, or volume, i.e. they are zero-dimensional. Real observations, however, have finite geospatial extent. Each of the swath encodings can be augmented with boundary variables to describe the geospatial extent of each observation. This approach is appropriate if:

  • Observation’s geospatial extent can be represented with a single geopolygon with no holes; and

  • the number of vertices for all such geopolygons is the same.

To illustrate the concept, boundary variables are added to the Profile encoding example:

Example 21: Profile data with observation geopolygons.
dimensions:
  atrack = 512 ;
  xtrack = 1024 ;
  press = 15 ;
  vertices = 4 ; (1)

variables:
  float press(press) ;
    press:standard_name = "air_pressure" ;
    press:units = "Pa" ;
    press:positive = "up" ;

  double time(atrack) ;
    time:standard_name = "time" ;
    time:units = "<units> since <datetime string>" ;
    time:calendar = "gregorian" ;

  float lat(atrack, xtrack) ;
    lat:standard_name = "latitude" ;
    lat:units = "degrees_north" ;
    lat:bounds = "lat_vertex" ; (2)

  float lon(atrack, xtrack) ;
    lon:standard_name = "longitude" ;
    lon:units = "degrees_east" ;
    lon:bounds = "lon_vertex" ; (2)

  float lat_vertex(atrack, xtrack, vertices) ; (3)

  float lon_vertex(atrack, xtrack, vertices) ; (3)

  float swath_data(atrack, xtrack, press) ;
    swath_data:coordinates = "time lon lat" ;
  1. The dimension declares the number of geopolygon vertices.

  2. Boundary variables, lat_vertex and lon_vertex, are associated with their respective coordinates.

  3. The boundary variables.

Boundary variables have one more dimension, the fastest-varying one, than their respective coordinates. In the example above they link an observation (atrack, xtrack) with its geopolygon specified by the vertices (lat_vertex(atrack, xtrack, n), lon_vertex(atrack, xtrack, n)), for n=0,…​, vertices-1. The geolocation (lat(atrack, xtrack), lon(atrack, xtrack)) must be contained within this geopolygon. The vertices must be ordered counterclockwise when viewed in the lon-lat plane from above.

2.7. Group Hierarchies

This kind of hierarchy is created using the grouping feature of the netCDF Enhanced Data Model and is colloquially known as groups in files. Since the current CF convention is not based on the Enhanced Data Model such files are not strictly CF compliant. However, groups are present in many swath data files.

The swath data encodings presented here are deliberately group-agnostic although that does not mean the use of groups is prohibited. A proposal on group hierarchies in the CF convention is being developed as part of the same project as this proposal. The most relevant aspect of that proposal for swath data encodings is the set of rules describing how to locate variables referenced in CF attributes like coordinates. It is enough to use just variable names in such attributes for files without groups but this practice is ambiguous in the presence of groups as there could be more than one same-named variable. The group proposal describes how to handle all such cases. The following example illustrates two possible options for referencing variables in the swath encodings: using their names, or with the complete hierarchy starting from the top group.

Example 22: A swath file with groups holding science, ancillary, and coordinates with observation geospatial extent. Two approaches to referencing variables are shown depending on file content organization.

Variables are located in separate groups based on their category: scientific, geolocation, quality information.

dimensions:
  vertex = 4 ;
  time = 392 ;
  band = 4 ;
  xtrack = 35 ;

variables:
  double time(time) ;
    :standard_name = "time" ;
    :units = "<units> since <datetime string>" ;
    :calendar = "gregorian"

  float band(band) ;
    :standard_name = "sensor_band_central_radiation_wavelength" ;
    :units = "nm" ;

group: ancillary {
  variables:
    float quality(time, xtrack, band) ;
      :coordinates = "/time /geolocation/lat /geolocation/lon /band" ;  (1)
}

group: geolocation {
  variables:
    float lat_vertex(time, xtrack, vertex) ;

    float lon_vertex(time, xtrack, vertex) ;

    float lat(time, xtrack) ;
      :standard_name = "latitude" ;
      :units = "degrees_north" ;
      :bounds = "/geolocation/lat_vertex" ;  (1)

    float lon(time, xtrack) ;
      :standard_name = "longitude" ;
      :units = "degrees_east" ;
      :bounds = "/geolocation/lon_vertex" ;
}

group: science {
  variables:
    float science_data(time, xtrack) ;
      :coordinates = "/time /geolocation/lat /geolocation/lon" ;
      :units = "K" ;
      :ancillary_variables = "/ancillary/quality" ;  (1)

    float radiance(time, xtrack, band) ;
      :coordinates = "/time /geolocation/lat /geolocation/lon /band" ;
      :units = "W sr m−2" ;
      :ancillary_variables = "/ancillary/quality" ;
}
  1. Variable reference starting with a / represents the complete hierarchical path to that variable.

All coordinate variables (temporal, spectral, geospatial) are located in a parent group of where other variables are.

dimensions:
  vertex = 4 ;
  time = 392 ;
  band = 4 ;
  xtrack = 35 ;

variables:
  double time(time) ;
    :standard_name = "time" ;
    :units = "<units> since <datetime string>" ;
    :calendar = "gregorian"

  float band(band) ;
    :standard_name = "sensor_band_central_radiation_wavelength" ;
    :units = "nm" ;

  float lat_vertex(time, xtrack, vertex) ;

  float lon_vertex(time, xtrack, vertex) ;

  float lat(time, xtrack) ;
    :standard_name = "latitude" ;
    :units = "degrees_north" ;
    :bounds = "lat_vertex" ;

  float lon(time, xtrack) ;
    :standard_name = "longitude" ;
    :units = "degrees_east" ;
    :bounds = "lon_vertex" ;

group: ancillary {
  variables:
    float quality(time, xtrack, band) ;
      :coordinates = "time lat lon band" ;  (1)
}

group: science {
  variables:
    float science_data(time, xtrack) ;
      :coordinates = "time lat lon" ;
      :units = "K" ;
      :ancillary_variables = "/ancillary/quality" ;  (2)

    float radiance(time, xtrack, band) ;
      :coordinates = "time lat lon band" ;
      :units = "W sr m−2" ;
      :ancillary_variables = "/ancillary/quality" ;
}
  1. Variable references without any / character require search up the ancestral group hierarchy to locate the first variable of the same name.

  2. It is allowed to mix different methods for referencing variables in the same file.

Appendix A: Non-Compliant Geospatial Examples

The following examples are from a variety of satellite swath data stored in the netCDF, HDF4, and HDF5 file formats. This is to illustrate the variety of encodings for swath data, which on one hand provides an impetus to define a CF standard, but on the other hand exemplify the difficulty in getting all satellite data to conform to a standard.

A.1. MODIS: HDF-EOS Format

The Moderate Resolution Imaging Spectroradiometer (MODIS) is a key instrument aboard the Terra (originally known as EOS AM-1) and Aqua (originally known as EOS PM-1) satellites. [1]

CF non-compliant attributes:

  • Groups are used in variables and geolocation fields

  • Non-standard dimension names (2*nscans)

  • Latitude and longitude variables lack standard_name attribute

Example 23: Geospatial array for MODIS (starts at 3,3 and sampled 5x5).
  group: MODIS_SWATH_Type_L1B {
    dimensions:
      2*nscans = 406 ;
      1KM_geo_dim = 271 ;

    variables:
      int 10*nscans(2*nscans=406) ;
        :_DimensionMap = "" ;

      int Max_EV_frames(1KM_geo_dim=271) ;
        :_DimensionMap = "" ;


    group: Geolocation_Fields {
      variables:
        float Latitude(2*nscans=406, 1KM_geo_dim=271) ;
          :units = "degrees_north" ;
          :valid_range = -90.0f, 90.0f ; // float
          :_FillValue = -999.0f ; // float
          :line_numbers = "3,8" ;
          :frame_numbers = "3,8,13,..." ;

        float Longitude(2*nscans=406, 1KM_geo_dim=271) ;
          :units = "degrees_east" ;
          :valid_range = -180.0f, 180.0f ; // float
          :_FillValue = -999.0f ; // float
          :line_numbers = "3,8" ;
          :frame_numbers = "3,8,13,..." ;
    }
}

A.2. TRMM: NetCDF Format

The Tropical Rainfall Measuring Mission (TRMM), a joint mission of NASA and the Japan Aerospace Exploration Agency, was launched in 1997 to study rainfall for weather and climate research. The satellite was retired on 8 April 2015. [2]

CF non-compliant attributes:

  • Latitude and longitude variables lack standard_name attribute.

  • Non-physical values of the units attributes: std_latitude, std_longitude.

Example 24: Geospatial array for TRMM.
dimensions:
  line = 2939 ;
  samp_lo = 104 ;
  samp_hi = 208 ;

variables:
  short lat_hi(line, samp_hi) ;
    lat_hi:units = "std_latitude" ;
    lat_hi:missing_value = -32768s ;
    lat_hi:valid_min = -9000s ;
    lat_hi:valid_max = 9000s ;
    lat_hi:scale_factor = 0.01 ;

  short lat_lo(line, samp_lo) ;
    lat_lo:units = "std_latitude" ;
    lat_lo:missing_value = -32768s ;
    lat_lo:valid_min = -9000s ;
    lat_lo:valid_max = 9000s ;
    lat_lo:scale_factor = 0.01 ;

  short lon_hi(line, samp_hi) ;
    lon_hi:units = "std_longitude" ;
    lon_hi:missing_value = -32768s ;
    lon_hi:valid_min = -18000s ;
    lon_hi:valid_max = 18000s ;
    lon_hi:scale_factor = 0.01 ;

  short lon_lo(line, samp_lo) ;
    lon_lo:units = "std_longitude" ;
    lon_lo:missing_value = -32768s ;
    lon_lo:valid_min = -18000s ;
    lon_lo:valid_max = 18000s ;
    lon_lo:scale_factor = 0.01 ;

A.3. SSM/I: NetCDF Format

The Special Sensor Microwave Imager (SSM/I) Sensor is carried aboard Defense Meteorological Satellite Program (DMSP) satellites DMSP F-8, DMSP F-10, DMSP F-11, DMSP F-12, and DMSP F-13. [3]

CF non-compliant attributes:

  • Latitude and longitude variables lack standard_name attribute.

  • Non-physical values of the units attributes: std_latitude, std_longitude.

Example 25: Geospatial array for SSM/I.
dimensions:
  miline_hi = 1124 ;
  misamp_hi = 128 ;
  miline_lo = 562 ;
  misamp_lo = 64 ;

variables:
  short lat_hi(miline_hi, misamp_hi) ;
    lat_hi:units = "std_latitude" ;
    lat_hi:missing_value = -32768s ;
    lat_hi:valid_min = -32768s ;
    lat_hi:valid_max = 32767s ;
    lat_hi:scale_factor = 0.01 ;

  short lat_lo(miline_lo, misamp_lo) ;
    lat_lo:units = "std_latitude" ;
    lat_lo:missing_value = -32768s ;
    lat_lo:valid_min = -32768s ;
    lat_lo:valid_max = 32767s ;
    lat_lo:scale_factor = 0.01 ;

  short lon_hi(miline_hi, misamp_hi) ;
    lon_hi:units = "std_longitude" ;
    lon_hi:missing_value = -32768s ;
    lon_hi:valid_min = -32768s ;
    lon_hi:valid_max = 32767s ;
    lon_hi:scale_factor = 0.01 ;

  short lon_lo(miline_lo, misamp_lo) ;
    lon_lo:units = "std_longitude" ;
    lon_lo:missing_value = -32768s ;
    lon_lo:valid_min = -32768s ;
    lon_lo:valid_max = 32767s ;
    lon_lo:scale_factor = 0.01 ;

Appendix B: How To Contribute

The adoption of this document relies on its quality and applicability which greatly depends on community feedback. The document is hosted in a GitHub repository and its most up-to-date version can be viewed here. Comments or suggestions are welcome either as issues, pull requests, or emails to the authors.

Sample swath files for testing data access software are available for both the document’s examples (Hyrax, THREDDS) and community-submitted files (Hyrax, THREDDS).

Appendix C: Decision Flowchart

The flowchart below (Figure 2) describes the process for identifying the proposed swath data encodings. It starts from a netCDF variable that is already assumed to represent a swath data parameter. The variable’s rank and the type and rank of its coordinates are used to determine the actual encoding type.

Swath encoding type decision flowchart
Figure 2. Decision flowchart for identifying swath data encoding types.

Appendix D: Revision History

Release Description

DRAFT/2017-05-16

Initial version.

DRAFT/2017-08-31

  • A section on group hierarchy.

  • A section on field-of-view geospatial extents for all swath encodings.

  • featureType variable attribute removed from all examples and text.

  • Vertical coordinates included in the geospatial coordinates section.

  • An example for non-monotonic spectral coordinate.

FINAL DRAFT/2018-03-01

  • Updated the status of the CF convention’s version 1.7 and its link.

  • Replaced "field-of-view" with "observation" throughout the text and examples.

  • Updated the Group Hierarchies section.

FINAL DRAFT/2018-04-27

  • Updated the link for the CF2 Group proposal document.

FINAL DRAFT/2018-06-19

  • Added an annex with decision flowchart for identifying swath encoding types.

  • Added a diagram depicting a hyperspectral sounding instrument’s observation geometry.