Skip to content
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.11]
python-version: [3.11, 3.12]

steps:
- uses: actions/checkout@v7
Expand Down
3 changes: 1 addition & 2 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ version: 2
build:
os: ubuntu-lts-latest
tools:
python: "3.11"
python: "3.12"

# Build documentation in the docs/ directory with Sphinx
sphinx:
Expand All @@ -26,4 +26,3 @@ python:
extra_requirements:
- nbtest
- rtd

6 changes: 3 additions & 3 deletions docs/advanced_stress_packages/01_lake.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@
"outputs": [],
"source": [
"lak_stage = pd.read_csv(os.path.join(ds.model_ws, \"lak_STAGE.csv\"), index_col=0)\n",
"lak_stage.index = pd.to_datetime(ds.time.start) + pd.to_timedelta(lak_stage.index, \"d\")\n",
"lak_stage.index = pd.to_datetime(ds.time.start) + pd.to_timedelta(lak_stage.index, \"D\")\n",
"lak_stage.columns = [x.capitalize() for x in lak_stage.columns]\n",
"\n",
"f, ax = plt.subplots(figsize=(10, 6), layout=\"constrained\")\n",
Expand Down Expand Up @@ -533,7 +533,7 @@
" for key in lake_bgt.keys():\n",
" data[key] = [x[x[\"node\"] == lakeno + 1][\"q\"].sum() for x in lake_bgt[key]]\n",
"\n",
" index = pd.to_datetime(ds.time.start) + pd.to_timedelta(cbf_lak.get_times(), \"d\")\n",
" index = pd.to_datetime(ds.time.start) + pd.to_timedelta(cbf_lak.get_times(), \"D\")\n",
" df = pd.DataFrame(data, index=index)\n",
"\n",
" f, ax = plt.subplots(figsize=(10, 6), layout=\"constrained\")\n",
Expand All @@ -548,7 +548,7 @@
" outflow = df_block.where(df_block < 0, 0.0)\n",
" outflow = outflow.loc[:, ~(outflow == 0).all()]\n",
"\n",
" ymax = max(inflow.sum(1).max(), outflow.sum(1).max())\n",
" ymax = max(inflow.sum(axis=1).max(), outflow.sum(axis=1).max())\n",
"\n",
" inflow.plot.area(color=colors, ax=ax, linewidth=0)\n",
" handles_in, labels_in = ax.get_legend_handles_labels()\n",
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/09_schoonhoven.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -854,7 +854,7 @@
"# add grid\n",
"dcs.plot_grid()\n",
"# add labels with layer names\n",
"dcs.plot_layers(alpha=0.0, min_label_area=1000)"
"dcs.plot_layers(alpha=0.0, min_label_area=1000);"
]
}
],
Expand Down
10 changes: 6 additions & 4 deletions docs/examples/16_groundwater_transport.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
"start_time = \"2010-1-1\"\n",
"starting_head = 1.0\n",
"\n",
"municipalities = nlmod.read.administrative.download_municipalities(\n",
"municipalities = nlmod.read.administrative.download_municipalities_gdf(\n",
" extent=extent_hbossche\n",
")"
]
Expand All @@ -90,7 +90,7 @@
"metadata": {},
"outputs": [],
"source": [
"fig, ax = nlmod.plot.get_map(extent_hbossche, background=\"OpenStreetMap.Mapnik\")"
"fig, ax = nlmod.plot.get_map(extent_hbossche, background=True)"
]
},
{
Expand Down Expand Up @@ -254,7 +254,9 @@
"outputs": [],
"source": [
"# aggregate chloride to our layer model using weighted mean\n",
"cli_da = nlmod.layers.aggregate_by_weighted_mean_to_ds(ds, cli, \"3d-chloride\")\n",
"cli_da = nlmod.layers.aggregate_by_weighted_mean_to_ds(\n",
" ds, cli, \"3d-chloride\", source_botm_name=\"bottom\"\n",
")\n",
"\n",
"# interpolate NaNs nearest\n",
"for ilay in range(cli_da.shape[0]):\n",
Expand Down Expand Up @@ -496,7 +498,7 @@
"outputs": [],
"source": [
"# plot using flopy\n",
"fig, ax = nlmod.plot.get_map(extent_hbossche, background=\"OpenStreetMap.Mapnik\")\n",
"fig, ax = nlmod.plot.get_map(extent_hbossche, background=True)\n",
"pmv = fp.plot.PlotMapView(model=gwf, layer=0, ax=ax)\n",
"# pc = pmv.plot_array(c.isel(time=0), cmap=\"Spectral_r\")\n",
"pmv.plot_bc(\"GHB\", plotAll=True, alpha=0.1, label=\"GHB\")\n",
Expand Down
10 changes: 10 additions & 0 deletions docs/examples/nested_models.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@
"import nlmod"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"nlmod.util.get_color_logger(\"INFO\")\n",
"nlmod.show_versions()"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand Down
2 changes: 1 addition & 1 deletion nlmod/dims/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ def get_ds(
coords = {"x": x, "y": y, "layer": layer}
if angrot != 0.0:
affine = grid.get_affine_mod_to_world(attrs)
xc, yc = affine * np.meshgrid(x, y)
xc, yc = affine @ np.meshgrid(x, y)
coords["xc"] = (("y", "x"), xc)
coords["yc"] = (("y", "x"), yc)

Expand Down
12 changes: 6 additions & 6 deletions nlmod/dims/grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -832,7 +832,7 @@ def ds_to_gridprops(ds_in, gridprops, method="nearest", icvert_nodata=-1):
)
if is_rotated(ds_out):
affine = get_affine_mod_to_world(ds_out)
ds_out["xc"], ds_out["yc"] = affine * (ds_out.x, ds_out.y)
ds_out["xc"], ds_out["yc"] = affine @ (ds_out.x, ds_out.y)

if "area" in gridprops:
if "area" in ds_out:
Expand Down Expand Up @@ -2553,7 +2553,7 @@ def get_affine_mod_to_world(ds):
xorigin = attrs["xorigin"]
yorigin = attrs["yorigin"]
angrot = attrs["angrot"]
return Affine.translation(xorigin, yorigin) * Affine.rotation(angrot)
return Affine.translation(xorigin, yorigin) @ Affine.rotation(angrot)


def get_affine_world_to_mod(ds):
Expand All @@ -2562,7 +2562,7 @@ def get_affine_world_to_mod(ds):
xorigin = attrs["xorigin"]
yorigin = attrs["yorigin"]
angrot = attrs["angrot"]
return Affine.rotation(-angrot) * Affine.translation(-xorigin, -yorigin)
return Affine.rotation(-angrot) @ Affine.translation(-xorigin, -yorigin)


def get_affine(ds, sx=None, sy=None):
Expand All @@ -2588,10 +2588,10 @@ def get_affine(ds, sx=None, sy=None):
yoff = yorigin + dy * np.cos(angrot * np.pi / 180)
return (
Affine.translation(xoff, yoff)
* Affine.scale(sx, sy)
* Affine.rotation(angrot)
@ Affine.scale(sx, sy)
@ Affine.rotation(angrot)
)
else:
xoff = attrs["extent"][0]
yoff = attrs["extent"][3]
return Affine.translation(xoff, yoff) * Affine.scale(sx, sy)
return Affine.translation(xoff, yoff) @ Affine.scale(sx, sy)
3 changes: 2 additions & 1 deletion nlmod/observations.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
import xarray as xr
from geopandas import GeoDataFrame, points_from_xy
from scipy.spatial import Delaunay
from tqdm import tqdm

from nlmod.dims import grid

from .util import tqdm

logger = logging.getLogger(__name__)


Expand Down
41 changes: 36 additions & 5 deletions nlmod/read/nhi.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def add_buisdrainage(
depth_var="buisdrain_depth",
cond_method="average",
depth_method="mode",
fix_crs=None,
):
"""Add data about the buisdrainage to the model Dataset.

Expand Down Expand Up @@ -114,6 +115,11 @@ def add_buisdrainage(
depth_method : str, optional
The method to transform the depth of buisdrainage to the model Dataset. The
default is "mode".
fix_crs : bool, optional

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add a reference to this PR. The image in the PR speaks a thousand bookparts.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea, will add a link to the docstring

Fix the coordinate shift (~100 m to the northeast) in the original NHI
buisdrainage dataset caused by a missing datum transformation (Bessel 1841 to
WGS84) in the source dataset. If None, a warning is issued and fix_crs
defaults to False to preserve backwards compatibility. The default is None.

Returns
-------
Expand All @@ -130,11 +136,12 @@ def add_buisdrainage(
)
ds_out = discretize_buisdrainage(
ds,
pathname,
cond_var,
depth_var,
cond_method,
depth_method,
pathname=pathname,
cond_var=cond_var,
depth_var=depth_var,
cond_method=cond_method,
depth_method=depth_method,
fix_crs=fix_crs,
)
ds.update(ds_out)
return ds
Expand All @@ -147,6 +154,7 @@ def discretize_buisdrainage(
depth_var="buisdrain_depth",
cond_method="average",
depth_method="mode",
fix_crs=None,
):
"""Add data about the buisdrainage to the model Dataset.

Expand Down Expand Up @@ -180,13 +188,27 @@ def discretize_buisdrainage(
depth_method : str, optional
The method to transform the depth of buisdrainage to the model Dataset. The
default is "mode".
fix_crs : bool, optional
Fix the coordinate shift (~100 m to the northeast) in the original NHI
buisdrainage dataset caused by a missing datum transformation (Bessel 1841 to
WGS84) in the source dataset. If None, a warning is issued and fix_crs
defaults to False to preserve backwards compatibility. The default is None.

Returns
-------
ds : xr.Dataset
The model dataset with added variables with the names `cond_var` and
`depth_var`.
"""
if fix_crs is None:
logger.warning(
"`fix_crs` was not specified and defaults to False. Set `fix_crs=True` to "
"fix the ~100 m coordinate shift (to the northeast) in the original NHI "
"buisdrainage dataset caused by a missing datum transformation in the "
"source dataset."
)
fix_crs = False

if pathname is None:
pathname = ds.cachedir
# download files if needed
Expand All @@ -198,10 +220,17 @@ def discretize_buisdrainage(

ds_out = get_ds_empty(ds, keep_coords=("y", "x"))

src_crs = (
"+proj=longlat +ellps=bessel "
"+towgs84=565.417,50.3319,465.552,-0.398957,0.343988,-1.8774,4.0725 +no_defs"
)

# use cond_methd for conductance
# (default is "average" to account for locations without pipe drainage, where the
# conductance is 0)
buisdrain_c = rioxarray.open_rasterio(fname_c, mask_and_scale=True)[0]
if fix_crs:
buisdrain_c = buisdrain_c.rio.write_crs(src_crs)
# calculate a conductance (per m2) from a resistance
cond = 1 / buisdrain_c
# set conductance to 0 where resistance is infinite or 0
Expand All @@ -216,6 +245,8 @@ def discretize_buisdrainage(
# (default is "mode" for depth that occurs most in each cell)
mask_and_scale = False
buisdrain_d = rioxarray.open_rasterio(fname_d, mask_and_scale=mask_and_scale)[0]
if fix_crs:
buisdrain_d = buisdrain_d.rio.write_crs(src_crs)
if mask_and_scale:
nodata = np.nan
else:
Expand Down
65 changes: 35 additions & 30 deletions nlmod/read/rws.py

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what is happening with these changes but I assume it is some fix for RWS bathymetry download?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the FeatureServer is not avialable anymore, but the MapServer is. Then in Python 3.12, the stuff with the env-variable GDAL_DISABLE_READDIR_ON_OPEN was needed, otherwise it would look for non-existing files and fail.

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import numpy as np
import xarray as xr
from rioxarray.merge import merge_arrays
from rasterio.env import Env

from .. import NLMOD_DATADIR, cache, dims, util
from ..util import tqdm
Expand Down Expand Up @@ -340,9 +341,9 @@ def get_gdr_configuration() -> dict:
config["bodemhoogte_1m"] = {
"url": (
"https://geo.rijkswaterstaat.nl/arcgis/rest/services/GDR/"
"bodemhoogte_index/FeatureServer"
"bodemhoogte_index/MapServer"
),
"layer": 1,
"layer": 1, # bodemhoogte_1mtr
}
# NOTE: the 20m resolution is no longer available from the GDR service via a
# geodataframe containing the url.
Expand Down Expand Up @@ -529,34 +530,38 @@ def download_bathymetry(
xmin, xmax, ymin, ymax = extent
dataarrays = []

for _, row in tqdm(
gdf.iterrows(), desc="Downloading bathymetry", total=gdf.index.size
):
url = row["geotiff"]
ds = xr.open_dataset(url, engine="rasterio")
ds = ds.assign_coords({"y": ds["y"].round(0), "x": ds["x"].round(0)})
da = (
ds["band_data"]
.sel(band=1, x=slice(xmin, xmax), y=slice(ymax, ymin))
.drop_vars("band")
)
if chunks:
da = da.chunk(chunks)
dataarrays.append(da)

if len(dataarrays) > 1:
da = merge_arrays(
dataarrays,
bounds=[xmin, ymin, xmax, ymax],
res=res,
method=method,
)
else:
da = dataarrays[0]
if res is not None:
da = da.rio.reproject(
da.rio.crs,
rasterio_env = {
"GDAL_DISABLE_READDIR_ON_OPEN": "YES",
}
with Env(**rasterio_env):
for _, row in tqdm(
gdf.iterrows(), desc="Downloading bathymetry", total=gdf.index.size
):
url = row["geotiff"]
ds = xr.open_dataset(url, engine="rasterio")
ds = ds.assign_coords({"y": ds["y"].round(0), "x": ds["x"].round(0)})
da = (
ds["band_data"]
.sel(band=1, x=slice(xmin, xmax), y=slice(ymax, ymin))
.drop_vars("band")
)
if chunks:
da = da.chunk(chunks)
dataarrays.append(da)

if len(dataarrays) > 1:
da = merge_arrays(
dataarrays,
bounds=[xmin, ymin, xmax, ymax],
res=res,
resampling=method,
method=method,
)
else:
da = dataarrays[0]
if res is not None:
da = da.rio.reproject(
da.rio.crs,
res=res,
resampling=method,
)
return da
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ classifiers = [
"Intended Audience :: Other Audience",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
Expand Down Expand Up @@ -84,6 +83,8 @@ rtd = [
"sphinx_rtd_theme",
"nbconvert",
"openpyxl",
"numpy<2.5.0", # until there is a new version of netCDF4 (now 1.7.4) this solves "Setting the shape on a NumPy array has been deprecated"
"affine<3.0.1", # until there are newer versions of rasterio (now 1.5.1) and rioxarray (now 0.23.0), this solves "Use @ matmul instead of * mul operator for matrix multiplication"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But you did fix the matmul right? Is this <3.0.1 still necessary?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed it in our own code. But it still needs to be fixed in methods within rasterio and rioxarray, that we use.

]
lgn = ["xarray-spatial", "exactextract"]

Expand Down
Loading
Loading