eo-tides 0.7.6.dev2__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 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 : Combine satellite EO data with tide modelling
24
- stats : Calculate local tide dynamics and satellite bias statistics
25
- utils : Utility functions and helper tools
26
- validation : Load observed tide gauge data to validate modelled tides
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
- "model_tides",
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
- raise ValueError("`data` does not have a 'time' dimension, and no custom times were provided via `time`.")
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
- raise ValueError("If `data` is a GeoBox, custom times must be provided via `time`.")
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
- raise TypeError("`data` must be an xarray.DataArray, xarray.Dataset, or odc.geo.geobox.GeoBox.")
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
- """Resamples low resolution tides modelled by `pixel_tides` into the
107
- geobox (e.g. spatial resolution and extent) of the original higher
108
- resolution satellite dataset.
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
- The function models tides at the centroid of the dataset
180
- by default, but a custom tidal modelling location can be
181
- specified using `tidepost_lat` and `tidepost_lon`.
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
- This function uses the parallelised `model_tides` and
184
- `model_phases` functions under the hood. It supports all tidal
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, # type: ignore
263
- y=lat, # type: ignore
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, # type: ignore
276
- y=lat, # type: ignore
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.isnull().all():
287
- raise ValueError(
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
- These should be placed in a folder with subfolders matching
344
- the structure required by `pyTMD`. For more details:
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
- raise ValueError(
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
- raise ValueError(
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
- raise ValueError(
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