eo-tides 0.1.1__py3-none-any.whl → 0.3.0__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/utils.py CHANGED
@@ -1,5 +1,457 @@
1
+ # Used to postpone evaluation of type annotations
2
+ from __future__ import annotations
3
+
4
+ import datetime
5
+ import os
6
+ import pathlib
7
+ import textwrap
8
+ import warnings
9
+ from typing import List, Union
10
+
1
11
  import numpy as np
12
+ import odc.geo
13
+ import pandas as pd
14
+ import xarray as xr
15
+ from colorama import Style, init
16
+ from odc.geo.geom import BoundingBox
17
+ from pyTMD.io.model import load_database
18
+ from pyTMD.io.model import model as pytmd_model
2
19
  from scipy.spatial import cKDTree as KDTree
20
+ from tqdm import tqdm
21
+
22
+ # Type alias for all possible inputs to "time" params
23
+ DatetimeLike = Union[np.ndarray, pd.DatetimeIndex, pd.Timestamp, datetime.datetime, str, List[str]]
24
+
25
+
26
+ def _set_directory(
27
+ directory: str | os.PathLike | None = None,
28
+ ) -> os.PathLike:
29
+ """
30
+ Set tide modelling files directory. If no custom
31
+ path is provided, try global `EO_TIDES_TIDE_MODELS`
32
+ environmental variable instead.
33
+ """
34
+ if directory is None:
35
+ if "EO_TIDES_TIDE_MODELS" in os.environ:
36
+ directory = os.environ["EO_TIDES_TIDE_MODELS"]
37
+ else:
38
+ raise Exception(
39
+ "No tide model directory provided via `directory`, and/or no "
40
+ "`EO_TIDES_TIDE_MODELS` environment variable found. "
41
+ "Please provide a valid path to your tide model directory."
42
+ )
43
+
44
+ # Verify path exists
45
+ directory = pathlib.Path(directory).expanduser()
46
+ if not directory.exists():
47
+ raise FileNotFoundError(f"No valid tide model directory found at path `{directory}`")
48
+ else:
49
+ return directory
50
+
51
+
52
+ def _standardise_time(
53
+ time: DatetimeLike | None,
54
+ ) -> np.ndarray | None:
55
+ """
56
+ Accept any time format accepted by `pd.to_datetime`,
57
+ and return a datetime64 ndarray. Return None if None
58
+ passed.
59
+ """
60
+ # Return time as-is if None
61
+ if time is None:
62
+ return None
63
+
64
+ # Use pd.to_datetime for conversion, then convert to numpy array
65
+ time = pd.to_datetime(time).to_numpy().astype("datetime64[ns]")
66
+
67
+ # Ensure that data has at least one dimension
68
+ return np.atleast_1d(time)
69
+
70
+
71
+ def _clip_model_file(
72
+ nc: xr.Dataset,
73
+ bbox: BoundingBox,
74
+ ydim: str,
75
+ xdim: str,
76
+ ycoord: str,
77
+ xcoord: str,
78
+ ) -> xr.Dataset:
79
+ """
80
+ Clips tide model netCDF datasets to a bounding box.
81
+
82
+ If the bounding box crosses 0 degrees longitude (e.g. Greenwich),
83
+ the function will clip the dataset into two parts and concatenate
84
+ them along the x-dimension to create a continuous result.
85
+
86
+ Parameters
87
+ ----------
88
+ nc : xr.Dataset
89
+ Input tide model xarray dataset.
90
+ bbox : odc.geo.geom.BoundingBox
91
+ A BoundingBox object for clipping the dataset in EPSG:4326
92
+ degrees coordinates. For example:
93
+ `BoundingBox(left=108, bottom=-48, right=158, top=-6, crs='EPSG:4326')`
94
+ ydim : str
95
+ The name of the xarray dimension representing the y-axis.
96
+ Depending on the tide model, this may or may not contain
97
+ actual latitude values.
98
+ xdim : str
99
+ The name of the xarray dimension representing the x-axis.
100
+ Depending on the tide model, this may or may not contain
101
+ actual longitude values.
102
+ ycoord : str
103
+ The name of the coordinate, variable or dimension containing
104
+ actual latitude values used for clipping the data.
105
+ xcoord : str
106
+ The name of the coordinate, variable or dimension containing
107
+ actual longitude values used for clipping the data.
108
+
109
+ Returns
110
+ -------
111
+ xr.Dataset
112
+ A dataset clipped to the specified bounding box, with
113
+ appropriate adjustments if the bounding box crosses 0
114
+ degrees longitude.
115
+
116
+ Examples
117
+ --------
118
+ >>> nc = xr.open_dataset("GOT5.5/ocean_tides/2n2.nc")
119
+ >>> bbox = BoundingBox(left=108, bottom=-48, right=158, top=-6, crs='EPSG:4326')
120
+ >>> clipped_nc = _clip_model_file(nc, bbox, xdim="lon", ydim="lat", ycoord="latitude", xcoord="longitude")
121
+ """
122
+
123
+ # Extract x and y coords from xarray and load into memory
124
+ xcoords = nc[xcoord].compute()
125
+ ycoords = nc[ycoord].compute()
126
+
127
+ # If data falls within 0-360 degree bounds, then clip directly
128
+ if (bbox.left >= 0) & (bbox.right <= 360):
129
+ nc_clipped = nc.sel({
130
+ ydim: (ycoords >= bbox.bottom) & (ycoords <= bbox.top),
131
+ xdim: (xcoords >= bbox.left) & (xcoords <= bbox.right),
132
+ })
133
+
134
+ # If bbox crosses zero longitude, extract left and right
135
+ # separately and then combine into one concatenated dataset
136
+ elif (bbox.left < 0) & (bbox.right > 0):
137
+ # Convert longitudes to 0-360 range
138
+ left = bbox.left % 360
139
+ right = bbox.right % 360
140
+
141
+ # Extract data from left of 0 longitude, and convert lon
142
+ # coords to -180 to 0 range to enable continuous interpolation
143
+ # across 0 boundary
144
+ nc_left = nc.sel({
145
+ ydim: (ycoords >= bbox.bottom) & (ycoords <= bbox.top),
146
+ xdim: (xcoords >= left) & (xcoords <= 360),
147
+ }).assign({xcoord: lambda x: x[xcoord] - 360})
148
+
149
+ # Convert additional lon variables for TXPO
150
+ if "lon_v" in nc_left:
151
+ nc_left = nc_left.assign({
152
+ "lon_v": lambda x: x["lon_v"] - 360,
153
+ "lon_u": lambda x: x["lon_u"] - 360,
154
+ })
155
+
156
+ # Extract data to right of 0 longitude
157
+ nc_right = nc.sel({
158
+ ydim: (ycoords >= bbox.bottom) & (ycoords <= bbox.top),
159
+ xdim: (xcoords > 0) & (xcoords <= right),
160
+ })
161
+
162
+ # Combine left and right data along x dimension
163
+ nc_clipped = xr.concat([nc_left, nc_right], dim=xdim)
164
+
165
+ # Hack fix to remove expanded x dim on lat variables issue
166
+ # for TPXO data; remove x dim by selecting the first obs
167
+ for i in ["lat_z", "lat_v", "lat_u", "con"]:
168
+ try:
169
+ nc_clipped[i] = nc_clipped[i].isel(nx=0)
170
+ except:
171
+ pass
172
+
173
+ return nc_clipped
174
+
175
+
176
+ def clip_models(
177
+ input_directory: str | os.PathLike,
178
+ output_directory: str | os.PathLike,
179
+ bbox: tuple[float, float, float, float],
180
+ model: list | None = None,
181
+ buffer: float = 1,
182
+ overwrite: bool = False,
183
+ ):
184
+ """
185
+ Clip NetCDF-format ocean tide models to a bounding box.
186
+
187
+ This function identifies all NetCDF-format tide models in a
188
+ given input directory, including "ATLAS-netcdf" (e.g. TPXO9-atlas-nc),
189
+ "FES-netcdf" (e.g. FES2022, EOT20), and "GOT-netcdf" (e.g. GOT5.5)
190
+ format files. Files for each model are then clipped to the extent of
191
+ the provided bounding box, handling model-specific file structures.
192
+ After each model is clipped, the result is exported to the output
193
+ directory and verified with `pyTMD` to ensure the clipped data is
194
+ suitable for tide modelling.
195
+
196
+ For instructions on accessing and downloading tide models, see:
197
+ <https://geoscienceaustralia.github.io/eo-tides/setup/>
198
+
199
+ Parameters
200
+ ----------
201
+ input_directory : str or os.PathLike
202
+ Path to directory containing input NetCDF-format tide model files.
203
+ output_directory : str or os.PathLike
204
+ Path to directory where clipped NetCDF files will be exported.
205
+ bbox : tuple of float
206
+ Bounding box for clipping the tide models in EPSG:4326 degrees
207
+ coordinates, specified as `(left, bottom, right, top)`.
208
+ model : str or list of str, optional
209
+ The tide model (or models) to clip. Defaults to None, which
210
+ will automatically identify and clip all NetCDF-format models
211
+ in the input directly.
212
+ buffer : float, optional
213
+ Buffer distance (in degrees) added to the bounding box to provide
214
+ sufficient data on edges of study area. Defaults to 1 degree.
215
+ overwrite : bool, optional
216
+ If True, overwrite existing files in the output directory.
217
+ Defaults to False.
218
+
219
+ Examples
220
+ --------
221
+ >>> clip_models(
222
+ ... input_directory="tide_models/",
223
+ ... output_directory="tide_models_clipped/",
224
+ ... bbox=(-8.968392, 50.070574, 2.447160, 59.367122),
225
+ ... )
226
+ """
227
+
228
+ # Get input and output paths
229
+ input_directory = _set_directory(input_directory)
230
+ output_directory = pathlib.Path(output_directory)
231
+
232
+ # Prepare bounding box
233
+ bbox = odc.geo.geom.BoundingBox(*bbox, crs="EPSG:4326").buffered(buffer)
234
+
235
+ # Identify NetCDF models
236
+ model_database = load_database()["elevation"]
237
+ netcdf_formats = ["ATLAS-netcdf", "FES-netcdf", "GOT-netcdf"]
238
+ netcdf_models = {k for k, v in model_database.items() if v["format"] in netcdf_formats}
239
+
240
+ # Identify subset of available and requested NetCDF models
241
+ available_models, _ = list_models(directory=input_directory, show_available=False, show_supported=False)
242
+ requested_models = list(np.atleast_1d(model)) if model is not None else available_models
243
+ available_netcdf_models = list(set(available_models) & set(requested_models) & set(netcdf_models))
244
+
245
+ # Raise error if no valid models found
246
+ if len(available_netcdf_models) == 0:
247
+ raise ValueError(f"No valid NetCDF models found in {input_directory}.")
248
+
249
+ # If model list is provided,
250
+ print(f"Preparing to clip suitable NetCDF models: {available_netcdf_models}\n")
251
+
252
+ # Loop through suitable models and export
253
+ for m in available_netcdf_models:
254
+ # Get model file and grid file list if they exist
255
+ model_files = model_database[m].get("model_file", [])
256
+ grid_file = model_database[m].get("grid_file", [])
257
+
258
+ # Convert to list if strings and combine
259
+ model_files = model_files if isinstance(model_files, list) else [model_files]
260
+ grid_file = grid_file if isinstance(grid_file, list) else [grid_file]
261
+ all_files = model_files + grid_file
262
+
263
+ # Loop through each model file and clip
264
+ for file in tqdm(all_files, desc=f"Clipping {m}"):
265
+ # Skip if it exists in output directory
266
+ if (output_directory / file).exists() and not overwrite:
267
+ continue
268
+
269
+ # Load model file
270
+ nc = xr.open_mfdataset(input_directory / file)
271
+
272
+ # Open file and clip according to model
273
+ if m in (
274
+ "GOT5.5",
275
+ "GOT5.5_load",
276
+ "GOT5.5_extrapolated",
277
+ "GOT5.5D",
278
+ "GOT5.5D_extrapolated",
279
+ "GOT5.6",
280
+ "GOT5.6_extrapolated",
281
+ ):
282
+ nc_clipped = _clip_model_file(
283
+ nc,
284
+ bbox,
285
+ xdim="lon",
286
+ ydim="lat",
287
+ ycoord="latitude",
288
+ xcoord="longitude",
289
+ )
290
+
291
+ elif m in ("HAMTIDE11",):
292
+ nc_clipped = _clip_model_file(nc, bbox, xdim="LON", ydim="LAT", ycoord="LAT", xcoord="LON")
293
+
294
+ elif m in (
295
+ "EOT20",
296
+ "EOT20_load",
297
+ "FES2012",
298
+ "FES2014",
299
+ "FES2014_extrapolated",
300
+ "FES2014_load",
301
+ "FES2022",
302
+ "FES2022_extrapolated",
303
+ "FES2022_load",
304
+ ):
305
+ nc_clipped = _clip_model_file(nc, bbox, xdim="lon", ydim="lat", ycoord="lat", xcoord="lon")
306
+
307
+ elif m in (
308
+ "TPXO8-atlas-nc",
309
+ "TPXO9-atlas-nc",
310
+ "TPXO9-atlas-v2-nc",
311
+ "TPXO9-atlas-v3-nc",
312
+ "TPXO9-atlas-v4-nc",
313
+ "TPXO9-atlas-v5-nc",
314
+ "TPXO10-atlas-v2-nc",
315
+ ):
316
+ nc_clipped = _clip_model_file(
317
+ nc,
318
+ bbox,
319
+ xdim="nx",
320
+ ydim="ny",
321
+ ycoord="lat_z",
322
+ xcoord="lon_z",
323
+ )
324
+
325
+ else:
326
+ raise Exception(f"Model {m} not supported")
327
+
328
+ # Create directory and export
329
+ (output_directory / file).parent.mkdir(parents=True, exist_ok=True)
330
+ nc_clipped.to_netcdf(output_directory / file, mode="w")
331
+
332
+ # Verify that models are ready
333
+ pytmd_model(directory=output_directory).elevation(m=m).verify
334
+ print(" ✅ Clipped model exported and verified")
335
+
336
+ print(f"\nOutputs exported to {output_directory}")
337
+ list_models(directory=output_directory, show_available=True, show_supported=False)
338
+
339
+
340
+ def list_models(
341
+ directory: str | os.PathLike | None = None,
342
+ show_available: bool = True,
343
+ show_supported: bool = True,
344
+ raise_error: bool = False,
345
+ ) -> tuple[list[str], list[str]]:
346
+ """
347
+ List all tide models available for tide modelling.
348
+
349
+ This function scans the specified tide model directory
350
+ and returns a list of models that are available in the
351
+ directory as well as the full list of all models supported
352
+ by `eo-tides` and `pyTMD`.
353
+
354
+ For instructions on setting up tide models, see:
355
+ <https://geoscienceaustralia.github.io/eo-tides/setup/>
356
+
357
+ Parameters
358
+ ----------
359
+ directory : str, optional
360
+ The directory containing tide model data files. If no path is
361
+ provided, this will default to the environment variable
362
+ `EO_TIDES_TIDE_MODELS` if set, or raise an error if not.
363
+ Tide modelling files should be stored in sub-folders for each
364
+ model that match the structure required by `pyTMD`
365
+ (<https://geoscienceaustralia.github.io/eo-tides/setup/>).
366
+ show_available : bool, optional
367
+ Whether to print a list of locally available models.
368
+ show_supported : bool, optional
369
+ Whether to print a list of all supported models, in
370
+ addition to models available locally.
371
+ raise_error : bool, optional
372
+ If True, raise an error if no available models are found.
373
+ If False, raise a warning.
374
+
375
+ Returns
376
+ -------
377
+ available_models : list of str
378
+ A list of all tide models available within `directory`.
379
+ supported_models : list of str
380
+ A list of all tide models supported by `eo-tides`.
381
+ """
382
+ init() # Initialize colorama
383
+
384
+ # Set tide modelling files directory. If no custom path is
385
+ # provided, try global environment variable.
386
+ directory = _set_directory(directory)
387
+
388
+ # Get full list of supported models from pyTMD database
389
+ model_database = load_database()["elevation"]
390
+ supported_models = list(model_database.keys())
391
+
392
+ # Extract expected model paths
393
+ expected_paths = {}
394
+ for m in supported_models:
395
+ model_file = model_database[m]["model_file"]
396
+ model_file = model_file[0] if isinstance(model_file, list) else model_file
397
+ expected_paths[m] = str(directory / pathlib.Path(model_file).expanduser().parent)
398
+
399
+ # Define column widths
400
+ status_width = 4 # Width for emoji
401
+ name_width = max(len(name) for name in supported_models)
402
+ path_width = max(len(path) for path in expected_paths.values())
403
+
404
+ # Print list of supported models, marking available and
405
+ # unavailable models and appending available to list
406
+ if show_available or show_supported:
407
+ total_width = min(status_width + name_width + path_width + 6, 80)
408
+ print("─" * total_width)
409
+ print(f"{'󠀠🌊':^{status_width}} | {'Model':<{name_width}} | {'Expected path':<{path_width}}")
410
+ print("─" * total_width)
411
+
412
+ available_models = []
413
+ for m in supported_models:
414
+ try:
415
+ model_file = pytmd_model(directory=directory).elevation(m=m)
416
+ available_models.append(m)
417
+
418
+ if show_available:
419
+ # Mark available models with a green tick
420
+ status = "✅"
421
+ print(f"{status:^{status_width}}│ {m:<{name_width}} │ {expected_paths[m]:<{path_width}}")
422
+ except FileNotFoundError:
423
+ if show_supported:
424
+ # Mark unavailable models with a red cross
425
+ status = "❌"
426
+ print(
427
+ f"{status:^{status_width}}│ {Style.DIM}{m:<{name_width}} │ {expected_paths[m]:<{path_width}}{Style.RESET_ALL}"
428
+ )
429
+
430
+ if show_available or show_supported:
431
+ print("─" * total_width)
432
+
433
+ # Print summary
434
+ print(f"\n{Style.BRIGHT}Summary:{Style.RESET_ALL}")
435
+ print(f"Available models: {len(available_models)}/{len(supported_models)}")
436
+
437
+ # Raise error or warning if no models are available
438
+ if not available_models:
439
+ warning_msg = textwrap.dedent(
440
+ f"""
441
+ No valid tide models are available in `{directory}`.
442
+ Are you sure you have provided the correct `directory` path, or set the
443
+ `EO_TIDES_TIDE_MODELS` environment variable to point to the location of your
444
+ tide model directory?
445
+ """
446
+ ).strip()
447
+
448
+ if raise_error:
449
+ raise Exception(warning_msg)
450
+ else:
451
+ warnings.warn(warning_msg, UserWarning)
452
+
453
+ # Return list of available and supported models
454
+ return available_models, supported_models
3
455
 
