# Reading and Writing NetCDF-CF Geometry

## Introduction

This example shows how to

1. add polygon data to a NetCDF-CF Discrete Sampling Geometry timeSeries featureType file,
2. read the polygon data from the NetCDF file,
3. and write it to a standard GIS format.

Below the example, all the geometry types that `ncdfgeom` handles are shown in detail.

## Load Spatial Data

Tables of point, line, or polygon features with associated timeseries are the target for this functionality. Here, we use some sample data from the `ncdfgeom` package.

    example_file <- tempfile()

    file.copy(from = system.file('extdata/example_huc_eta.nc', package = 'ncdfgeom'),
                        to = example_file,
                        overwrite = TRUE) -> quiet

    polygons <- sf::read_sf(system.file('extdata/example_huc_eta.json', package = 'ncdfgeom'))

    polygons <- dplyr::select(polygons, LOADDATE, AREASQKM, HUC12, NAME)

    plot(sf::st_geometry(polygons))

Now we have the polygons as shown above and a NetCDF file with a header that looks like:

Now we can use the `write_geometry` function to add the polygon data to the NetCDF file.

    (vars <- ncmeta::nc_vars(example_file))
    #> # A tibble: 5 × 5
    #>      id name         type      ndims natts
    #>   <int> <chr>        <chr>     <int> <int>
    #> 1     0 lat          NC_DOUBLE     1     4
    #> 2     1 lon          NC_DOUBLE     1     4
    #> 3     2 time         NC_DOUBLE     1     4
    #> 4     3 station_name NC_CHAR       2     5
    #> 5     4 et           NC_INT        2     4

    ncdfgeom::write_geometry(nc_file=example_file,
                             geom_data = polygons,
                             instance_dim_name = "station",
                             variables = vars$name) -> example_file

Now the NetCDF file looks like:

    #> netcdf file9914fa6da7 {
    #> dimensions:
    #>  maxStrlen64 = 64 ;
    #>  station = 2 ;
    #>  time = 25 ;
    #>  char = 12 ;
    #>  node = 2628 ;
    #> variables:
    #>  double lat(station) ;
    #>      lat:units = "degrees_north" ;
    #>      lat:missing_value = -999. ;
    #>      lat:long_name = "latitude of the observation" ;
    #>      lat:standard_name = "latitude" ;
    #>      lat:grid_mapping = "grid_mapping" ;
    #>      lat:geometry = "geometry_container" ;
    #>  double lon(station) ;
    #>      lon:units = "degrees_east" ;
    #>      lon:missing_value = -999. ;
    #>      lon:long_name = "longitude of the observation" ;
    #>      lon:standard_name = "longitude" ;
    #>      lon:grid_mapping = "grid_mapping" ;
    #>      lon:geometry = "geometry_container" ;
    #>  double time(time) ;
    #>      time:units = "days since 1970-01-01 00:00:00" ;
    #>      time:missing_value = -999. ;
    #>      time:long_name = "time of measurement" ;
    #>      time:standard_name = "time" ;
    #>      time:grid_mapping = "grid_mapping" ;
    #>      time:geometry = "geometry_container" ;
    #>  char station_name(station, maxStrlen64) ;
    #>      station_name:units = "" ;
    #>      station_name:missing_value = "" ;
    #>      station_name:long_name = "Station Names" ;
    #>      station_name:cf_role = "timeseries_id" ;
    #>      station_name:standard_name = "station_id" ;
    #>      station_name:grid_mapping = "grid_mapping" ;
    #>      station_name:geometry = "geometry_container" ;
    #>  int et(station, time) ;
    #>      et:units = "mm" ;
    #>      et:missing_value = -999 ;
    #>      et:long_name = "Area Weighted Mean Actual Evapotranspiration" ;
    #>      et:coordinates = "time lat lon" ;
    #>      et:grid_mapping = "grid_mapping" ;
    #>      et:geometry = "geometry_container" ;
    #>  char LOADDATE(station, char) ;
    #>      LOADDATE:units = "unknown" ;
    #>      LOADDATE:grid_mapping = "grid_mapping" ;
    #>      LOADDATE:geometry = "geometry_container" ;
    #>  double AREASQKM(station) ;
    #>      AREASQKM:units = "unknown" ;
    #>      AREASQKM:missing_value = -9999.999 ;
    #>      AREASQKM:grid_mapping = "grid_mapping" ;
    #>      AREASQKM:geometry = "geometry_container" ;
    #>  char HUC12(station, char) ;
    #>      HUC12:units = "unknown" ;
    #>      HUC12:grid_mapping = "grid_mapping" ;
    #>      HUC12:geometry = "geometry_container" ;
    #>  char NAME(station, char) ;
    #>      NAME:units = "unknown" ;
    #>      NAME:grid_mapping = "grid_mapping" ;
    #>      NAME:geometry = "geometry_container" ;
    #>  double x_nodes(node) ;
    #>      x_nodes:units = "degrees_east" ;
    #>      x_nodes:axis = "X" ;
    #>  double y_nodes(node) ;
    #>      y_nodes:units = "degrees_north" ;
    #>      y_nodes:axis = "Y" ;
    #>  int geometry_container ;
    #>      geometry_container:node_coordinates = "x_nodes y_nodes" ;
    #>      geometry_container:geometry_type = "polygon" ;
    #>      geometry_container:node_count = "node_count" ;
    #>      geometry_container:grid_mapping = "grid_mapping" ;
    #>  int node_count(station) ;
    #>      node_count:long_name = "count of coordinates in each instance geometry" ;
    #>  int grid_mapping ;
    #>      grid_mapping:grid_mapping_name = "latitude_longitude" ;
    #>      grid_mapping:semi_major_axis = 6378137. ;
    #>      grid_mapping:inverse_flattening = 298.257223563 ;
    #>      grid_mapping:longitude_of_prime_meridian = 0. ;
    #>
    #> // global attributes:
    #>      :Conventions = "CF-1.8" ;
    #>      :featureType = "timeSeries" ;
    #>      :cdm_data_type = "Station" ;
    #> }

