Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quadtree read/write, no build #226

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
727bf41
potential bugfix havg
roeldegoede Oct 31, 2024
e128fda
undo previous commit and update computation of wet fractions (and inc…
roeldegoede Oct 31, 2024
394b174
fix wet fractions and corresponding havg
roeldegoede Nov 27, 2024
5de8c07
Merge branch 'bugfix_havg' of https://github.com/Deltares/hydromt_sfi…
roeldegoede Nov 27, 2024
89812ae
add dependencies
roeldegoede Nov 29, 2024
67eca40
add reading/writing quadtree netcdf files
roeldegoede Nov 29, 2024
f6c4039
add read/write subgrid quadtree
roeldegoede Nov 29, 2024
c74ec03
add quadtree io in main sfincs.py
roeldegoede Nov 29, 2024
e932bd7
Solve some linting warnings
roeldegoede Nov 29, 2024
8d4c697
Merge branch 'main' of https://github.com/Deltares/hydromt_sfincs int…
roeldegoede Nov 29, 2024
4cb09ce
Merge branch 'bugfix_havg' of https://github.com/Deltares/hydromt_sfi…
roeldegoede Nov 29, 2024
5154f11
make datashader optional dep
DirkEilander Dec 11, 2024
84c96f1
add bounds property (since the original calls read_grid) and remove g…
roeldegoede Dec 19, 2024
cd14730
fixing read_results and plot_basemap for quadtree models. Also fixed …
roeldegoede Dec 19, 2024
2446903
add quadtree IO tests
roeldegoede Dec 19, 2024
7c6ebed
extended tests for quadtree io, plot_basemap and read_results
roeldegoede Dec 20, 2024
584ca56
imrpoved downscaling methods (both bugfixes and allow for ugrids)
roeldegoede Dec 20, 2024
abefe02
pre-commit linting
roeldegoede Dec 20, 2024
be8e811
pre-commit test-data
roeldegoede Dec 20, 2024
b94a9b1
changed fix for plotting; not longer reprojecting to epsg4326 by defa…
roeldegoede Dec 20, 2024
5ca44b7
fix pyflwdir version for now (to be investigated)
roeldegoede Dec 20, 2024
fd38678
fix typo in pyproject.toml
roeldegoede Dec 20, 2024
f3303d1
test fixing docs workflows, since Mambaforge gets deprecated ...
roeldegoede Dec 20, 2024
a7a30e1
miniforge3?
roeldegoede Dec 20, 2024
a8cbef7
Delete tests/data/sfincs_test_quadtree/sfincs_log.txt
Leynse Dec 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ jobs:
- name: checkout code
uses: actions/checkout@v3

- name: Setup Mambaforge
- name: Setup Miniforge
uses: conda-incubator/setup-miniconda@v2
with:
python-version: '3.11'
miniforge-variant: Mambaforge
miniforge-variant: Miniforge3
miniforge-version: latest
use-mamba: true

Expand Down
111 changes: 56 additions & 55 deletions .github/workflows/tests_dev.yml
Original file line number Diff line number Diff line change
@@ -1,62 +1,63 @@
name: Tests with HydroMT dev
# Uncomment when we move to HydroMT v1
# name: Tests with HydroMT dev

on:
# trigger weekly on monday at 00:00 UTC
schedule:
- cron: '0 0 * * 1'
push:
branches: [main]
paths:
- .github/workflows/tests_dev.yml
- tests/*
- hydromt_sfincs/*
- pyproject.toml
pull_request:
branches: [main]
paths:
- .github/workflows/tests_dev.yml
- tests/*
- hydromt_sfincs/*
- pyproject.toml
# on:
# # trigger weekly on monday at 00:00 UTC
# schedule:
# - cron: '0 0 * * 1'
# push:
# branches: [main]
# paths:
# - .github/workflows/tests_dev.yml
# - tests/*
# - hydromt_sfincs/*
# - pyproject.toml
# pull_request:
# branches: [main]
# paths:
# - .github/workflows/tests_dev.yml
# - tests/*
# - hydromt_sfincs/*
# - pyproject.toml

jobs:
build:
defaults:
run:
shell: bash -l {0}
strategy:
fail-fast: false
runs-on: ubuntu-latest
timeout-minutes: 30
concurrency:
group: ${{ github.workflow }}-${{ matrix.python-version }}-${{ github.ref }}
cancel-in-progress: true
steps:
# jobs:
# build:
# defaults:
# run:
# shell: bash -l {0}
# strategy:
# fail-fast: false
# runs-on: ubuntu-latest
# timeout-minutes: 30
# concurrency:
# group: ${{ github.workflow }}-${{ matrix.python-version }}-${{ github.ref }}
# cancel-in-progress: true
# steps:

- uses: actions/checkout@v3
# - uses: actions/checkout@v3

- uses: actions/setup-python@v5
id: pip
with:
# caching, see https://github.com/actions/setup-python/blob/main/docs/advanced-usage.md#caching-packages
cache: 'pip'
python-version: '3.9' # 3.9 is not supported by last released version of hydromt
cache-dependency-path: pyproject.toml
# - uses: actions/setup-python@v5
# id: pip
# with:
# # caching, see https://github.com/actions/setup-python/blob/main/docs/advanced-usage.md#caching-packages
# cache: 'pip'
# python-version: '3.9' # 3.9 is not supported by last released version of hydromt
# cache-dependency-path: pyproject.toml

# true if cache-hit occurred on the primary key
- name: Cache hit
run: echo '${{ steps.pip.outputs.cache-hit }}'
# # true if cache-hit occurred on the primary key
# - name: Cache hit
# run: echo '${{ steps.pip.outputs.cache-hit }}'

# build environment with pip
- name: Install hydromt-sfincs
run: |
pip install --upgrade pip
pip install .[test,examples]
pip install git+https://github.com/Deltares/hydromt.git
pip list
# # build environment with pip
# - name: Install hydromt-sfincs
# run: |
# pip install --upgrade pip
# pip install .[test,examples]
# pip install git+https://github.com/Deltares/hydromt.git
# pip list

# run test
- name: Test
run: |
export NUMBA_DISABLE_JIT=1
python -m pytest --verbose
# # run test
# - name: Test
# run: |
# export NUMBA_DISABLE_JIT=1
# python -m pytest --verbose
2 changes: 2 additions & 0 deletions envs/hydromt-sfincs-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ channels:
dependencies:
- affine
- cartopy
- datashader
- descartes
- ffmpeg
- geopandas>1.0
Expand All @@ -30,6 +31,7 @@ dependencies:
- shapely
- sphinx
- xarray
- xugrid
- pip:
- black[jupyter]
- sphinx_design
106 changes: 75 additions & 31 deletions hydromt_sfincs/plots.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
"""Plotting functions for SFINCS model data."""

from typing import Dict, List, Tuple
from typing import Dict, List, Tuple, Union

import numpy as np
import logging
import pandas as pd
import xarray as xr
import xugrid as xu

from .utils import get_bounds_vector

logger = logging.getLogger(__name__)

__all__ = ["plot_forcing", "plot_basemap"]

geom_style = {
Expand Down Expand Up @@ -107,7 +110,7 @@ def plot_forcing(forcing: Dict, **kwargs):


def plot_basemap(
ds: xr.Dataset,
ds: Union[xr.Dataset, xu.UgridDataset],
geoms: Dict,
variable: str = "dep",
shaded: bool = False,
Expand All @@ -121,6 +124,7 @@ def plot_basemap(
geom_kwargs: Dict = {},
legend_kwargs: Dict = {},
bmap_kwargs: Dict = {},
logger=logger,
**kwargs,
):
"""Create basemap plot.
Expand Down Expand Up @@ -175,10 +179,43 @@ def plot_basemap(
has_cx = False

# read crs and utm zone > convert to cartopy
proj_crs = ds.raster.crs
if isinstance(ds, xr.Dataset):
proj_crs = ds.raster.crs
bounds = ds.raster.box.buffer(abs(ds.raster.res[0] * 1)).total_bounds
bbox = ds.raster.transform_bounds(4326)
ratio = ds.raster.ycoords.size / (ds.raster.xcoords.size * 1.4)
elif isinstance(ds, xu.UgridDataset):
proj_crs = ds.grid.crs
bounds = ds.ugrid.total_bounds
bbox = ds.ugrid.to_crs(4326).ugrid.total_bounds
ratio = (bounds[3] - bounds[1]) / ((bounds[2] - bounds[0]) * 1.4)
proj_str = proj_crs.name
extent = np.array(bounds)[[0, 2, 1, 3]]

if proj_crs.is_projected and proj_crs.to_epsg() is not None:
crs = ccrs.epsg(ds.raster.crs.to_epsg())
if "UTM" in proj_str:
# Extract the UTM zone number
utm_zone = proj_crs.utm_zone
# Parse the zone number and hemisphere
zone_number = int(utm_zone[:-1]) # Extract numeric part
hemisphere = utm_zone[-1] # Last character is 'N' or 'S'
# Determine if it's in the southern hemisphere
is_southern_hemisphere = hemisphere == "S"
# Create the Cartopy UTM projection
crs = ccrs.UTM(zone_number, southern_hemisphere=is_southern_hemisphere)
else:
crs = ccrs.epsg(proj_crs.to_epsg())

# now check whether the model extent is within the extent of the crs
bounds_crs = crs.boundary.bounds
if (
bounds[0] < bounds_crs[0]
or bounds[1] < bounds_crs[1]
or bounds[2] > bounds_crs[2]
or bounds[3] > bounds_crs[3]
):
logger.warning("Model domain exceeds the area of the CRS.")
logger.warning("Consider reprojecting to EPSG:4326 for plotting.")
unit = proj_crs.axis_info[0].unit_name
unit = "m" if unit == "metre" else unit
xlab, ylab = f"x [{unit}] - {proj_str}", f"y [{unit}]"
Expand All @@ -187,20 +224,17 @@ def plot_basemap(
xlab, ylab = f"lon [deg] - {proj_str}", "lat [deg]"
else:
raise ValueError("Unsupported CRS")
bounds = ds.raster.box.buffer(abs(ds.raster.res[0] * 1)).total_bounds
extent = np.array(bounds)[[0, 2, 1, 3]]

# create fig with geo-axis and set background
if figsize is None:
ratio = ds.raster.ycoords.size / (ds.raster.xcoords.size * 1.4)
figsize = (6, 6 * ratio)
fig = plt.figure(figsize=figsize)
ax = plt.subplot(projection=crs)
ax.set_extent(extent, crs=crs)
if bmap is not None:
if zoomlevel == "auto": # auto zoomlevel
c = 2 * np.pi * 6378137 # Earth circumference
lat = np.array(ds.raster.transform_bounds(4326))[[1, 3]].mean()
lat = np.array(bbox)[[1, 3]].mean()
# max 4 x 4 tiles per image
tile_size = max(bounds[2] - bounds[0], bounds[3] - bounds[1]) / 4
if proj_crs.is_geographic:
Expand Down Expand Up @@ -247,30 +281,35 @@ def plot_basemap(
kwargs0["cbar_kwargs"].update(ticks=[1, 2, 3])

if variable in ds:
da = ds[variable].raster.mask_nodata()
da = ds[variable]
if "msk" in ds and np.any(ds["msk"] > 0):
da = da.where(ds["msk"] > 0)
if da.raster.rotation != 0 and "xc" in da.coords and "yc" in da.coords:
da.plot(transform=crs, x="xc", y="yc", ax=ax, zorder=1, **kwargs0)
else:
da.plot.imshow(transform=crs, ax=ax, zorder=1, **kwargs0)
if shaded and variable == "dep" and da.raster.rotation == 0:
ls = colors.LightSource(azdeg=315, altdeg=45)
dx, dy = da.raster.res
_rgb = ls.shade(
da.fillna(0).values,
norm=kwargs0["norm"],
cmap=kwargs0["cmap"],
blend_mode="soft",
dx=dx,
dy=dy,
vert_exag=2,
)
rgb = xr.DataArray(
dims=("y", "x", "rgb"), data=_rgb, coords=da.raster.coords
)
rgb = xr.where(np.isnan(da), np.nan, rgb)
rgb.plot.imshow(transform=crs, ax=ax, zorder=1)
if isinstance(da, xr.DataArray):
# mask_nodata converts it to an xr.DataArray, so performed inside if-else statement
da = da.raster.mask_nodata()
if da.raster.rotation != 0 and "xc" in da.coords and "yc" in da.coords:
da.plot(transform=crs, x="xc", y="yc", ax=ax, zorder=1, **kwargs0)
else:
da.plot.imshow(transform=crs, ax=ax, zorder=1, **kwargs0)
if shaded and variable == "dep" and da.raster.rotation == 0:
ls = colors.LightSource(azdeg=315, altdeg=45)
dx, dy = da.raster.res
_rgb = ls.shade(
da.fillna(0).values,
norm=kwargs0["norm"],
cmap=kwargs0["cmap"],
blend_mode="soft",
dx=dx,
dy=dy,
vert_exag=2,
)
rgb = xr.DataArray(
dims=("y", "x", "rgb"), data=_rgb, coords=da.raster.coords
)
rgb = xr.where(np.isnan(da), np.nan, rgb)
rgb.plot.imshow(transform=crs, ax=ax, zorder=1)
elif isinstance(da, xu.UgridDataArray):
da.ugrid.plot(transform=crs, ax=ax, zorder=1, **kwargs0)

# geometry plotting and annotate kwargs
for k, d in geom_kwargs.items():
Expand All @@ -290,6 +329,11 @@ def plot_basemap(
"No 'msk' (sfincs.msk) found in ds required to plot the model bounds "
"Set plot_bounds=False or add 'msk' to ds"
)
elif plot_bounds and isinstance(ds, xu.UgridDataset):
raise NotImplementedError(
"Plotting of the boundaries for quadtree grids is not yet implemented. "
"Set plot_bounds=False to proceed."
)
elif plot_bounds and (ds["msk"] >= 1).any():
gdf_msk = get_bounds_vector(ds["msk"])
gdf_msk2 = gdf_msk[gdf_msk["value"] == 2]
Expand Down
Loading
Loading