4
456
 
5
457
  def idw(
@@ -22,7 +474,7 @@ def idw(
22
474
  inverse distance to each neighbor, with weights descreasing with
23
475
  increasing distance.
24
476
 
25
- Code inspired by: https://github.com/DahnJ/REM-xarray
477
+ Code inspired by: <https://github.com/DahnJ/REM-xarray>
26
478
 
27
479
  Parameters
28
480
  ----------
eo_tides/validation.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import datetime
2
- import glob
3
2
  import warnings
4
3
  from math import sqrt
5
4
  from numbers import Number
@@ -193,11 +192,12 @@ def load_gauge_gesla(
193
192
  metadata_path="/gdata1/data/sea_level/GESLA3_ALL 2.csv",
194
193
  ):
195
194
  """
196
- Load and process all available Global Extreme Sea Level Analysis
197
- (GESLA) tide gauge data with an `x, y, time` spatiotemporal query,
198
- or from a list of specific tide gauges.
195
+ Load Global Extreme Sea Level Analysis (GESLA) tide gauge data.
199
196
 
200
- Can optionally filter by gauge quality and append detailed gauge metadata.
197
+ Load and process all available GESLA measured sea-level data
198
+ with an `x, y, time` spatio-temporal query, or from a list of
199
+ specific tide gauges. Can optionally filter by gauge quality
200
+ and append detailed gauge metadata.
201
201
 
202
202
  Modified from original code in <https://github.com/philiprt/GeslaDataset>.
203
203
 
@@ -1,7 +1,8 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: eo-tides
3
- Version: 0.1.1
3
+ Version: 0.3.0
4
4
  Summary: Tide modelling tools for large-scale satellite earth observation analysis
5
+ Author: Stephen Sagar, Claire Phillips, Vanessa Newey
5
6
  Author-email: Robbi Bishop-Taylor <Robbi.BishopTaylor@ga.gov.au>
6
7
  Project-URL: Homepage, https://GeoscienceAustralia.github.io/eo-tides/
7
8
  Project-URL: Repository, https://github.com/GeoscienceAustralia/eo-tides
@@ -19,6 +20,7 @@ Classifier: Programming Language :: Python :: 3.9
19
20
  Classifier: Programming Language :: Python :: 3.10
20
21
  Classifier: Programming Language :: Python :: 3.11
21
22
  Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
22
24
  Requires-Python: <4.0,>=3.9
23
25
  Description-Content-Type: text/markdown
24
26
  License-File: LICENSE
@@ -28,8 +30,9 @@ Requires-Dist: matplotlib >=3.8.0
28
30
  Requires-Dist: numpy >=1.26.0
29
31
  Requires-Dist: odc-geo >=0.4.7
30
32
  Requires-Dist: pandas >=2.2.0
33
+ Requires-Dist: psutil >=5.8.0
31
34
  Requires-Dist: pyproj >=3.6.1
32
- Requires-Dist: pyTMD ==2.1.6
35
+ Requires-Dist: pyTMD ==2.1.8
33
36
  Requires-Dist: scikit-learn >=1.4.0
34
37
  Requires-Dist: scipy >=1.11.2
35
38
  Requires-Dist: shapely >=2.0.6
@@ -44,16 +47,17 @@ Requires-Dist: planetary-computer >=1.0.0 ; extra == 'notebooks'
44
47
 
45
48
  # `eo-tides`: Tide modelling tools for large-scale satellite earth observation analysis
46
49
 
47
- <img align="right" width="200" src="docs/assets/eo-tides-logo.gif" alt="eo-tides logo" style="margin-right: 40px;">
50
+ <img align="right" width="200" src="https://github.com/GeoscienceAustralia/eo-tides/blob/main/docs/assets/eo-tides-logo.gif?raw=true" alt="eo-tides logo" style="margin-right: 40px;">
48
51
 
49
52
  [![Release](https://img.shields.io/github/v/release/GeoscienceAustralia/eo-tides)](https://pypi.org/project/eo-tides/)
50
53
  [![Build status](https://img.shields.io/github/actions/workflow/status/GeoscienceAustralia/eo-tides/main.yml?branch=main)](https://github.com/GeoscienceAustralia/eo-tides/actions/workflows/main.yml?query=branch%3Amain)
51
- ![Python Version from PEP 621 TOML](https://img.shields.io/pypi/pyversions/eo-tides)
54
+ [![Python Version from PEP 621 TOML](https://img.shields.io/pypi/pyversions/eo-tides)](https://github.com/GeoscienceAustralia/eo-tides/blob/main/pyproject.toml)
52
55
  [![codecov](https://codecov.io/gh/GeoscienceAustralia/eo-tides/branch/main/graph/badge.svg)](https://codecov.io/gh/GeoscienceAustralia/eo-tides)
53
56
  [![License](https://img.shields.io/github/license/GeoscienceAustralia/eo-tides)](https://img.shields.io/github/license/GeoscienceAustralia/eo-tides)
54
57
 
55
- - **Github repository**: <https://github.com/GeoscienceAustralia/eo-tides/>
56
- - **Documentation** <https://GeoscienceAustralia.github.io/eo-tides/>
58
+ - ⚙️ **Github repository**: <https://github.com/GeoscienceAustralia/eo-tides/>
59
+ - 📘 **Documentation**: <https://GeoscienceAustralia.github.io/eo-tides/>
60
+ - 🐍 **PyPI**: <https://pypi.org/project/eo-tides/>
57
61
 
58
62
  > [!CAUTION]
59
63
  > This package is a work in progress, and not currently ready for operational use.
@@ -64,13 +68,13 @@ Requires-Dist: planetary-computer >=1.0.0 ; extra == 'notebooks'
64
68
 
65
69
  These tools can be applied to petabytes of freely available satellite data (e.g. from [Digital Earth Australia](https://knowledge.dea.ga.gov.au/) or [Microsoft Planetary Computer](https://planetarycomputer.microsoft.com/)) loaded via Open Data Cube's [`odc-stac`](https://odc-stac.readthedocs.io/en/latest/) or [`datacube`](https://opendatacube.readthedocs.io/en/latest/) packages, supporting coastal and ocean earth observation analysis for any time period or location globally.
66
70
 
67
- ![eo-tides abstract showing satellite data, tide data array and tide animation](docs/assets/eo-tides-abstract.gif)
71
+ ![eo-tides abstract showing satellite data, tide data array and tide animation](https://github.com/GeoscienceAustralia/eo-tides/blob/main/docs/assets/eo-tides-abstract.gif?raw=true)
68
72
 
69
73
  ## Highlights
70
74
 
71
- - 🌊 Model tides from multiple global ocean tide models in parallel, and return tide heights in standardised `pandas.DataFrame` format for further analysis
72
- - 🛰️ "Tag" satellite data with tide height and stage based on the exact moment of image acquisition
73
- - 🌐 Model tides for every individual satellite pixel, producing three-dimensional "tide height" `xarray`-format datacubes that can be integrated with satellite data
75
+ - 🌊 Model tide heights and phases (e.g. high, low, ebb, flow) from multiple global ocean tide models in parallel, and return a `pandas.DataFrame` for further analysis
76
+ - 🛰️ "Tag" satellite data with tide heights based on the exact moment of image acquisition
77
+ - 🌐 Model tides for every individual satellite pixel through time, producing three-dimensional "tide height" `xarray`-format datacubes that can be integrated with satellite data
74
78
  - 📈 Calculate statistics describing local tide dynamics, as well as biases caused by interactions between tidal processes and satellite orbits
75
79
  - 🛠️ Validate modelled tides using measured sea levels from coastal tide gauges (e.g. [GESLA Global Extreme Sea Level Analysis](https://gesla.org/))
76
80
  <!-- - 🎯 Combine multiple tide models into a single locally-optimised "ensemble" model informed by satellite altimetry and satellite-observed patterns of tidal inundation -->
@@ -103,6 +107,12 @@ To cite `eo-tides` in your work, please use the following citation:
103
107
  Bishop-Taylor, R., Sagar, S., Phillips, C., & Newey, V. (2024). eo-tides: Tide modelling tools for large-scale satellite earth observation analysis. https://github.com/GeoscienceAustralia/eo-tides
104
108
  ```
105
109
 
110
+ In addition, please consider also citing the underlying [`pyTMD` Python package](https://pytmd.readthedocs.io/en/latest/) which powers the tide modelling functionality behind `eo-tides`:
111
+
112
+ ```
113
+ Sutterley, T. C., Alley, K., Brunt, K., Howard, S., Padman, L., Siegfried, M. (2017) pyTMD: Python-based tidal prediction software. 10.5281/zenodo.5555395
114
+ ```
115
+
106
116
  ## Acknowledgements
107
117
 
108
118
  For a full list of acknowledgements, refer to [Citations and Credits](https://geoscienceaustralia.github.io/eo-tides/credits/).
@@ -0,0 +1,11 @@
1
+ eo_tides/__init__.py,sha256=rn6slQP0bAhqM9cL2W8omiEM8f0b7libt6WsQ5DvYTE,1655
2
+ eo_tides/eo.py,sha256=Vc_AHqT0_IDqdwbOpNcONjHhiCtbCe8Osk2gvzUeNmU,22377
3
+ eo_tides/model.py,sha256=T0IFvSiXjiTvlY-Vi7-jneHPfaCLt95pmcRd8CZxOjE,34478
4
+ eo_tides/stats.py,sha256=lchWWJ5gBDuZWvaD8TF-12Xlo2qCWiNI2IcgAaKWy2U,22668
5
+ eo_tides/utils.py,sha256=tqXAkLk_Dm8s3yuXPxBBkHW3PZ42ayV8VCQjNw3wDZw,22433
6
+ eo_tides/validation.py,sha256=UREsc0yWRO4x0PJXvyoIx8gYiBZiRSim4z6TmAz_VDM,11857
7
+ eo_tides-0.3.0.dist-info/LICENSE,sha256=owxWsXViCL2J6Ks3XYhot7t4Y93nstmXAT95Zf030Cc,11350
8
+ eo_tides-0.3.0.dist-info/METADATA,sha256=lajVK9XoAA2fJqA9R3HHYJ7iUJFMtmxh3U07uGTs4_4,8040
9
+ eo_tides-0.3.0.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
10
+ eo_tides-0.3.0.dist-info/top_level.txt,sha256=lXZDUUM1DlLdKWHRn8zdmtW8Rx-eQOIWVvt0b8VGiyQ,9
11
+ eo_tides-0.3.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.2.0)
2
+ Generator: setuptools (75.3.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,11 +0,0 @@
1
- eo_tides/__init__.py,sha256=TWmQNplePCcNAlua5WI_H7SShkWNk-Gd3X70EDEknSo,1557
2
- eo_tides/eo.py,sha256=OTFjchQWpSfgi2Q62mFBj6ckmy_1B_ExCAmex4UT4js,21616
3
- eo_tides/model.py,sha256=Bwc2VVhDDBJB0wuKUAdSKYfBPZG4XDfldb_BO5Wcy6Y,34517
4
- eo_tides/stats.py,sha256=JByNxsDBt88PKX-k0PP2KLg7G6-3_hsOilcbtnAbFY8,20681
5
- eo_tides/utils.py,sha256=l9VXJawQzaRBYaFMsP8VBeaN5VA3rFDdzcvF7Rk04Vc,5620
6
- eo_tides/validation.py,sha256=JjTUqDfbR189m_6W1bpaSolQIHNTLicTHN7z9O_nr3s,11828
7
- eo_tides-0.1.1.dist-info/LICENSE,sha256=owxWsXViCL2J6Ks3XYhot7t4Y93nstmXAT95Zf030Cc,11350
8
- eo_tides-0.1.1.dist-info/METADATA,sha256=JSTzMnxvPrQf1Ta0KXiRUQaENaleaWyzwu4m-DFdYGE,7260
9
- eo_tides-0.1.1.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
10
- eo_tides-0.1.1.dist-info/top_level.txt,sha256=lXZDUUM1DlLdKWHRn8zdmtW8Rx-eQOIWVvt0b8VGiyQ,9
11
- eo_tides-0.1.1.dist-info/RECORD,,