Read the polygon data from the file and write it out to a geopackage.

    polygons_sf <- ncdfgeom::read_geometry(example_file)

    plot(sf::st_geometry(polygons_sf))

    sf::write_sf(polygons_sf, "polygons.gpkg")

## Geometry Types

# Examples - Contiguous Ragged Arrays

## Point (2D)

Well Known Text (WKT): `POINT (30 10)`
Common Data Language (CDL):

    netcdf sample_point {
    dimensions:
        instance = 1 ;
    variables:
        double x_nodes(instance) ;
            x_nodes:units = "degrees_east" ;
            x_nodes:axis = "X" ;
        double y_nodes(instance) ;
            y_nodes:units = "degrees_north" ;
            y_nodes:axis = "Y" ;
        int geometry_container ;
            geometry_container:node_coordinates = "x_nodes y_nodes" ;
            geometry_container:geometry_type = "point" ;
            geometry_container:grid_mapping = "grid_mapping" ;
        int grid_mapping ;
            grid_mapping:grid_mapping_name = "latitude_longitude" ;
            grid_mapping:semi_major_axis = 6378137. ;
            grid_mapping:inverse_flattening = 298.257223563 ;
            grid_mapping:longitude_of_prime_meridian = 0. ;

    // global attributes:
            :Conventions = "CF-1.8" ;
    data:

     x_nodes = 30 ;

     y_nodes = 10 ;

     geometry_container = _ ;

     grid_mapping = _ ;
    }

## LineString (2D)

