eo-tides 0.0.19__tar.gz → 0.0.20__tar.gz
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-0.0.19 → eo_tides-0.0.20}/PKG-INFO +1 -1
- {eo_tides-0.0.19 → eo_tides-0.0.20}/eo_tides/model.py +115 -111
- {eo_tides-0.0.19 → eo_tides-0.0.20}/eo_tides.egg-info/PKG-INFO +1 -1
- {eo_tides-0.0.19 → eo_tides-0.0.20}/eo_tides.egg-info/SOURCES.txt +1 -0
- {eo_tides-0.0.19 → eo_tides-0.0.20}/pyproject.toml +1 -1
- {eo_tides-0.0.19 → eo_tides-0.0.20}/tests/test_model.py +7 -4
- eo_tides-0.0.20/tests/test_utils.py +84 -0
- {eo_tides-0.0.19 → eo_tides-0.0.20}/LICENSE +0 -0
- {eo_tides-0.0.19 → eo_tides-0.0.20}/README.md +0 -0
- {eo_tides-0.0.19 → eo_tides-0.0.20}/eo_tides/__init__.py +0 -0
- {eo_tides-0.0.19 → eo_tides-0.0.20}/eo_tides/stats.py +0 -0
- {eo_tides-0.0.19 → eo_tides-0.0.20}/eo_tides/utils.py +0 -0
- {eo_tides-0.0.19 → eo_tides-0.0.20}/eo_tides/validation.py +0 -0
- {eo_tides-0.0.19 → eo_tides-0.0.20}/eo_tides.egg-info/dependency_links.txt +0 -0
- {eo_tides-0.0.19 → eo_tides-0.0.20}/eo_tides.egg-info/requires.txt +0 -0
- {eo_tides-0.0.19 → eo_tides-0.0.20}/eo_tides.egg-info/top_level.txt +0 -0
- {eo_tides-0.0.19 → eo_tides-0.0.20}/setup.cfg +0 -0
- {eo_tides-0.0.19 → eo_tides-0.0.20}/tests/test_validation.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: eo-tides
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.20
|
4
4
|
Summary: Tide modelling tools for large-scale satellite earth observation analysis
|
5
5
|
Author-email: Robbi Bishop-Taylor <Robbi.BishopTaylor@ga.gov.au>
|
6
6
|
Project-URL: Homepage, https://GeoscienceAustralia.github.io/eo-tides/
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import os
|
2
2
|
import pathlib
|
3
|
+
import warnings
|
3
4
|
from concurrent.futures import ProcessPoolExecutor
|
4
5
|
from functools import partial
|
5
6
|
|
@@ -15,65 +16,103 @@ from tqdm import tqdm
|
|
15
16
|
from eo_tides.utils import idw
|
16
17
|
|
17
18
|
|
18
|
-
def
|
19
|
+
def _set_directory(directory):
|
19
20
|
"""
|
20
|
-
|
21
|
-
|
21
|
+
Set tide modelling files directory. If no custom
|
22
|
+
path is provided, try global environmental variable
|
23
|
+
instead.
|
24
|
+
"""
|
25
|
+
if directory is None:
|
26
|
+
if "EO_TIDES_TIDE_MODELS" in os.environ:
|
27
|
+
directory = os.environ["EO_TIDES_TIDE_MODELS"]
|
28
|
+
else:
|
29
|
+
raise Exception(
|
30
|
+
"No tide model directory provided via `directory`, and/or no "
|
31
|
+
"`EO_TIDES_TIDE_MODELS` environment variable found. "
|
32
|
+
"Please provide a valid path to your tide model directory."
|
33
|
+
)
|
34
|
+
|
35
|
+
# Verify path exists
|
36
|
+
directory = pathlib.Path(directory).expanduser()
|
37
|
+
if not directory.exists():
|
38
|
+
raise FileNotFoundError(f"No valid tide model directory found at path `{directory}`")
|
39
|
+
else:
|
40
|
+
return directory
|
41
|
+
|
42
|
+
|
43
|
+
def list_models(directory=None, show_available=True, show_supported=True, raise_error=False):
|
44
|
+
"""
|
45
|
+
List all tide models available for tide modelling, and
|
46
|
+
all models supported by `eo-tides` and `pyTMD`.
|
22
47
|
|
23
48
|
This function scans the specified tide model directory
|
24
|
-
|
25
|
-
|
26
|
-
directory as well as the full list of supported models.
|
49
|
+
and returns a list of models that are available in the
|
50
|
+
directory as well as the full list of all supported models.
|
27
51
|
|
28
52
|
For instructions on setting up tide models, see:
|
29
53
|
<https://geoscienceaustralia.github.io/eo-tides/setup/>
|
30
54
|
|
31
55
|
Parameters
|
32
56
|
----------
|
33
|
-
directory :
|
34
|
-
|
57
|
+
directory : string, optional
|
58
|
+
The directory containing tide model data files. If no path is
|
59
|
+
provided, this will default to the environment variable
|
60
|
+
`EO_TIDES_TIDE_MODELS` if set, or raise an error if not.
|
61
|
+
Tide modelling files should be stored in sub-folders for each
|
62
|
+
model that match the structure required by `pyTMD`
|
63
|
+
(<https://geoscienceaustralia.github.io/eo-tides/setup/>).
|
64
|
+
show_available : bool, optional
|
65
|
+
Whether to print a list of locally available models.
|
66
|
+
show_supported : bool, optional
|
67
|
+
Whether to print a list of all supported models, in
|
68
|
+
addition to models available locally.
|
35
69
|
|
36
70
|
Returns
|
37
71
|
-------
|
38
|
-
|
39
|
-
A list of
|
40
|
-
|
72
|
+
available_models : list
|
73
|
+
A list of alltide models available within `directory`.
|
74
|
+
supported_models : list
|
75
|
+
A list of all tide models supported by `eo-tides`.
|
41
76
|
"""
|
42
|
-
#
|
43
|
-
|
44
|
-
|
45
|
-
# first try global environmental var, then "/var/share/tide_models"
|
46
|
-
if directory is None:
|
47
|
-
if "EO_TIDES_TIDE_MODELS" in os.environ:
|
48
|
-
directory = os.environ["EO_TIDES_TIDE_MODELS"]
|
49
|
-
else:
|
50
|
-
directory = "/var/share/tide_models"
|
77
|
+
# Set tide modelling files directory. If no custom path is
|
78
|
+
# provided, try global environment variable.
|
79
|
+
directory = _set_directory(directory)
|
51
80
|
|
52
|
-
#
|
53
|
-
|
54
|
-
if not directory.exists():
|
55
|
-
raise FileNotFoundError("Invalid tide directory")
|
56
|
-
|
57
|
-
# Get full list of supported models from the database
|
58
|
-
supported_models = load_database()["elevation"].keys()
|
81
|
+
# Get full list of supported models from pyTMD database
|
82
|
+
supported_models = list(load_database()["elevation"].keys())
|
59
83
|
|
60
84
|
# Print list of supported models, marking available and
|
61
85
|
# unavailable models and appending available to list
|
62
|
-
|
63
|
-
|
86
|
+
if show_available or show_supported:
|
87
|
+
print(f"Tide models available in `{directory}`:")
|
88
|
+
available_models = []
|
64
89
|
for m in supported_models:
|
65
90
|
try:
|
66
91
|
model(directory=directory).elevation(m=m)
|
67
|
-
|
68
|
-
|
69
|
-
|
92
|
+
if show_available:
|
93
|
+
# Mark available models with a green tick
|
94
|
+
print(f" ✅ {m}")
|
95
|
+
available_models.append(m)
|
70
96
|
except:
|
71
97
|
if show_supported:
|
72
98
|
# Mark unavailable models with a red cross
|
73
99
|
print(f" ❌ {m}")
|
74
100
|
|
75
|
-
#
|
76
|
-
|
101
|
+
# Raise error or warning if no models are available
|
102
|
+
if not available_models:
|
103
|
+
warning_text = (
|
104
|
+
f"No valid tide models are available in `{directory}`. "
|
105
|
+
"Verify that you have provided the correct `directory` path, "
|
106
|
+
"or set the `EO_TIDES_TIDE_MODELS` environment variable "
|
107
|
+
"to point to the location of your tide model directory."
|
108
|
+
)
|
109
|
+
if raise_error:
|
110
|
+
raise Exception(warning_text)
|
111
|
+
else:
|
112
|
+
warnings.warn(warning_text, UserWarning)
|
113
|
+
|
114
|
+
# Return list of available and supported models
|
115
|
+
return available_models, supported_models
|
77
116
|
|
78
117
|
|
79
118
|
def _model_tides(
|
@@ -94,34 +133,7 @@ def _model_tides(
|
|
94
133
|
extraction of tide modelling constituents and tide modelling using
|
95
134
|
`pyTMD`.
|
96
135
|
"""
|
97
|
-
#
|
98
|
-
# import pyTMD.io
|
99
|
-
# import pyTMD.io.model
|
100
|
-
# import pyTMD.predict
|
101
|
-
# import pyTMD.spatial
|
102
|
-
# import pyTMD.time
|
103
|
-
# import pyTMD.utilities
|
104
|
-
|
105
|
-
# Get parameters for tide model; use custom definition file for
|
106
|
-
# FES2012 (leave this as an undocumented feature for now)
|
107
|
-
# if model == "FES2012":
|
108
|
-
# pytmd_model = pyTMD.io.model(directory).from_file(
|
109
|
-
# directory / "model_FES2012.def"
|
110
|
-
# )
|
111
|
-
# elif model == "TPXO8-atlas-v1":
|
112
|
-
# pytmd_model = pyTMD.io.model(directory).from_file(directory / "model_TPXO8.def")
|
113
|
-
# else:
|
114
|
-
# pytmd_model = pyTMD.io.model(
|
115
|
-
# directory, format="netcdf", compressed=False
|
116
|
-
# ).elevation(model)
|
117
|
-
|
118
|
-
# if model in NONSTANDARD_MODELS:
|
119
|
-
# model_params = NONSTANDARD_MODELS[model]
|
120
|
-
# model_params_bytes = io.BytesIO(json.dumps(model_params).encode("utf-8"))
|
121
|
-
# pytmd_model = pyTMD.io.model(directory).from_file(definition_file=model_params_bytes)
|
122
|
-
|
123
|
-
# else:
|
124
|
-
|
136
|
+
# Obtain model details
|
125
137
|
pytmd_model = pyTMD.io.model(directory).elevation(model)
|
126
138
|
|
127
139
|
# Convert x, y to latitude/longitude
|
@@ -474,11 +486,11 @@ def model_tides(
|
|
474
486
|
This function is parallelised to improve performance, and
|
475
487
|
supports all tidal models supported by `pyTMD`, including:
|
476
488
|
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
489
|
+
- Empirical Ocean Tide model (`EOT20`)
|
490
|
+
- Finite Element Solution tide models (`FES2022`, `FES2014`, `FES2012`)
|
491
|
+
- TOPEX/POSEIDON global tide models (`TPXO10`, `TPXO9`, `TPXO8`)
|
492
|
+
- Global Ocean Tide models (`GOT5.6`, `GOT5.5`, `GOT4.10`, `GOT4.8`, `GOT4.7`)
|
493
|
+
- Hamburg direct data Assimilation Methods for Tides models (`HAMTIDE11`)
|
482
494
|
|
483
495
|
This function requires access to tide model data files.
|
484
496
|
These should be placed in a folder with subfolders matching
|
@@ -504,11 +516,11 @@ def model_tides(
|
|
504
516
|
model : string, optional
|
505
517
|
The tide model used to model tides. Options include:
|
506
518
|
|
507
|
-
- "
|
519
|
+
- "EOT20"
|
520
|
+
- "FES2014"
|
508
521
|
- "FES2022"
|
509
522
|
- "TPXO9-atlas-v5"
|
510
523
|
- "TPXO8-atlas"
|
511
|
-
- "EOT20"
|
512
524
|
- "HAMTIDE11"
|
513
525
|
- "GOT4.10"
|
514
526
|
- "ensemble" (advanced ensemble tide model functionality;
|
@@ -516,7 +528,7 @@ def model_tides(
|
|
516
528
|
directory : string, optional
|
517
529
|
The directory containing tide model data files. If no path is
|
518
530
|
provided, this will default to the environment variable
|
519
|
-
`EO_TIDES_TIDE_MODELS` if set,
|
531
|
+
`EO_TIDES_TIDE_MODELS` if set, or raise an error if not.
|
520
532
|
Tide modelling files should be stored in sub-folders for each
|
521
533
|
model that match the structure provided by `pyTMD`.
|
522
534
|
|
@@ -602,23 +614,6 @@ def model_tides(
|
|
602
614
|
A dataframe containing modelled tide heights.
|
603
615
|
|
604
616
|
"""
|
605
|
-
# Set tide modelling files directory. If no custom path is provided,
|
606
|
-
# first try global environmental var, then "/var/share/tide_models"
|
607
|
-
if directory is None:
|
608
|
-
if "EO_TIDES_TIDE_MODELS" in os.environ:
|
609
|
-
directory = os.environ["EO_TIDES_TIDE_MODELS"]
|
610
|
-
else:
|
611
|
-
directory = "/var/share/tide_models"
|
612
|
-
|
613
|
-
# Verify path exists
|
614
|
-
directory = pathlib.Path(directory).expanduser()
|
615
|
-
if not directory.exists():
|
616
|
-
raise FileNotFoundError("Invalid tide directory")
|
617
|
-
|
618
|
-
# If time passed as a single Timestamp, convert to datetime64
|
619
|
-
if isinstance(time, pd.Timestamp):
|
620
|
-
time = time.to_datetime64()
|
621
|
-
|
622
617
|
# Turn inputs into arrays for consistent handling
|
623
618
|
models_requested = np.atleast_1d(model)
|
624
619
|
x = np.atleast_1d(x)
|
@@ -644,32 +639,41 @@ def model_tides(
|
|
644
639
|
"you intended to model multiple timesteps at each point."
|
645
640
|
)
|
646
641
|
|
647
|
-
#
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
# Advanced ensemble model functionality
|
665
|
-
"ensemble",
|
666
|
-
]
|
642
|
+
# If time passed as a single Timestamp, convert to datetime64
|
643
|
+
if isinstance(time, pd.Timestamp):
|
644
|
+
time = time.to_datetime64()
|
645
|
+
|
646
|
+
# Set tide modelling files directory. If no custom path is
|
647
|
+
# provided, try global environment variable.
|
648
|
+
directory = _set_directory(directory)
|
649
|
+
|
650
|
+
# Get full list of supported models from pyTMD database;
|
651
|
+
# add ensemble option to list of models
|
652
|
+
available_models, valid_models = list_models(
|
653
|
+
directory, show_available=False, show_supported=False, raise_error=True
|
654
|
+
)
|
655
|
+
available_models = available_models + ["ensemble"]
|
656
|
+
valid_models = valid_models + ["ensemble"]
|
657
|
+
|
658
|
+
# Error if any models are not supported
|
667
659
|
if not all(m in valid_models for m in models_requested):
|
668
|
-
|
669
|
-
f"One or more of the models
|
670
|
-
f"
|
671
|
-
|
660
|
+
error_text = (
|
661
|
+
f"One or more of the requested models are not valid:\n"
|
662
|
+
f"{models_requested}\n\n"
|
663
|
+
"The following models are supported:\n"
|
664
|
+
f"{valid_models}"
|
665
|
+
)
|
666
|
+
raise ValueError(error_text)
|
667
|
+
|
668
|
+
# Error if any models are not available in `directory`
|
669
|
+
if not all(m in available_models for m in models_requested):
|
670
|
+
error_text = (
|
671
|
+
f"One or more of the requested models are valid, but not available in `{directory}`:\n"
|
672
|
+
f"{models_requested}\n\n"
|
673
|
+
f"The following models are available in `{directory}`:\n"
|
674
|
+
f"{available_models}"
|
672
675
|
)
|
676
|
+
raise ValueError(error_text)
|
673
677
|
|
674
678
|
# If ensemble modelling is requested, use a custom list of models
|
675
679
|
# for subsequent processing
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: eo-tides
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.20
|
4
4
|
Summary: Tide modelling tools for large-scale satellite earth observation analysis
|
5
5
|
Author-email: Robbi Bishop-Taylor <Robbi.BishopTaylor@ga.gov.au>
|
6
6
|
Project-URL: Homepage, https://GeoscienceAustralia.github.io/eo-tides/
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "eo-tides"
|
3
|
-
version = "0.0.
|
3
|
+
version = "0.0.20"
|
4
4
|
description = "Tide modelling tools for large-scale satellite earth observation analysis"
|
5
5
|
authors = [{ name = "Robbi Bishop-Taylor", email = "Robbi.BishopTaylor@ga.gov.au" }]
|
6
6
|
readme = "README.md"
|
@@ -6,7 +6,7 @@ import pystac_client
|
|
6
6
|
import pytest
|
7
7
|
import xarray as xr
|
8
8
|
|
9
|
-
from eo_tides.model import
|
9
|
+
from eo_tides.model import list_models, model_tides, pixel_tides
|
10
10
|
from eo_tides.validation import eval_metrics
|
11
11
|
|
12
12
|
GAUGE_X = 122.2183
|
@@ -91,9 +91,12 @@ def satellite_ds(request):
|
|
91
91
|
|
92
92
|
|
93
93
|
# Test available tide models
|
94
|
-
def
|
95
|
-
|
96
|
-
assert
|
94
|
+
def test_list_models():
|
95
|
+
available_models, supported_models = list_models()
|
96
|
+
assert available_models == ["FES2014", "HAMTIDE11"]
|
97
|
+
|
98
|
+
available_models, supported_models = list_models(show_available=False, show_supported=False)
|
99
|
+
assert available_models == ["FES2014", "HAMTIDE11"]
|
97
100
|
|
98
101
|
|
99
102
|
# Run test for multiple input coordinates, CRSs and interpolation methods
|
@@ -0,0 +1,84 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import pytest
|
3
|
+
|
4
|
+
from eo_tides.utils import idw
|
5
|
+
|
6
|
+
|
7
|
+
# Test Inverse Distance Weighted function
|
8
|
+
def test_idw():
|
9
|
+
# Basic psuedo-1D example
|
10
|
+
input_z = [1, 2, 3, 4, 5]
|
11
|
+
input_x = [0, 1, 2, 3, 4]
|
12
|
+
input_y = [0, 0, 0, 0, 0]
|
13
|
+
output_x = [0.5, 1.5, 2.5, 3.5]
|
14
|
+
output_y = [0.0, 0.0, 0.0, 0.0]
|
15
|
+
out = idw(input_z, input_x, input_y, output_x, output_y, k=2)
|
16
|
+
assert np.allclose(out, [1.5, 2.5, 3.5, 4.5])
|
17
|
+
|
18
|
+
# Verify that k > input points gives error
|
19
|
+
with pytest.raises(ValueError):
|
20
|
+
idw(input_z, input_x, input_y, output_x, output_y, k=6)
|
21
|
+
|
22
|
+
# 2D nearest neighbour case
|
23
|
+
input_z = [1, 2, 3, 4]
|
24
|
+
input_x = [0, 4, 0, 4]
|
25
|
+
input_y = [0, 0, 4, 4]
|
26
|
+
output_x = [1, 4, 0, 3]
|
27
|
+
output_y = [0, 1, 3, 4]
|
28
|
+
out = idw(input_z, input_x, input_y, output_x, output_y, k=1)
|
29
|
+
assert np.allclose(out, [1, 2, 3, 4])
|
30
|
+
|
31
|
+
# Two neighbours
|
32
|
+
input_z = [1, 2, 3, 4]
|
33
|
+
input_x = [0, 4, 0, 4]
|
34
|
+
input_y = [0, 0, 4, 4]
|
35
|
+
output_x = [2, 0, 4, 2]
|
36
|
+
output_y = [0, 2, 2, 4]
|
37
|
+
out = idw(input_z, input_x, input_y, output_x, output_y, k=2)
|
38
|
+
assert np.allclose(out, [1.5, 2, 3, 3.5])
|
39
|
+
|
40
|
+
# Four neighbours
|
41
|
+
out = idw(input_z, input_x, input_y, output_x, output_y, k=4)
|
42
|
+
assert np.allclose(out, [2.11, 2.30, 2.69, 2.88], rtol=0.01)
|
43
|
+
|
44
|
+
# Four neighbours; max distance of 2
|
45
|
+
out = idw(input_z, input_x, input_y, output_x, output_y, k=4, max_dist=2)
|
46
|
+
assert np.allclose(out, [1.5, 2, 3, 3.5])
|
47
|
+
|
48
|
+
# Four neighbours; max distance of 2, k_min of 4 (should return NaN)
|
49
|
+
out = idw(input_z, input_x, input_y, output_x, output_y, k=4, max_dist=2, k_min=4)
|
50
|
+
assert np.isnan(out).all()
|
51
|
+
|
52
|
+
# Four neighbours; power function p=0
|
53
|
+
out = idw(input_z, input_x, input_y, output_x, output_y, k=4, p=0)
|
54
|
+
assert np.allclose(out, [2.5, 2.5, 2.5, 2.5])
|
55
|
+
|
56
|
+
# Four neighbours; power function p=2
|
57
|
+
out = idw(input_z, input_x, input_y, output_x, output_y, k=4, p=2)
|
58
|
+
assert np.allclose(out, [1.83, 2.17, 2.83, 3.17], rtol=0.01)
|
59
|
+
|
60
|
+
# Different units, nearest neighbour case
|
61
|
+
input_z = [10, 20, 30, 40]
|
62
|
+
input_x = [1125296, 1155530, 1125296, 1155530]
|
63
|
+
input_y = [-4169722, -4169722, -4214782, -4214782]
|
64
|
+
output_x = [1124952, 1159593, 1120439, 1155284]
|
65
|
+
output_y = [-4169749, -4172892, -4211108, -4214332]
|
66
|
+
out = idw(input_z, input_x, input_y, output_x, output_y, k=1)
|
67
|
+
assert np.allclose(out, [10, 20, 30, 40])
|
68
|
+
|
69
|
+
# Verify distance works on different units
|
70
|
+
output_x = [1142134, 1138930]
|
71
|
+
output_y = [-4171232, -4213451]
|
72
|
+
out = idw(input_z, input_x, input_y, output_x, output_y, k=4, max_dist=20000)
|
73
|
+
assert np.allclose(out, [15, 35], rtol=0.1)
|
74
|
+
|
75
|
+
# Test multidimensional input
|
76
|
+
input_z = np.column_stack(([1, 2, 3, 4], [10, 20, 30, 40]))
|
77
|
+
input_x = [0, 4, 0, 4]
|
78
|
+
input_y = [0, 0, 4, 4]
|
79
|
+
output_x = [1, 4, 0, 3]
|
80
|
+
output_y = [0, 1, 3, 4]
|
81
|
+
out = idw(input_z, input_x, input_y, output_x, output_y, k=1)
|
82
|
+
assert input_z.shape == out.shape
|
83
|
+
assert np.allclose(out[:, 0], [1, 2, 3, 4])
|
84
|
+
assert np.allclose(out[:, 1], [10, 20, 30, 40])
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|