Skip to content

Commit

Permalink
chore: bump version to 0.0.5 (#33)
Browse files Browse the repository at this point in the history
* docs: add release notes
* final PR before R003 🎉
* docs: switch to RTD [`build.os`](https://blog.readthedocs.com/use-build-os-config/)
* ci: use mamba for CI builds
* docs: update NSIDC comments for AWS
* feat: add option for viewing full screen leaflet map
* feat: add option to specify the start and end cycle for a local granule
* 003 end cycle now 18
* fix: assert cycle (if entered) is length 2
* feat: add shortcuts for merging Arctic and Antarctic regions
* ci: bump actions to v3
* refactor: create wrapper function `open_dataset`
* fix: pass format to from_file
* fix: don't check if string in open_dataset
* refactor: pass through `HBox` and `VBox` to `widgets`
* docs: use micromamba for RTD builds
* chore: set 003 as the default version
* added note about granule needing to be `s3fs` object in `from_xarray` function
* ci: bump versions to prevent deprecation
* feat: add delayed reads with `dask` for multiple granules
* fix: filter CMR request type using regular expressions
* docs: update release notes

Co-authored-by: Wilson Sauthoff <63430469+wsauthoff@users.noreply.github.com>
  • Loading branch information
tsutterley and wsauthoff authored Oct 26, 2023
1 parent 2cee565 commit 6e366b6
Show file tree
Hide file tree
Showing 14 changed files with 247 additions and 100 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ jobs:
runs-on: ubuntu-20.04

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install dependencies
Expand Down
24 changes: 15 additions & 9 deletions .github/workflows/python-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,23 @@ jobs:
shell: bash -l {0}

steps:
- uses: actions/checkout@v2
- name: Set up conda ${{ matrix.python-version }}
uses: conda-incubator/setup-miniconda@v2
- uses: actions/checkout@v4
- name: Set up mamba ${{ matrix.python-version }}
uses: mamba-org/setup-micromamba@v1
with:
auto-update-conda: true
python-version: ${{ matrix.python-version }}
activate-environment: IS2view
micromamba-version: 'latest'
environment-file: environment.yml
- name: Create conda Test Environment
run: |
conda install flake8 pytest pytest-cov pytest-runner cython
init-shell: bash
environment-name: IS2view
cache-environment: true
post-cleanup: 'all'
create-args: >-
python=${{ matrix.python-version }}
flake8
pytest
pytest-cov
pytest-runner
cython
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
Expand Down
2 changes: 1 addition & 1 deletion IS2view/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import IS2view.version
from IS2view.api import Leaflet, layers, image_service_layer
from IS2view.convert import convert
from IS2view.io import from_file, from_rasterio, from_xarray
from IS2view.io import open_dataset, from_file, from_rasterio, from_xarray
from IS2view.tools import widgets
# get semantic version from setuptools-scm
__version__ = IS2view.version.version
26 changes: 17 additions & 9 deletions IS2view/api.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python
u"""
api.py
Written by Tyler Sutterley (07/2023)
Written by Tyler Sutterley (08/2023)
Plotting tools for visualizing rioxarray variables on leaflet maps
PYTHON DEPENDENCIES:
Expand All @@ -26,6 +26,7 @@
https://docs.xarray.dev/en/stable/
UPDATE HISTORY:
Updated 08/2023: add option for viewing full screen leaflet map
Updated 07/2023: renamed module from IS2view.py to api.py
add plot functions for map basemaps and added geometries
add imshow function for visualizing current leaflet map
Expand All @@ -50,36 +51,36 @@
# attempt imports
try:
import geopandas as gpd
except (ImportError, ModuleNotFoundError) as exc:
except (AttributeError, ImportError, ModuleNotFoundError) as exc:
logging.debug("geopandas not available")
try:
import ipywidgets
except (ImportError, ModuleNotFoundError) as exc:
except (AttributeError, ImportError, ModuleNotFoundError) as exc:
logging.debug("ipywidgets not available")
try:
import ipyleaflet
except (ImportError, ModuleNotFoundError) as exc:
except (AttributeError, ImportError, ModuleNotFoundError) as exc:
logging.debug("ipyleaflet not available")
try:
import matplotlib
import matplotlib.cm as cm
import matplotlib.colorbar
import matplotlib.pyplot as plt
import matplotlib.colors as colors
except (ImportError, ModuleNotFoundError) as exc:
except (AttributeError, ImportError, ModuleNotFoundError) as exc:
logging.critical("matplotlib not available")
try:
import owslib.wms
except (ImportError, ModuleNotFoundError) as exc:
except (AttributeError, ImportError, ModuleNotFoundError) as exc:
logging.debug("owslib not available")
try:
import rasterio.transform
import rasterio.warp
except (ImportError, ModuleNotFoundError) as exc:
except (AttributeError, ImportError, ModuleNotFoundError) as exc:
logging.critical("rasterio not available")
try:
import xarray as xr
except (ImportError, ModuleNotFoundError) as exc:
except (AttributeError, ImportError, ModuleNotFoundError) as exc:
logging.critical("xarray not available")

# set environmental variable for anonymous s3 access
Expand Down Expand Up @@ -167,6 +168,8 @@ class Leaflet:
Include spatial scale bar to map
cursor_control : bool, default True
Include display for cursor location
full_screen_control: bool, default False
Include control for full screen map view
layer_control : bool, default True
Include control for added map layers
draw_control : bool, default False
Expand Down Expand Up @@ -199,6 +202,7 @@ def __init__(self, projection, **kwargs):
# set default keyword arguments
kwargs.setdefault('map', None)
kwargs.setdefault('attribution', False)
kwargs.setdefault('full_screen_control', False)
kwargs.setdefault('scale_control', False)
kwargs.setdefault('cursor_control', True)
kwargs.setdefault('layer_control', True)
Expand Down Expand Up @@ -227,6 +231,10 @@ def __init__(self, projection, **kwargs):
# use a predefined ipyleaflet map
self.map = kwargs['map']
self.crs = self.map.crs['name']
# add control for full screen
if kwargs['full_screen_control']:
self.full_screen_control = ipyleaflet.FullScreenControl()
self.map.add(self.full_screen_control)
# add control for layers
if kwargs['layer_control']:
self.layer_control = ipyleaflet.LayersControl(position='topleft')
Expand All @@ -239,7 +247,7 @@ def __init__(self, projection, **kwargs):
if kwargs['cursor_control']:
self.cursor = ipywidgets.Label()
cursor_control = ipyleaflet.WidgetControl(widget=self.cursor,
position='bottomleft')
position='bottomright')
self.map.add(cursor_control)
# keep track of cursor position
self.map.on_interaction(self.handle_interaction)
Expand Down
8 changes: 4 additions & 4 deletions IS2view/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@
# attempt imports
try:
import h5netcdf
except (ImportError, ModuleNotFoundError) as exc:
except (AttributeError, ImportError, ModuleNotFoundError) as exc:
logging.critical("h5netcdf not available")
try:
import xarray as xr
except (ImportError, ModuleNotFoundError) as exc:
except (AttributeError, ImportError, ModuleNotFoundError) as exc:
logging.critical("xarray not available")

# default groups to skip
Expand All @@ -58,7 +58,7 @@ def nc_to_zarr(self, **kwds):
Parameters
----------
**kwds: dict
keyword arguments for output zarr converter
keyword arguments for output
"""
kwds.setdefault('filename', self.filename)
kwds.setdefault('output', self.output)
Expand All @@ -67,7 +67,7 @@ def nc_to_zarr(self, **kwds):
self.filename = kwds['filename']
self.output = kwds['output']
# split extension from netCDF4 file
if isinstance(self.filename, str):
if isinstance(self.filename, (str, pathlib.Path)):
filename = pathlib.Path(self.filename)
else:
filename = pathlib.Path(self.filename.filename)
Expand Down
130 changes: 109 additions & 21 deletions IS2view/io.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
io.py
Written by Tyler Sutterley (08/2023)
Written by Tyler Sutterley (10/2023)
Utilities for reading gridded ICESat-2 files using rasterio and xarray
PYTHON DEPENDENCIES:
Expand All @@ -18,23 +18,30 @@
https://docs.xarray.dev/en/stable/
UPDATE HISTORY:
Updated 10/2023: use dask.delayed to read multiple files in parallel
Updated 08/2023: use xarray h5netcdf to read files streaming from s3
add open_dataset function for opening multiple granules
add merging of datasets in preparation for Release-3 data
Updated 07/2023: use logging instead of warnings for import attempts
Written 11/2022
"""
from __future__ import annotations
import os
import logging

# attempt imports
try:
import rioxarray
import rioxarray.merge
except (ImportError, ModuleNotFoundError) as exc:
except (AttributeError, ImportError, ModuleNotFoundError) as exc:
logging.critical("rioxarray not available")
try:
import dask
except (AttributeError, ImportError, ModuleNotFoundError) as exc:
logging.critical("dask not available")
try:
import xarray as xr
except (ImportError, ModuleNotFoundError) as exc:
except (AttributeError, ImportError, ModuleNotFoundError) as exc:
logging.critical("xarray not available")

# set environmental variable for anonymous s3 access
Expand All @@ -43,18 +50,25 @@
# default engine for xarray
_default_engine = dict(nc='h5netcdf', zarr='zarr')

def from_file(granule, group=None, format='nc', **kwargs):
def open_dataset(granule,
group: str | None = None,
format: str = 'nc',
parallel: bool = True,
**kwargs
):
"""
Opens gridded ICESat-2 files as ``xarray`` datasets
Reads and optionally merges gridded ICESat-2 files
Parameters
----------
granule: str or list
presigned url or path for granule(s)
presigned url or path for granule(s) as a s3fs object
group: str or NoneType, default None
Data group to read
format: str, default 'nc'
Data format to read
parallel: bool, default True
Open files in parallel using ``dask.delayed``
kwargs: dict
Keyword arguments to pass to ``xarray`` reader
Expand All @@ -63,28 +77,86 @@ def from_file(granule, group=None, format='nc', **kwargs):
ds: object
``xarray`` dataset
"""
# set default engine
kwargs.setdefault('engine', _default_engine[format])
# check if merging multiple granules
if isinstance(granule, list):
# merge multiple granules
datasets = []
closers = []
if parallel:
opener = dask.delayed(from_file)
getattrs = dask.delayed(getattr)
else:
opener = from_file
getattrs = getattr
# read each granule and append to list
for g in granule:
datasets.append(from_xarray(g, group=group, **kwargs))
datasets.append(opener(g,
group=group,
format=format,
**kwargs)
)
closers = [getattrs(ds, "_close") for ds in datasets]
# read datasets as dask arrays
if parallel:
datasets, closers = dask.compute(datasets, closers)
# merge datasets
ds = rioxarray.merge.merge_datasets(datasets)
elif isinstance(granule, str) and format in ('nc',):
ds = from_rasterio(granule, group=group, **kwargs)
else:
# read a single granule
ds = from_xarray(granule, group=group, **kwargs)
ds = from_file(granule,
group=group,
format=format,
**kwargs
)
# return the dataset
return ds

def from_file(granule,
group: str | None = None,
format: str = 'nc',
**kwargs
):
"""
Reads a gridded ICESat-2 file using ``rioxarray`` or ``xarray``
Parameters
----------
granule: str
presigned url or path for granule
group: str or NoneType, default None
Data group to read
format: str, default 'nc'
Data format to read
kwargs: dict
Keyword arguments to pass to ``xarray`` reader
Returns
-------
ds: object
``xarray`` dataset
"""
# set default engine
kwargs.setdefault('engine', _default_engine[format])
if isinstance(granule, str) and format in ('nc',):
ds = from_rasterio(granule,
group=group,
**kwargs
)
else:
# read a single granule
ds = from_xarray(granule,
group=group,
**kwargs
)
# return the dataset
return ds

def from_rasterio(granule, group=None, **kwargs):
def from_rasterio(granule,
group: str | None = None,
**kwargs
):
"""
Reads gridded ICESat-2 files using ``rioxarray``
Reads a gridded ICESat-2 file using ``rioxarray``
Parameters
----------
Expand All @@ -100,12 +172,20 @@ def from_rasterio(granule, group=None, **kwargs):
ds: object
``xarray`` dataset
"""
ds = rioxarray.open_rasterio(granule, group=group, masked=True, **kwargs)
ds = rioxarray.open_rasterio(granule,
group=group,
masked=True,
**kwargs
)
return ds

def from_xarray(granule, group=None, engine='h5netcdf', **kwargs):
def from_xarray(granule,
group: str | None = None,
engine: str = 'h5netcdf',
**kwargs
):
"""
Reads gridded ICESat-2 files using ``xarray``
Reads a gridded ICESat-2 file using ``xarray``
Parameters
----------
Expand All @@ -126,10 +206,18 @@ def from_xarray(granule, group=None, engine='h5netcdf', **kwargs):
kwargs.setdefault('variable', [])
variable = kwargs.pop('variable')
# read xarray dataset
ds = xr.open_dataset(granule, group=group, engine=engine,
chunks='auto', decode_cf=True, mask_and_scale=True,
decode_times=False, concat_characters=True, decode_coords=True,
overwrite_encoded_chunks=False, **kwargs)
ds = xr.open_dataset(granule,
group=group,
engine=engine,
chunks='auto',
decode_cf=True,
mask_and_scale=True,
decode_times=False,
concat_characters=True,
decode_coords=True,
overwrite_encoded_chunks=False,
**kwargs
)
# set the coordinate reference system
ds.rio.write_crs(ds.Polar_Stereographic.attrs['crs_wkt'], inplace=True)
# reduce xarray dataset to specific variables
Expand Down
Loading

0 comments on commit 6e366b6

Please sign in to comment.