Well Known Text (WKT): `LINESTRING (30 10, 10 30, 40 40)`
Common Data Language (CDL):

    netcdf sample_linestring {
    dimensions:
        node = 3 ;
        instance = 1 ;
    variables:
        double x_nodes(node) ;
            x_nodes:units = "degrees_east" ;
            x_nodes:axis = "X" ;
        double y_nodes(node) ;
            y_nodes:units = "degrees_north" ;
            y_nodes:axis = "Y" ;
        int geometry_container ;
            geometry_container:node_coordinates = "x_nodes y_nodes" ;
            geometry_container:geometry_type = "line" ;
            geometry_container:node_count = "node_count" ;
            geometry_container:grid_mapping = "grid_mapping" ;
        int node_count(instance) ;
            node_count:long_name = "count of coordinates in each instance geometry" ;
        int grid_mapping ;
            grid_mapping:grid_mapping_name = "latitude_longitude" ;
            grid_mapping:semi_major_axis = 6378137. ;
            grid_mapping:inverse_flattening = 298.257223563 ;
            grid_mapping:longitude_of_prime_meridian = 0. ;

    // global attributes:
            :Conventions = "CF-1.8" ;
    data:

     x_nodes = 30, 10, 40 ;

     y_nodes = 10, 30, 40 ;

     geometry_container = _ ;

     node_count = 3 ;

     grid_mapping = _ ;
    }

## Polygon (2D)

Well Known Text (WKT): `POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))`
Common Data Language (CDL):

    netcdf sample_polygon {
    dimensions:
        node = 5 ;
        instance = 1 ;
    variables:
        double x_nodes(node) ;
            x_nodes:units = "degrees_east" ;
            x_nodes:axis = "X" ;
        double y_nodes(node) ;
            y_nodes:units = "degrees_north" ;
            y_nodes:axis = "Y" ;
        int geometry_container ;
            geometry_container:node_coordinates = "x_nodes y_nodes" ;
            geometry_container:geometry_type = "polygon" ;
            geometry_container:node_count = "node_count" ;
            geometry_container:grid_mapping = "grid_mapping" ;
        int node_count(instance) ;
            node_count:long_name = "count of coordinates in each instance geometry" ;
        int grid_mapping ;
            grid_mapping:grid_mapping_name = "latitude_longitude" ;
            grid_mapping:semi_major_axis = 6378137. ;
            grid_mapping:inverse_flattening = 298.257223563 ;
            grid_mapping:longitude_of_prime_meridian = 0. ;

    // global attributes:
            :Conventions = "CF-1.8" ;
    data:

     x_nodes = 30, 40, 20, 10, 30 ;

     y_nodes = 10, 40, 40, 20, 10 ;

     geometry_container = _ ;

     node_count = 5 ;

     grid_mapping = _ ;
    }

## MultiLineString (2D)

Well Known Text (WKT): `MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))`
Common Data Language (CDL):

    netcdf sample_multilinestring {
    dimensions:
        node = 7 ;
        instance = 1 ;
        part = 2 ;
    variables:
        double x_nodes(node) ;
            x_nodes:units = "degrees_east" ;
            x_nodes:axis = "X" ;
        double y_nodes(node) ;
            y_nodes:units = "degrees_north" ;
            y_nodes:axis = "Y" ;
        int geometry_container ;
            geometry_container:node_coordinates = "x_nodes y_nodes" ;
            geometry_container:geometry_type = "line" ;
            geometry_container:node_count = "node_count" ;
            geometry_container:part_node_count = "part_node_count" ;
            geometry_container:grid_mapping = "grid_mapping" ;
        int node_count(instance) ;
            node_count:long_name = "count of coordinates in each instance geometry" ;
        int part_node_count(part) ;
            part_node_count:long_name = "count of nodes in each geometry part" ;
        int grid_mapping ;
            grid_mapping:grid_mapping_name = "latitude_longitude" ;
            grid_mapping:semi_major_axis = 6378137. ;
            grid_mapping:inverse_flattening = 298.257223563 ;
            grid_mapping:longitude_of_prime_meridian = 0. ;

    // global attributes:
            :Conventions = "CF-1.8" ;
    data:

     x_nodes = 10, 20, 10, 40, 30, 40, 30 ;

     y_nodes = 10, 20, 40, 40, 30, 20, 10 ;

     geometry_container = _ ;

     node_count = 7 ;

     part_node_count = 3, 4 ;

     grid_mapping = _ ;
    }

## MultiPolygon (2D)

