eo-tides 0.7.6.dev1__py3-none-any.whl → 0.7.6.dev3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- eo_tides/__init__.py +14 -18
- eo_tides/eo.py +57 -41
- eo_tides/model.py +227 -153
- eo_tides/stats.py +65 -27
- eo_tides/utils.py +107 -71
- eo_tides/validation.py +26 -21
- {eo_tides-0.7.6.dev1.dist-info → eo_tides-0.7.6.dev3.dist-info}/METADATA +2 -2
- eo_tides-0.7.6.dev3.dist-info/RECORD +10 -0
- eo_tides-0.7.6.dev1.dist-info/RECORD +0 -10
- {eo_tides-0.7.6.dev1.dist-info → eo_tides-0.7.6.dev3.dist-info}/WHEEL +0 -0
- {eo_tides-0.7.6.dev1.dist-info → eo_tides-0.7.6.dev3.dist-info}/licenses/LICENSE +0 -0
eo_tides/__init__.py
CHANGED
@@ -1,8 +1,4 @@
|
|
1
|
-
"""
|
2
|
-
eo_tides
|
3
|
-
========
|
4
|
-
|
5
|
-
Tide modelling tools for large-scale satellite earth observation analysis.
|
1
|
+
"""eo_tides: Tide modelling tools for large-scale satellite earth observation analysis.
|
6
2
|
|
7
3
|
`eo-tides` provides powerful parallelized tools for integrating satellite
|
8
4
|
Earth observation data with tide modelling. `eo-tides` combines advanced
|
@@ -20,10 +16,10 @@ location globally.
|
|
20
16
|
Modules
|
21
17
|
-------
|
22
18
|
model : Core tide modelling functionality
|
23
|
-
eo :
|
24
|
-
stats :
|
25
|
-
utils :
|
26
|
-
validation :
|
19
|
+
eo : Tools for integrating satellite EO data with tide modelling
|
20
|
+
stats : Tools for analysing local tide dynamics and satellite biases
|
21
|
+
utils : General-purpose utilities for tide model setup and data processing
|
22
|
+
validation : Validation tools for comparing modelled tides to observed tide gauge data.
|
27
23
|
"""
|
28
24
|
|
29
25
|
from importlib.metadata import version
|
@@ -37,18 +33,18 @@ from .validation import eval_metrics, load_gauge_gesla
|
|
37
33
|
|
38
34
|
# Define what should be imported with "from eo_tides import *"
|
39
35
|
__all__ = [
|
40
|
-
"
|
41
|
-
"model_phases",
|
36
|
+
"clip_models",
|
42
37
|
"ensemble_tides",
|
43
|
-
"tag_tides",
|
44
|
-
"pixel_tides",
|
45
|
-
"tide_stats",
|
46
|
-
"pixel_stats",
|
47
38
|
"eval_metrics",
|
48
|
-
"load_gauge_gesla",
|
49
|
-
"clip_models",
|
50
|
-
"list_models",
|
51
39
|
"idw",
|
40
|
+
"list_models",
|
41
|
+
"load_gauge_gesla",
|
42
|
+
"model_phases",
|
43
|
+
"model_tides",
|
44
|
+
"pixel_stats",
|
45
|
+
"pixel_tides",
|
46
|
+
"tag_tides",
|
47
|
+
"tide_stats",
|
52
48
|
]
|
53
49
|
|
54
50
|
# Add version metadata to package
|
eo_tides/eo.py
CHANGED
@@ -1,7 +1,13 @@
|
|
1
|
+
"""Tools for integrating satellite EO data with tide modelling.
|
2
|
+
|
3
|
+
This module provides tools for combining satellite EO observations with
|
4
|
+
tide heights and phases using one or more ocean tide models, both at
|
5
|
+
the timestep and at the pixel level.
|
6
|
+
"""
|
7
|
+
|
1
8
|
# Used to postpone evaluation of type annotations
|
2
9
|
from __future__ import annotations
|
3
10
|
|
4
|
-
import os
|
5
11
|
import textwrap
|
6
12
|
import warnings
|
7
13
|
from typing import TYPE_CHECKING
|
@@ -13,6 +19,8 @@ from odc.geo.geobox import GeoBox
|
|
13
19
|
|
14
20
|
# Only import if running type checking
|
15
21
|
if TYPE_CHECKING:
|
22
|
+
import os
|
23
|
+
|
16
24
|
from odc.geo import Shape2d
|
17
25
|
|
18
26
|
from .model import model_phases, model_tides
|
@@ -23,13 +31,13 @@ def _resample_chunks(
|
|
23
31
|
data: xr.DataArray | xr.Dataset | GeoBox,
|
24
32
|
dask_chunks: tuple | None = None,
|
25
33
|
) -> tuple | Shape2d:
|
26
|
-
"""
|
34
|
+
"""Create optimised dask chunks for reprojection.
|
35
|
+
|
27
36
|
Automatically return optimised dask chunks
|
28
37
|
for reprojection with `_pixel_tides_resample`.
|
29
38
|
Use entire image if GeoBox or if no default
|
30
39
|
chunks; use existing chunks if they exist.
|
31
40
|
"""
|
32
|
-
|
33
41
|
# If dask_chunks is provided, return directly
|
34
42
|
if dask_chunks is not None:
|
35
43
|
return dask_chunks
|
@@ -51,12 +59,12 @@ def _standardise_inputs(
|
|
51
59
|
data: xr.DataArray | xr.Dataset | GeoBox,
|
52
60
|
time: DatetimeLike | None,
|
53
61
|
) -> tuple[GeoBox, np.ndarray | None]:
|
54
|
-
"""
|
62
|
+
"""Standardise location and time inputs to tide modelling functions.
|
63
|
+
|
55
64
|
Takes an xarray or GeoBox input and an optional custom times,
|
56
65
|
and returns a standardised GeoBox and times (usually an
|
57
66
|
array, but possibly None).
|
58
67
|
"""
|
59
|
-
|
60
68
|
# If `data` is an xarray object, extract its GeoBox and time
|
61
69
|
if isinstance(data, xr.DataArray | xr.Dataset):
|
62
70
|
# Try to extract GeoBox
|
@@ -70,7 +78,7 @@ def _standardise_inputs(
|
|
70
78
|
Import `odc.geo.xr` then run `data = data.odc.assign_crs(crs=...)`
|
71
79
|
to prepare your data before passing it to this function.
|
72
80
|
"""
|
73
|
-
raise Exception(textwrap.dedent(error_msg).strip())
|
81
|
+
raise Exception(textwrap.dedent(error_msg).strip()) from None
|
74
82
|
|
75
83
|
# Use custom time by default if provided; otherwise try and extract from `data`
|
76
84
|
if time is not None:
|
@@ -78,7 +86,8 @@ def _standardise_inputs(
|
|
78
86
|
elif "time" in data.dims:
|
79
87
|
time = np.asarray(data.coords["time"].values)
|
80
88
|
else:
|
81
|
-
|
89
|
+
err_msg = "`data` does not have a 'time' dimension, and no custom times were provided via `time`."
|
90
|
+
raise ValueError(err_msg)
|
82
91
|
|
83
92
|
# If `data` is a GeoBox, use it directly; raise an error if no time was provided
|
84
93
|
elif isinstance(data, GeoBox):
|
@@ -86,11 +95,13 @@ def _standardise_inputs(
|
|
86
95
|
if time is not None:
|
87
96
|
time = _standardise_time(time)
|
88
97
|
else:
|
89
|
-
|
98
|
+
err_msg = "If `data` is a GeoBox, custom times must be provided via `time`."
|
99
|
+
raise ValueError(err_msg)
|
90
100
|
|
91
101
|
# Raise error if no valid inputs were provided
|
92
102
|
else:
|
93
|
-
|
103
|
+
err_msg = "`data` must be an xarray.DataArray, xarray.Dataset, or odc.geo.geobox.GeoBox."
|
104
|
+
raise TypeError(err_msg)
|
94
105
|
|
95
106
|
return gbox, time
|
96
107
|
|
@@ -103,9 +114,11 @@ def _pixel_tides_resample(
|
|
103
114
|
dask_compute=True,
|
104
115
|
name="tide_height",
|
105
116
|
):
|
106
|
-
"""
|
107
|
-
|
108
|
-
|
117
|
+
"""Resample low resolution tides modelled by `pixel_tides` to higher resolution.
|
118
|
+
|
119
|
+
Uses `odc-geo` to reproject data to match the geobox (e.g.
|
120
|
+
spatial resolution and extent) of the original higher resolution
|
121
|
+
satellite dataset.
|
109
122
|
|
110
123
|
Parameters
|
111
124
|
----------
|
@@ -171,18 +184,14 @@ def tag_tides(
|
|
171
184
|
return_phases: bool = False,
|
172
185
|
**model_tides_kwargs,
|
173
186
|
) -> xr.DataArray | xr.Dataset:
|
174
|
-
"""
|
175
|
-
Model tide heights and tide phases for every timestep in a
|
176
|
-
multi-dimensional dataset, and return a new array that can
|
177
|
-
be used to "tag" each observation with tide information.
|
187
|
+
"""Model tide heights and phases for every dataset timestep using multiple ocean tide models.
|
178
188
|
|
179
|
-
|
180
|
-
|
181
|
-
|
189
|
+
Tides are modelled using the centroid of the dataset by
|
190
|
+
default; use `tidepost_lat` and `tidepost_lon` to specify
|
191
|
+
a custom tidal modelling location.
|
182
192
|
|
183
|
-
|
184
|
-
|
185
|
-
models supported by `pyTMD`, including:
|
193
|
+
The function supports all tidal models supported by `pyTMD`,
|
194
|
+
including:
|
186
195
|
|
187
196
|
- Empirical Ocean Tide model (EOT20)
|
188
197
|
- Finite Element Solution tide models (FES2022, FES2014, FES2012)
|
@@ -191,6 +200,10 @@ def tag_tides(
|
|
191
200
|
- Hamburg direct data Assimilation Methods for Tides models (HAMTIDE11)
|
192
201
|
- Technical University of Denmark tide models (DTU23)
|
193
202
|
|
203
|
+
This function requires access to tide model data files.
|
204
|
+
For tide model setup instructions, refer to the guide:
|
205
|
+
https://geoscienceaustralia.github.io/eo-tides/setup/
|
206
|
+
|
194
207
|
Parameters
|
195
208
|
----------
|
196
209
|
data : xarray.Dataset or xarray.DataArray or odc.geo.geobox.GeoBox
|
@@ -242,6 +255,7 @@ def tag_tides(
|
|
242
255
|
"tide_height" and "tide_phase" variables.
|
243
256
|
Outputs will contain values for every timestep in `data`, or for
|
244
257
|
every time in `times` if provided.
|
258
|
+
|
245
259
|
"""
|
246
260
|
# Standardise data inputs, time and models
|
247
261
|
gbox, time_coords = _standardise_inputs(data, time)
|
@@ -259,8 +273,8 @@ def tag_tides(
|
|
259
273
|
if return_phases:
|
260
274
|
# Model tide phases and heights for each observation
|
261
275
|
tide_df = model_phases(
|
262
|
-
x=lon,
|
263
|
-
y=lat,
|
276
|
+
x=lon,
|
277
|
+
y=lat,
|
264
278
|
time=time_coords,
|
265
279
|
model=model,
|
266
280
|
directory=directory,
|
@@ -272,8 +286,8 @@ def tag_tides(
|
|
272
286
|
else:
|
273
287
|
# Model tide heights for each observation
|
274
288
|
tide_df = model_tides(
|
275
|
-
x=lon,
|
276
|
-
y=lat,
|
289
|
+
x=lon,
|
290
|
+
y=lat,
|
277
291
|
time=time_coords,
|
278
292
|
model=model,
|
279
293
|
directory=directory,
|
@@ -283,14 +297,15 @@ def tag_tides(
|
|
283
297
|
|
284
298
|
# If tides cannot be successfully modeled (e.g. if the centre of the
|
285
299
|
# xarray dataset is located is over land), raise an exception
|
286
|
-
if tide_df.tide_height.
|
287
|
-
|
300
|
+
if tide_df.tide_height.isna().all():
|
301
|
+
err_msg = (
|
288
302
|
f"Tides could not be modelled for dataset centroid located "
|
289
303
|
f"at {tidepost_lon:.2f}, {tidepost_lat:.2f}. This can occur if "
|
290
304
|
f"this coordinate occurs over land. Please manually specify "
|
291
305
|
f"a tide modelling location located over water using the "
|
292
|
-
f"`tidepost_lat` and `tidepost_lon` parameters."
|
306
|
+
f"`tidepost_lat` and `tidepost_lon` parameters.",
|
293
307
|
)
|
308
|
+
raise ValueError(err_msg)
|
294
309
|
|
295
310
|
# Convert to xarray format, squeezing to return an xr.DataArray if
|
296
311
|
# dataframe contains only one "tide_height" column
|
@@ -317,9 +332,7 @@ def pixel_tides(
|
|
317
332
|
dask_compute: bool = True,
|
318
333
|
**model_tides_kwargs,
|
319
334
|
) -> xr.DataArray:
|
320
|
-
"""
|
321
|
-
Model tide heights for every pixel in a multi-dimensional
|
322
|
-
dataset, using one or more ocean tide models.
|
335
|
+
"""Model tide heights for every dataset pixel using multiple ocean tide models.
|
323
336
|
|
324
337
|
This function models tides into a low-resolution tide
|
325
338
|
modelling grid covering the spatial extent of the input
|
@@ -340,10 +353,8 @@ def pixel_tides(
|
|
340
353
|
- Technical University of Denmark tide models (DTU23)
|
341
354
|
|
342
355
|
This function requires access to tide model data files.
|
343
|
-
|
344
|
-
|
345
|
-
<https://geoscienceaustralia.github.io/eo-tides/setup/>
|
346
|
-
<https://pytmd.readthedocs.io/en/latest/getting_started/Getting-Started.html#directories>
|
356
|
+
For tide model setup instructions, refer to the guide:
|
357
|
+
https://geoscienceaustralia.github.io/eo-tides/setup/
|
347
358
|
|
348
359
|
Parameters
|
349
360
|
----------
|
@@ -424,6 +435,7 @@ def pixel_tides(
|
|
424
435
|
extrapolate modelled tides away from the coast; defaults to
|
425
436
|
`np.inf`), `crop` (whether to crop tide model constituent files
|
426
437
|
on-the-fly to improve performance) etc.
|
438
|
+
|
427
439
|
Returns
|
428
440
|
-------
|
429
441
|
tides_da : xr.DataArray
|
@@ -435,6 +447,7 @@ def pixel_tides(
|
|
435
447
|
quantiles for every quantile provided by `calculate_quantiles`.
|
436
448
|
If `resample=False`, results for the intermediate low-resolution
|
437
449
|
tide modelling grid will be returned instead.
|
450
|
+
|
438
451
|
"""
|
439
452
|
# Standardise data inputs, time and models
|
440
453
|
gbox, time_coords = _standardise_inputs(data, time)
|
@@ -446,39 +459,41 @@ def pixel_tides(
|
|
446
459
|
|
447
460
|
# Determine resolution and buffer, using different defaults for
|
448
461
|
# geographic (i.e. degrees) and projected (i.e. metres) CRSs:
|
449
|
-
assert gbox.crs is not None
|
462
|
+
assert gbox.crs is not None # noqa: S101
|
450
463
|
crs_units = gbox.crs.units[0][0:6]
|
451
464
|
if gbox.crs.geographic:
|
452
465
|
if resolution is None:
|
453
466
|
resolution = 0.05
|
454
467
|
elif resolution > 360:
|
455
|
-
|
468
|
+
err_msg = (
|
456
469
|
f"A resolution of greater than 360 was "
|
457
470
|
f"provided, but `data` has a geographic CRS "
|
458
471
|
f"in {crs_units} units. Did you accidently "
|
459
472
|
f"provide a resolution in projected "
|
460
473
|
f"(i.e. metre) units?",
|
461
474
|
)
|
475
|
+
raise ValueError(err_msg)
|
462
476
|
if buffer is None:
|
463
477
|
buffer = 0.12
|
464
478
|
else:
|
465
479
|
if resolution is None:
|
466
480
|
resolution = 5000
|
467
481
|
elif resolution < 1:
|
468
|
-
|
482
|
+
err_msg = (
|
469
483
|
f"A resolution of less than 1 was provided, "
|
470
484
|
f"but `data` has a projected CRS in "
|
471
485
|
f"{crs_units} units. Did you accidently "
|
472
486
|
f"provide a resolution in geographic "
|
473
487
|
f"(degree) units?",
|
474
488
|
)
|
489
|
+
raise ValueError(err_msg)
|
475
490
|
if buffer is None:
|
476
491
|
buffer = 12000
|
477
492
|
|
478
493
|
# Raise error if resolution is less than dataset resolution
|
479
494
|
dataset_res = gbox.resolution.x
|
480
495
|
if resolution < dataset_res:
|
481
|
-
|
496
|
+
err_msg = (
|
482
497
|
f"The resolution of the low-resolution tide "
|
483
498
|
f"modelling grid ({resolution:.2f}) is less "
|
484
499
|
f"than `data`'s pixel resolution ({dataset_res:.2f}). "
|
@@ -487,6 +502,7 @@ def pixel_tides(
|
|
487
502
|
f"greater than {dataset_res:.2f} using "
|
488
503
|
f"`pixel_tides`'s 'resolution' parameter.",
|
489
504
|
)
|
505
|
+
raise ValueError(err_msg)
|
490
506
|
|
491
507
|
# Create a new reduced resolution tide modelling grid after
|
492
508
|
# first buffering the grid
|
@@ -496,7 +512,7 @@ def pixel_tides(
|
|
496
512
|
rescaled_ds = odc.geo.xr.xr_zeros(rescaled_geobox)
|
497
513
|
|
498
514
|
# Flatten grid to 1D, then add time dimension
|
499
|
-
flattened_ds = rescaled_ds.stack(z=(x_dim, y_dim))
|
515
|
+
flattened_ds = rescaled_ds.stack(z=(x_dim, y_dim)) # noqa: PD013
|
500
516
|
flattened_ds = flattened_ds.expand_dims(dim={"time": time_coords})
|
501
517
|
|
502
518
|
# Model tides in parallel, returning a pandas.DataFrame
|