Skip to content

Commit 284a9bc

Browse files
authored
Merge pull request #5 from nsidc/due-cryo-119-netcdf-coords
Due cryo 119 netcdf coords
2 parents 8f14d15 + 22ccea9 commit 284a9bc

File tree

4 files changed

+117
-1
lines changed

4 files changed

+117
-1
lines changed

_quarto.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,13 @@ website:
2828
- section: "How do I..."
2929
href: nsidc-data-cookbook/how-to-guides/overview.qmd
3030
contents:
31-
- section: "reproject data"
31+
- section: "reproject data?"
32+
- section: "work with NetCDF files?"
33+
contents:
34+
- text: "get the bounding box of a netcdf file"
35+
href: nsidc-data-cookbook/how-to-guides/netcdf_cf.qmd
36+
- text: "get the latitude and longitudes for grid cells"
37+
href: nsidc-data-cookbook/how-to-guides/get_latitude_and_longitude.qmd
3238
- section: "Tutorials"
3339
contents:
3440
- section: "Reference Guides"
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
title: "How to get latitude and longitude for the grid cells of a NetCDF file"
3+
author: Andrew P. Barrett
4+
date: last-modified
5+
---
6+
7+
## Problem
8+
My data are in a NetCDF file but there are no latitude and longitude coordinates. How do I get the latitude and longitude for each grid cell center?
9+
10+
## Solution
11+
```
12+
import numpy as np
13+
14+
import rioxarray
15+
import xarray as xr
16+
from pyproj import CRS, Transformer
17+
18+
19+
# Load dataset using decode_coords='all'
20+
ds = xr.open_dataset('example_data/NSIDC0081_SEAICE_PS_N25km_20230627_v2.0.nc',
21+
decode_coords='all')
22+
23+
# Instantiate source pyproj CRS from ds.rio.crs object
24+
source_crs = CRS(ds.rio.crs.to_wkt())
25+
# Instantiate destination pyproj CRS using EPSG code for WGS84
26+
destination_crs = CRS(4326)
27+
28+
# Instantiate pyproj transformer instance using source and destination CRS
29+
transform = Transformer.from_crs(source_crs, destination_crs)
30+
31+
# Create 2D arrays of x and y coordinates
32+
x2d, y2d = np.meshgrid(ds.x, ds.y)
33+
34+
# Calculate latitude and longitudes for each grid cell
35+
lat, lon = transform.transform(x2d, y2d)
36+
```
37+
38+
## Discussion
39+
The workflow above assumes that the NetCDF file is CF-compliant and has a `grid_mapping` variable and projected coordinates defined. See the `rioxarray` [CRS management documentation](https://corteva.github.io/rioxarray/stable/getting_started/crs_management.html#) if this is not the case.
40+
41+
If the dataset `rio.crs` is not set, the `source_crs` could be defined using the EPSG code or other projection information directly, without having to set the dataset crs.
42+
43+
For all datasets on projected grids, latitude and longitudes for grid cells will be 2-dimensional arrays. x- and y-coordinates for these datasets will be vectors. So 2-dimensional arrays of x and y coordinates need to be created. `np.meshgrid` is designed to do this.
44+
45+
If you want the latitudes and longitudes of the corners of grid cells, x and y coordinates can be incremented by half the grid cells width and height, and the resoluting projected coordinates passed to `transform.transformer`.
46+
47+
It is worth considering if you really need to have latitude and longitude arrays for your dataset. One reason that CF-conventions were changed, was that 2D arrays of geographic coordinates can increase the file size, especially for high resolution datasets. Projected coordinates as vectors take up much less space. Plotting tools such as `cartopy` can make maps without latitude and longitude. If you need to extract cell values for specific geographic coordinates, for example for a weather station or buoy, it is quicker and easier to transform these coordinates into the projected coordinate system of the dataset. The `xarray.Dataset.sel` or `xarray.DataArray.sel` methods can then be used to select the data for that location.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
---
2+
title: "How do I get the bounding box of a NetCDF file in latitude and longitude?"
3+
author: Andrew P. Barrett
4+
date: last-modified
5+
---
6+
7+
## Problem
8+
My data are in a NetCDF file but there are no latitude and longitude coordinates. How do I find the bounding box in latitude and longitude?
9+
10+
## Solution
11+
### Using xarray and rioxarray
12+
```
13+
import rioxarray
14+
import xarray as xr
15+
16+
17+
# Load dataset using decode_coords='all'
18+
ds = xr.open_dataset('example_data/NSIDC0081_SEAICE_PS_N25km_20230627_v2.0.nc',
19+
decode_coords='all')
20+
21+
# Get the bounds of the data grid in WGS84 (EPSG code 4362)
22+
ds.rio.transform_bounds(4326)
23+
```
24+
```
25+
(-180.0, 30.98056405144958, 180.0, 90.0)
26+
```
27+
28+
## Discussion
29+
30+
As of [CF v1.8](https://cfconventions.org/Data/cf-conventions/cf-conventions-1.10/cf-conventions.html#grid-mappings-and-projections), latitude and longitude coordinate variables are no longer required in CF-compliant NetCDF files, as long as projected horizontal spatial coordinates (e.g. `x` and `y`, or `easting` and `northing`) and a `grid_mapping` variable is provided. The `grid_mapping` variable defines the coordinate reference system (CRS) of the projected horizontal coordinates.
31+
32+
The easiest, and frankly best, way to read and work with NetCDF files is to use `xarray`. `rioxarray` extends the `xarray` package to use CRS and make geospatial tasks, such as reprojecting and regridding, easier. By setting the keyword `decode-coords='all'`, `rioxarray` searches the `xarray.DataArray` or `xarray.Dataset` for the CRS. `rioxarray` also determines the `transform` or `geotransform`, which defines the image CRS that transforms cell coordinates (column and row) into projected coordinates (x, y). The `transform` is calculated from the coordinates of the data. See the `rioxarray` [documentaton](https://corteva.github.io/rioxarray/stable/getting_started/crs_management.html#) for more details.
33+
34+
Both these spatial coordinates can be accessed as follows:
35+
36+
```
37+
ds.rio.crs
38+
```
39+
```
40+
CRS.from_wkt('PROJCS["NSIDC Sea Ice Polar Stereographic North",GEOGCS["Unspecified datum based upon the Hughes 1980 ellipsoid",DATUM["Not_specified_based_on_Hughes_1980_ellipsoid",SPHEROID["Hughes 1980",6378273,298.279411123061,AUTHORITY["EPSG","7058"]],AUTHORITY["EPSG","6054"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4054"]],PROJECTION["Polar_Stereographic"],PARAMETER["latitude_of_origin",70],PARAMETER["central_meridian",-45],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH],AUTHORITY["EPSG","3411"]]')
41+
```
42+
43+
```
44+
ds.rio.transform()
45+
```
46+
```
47+
Affine(25000.0, 0.0, -3850000.0,
48+
0.0, -25000.0, 5850000.0)
49+
```
50+
51+
If this information is not found, all is not lost. [`rio.write_crs`](https://corteva.github.io/rioxarray/stable/getting_started/crs_management.html#Setting-the-CRS) and [`rio.write_transform`](https://corteva.github.io/rioxarray/stable/getting_started/crs_management.html#Setting-the-transform-of-the-dataset) can be used to set the CRS and the transform of the dataset.
52+
53+
Once these variables are set, the horizontal spatial bounds of the dataset can be queried as described above. If all you want is the bounds in projected coordinates, these can be accessed using `ds.rio.bounds()`.
54+
55+
```
56+
ds.rio.bounds()
57+
```
58+
```
59+
(-3850000.0, -5350000.0, 3750000.0, 5850000.0)
60+
```
61+

nsidc-data-cookbook/how-to-guides/overview.qmd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@ _Maybe add a short description of each section here_
2525
- Searching for data;
2626
- Accessing data;
2727
- Reprojecting and resampling data;
28+
- Working with CF-compliant NetCDF files
29+
- [How do I get the bounding box of a NetCDF file in latitude and longitude?](netcdf_cf.qmd)
2830

0 commit comments

Comments
 (0)