Well Known Text (WKT): `MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))`
Common Data Language (CDL):

    netcdf sample_multipolygon {
    dimensions:
        node = 9 ;
        instance = 1 ;
        part = 2 ;
    variables:
        double x_nodes(node) ;
            x_nodes:units = "degrees_east" ;
            x_nodes:axis = "X" ;
        double y_nodes(node) ;
            y_nodes:units = "degrees_north" ;
            y_nodes:axis = "Y" ;
        int geometry_container ;
            geometry_container:node_coordinates = "x_nodes y_nodes" ;
            geometry_container:geometry_type = "polygon" ;
            geometry_container:node_count = "node_count" ;
            geometry_container:interior_ring = "interior_ring" ;
            geometry_container:grid_mapping = "grid_mapping" ;
            geometry_container:part_node_count = "part_node_count" ;
        int node_count(instance) ;
            node_count:long_name = "count of coordinates in each instance geometry" ;
        int part_node_count(part) ;
            part_node_count:long_name = "count of nodes in each geometry part" ;
        int interior_ring(part) ;
            interior_ring:long_name = "type of each geometry part" ;
        int grid_mapping ;
            grid_mapping:grid_mapping_name = "latitude_longitude" ;
            grid_mapping:semi_major_axis = 6378137. ;
            grid_mapping:inverse_flattening = 298.257223563 ;
            grid_mapping:longitude_of_prime_meridian = 0. ;

    // global attributes:
            :Conventions = "CF-1.8" ;
    data:

     x_nodes = 30, 45, 10, 30, 15, 40, 10, 5, 15 ;

     y_nodes = 20, 40, 40, 20, 5, 10, 20, 10, 5 ;

     geometry_container = _ ;

     node_count = 9 ;

     part_node_count = 4, 5 ;

     interior_ring = 0, 0 ;

     grid_mapping = _ ;
    }

## Polygon with One Interior Ring (2D)

Well Known Text (WKT): `POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30))`
Common Data Language (CDL):

    netcdf sample_polygon_hole {
    dimensions:
        node = 9 ;
        instance = 1 ;
        part = 2 ;
    variables:
        double x_nodes(node) ;
            x_nodes:units = "degrees_east" ;
            x_nodes:axis = "X" ;
        double y_nodes(node) ;
            y_nodes:units = "degrees_north" ;
            y_nodes:axis = "Y" ;
        int geometry_container ;
            geometry_container:node_coordinates = "x_nodes y_nodes" ;
            geometry_container:geometry_type = "polygon" ;
            geometry_container:node_count = "node_count" ;
            geometry_container:interior_ring = "interior_ring" ;
            geometry_container:grid_mapping = "grid_mapping" ;
            geometry_container:part_node_count = "part_node_count" ;
        int node_count(instance) ;
            node_count:long_name = "count of coordinates in each instance geometry" ;
        int part_node_count(part) ;
            part_node_count:long_name = "count of nodes in each geometry part" ;
        int interior_ring(part) ;
            interior_ring:long_name = "type of each geometry part" ;
        int grid_mapping ;
            grid_mapping:grid_mapping_name = "latitude_longitude" ;
            grid_mapping:semi_major_axis = 6378137. ;
            grid_mapping:inverse_flattening = 298.257223563 ;
            grid_mapping:longitude_of_prime_meridian = 0. ;

    // global attributes:
            :Conventions = "CF-1.8" ;
    data:

     x_nodes = 35, 45, 15, 10, 35, 20, 35, 30, 20 ;

     y_nodes = 10, 45, 40, 20, 10, 30, 35, 20, 30 ;

     geometry_container = _ ;

     node_count = 9 ;

     part_node_count = 5, 4 ;

     interior_ring = 0, 1 ;

     grid_mapping = _ ;
    }

## MultiPolygon with One Interior Ring (2D)

