tobac 1.6.2__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.
- tobac/__init__.py +112 -0
- tobac/analysis/__init__.py +31 -0
- tobac/analysis/cell_analysis.py +628 -0
- tobac/analysis/feature_analysis.py +212 -0
- tobac/analysis/spatial.py +619 -0
- tobac/centerofgravity.py +226 -0
- tobac/feature_detection.py +1758 -0
- tobac/merge_split.py +324 -0
- tobac/plotting.py +2321 -0
- tobac/segmentation/__init__.py +10 -0
- tobac/segmentation/watershed_segmentation.py +1316 -0
- tobac/testing.py +1179 -0
- tobac/tests/segmentation_tests/test_iris_xarray_segmentation.py +0 -0
- tobac/tests/segmentation_tests/test_segmentation.py +1183 -0
- tobac/tests/segmentation_tests/test_segmentation_time_pad.py +104 -0
- tobac/tests/test_analysis_spatial.py +1109 -0
- tobac/tests/test_convert.py +265 -0
- tobac/tests/test_datetime.py +216 -0
- tobac/tests/test_decorators.py +148 -0
- tobac/tests/test_feature_detection.py +1321 -0
- tobac/tests/test_generators.py +273 -0
- tobac/tests/test_import.py +24 -0
- tobac/tests/test_iris_xarray_match_utils.py +244 -0
- tobac/tests/test_merge_split.py +351 -0
- tobac/tests/test_pbc_utils.py +497 -0
- tobac/tests/test_sample_data.py +197 -0
- tobac/tests/test_testing.py +747 -0
- tobac/tests/test_tracking.py +714 -0
- tobac/tests/test_utils.py +650 -0
- tobac/tests/test_utils_bulk_statistics.py +789 -0
- tobac/tests/test_utils_coordinates.py +328 -0
- tobac/tests/test_utils_internal.py +97 -0
- tobac/tests/test_xarray_utils.py +232 -0
- tobac/tracking.py +613 -0
- tobac/utils/__init__.py +27 -0
- tobac/utils/bulk_statistics.py +360 -0
- tobac/utils/datetime.py +184 -0
- tobac/utils/decorators.py +540 -0
- tobac/utils/general.py +753 -0
- tobac/utils/generators.py +87 -0
- tobac/utils/internal/__init__.py +2 -0
- tobac/utils/internal/coordinates.py +430 -0
- tobac/utils/internal/iris_utils.py +462 -0
- tobac/utils/internal/label_props.py +82 -0
- tobac/utils/internal/xarray_utils.py +439 -0
- tobac/utils/mask.py +364 -0
- tobac/utils/periodic_boundaries.py +419 -0
- tobac/wrapper.py +244 -0
- tobac-1.6.2.dist-info/METADATA +154 -0
- tobac-1.6.2.dist-info/RECORD +53 -0
- tobac-1.6.2.dist-info/WHEEL +5 -0
- tobac-1.6.2.dist-info/licenses/LICENSE +29 -0
- tobac-1.6.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"""Unit tests for tobac.utils.generators module"""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timedelta
|
|
4
|
+
|
|
5
|
+
import cftime
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
import pytest
|
|
9
|
+
import xarray as xr
|
|
10
|
+
from pandas.testing import assert_frame_equal
|
|
11
|
+
|
|
12
|
+
from tobac.utils import generators
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_field_and_features_over_time():
|
|
16
|
+
"""Test iterating over field_and_features_over_time generator"""
|
|
17
|
+
test_data = xr.DataArray(
|
|
18
|
+
np.zeros([2, 10, 10]),
|
|
19
|
+
dims=("time", "y", "x"),
|
|
20
|
+
coords={"time": [datetime(2000, 1, 1), datetime(2000, 1, 1, 1)]},
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
test_features = pd.DataFrame(
|
|
24
|
+
{
|
|
25
|
+
"feature": [1, 2, 3],
|
|
26
|
+
"frame": [0, 0, 1],
|
|
27
|
+
"time": [
|
|
28
|
+
datetime(2000, 1, 1),
|
|
29
|
+
datetime(2000, 1, 1),
|
|
30
|
+
datetime(2000, 1, 1, 1),
|
|
31
|
+
],
|
|
32
|
+
}
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
iterator = generators.field_and_features_over_time(test_data, test_features)
|
|
36
|
+
|
|
37
|
+
iter_0 = next(iterator)
|
|
38
|
+
|
|
39
|
+
assert iter_0[0] == 0
|
|
40
|
+
assert iter_0[1] == np.datetime64("2000-01-01")
|
|
41
|
+
assert np.all(iter_0[2] == test_data.isel(time=0))
|
|
42
|
+
assert_frame_equal(
|
|
43
|
+
iter_0[3], test_features[test_features.time == datetime(2000, 1, 1)]
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
iter_1 = next(iterator)
|
|
47
|
+
|
|
48
|
+
assert iter_1[0] == 1
|
|
49
|
+
assert iter_1[1] == np.datetime64("2000-01-01 01:00:00")
|
|
50
|
+
assert np.all(iter_1[2] == test_data.isel(time=1))
|
|
51
|
+
assert_frame_equal(
|
|
52
|
+
iter_1[3], test_features[test_features.time == datetime(2000, 1, 1, 1)]
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
with pytest.raises(StopIteration):
|
|
56
|
+
next(iterator)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_field_and_features_over_time_no_0_dataframe():
|
|
60
|
+
"""Test that field and features over time works when the dataframe has no 0
|
|
61
|
+
index value
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
test_data = xr.DataArray(
|
|
65
|
+
np.zeros([2, 10, 10]),
|
|
66
|
+
dims=("time", "y", "x"),
|
|
67
|
+
coords={"time": [datetime(2000, 1, 1), datetime(2000, 1, 1, 1)]},
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
test_features = pd.DataFrame(
|
|
71
|
+
{
|
|
72
|
+
"feature": [1, 2, 3],
|
|
73
|
+
"frame": [0, 0, 1],
|
|
74
|
+
"time": [
|
|
75
|
+
datetime(2000, 1, 1),
|
|
76
|
+
datetime(2000, 1, 1),
|
|
77
|
+
datetime(2000, 1, 1, 1),
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
index=[1, 2, 3],
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
iterator = generators.field_and_features_over_time(test_data, test_features)
|
|
84
|
+
|
|
85
|
+
iter_0 = next(iterator)
|
|
86
|
+
|
|
87
|
+
assert iter_0[0] == 0
|
|
88
|
+
assert iter_0[1] == np.datetime64("2000-01-01")
|
|
89
|
+
assert np.all(iter_0[2] == test_data.isel(time=0))
|
|
90
|
+
assert_frame_equal(
|
|
91
|
+
iter_0[3], test_features[test_features.time == datetime(2000, 1, 1)]
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_field_and_features_over_time_time_padding():
|
|
96
|
+
"""Test the time_padding functionality of field_and_features_over_time
|
|
97
|
+
generator
|
|
98
|
+
"""
|
|
99
|
+
test_data = xr.DataArray(
|
|
100
|
+
np.zeros([1, 10, 10]),
|
|
101
|
+
dims=("time", "y", "x"),
|
|
102
|
+
coords={"time": [datetime(2000, 1, 1)]},
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
test_features = pd.DataFrame(
|
|
106
|
+
{
|
|
107
|
+
"feature": [1, 2, 3],
|
|
108
|
+
"frame": [0, 0, 0],
|
|
109
|
+
"time": [
|
|
110
|
+
datetime(2000, 1, 1),
|
|
111
|
+
datetime(2000, 1, 1, 0, 0, 1),
|
|
112
|
+
datetime(2000, 1, 1, 0, 0, 2),
|
|
113
|
+
],
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Test no time padding
|
|
118
|
+
_, _, _, df_slice = next(
|
|
119
|
+
generators.field_and_features_over_time(test_data, test_features)
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
assert len(df_slice) == 1
|
|
123
|
+
assert_frame_equal(df_slice, test_features.loc[0:0])
|
|
124
|
+
|
|
125
|
+
# Test time padding of 1 second
|
|
126
|
+
_, _, _, df_slice = next(
|
|
127
|
+
generators.field_and_features_over_time(
|
|
128
|
+
test_data, test_features, time_padding=timedelta(seconds=1)
|
|
129
|
+
)
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
assert len(df_slice) == 2
|
|
133
|
+
assert_frame_equal(df_slice, test_features.loc[0:1])
|
|
134
|
+
|
|
135
|
+
# Test time padding of 2 seconds
|
|
136
|
+
_, _, _, df_slice = next(
|
|
137
|
+
generators.field_and_features_over_time(
|
|
138
|
+
test_data, test_features, time_padding=timedelta(seconds=2)
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
assert len(df_slice) == 3
|
|
143
|
+
assert_frame_equal(df_slice, test_features.loc[0:2])
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def test_field_and_features_over_time_cftime():
|
|
147
|
+
"""Test field_and_features_over_time when given cftime datetime formats"""
|
|
148
|
+
test_data = xr.DataArray(
|
|
149
|
+
np.zeros([2, 10, 10]),
|
|
150
|
+
dims=("time", "y", "x"),
|
|
151
|
+
coords={
|
|
152
|
+
"time": [
|
|
153
|
+
cftime.Datetime360Day(2000, 1, 1),
|
|
154
|
+
cftime.Datetime360Day(2000, 1, 1, 1),
|
|
155
|
+
]
|
|
156
|
+
},
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
test_features = pd.DataFrame(
|
|
160
|
+
{
|
|
161
|
+
"feature": [1, 2, 3],
|
|
162
|
+
"frame": [0, 0, 1],
|
|
163
|
+
"time": [
|
|
164
|
+
cftime.Datetime360Day(2000, 1, 1),
|
|
165
|
+
cftime.Datetime360Day(2000, 1, 1, 0, 0, 1),
|
|
166
|
+
cftime.Datetime360Day(2000, 1, 1, 1),
|
|
167
|
+
],
|
|
168
|
+
}
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
iterator = generators.field_and_features_over_time(
|
|
172
|
+
test_data, test_features, time_padding=timedelta(seconds=1)
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
iter_0 = next(iterator)
|
|
176
|
+
|
|
177
|
+
assert iter_0[0] == 0
|
|
178
|
+
assert iter_0[1] == cftime.Datetime360Day(2000, 1, 1)
|
|
179
|
+
assert np.all(iter_0[2] == test_data.isel(time=0))
|
|
180
|
+
assert_frame_equal(iter_0[3], test_features.loc[0:1])
|
|
181
|
+
|
|
182
|
+
iter_1 = next(iterator)
|
|
183
|
+
|
|
184
|
+
assert iter_1[0] == 1
|
|
185
|
+
assert iter_1[1] == cftime.Datetime360Day(2000, 1, 1, 1)
|
|
186
|
+
assert np.all(iter_1[2] == test_data.isel(time=1))
|
|
187
|
+
assert_frame_equal(
|
|
188
|
+
iter_1[3],
|
|
189
|
+
test_features[test_features.time == cftime.Datetime360Day(2000, 1, 1, 1)],
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
with pytest.raises(StopIteration):
|
|
193
|
+
next(iterator)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def test_field_and_features_over_time_time_var_name():
|
|
197
|
+
"""Test field_and_features_over_time generator works correctly with a time
|
|
198
|
+
coordinate name other than "time"
|
|
199
|
+
"""
|
|
200
|
+
# Test non-standard time coord name:
|
|
201
|
+
test_data = xr.DataArray(
|
|
202
|
+
np.zeros([2, 10, 10]),
|
|
203
|
+
dims=("time_testing", "y", "x"),
|
|
204
|
+
coords={"time_testing": [datetime(2000, 1, 1), datetime(2000, 1, 1, 1)]},
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
test_features = pd.DataFrame(
|
|
208
|
+
{
|
|
209
|
+
"feature": [1, 2, 3],
|
|
210
|
+
"frame": [0, 0, 1],
|
|
211
|
+
"time_testing": [
|
|
212
|
+
datetime(2000, 1, 1),
|
|
213
|
+
datetime(2000, 1, 1),
|
|
214
|
+
datetime(2000, 1, 1, 1),
|
|
215
|
+
],
|
|
216
|
+
}
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
_ = next(
|
|
220
|
+
generators.field_and_features_over_time(
|
|
221
|
+
test_data, test_features, time_var_name="time_testing"
|
|
222
|
+
)
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def test_field_and_features_over_time_time_var_name_error():
|
|
227
|
+
"""Test that field_and_features_over_time generator raises the correct
|
|
228
|
+
error when the name of the time coordinates do not match between the given
|
|
229
|
+
data and dataframe
|
|
230
|
+
"""
|
|
231
|
+
# Test if time_var_name not in dataarray:
|
|
232
|
+
test_data = xr.DataArray(
|
|
233
|
+
np.zeros([2, 10, 10]),
|
|
234
|
+
dims=("time_testing", "y", "x"),
|
|
235
|
+
coords={"time_testing": [datetime(2000, 1, 1), datetime(2000, 1, 1, 1)]},
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
test_features = pd.DataFrame(
|
|
239
|
+
{
|
|
240
|
+
"feature": [1, 2, 3],
|
|
241
|
+
"frame": [0, 0, 1],
|
|
242
|
+
"time": [
|
|
243
|
+
datetime(2000, 1, 1),
|
|
244
|
+
datetime(2000, 1, 1),
|
|
245
|
+
datetime(2000, 1, 1, 1),
|
|
246
|
+
],
|
|
247
|
+
}
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
with pytest.raises(ValueError, match="time not present in input field*"):
|
|
251
|
+
next(generators.field_and_features_over_time(test_data, test_features))
|
|
252
|
+
|
|
253
|
+
# Test if time var name not in dataframe:
|
|
254
|
+
test_data = xr.DataArray(
|
|
255
|
+
np.zeros([2, 10, 10]),
|
|
256
|
+
dims=("time", "y", "x"),
|
|
257
|
+
coords={"time": [datetime(2000, 1, 1), datetime(2000, 1, 1, 1)]},
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
test_features = pd.DataFrame(
|
|
261
|
+
{
|
|
262
|
+
"feature": [1, 2, 3],
|
|
263
|
+
"frame": [0, 0, 1],
|
|
264
|
+
"time_testing": [
|
|
265
|
+
datetime(2000, 1, 1),
|
|
266
|
+
datetime(2000, 1, 1),
|
|
267
|
+
datetime(2000, 1, 1, 1),
|
|
268
|
+
],
|
|
269
|
+
}
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
with pytest.raises(ValueError, match="time not present in input feature*"):
|
|
273
|
+
next(generators.field_and_features_over_time(test_data, test_features))
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import tobac
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_dummy_function():
|
|
6
|
+
assert 1 == 1
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_version():
|
|
10
|
+
"""Test to make sure that we have a version number included.
|
|
11
|
+
Also test to make sure that the version number complies with
|
|
12
|
+
semantic versioning guidelines.
|
|
13
|
+
If it's not, this should result in an error.
|
|
14
|
+
"""
|
|
15
|
+
import re
|
|
16
|
+
|
|
17
|
+
assert type(tobac.__version__) is str
|
|
18
|
+
# Make sure that we are following semantic versioning
|
|
19
|
+
# i.e., our version is of form x.x.x, where x are all
|
|
20
|
+
# integer numbers.
|
|
21
|
+
assert (
|
|
22
|
+
re.match(r"[0-9]+\.[0-9]+\.[0-9]+", tobac.__version__) is not None
|
|
23
|
+
or tobac.__version__ == "unknown_dev_version"
|
|
24
|
+
)
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"""Tests to confirm that xarray and iris pathways work the same and produce the same data
|
|
2
|
+
for the same input datasets.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import copy
|
|
8
|
+
import datetime
|
|
9
|
+
|
|
10
|
+
import iris.cube
|
|
11
|
+
import numpy as np
|
|
12
|
+
import pandas as pd
|
|
13
|
+
import xarray as xr
|
|
14
|
+
import pytest
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
import tobac.testing as tbtest
|
|
18
|
+
import tobac.utils.internal.iris_utils as iris_utils
|
|
19
|
+
import tobac.utils.internal.xarray_utils as xr_utils
|
|
20
|
+
import tobac.utils.datetime as datetime_utils
|
|
21
|
+
from tobac.utils.decorators import convert_cube_to_dataarray
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.mark.parametrize(
|
|
25
|
+
"feature_positions, coordinates, expected_val",
|
|
26
|
+
[
|
|
27
|
+
(
|
|
28
|
+
((0, 0, 0), (9, 9, 9)),
|
|
29
|
+
{"x": ("x", np.linspace(0, 10, 10)), "z": ("z", np.linspace(0, 10, 10))},
|
|
30
|
+
{"x": (0, 10)},
|
|
31
|
+
),
|
|
32
|
+
(
|
|
33
|
+
((0, 0), (9, 9)),
|
|
34
|
+
{"x": ("x", np.linspace(0, 10, 10))},
|
|
35
|
+
{"x": (0, 10)},
|
|
36
|
+
),
|
|
37
|
+
(
|
|
38
|
+
((0, 0), (9, 9), (5, 7)),
|
|
39
|
+
{
|
|
40
|
+
"longitude": ("x", np.linspace(-30, 60, 10)),
|
|
41
|
+
"latitude": ("y", np.linspace(-70, 20, 10)),
|
|
42
|
+
},
|
|
43
|
+
{"latitude": (-70, 20, 0), "longitude": (-30, 60, 20)},
|
|
44
|
+
),
|
|
45
|
+
(
|
|
46
|
+
((0, 0), (9, 9), (5, 7), (3.6, 7.9)),
|
|
47
|
+
{
|
|
48
|
+
"longitude": (
|
|
49
|
+
("x", "y"),
|
|
50
|
+
np.arange(-180, -80).reshape(10, -1),
|
|
51
|
+
),
|
|
52
|
+
"latitude": (("x", "y"), np.arange(-50, 50).reshape(10, -1)),
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"latitude": (-50, 49, 7, -6.1),
|
|
56
|
+
"longitude": (-180, -81, -123, -136.1),
|
|
57
|
+
},
|
|
58
|
+
),
|
|
59
|
+
],
|
|
60
|
+
)
|
|
61
|
+
def test_add_coordinates_xarray_base(
|
|
62
|
+
feature_positions: tuple[tuple[float]],
|
|
63
|
+
coordinates: dict[str : tuple[str, np.ndarray]],
|
|
64
|
+
expected_val: dict[str : tuple[float]],
|
|
65
|
+
):
|
|
66
|
+
"""
|
|
67
|
+
Test that adding coordinates for xarray and iris are equal, using an
|
|
68
|
+
xarray generated dataset as the base.
|
|
69
|
+
|
|
70
|
+
Parameters
|
|
71
|
+
----------
|
|
72
|
+
feature_positions: tuple of tuple of floats
|
|
73
|
+
Locations of the features to test in (hdim_1, hdim_2, zdim [optional]) coordinates
|
|
74
|
+
coordinates: dict, key: str; value: tuple of str, numpy array
|
|
75
|
+
Coordinates to use, in xarray coordinate style. Dims will be ('x', 'y', 'z') for 3D
|
|
76
|
+
data (determined by feature_positions) and ('x', 'y') for 2D data. All axes will have
|
|
77
|
+
size 10.
|
|
78
|
+
expected_val: dict, key: str; value: tuple of floats
|
|
79
|
+
Expected interpolated coordinates
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
all_indiv_feats = []
|
|
84
|
+
if len(feature_positions[0]) == 2:
|
|
85
|
+
is_3D = False
|
|
86
|
+
elif len(feature_positions[0]) == 3:
|
|
87
|
+
is_3D = True
|
|
88
|
+
else:
|
|
89
|
+
raise ValueError("Feature positions should be 2 or 3D")
|
|
90
|
+
for i, single_feat_position in enumerate(feature_positions):
|
|
91
|
+
if not is_3D and len(single_feat_position) == 2:
|
|
92
|
+
all_indiv_feats.append(
|
|
93
|
+
tbtest.generate_single_feature(
|
|
94
|
+
single_feat_position[0],
|
|
95
|
+
single_feat_position[1],
|
|
96
|
+
feature_num=i,
|
|
97
|
+
max_h1=10,
|
|
98
|
+
max_h2=10,
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
elif is_3D and len(single_feat_position) == 3:
|
|
102
|
+
all_indiv_feats.append(
|
|
103
|
+
tbtest.generate_single_feature(
|
|
104
|
+
single_feat_position[0],
|
|
105
|
+
single_feat_position[1],
|
|
106
|
+
start_v=single_feat_position[2],
|
|
107
|
+
feature_num=i,
|
|
108
|
+
max_h1=10,
|
|
109
|
+
max_h2=10,
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
else:
|
|
114
|
+
raise ValueError("Feature positions should be 2 or 3D")
|
|
115
|
+
|
|
116
|
+
all_feats = pd.concat(all_indiv_feats)
|
|
117
|
+
|
|
118
|
+
da_size = (1, 10, 10, 10) if is_3D else (1, 10, 10)
|
|
119
|
+
dims = ("time", "x", "y", "z") if is_3D else ("time", "x", "y")
|
|
120
|
+
coordinates["time"] = np.array((datetime.datetime(2000, 1, 1, 0),))
|
|
121
|
+
da_with_coords = xr.DataArray(data=np.empty(da_size), dims=dims, coords=coordinates)
|
|
122
|
+
if is_3D:
|
|
123
|
+
iris_coord_interp = iris_utils.add_coordinates_3D(
|
|
124
|
+
all_feats, da_with_coords.to_iris()
|
|
125
|
+
)
|
|
126
|
+
xr_coord_interp = xr_utils.add_coordinates_to_features(
|
|
127
|
+
all_feats, da_with_coords
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
else:
|
|
131
|
+
iris_coord_interp = iris_utils.add_coordinates(
|
|
132
|
+
all_feats, da_with_coords.to_iris()
|
|
133
|
+
)
|
|
134
|
+
xr_coord_interp = xr_utils.add_coordinates_to_features(
|
|
135
|
+
all_feats, da_with_coords
|
|
136
|
+
)
|
|
137
|
+
for val_name in expected_val:
|
|
138
|
+
np.testing.assert_almost_equal(
|
|
139
|
+
iris_coord_interp[val_name], expected_val[val_name]
|
|
140
|
+
)
|
|
141
|
+
np.testing.assert_almost_equal(
|
|
142
|
+
xr_coord_interp[val_name], expected_val[val_name]
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# assert (iris_coord_interp[val_name] == expected_val[val_name]).all()
|
|
146
|
+
# assert (xr_coord_interp[val_name] == expected_val[val_name]).all()
|
|
147
|
+
|
|
148
|
+
# Convert datetimes to ensure that they are the same type:
|
|
149
|
+
xr_coord_interp["time"] = datetime_utils.match_datetime_format(
|
|
150
|
+
xr_coord_interp.time, iris_coord_interp.time
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
pd.testing.assert_frame_equal(iris_coord_interp, xr_coord_interp)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@pytest.mark.parametrize(
|
|
157
|
+
"coordinate_names, coordinate_standard_names",
|
|
158
|
+
[(("lat",), ("latitude",))],
|
|
159
|
+
)
|
|
160
|
+
def test_add_coordinates_xarray_std_names(
|
|
161
|
+
coordinate_names: tuple[str],
|
|
162
|
+
coordinate_standard_names: tuple[str],
|
|
163
|
+
):
|
|
164
|
+
"""
|
|
165
|
+
Test that adding coordinates for xarray and iris result in the same coordinate names
|
|
166
|
+
when standard_names are added to the xarray coordinates
|
|
167
|
+
|
|
168
|
+
Parameters
|
|
169
|
+
----------
|
|
170
|
+
coordinate_names: tuple of str
|
|
171
|
+
names of coordinates to give
|
|
172
|
+
coordinate_standard_name: tuple of str
|
|
173
|
+
standard_names of coordinates to give
|
|
174
|
+
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
all_feats = tbtest.generate_single_feature(
|
|
178
|
+
0,
|
|
179
|
+
0,
|
|
180
|
+
feature_num=1,
|
|
181
|
+
max_h1=10,
|
|
182
|
+
max_h2=10,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
da_size = (1, 10, 10)
|
|
186
|
+
dims = ("time", "x", "y")
|
|
187
|
+
coordinates = dict()
|
|
188
|
+
coordinates["time"] = np.array((datetime.datetime(2000, 1, 1, 0),))
|
|
189
|
+
|
|
190
|
+
for coord_name, coord_standard_name in zip(
|
|
191
|
+
coordinate_names, coordinate_standard_names
|
|
192
|
+
):
|
|
193
|
+
coordinates[coord_name] = xr.DataArray(data=np.arange(10), dims="x")
|
|
194
|
+
coordinates[coord_name].attrs["standard_name"] = coord_standard_name
|
|
195
|
+
|
|
196
|
+
da_with_coords = xr.DataArray(data=np.empty(da_size), dims=dims, coords=coordinates)
|
|
197
|
+
|
|
198
|
+
iris_coord_interp = iris_utils.add_coordinates(
|
|
199
|
+
copy.deepcopy(all_feats), da_with_coords.to_iris()
|
|
200
|
+
)
|
|
201
|
+
xr_coord_interp = xr_utils.add_coordinates_to_features(
|
|
202
|
+
copy.deepcopy(all_feats), da_with_coords
|
|
203
|
+
)
|
|
204
|
+
xr_coord_interp["time"] = datetime_utils.match_datetime_format(
|
|
205
|
+
xr_coord_interp.time, iris_coord_interp.time
|
|
206
|
+
)
|
|
207
|
+
pd.testing.assert_frame_equal(iris_coord_interp, xr_coord_interp)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def test_preserve_iris_datetime_types():
|
|
211
|
+
"""
|
|
212
|
+
Test that xarray.add_coordinates_to_features correctly returns the same time types as
|
|
213
|
+
iris when preserve_iris_datetime_types = True.
|
|
214
|
+
"""
|
|
215
|
+
|
|
216
|
+
all_feats = tbtest.generate_single_feature(
|
|
217
|
+
0,
|
|
218
|
+
0,
|
|
219
|
+
feature_num=1,
|
|
220
|
+
max_h1=10,
|
|
221
|
+
max_h2=10,
|
|
222
|
+
)
|
|
223
|
+
var_array: iris.cube.Cube = tbtest.make_simple_sample_data_2D(data_type="iris")
|
|
224
|
+
|
|
225
|
+
xarray_output = xr_utils.add_coordinates_to_features(
|
|
226
|
+
all_feats,
|
|
227
|
+
convert_cube_to_dataarray(var_array, preserve_iris_datetime_types=True),
|
|
228
|
+
)
|
|
229
|
+
iris_output = iris_utils.add_coordinates(all_feats, var_array)
|
|
230
|
+
|
|
231
|
+
pd.testing.assert_frame_equal(xarray_output, iris_output)
|
|
232
|
+
assert xarray_output["time"].values[0] == iris_output["time"].values[0]
|
|
233
|
+
assert isinstance(
|
|
234
|
+
xarray_output["time"].values[0], type(iris_output["time"].values[0])
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
xarray_output_datetime_preserve_off = xr_utils.add_coordinates_to_features(
|
|
238
|
+
all_feats,
|
|
239
|
+
convert_cube_to_dataarray(var_array, preserve_iris_datetime_types=False),
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
assert isinstance(
|
|
243
|
+
xarray_output_datetime_preserve_off["time"].values[0], np.datetime64
|
|
244
|
+
)
|