Well Known Text (WKT): `MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)))`
Common Data Language (CDL):

    netcdf sample_multipolygon_hole {
    dimensions:
        node = 14 ;
        instance = 1 ;
        part = 3 ;
    variables:
        double x_nodes(node) ;
            x_nodes:units = "degrees_east" ;
            x_nodes:axis = "X" ;
        double y_nodes(node) ;
            y_nodes:units = "degrees_north" ;
            y_nodes:axis = "Y" ;
        int geometry_container ;
            geometry_container:node_coordinates = "x_nodes y_nodes" ;
            geometry_container:geometry_type = "polygon" ;
            geometry_container:node_count = "node_count" ;
            geometry_container:interior_ring = "interior_ring" ;
            geometry_container:grid_mapping = "grid_mapping" ;
            geometry_container:part_node_count = "part_node_count" ;
        int node_count(instance) ;
            node_count:long_name = "count of coordinates in each instance geometry" ;
        int part_node_count(part) ;
            part_node_count:long_name = "count of nodes in each geometry part" ;
        int interior_ring(part) ;
            interior_ring:long_name = "type of each geometry part" ;
        int grid_mapping ;
            grid_mapping:grid_mapping_name = "latitude_longitude" ;
            grid_mapping:semi_major_axis = 6378137. ;
            grid_mapping:inverse_flattening = 298.257223563 ;
            grid_mapping:longitude_of_prime_meridian = 0. ;

    // global attributes:
            :Conventions = "CF-1.8" ;
    data:

     x_nodes = 40, 20, 45, 40, 20, 10, 10, 30, 45, 20, 30, 20, 20, 30 ;

     y_nodes = 40, 45, 30, 40, 35, 30, 10, 5, 20, 35, 20, 15, 25, 20 ;

     geometry_container = _ ;

     node_count = 14 ;

     part_node_count = 4, 6, 4 ;

     interior_ring = 0, 0, 1 ;

     grid_mapping = _ ;
    }

## Multiple MultiPolygons with Interior Rings (2D)

Well Known Text (WKT): `MULTIPOLYGON(((0 0, 20 0, 20 20, 0 20, 0 0), (1 1, 10 5, 19 1, 1 1), (5 15, 7 19, 9 15, 5 15), (11 15, 13 19, 15 15, 11 15)), ((5 25, 9 25, 7 29, 5 25)), ((11 25, 15 25, 13 29, 11 25)))`
Common Data Language (CDL):

    netcdf sample_multipolygons_holes {
    dimensions:
        node = 25 ;
        instance = 1 ;
        part = 6 ;
    variables:
        double x_nodes(node) ;
            x_nodes:units = "degrees_east" ;
            x_nodes:axis = "X" ;
        double y_nodes(node) ;
            y_nodes:units = "degrees_north" ;
            y_nodes:axis = "Y" ;
        int geometry_container ;
            geometry_container:node_coordinates = "x_nodes y_nodes" ;
            geometry_container:geometry_type = "polygon" ;
            geometry_container:node_count = "node_count" ;
            geometry_container:interior_ring = "interior_ring" ;
            geometry_container:grid_mapping = "grid_mapping" ;
            geometry_container:part_node_count = "part_node_count" ;
        int node_count(instance) ;
            node_count:long_name = "count of coordinates in each instance geometry" ;
        int part_node_count(part) ;
            part_node_count:long_name = "count of nodes in each geometry part" ;
        int interior_ring(part) ;
            interior_ring:long_name = "type of each geometry part" ;
        int grid_mapping ;
            grid_mapping:grid_mapping_name = "latitude_longitude" ;
            grid_mapping:semi_major_axis = 6378137. ;
            grid_mapping:inverse_flattening = 298.257223563 ;
            grid_mapping:longitude_of_prime_meridian = 0. ;

    // global attributes:
            :Conventions = "CF-1.8" ;
    data:

     x_nodes = 0, 20, 20, 0, 0, 1, 10, 19, 1, 5, 7, 9, 5, 11, 13, 15, 11, 5, 9,
        7, 5, 11, 15, 13, 11 ;

     y_nodes = 0, 0, 20, 20, 0, 1, 5, 1, 1, 15, 19, 15, 15, 15, 19, 15, 15, 25,
        25, 29, 25, 25, 25, 29, 25 ;

     geometry_container = _ ;

     node_count = 25 ;

     part_node_count = 5, 4, 4, 4, 4, 4 ;

     interior_ring = 0, 1, 1, 1, 0, 0 ;

     grid_mapping = _ ;
    }