eo-tides 0.0.21__py3-none-any.whl → 0.0.22__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/model.py CHANGED
@@ -243,7 +243,7 @@ def _model_tides(
243
243
  lon,
244
244
  lat,
245
245
  pytmd_model.model_file,
246
- grid=pytmd_model.type,
246
+ grid=pytmd_model.file_format,
247
247
  crop=crop,
248
248
  bounds=bounds,
249
249
  method=method,
@@ -277,6 +277,10 @@ def _model_tides(
277
277
 
278
278
  # Delta time (TT - UT1)
279
279
  deltat = timescale.tt_ut1
280
+ else:
281
+ raise Exception(
282
+ f"Unsupported model format ({pytmd_model.format}). This may be due to an incompatible version of `pyTMD`."
283
+ )
280
284
 
281
285
  # Calculate complex phase in radians for Euler's
282
286
  cph = -1j * ph * np.pi / 180.0
eo_tides/stats.py CHANGED
@@ -26,8 +26,10 @@ def tide_stats(
26
26
  tidepost_lon: float | None = None,
27
27
  plain_english: bool = True,
28
28
  plot: bool = True,
29
+ plot_col: str | None = None,
29
30
  modelled_freq: str = "2h",
30
31
  linear_reg: bool = False,
32
+ min_max_q: tuple = (0.0, 1.0),
31
33
  round_stats: int = 3,
32
34
  **model_tides_kwargs,
33
35
  ) -> pd.Series:
@@ -73,6 +75,10 @@ def tide_stats(
73
75
  An optional boolean indicating whether to plot how satellite-
74
76
  observed tide heights compare against the full tidal range.
75
77
  Defaults to True.
78
+ plot_col : str, optional
79
+ Optional name of a coordinate, dimension or variable in the array
80
+ that will be used to plot observations with unique symbols.
81
+ Defaults to None, which will plot all observations as circles.
76
82
  modelled_freq : str, optional
77
83
  An optional string giving the frequency at which to model tides
78
84
  when computing the full modelled tidal range. Defaults to '2h',
@@ -84,6 +90,11 @@ def tide_stats(
84
90
  increasing trends over time. This may indicate whether your
85
91
  satellite data may produce misleading trends based on uneven
86
92
  sampling of the local tide regime.
93
+ min_max_q : tuple, optional
94
+ Quantiles used to calculate max and min observed and modelled
95
+ astronomical tides. By default `(0.0, 1.0)` which is equivalent
96
+ to minimum and maximum; to use a softer threshold that is more
97
+ robust to outliers, use e.g. `(0.1, 0.9)`.
87
98
  round_stats : int, optional
88
99
  The number of decimal places used to round the output statistics.
89
100
  Defaults to 3.
@@ -135,6 +146,7 @@ def tide_stats(
135
146
  return_tideposts=True,
136
147
  **model_tides_kwargs,
137
148
  )
149
+ ds_tides = ds_tides.sortby("time")
138
150
 
139
151
  # Drop spatial ref for nicer plotting
140
152
  ds_tides = ds_tides.drop_vars("spatial_ref")
@@ -160,8 +172,8 @@ def tide_stats(
160
172
  # Get coarse statistics on all and observed tidal ranges
161
173
  obs_mean = ds_tides.tide_height.mean().item()
162
174
  all_mean = all_tides_df.tide_height.mean()
163
- obs_min, obs_max = ds_tides.tide_height.quantile([0.0, 1.0]).values
164
- all_min, all_max = all_tides_df.tide_height.quantile([0.0, 1.0]).values
175
+ obs_min, obs_max = ds_tides.tide_height.quantile(min_max_q).values
176
+ all_min, all_max = all_tides_df.tide_height.quantile(min_max_q).values
165
177
 
166
178
  # Calculate tidal range
167
179
  obs_range = obs_max - obs_min
@@ -169,47 +181,89 @@ def tide_stats(
169
181
 
170
182
  # Calculate Bishop-Taylor et al. 2018 tidal metrics
171
183
  spread = obs_range / all_range
172
- low_tide_offset = abs(all_min - obs_min) / all_range
173
- high_tide_offset = abs(all_max - obs_max) / all_range
184
+ low_tide_offset_m = abs(all_min - obs_min)
185
+ high_tide_offset_m = abs(all_max - obs_max)
186
+ low_tide_offset = low_tide_offset_m / all_range
187
+ high_tide_offset = high_tide_offset_m / all_range
188
+
189
+ # Plain text descriptors
190
+ mean_diff = "higher" if obs_mean > all_mean else "lower"
191
+ mean_diff_icon = "⬆️" if obs_mean > all_mean else "⬇️"
192
+ spread_icon = "🟢" if spread >= 0.9 else "🟡" if 0.7 < spread <= 0.9 else "🔴"
193
+ low_tide_icon = "🟢" if low_tide_offset <= 0.1 else "🟡" if 0.1 <= low_tide_offset < 0.2 else "🔴"
194
+ high_tide_icon = "🟢" if high_tide_offset <= 0.1 else "🟡" if 0.1 <= high_tide_offset < 0.2 else "🔴"
174
195
 
175
196
  # Extract x (time in decimal years) and y (distance) values
176
197
  all_times = all_tides_df.index.get_level_values("time")
177
- all_x = all_times.year + ((all_times.dayofyear - 1) / 365) + ((all_times.hour - 1) / 24)
198
+ all_x = all_times.year + ((all_times.dayofyear - 1) / 365) + ((all_times.hour) / 24)
178
199
  time_period = all_x.max() - all_x.min()
179
200
 
180
201
  # Extract x (time in decimal years) and y (distance) values
181
- obs_x = ds_tides.time.dt.year + ((ds_tides.time.dt.dayofyear - 1) / 365) + ((ds_tides.time.dt.hour - 1) / 24)
202
+ obs_x = ds_tides.time.dt.year + ((ds_tides.time.dt.dayofyear - 1) / 365) + ((ds_tides.time.dt.hour) / 24)
182
203
  obs_y = ds_tides.tide_height.values.astype(np.float32)
183
204
 
184
205
  # Compute linear regression
185
206
  obs_linreg = stats.linregress(x=obs_x, y=obs_y)
186
207
 
208
+ # return obs_linreg
209
+
187
210
  if plain_english:
211
+ print(f"\n\n🌊 Modelled astronomical tide range: {all_range:.2f} metres.")
212
+ print(f"🛰️ Observed tide range: {obs_range:.2f} metres.\n")
213
+ print(f" {spread_icon} {spread:.0%} of the modelled astronomical tide range was observed at this location.")
214
+ print(
215
+ f" {high_tide_icon} The highest {high_tide_offset:.0%} ({high_tide_offset_m:.2f} metres) of the tide range was never observed."
216
+ )
188
217
  print(
189
- f"\n{spread:.0%} of the {all_range:.2f} m modelled astronomical "
190
- f"tidal range is observed at this location.\nThe lowest "
191
- f"{low_tide_offset:.0%} and highest {high_tide_offset:.0%} "
192
- f"of astronomical tides are never observed.\n"
218
+ f" {low_tide_icon} The lowest {low_tide_offset:.0%} ({low_tide_offset_m:.2f} metres) of the tide range was never observed.\n"
219
+ )
220
+ print(f"🌊 Mean modelled astronomical tide height: {all_mean:.2f} metres.")
221
+ print(f"🛰️ Mean observed tide height: {obs_mean:.2f} metres.\n")
222
+ print(
223
+ f" {mean_diff_icon} The mean observed tide height was {obs_mean - all_mean:.2f} metres {mean_diff} than the mean modelled astronomical tide height."
193
224
  )
194
225
 
195
226
  if linear_reg:
196
- if obs_linreg.pvalue > 0.05:
197
- print(f"Observed tides show no significant trends " f"over the ~{time_period:.0f} year period.")
227
+ if obs_linreg.pvalue > 0.01:
228
+ print("Observed tides showed no significant trends over time.")
198
229
  else:
199
- obs_slope_desc = "decrease" if obs_linreg.slope < 0 else "increase"
230
+ obs_slope_desc = "decreasing" if obs_linreg.slope < 0 else "increasing"
200
231
  print(
201
- f"Observed tides {obs_slope_desc} significantly "
202
- f"(p={obs_linreg.pvalue:.3f}) over time by "
203
- f"{obs_linreg.slope:.03f} m per year (i.e. a "
204
- f"~{time_period * obs_linreg.slope:.2f} m "
205
- f"{obs_slope_desc} over the ~{time_period:.0f} year period)."
232
+ f" ⚠️ Observed tides showed a significant {obs_slope_desc} trend over time (p={obs_linreg.pvalue:.3f}, {obs_linreg.slope:.2f} metres per year)"
206
233
  )
207
234
 
208
235
  if plot:
209
236
  # Create plot and add all time and observed tide data
210
- fig, ax = plt.subplots(figsize=(10, 5))
211
- all_tides_df.reset_index(["x", "y"]).tide_height.plot(ax=ax, alpha=0.4)
212
- ds_tides.tide_height.plot.line(ax=ax, marker="o", linewidth=0.0, color="black", markersize=2)
237
+ fig, ax = plt.subplots(figsize=(10, 6))
238
+ all_tides_df.reset_index(["x", "y"]).tide_height.plot(ax=ax, alpha=0.4, label="Modelled tides")
239
+
240
+ # Look through custom column values if provided
241
+ if plot_col is not None:
242
+ # Create a list of marker styles
243
+ markers = ["o", "^", "s", "D", "v", "<", ">", "p", "*", "h", "H", "+", "x", "d", "|", "_"]
244
+ for i, value in enumerate(np.unique(ds_tides[plot_col])):
245
+ ds_tides.where(ds_tides[plot_col] == value, drop=True).tide_height.plot.line(
246
+ ax=ax,
247
+ linewidth=0.0,
248
+ color="black",
249
+ marker=markers[i % len(markers)],
250
+ markersize=4,
251
+ label=value,
252
+ )
253
+ # Otherwise, plot all data at once
254
+ else:
255
+ ds_tides.tide_height.plot.line(
256
+ ax=ax, marker="o", linewidth=0.0, color="black", markersize=3.5, label="Satellite observations"
257
+ )
258
+
259
+ ax.legend(loc="upper center", bbox_to_anchor=(0.5, 1.04), ncol=20, borderaxespad=0, frameon=False)
260
+
261
+ ax.plot(
262
+ ds_tides.time.isel(time=[0, -1]),
263
+ obs_linreg.intercept + obs_linreg.slope * obs_x[[0, -1]],
264
+ "r",
265
+ label="fitted line",
266
+ )
213
267
 
214
268
  # Add horizontal lines for spread/offsets
215
269
  ax.axhline(obs_min, color="black", linestyle=":", linewidth=1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: eo-tides
3
- Version: 0.0.21
3
+ Version: 0.0.22
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/
@@ -22,23 +22,24 @@ Classifier: Programming Language :: Python :: 3.12
22
22
  Requires-Python: <4.0,>=3.9
23
23
  Description-Content-Type: text/markdown
24
24
  License-File: LICENSE
25
- Requires-Dist: colorama
26
- Requires-Dist: geopandas >=1.0.0
27
- Requires-Dist: numpy
28
- Requires-Dist: odc-geo
29
- Requires-Dist: pandas
30
- Requires-Dist: pyproj
25
+ Requires-Dist: colorama >=0.4.3
26
+ Requires-Dist: geopandas >=0.10.0
27
+ Requires-Dist: matplotlib >=3.8.0
28
+ Requires-Dist: numpy >=1.26.0
29
+ Requires-Dist: odc-geo >=0.4.7
30
+ Requires-Dist: pandas >=2.2.0
31
+ Requires-Dist: pyproj >=3.6.1
31
32
  Requires-Dist: pyTMD ==2.1.6
32
- Requires-Dist: scikit-learn
33
- Requires-Dist: scipy
34
- Requires-Dist: shapely
35
- Requires-Dist: tqdm
36
- Requires-Dist: xarray
33
+ Requires-Dist: scikit-learn >=1.4.0
34
+ Requires-Dist: scipy >=1.11.2
35
+ Requires-Dist: shapely >=2.0.6
36
+ Requires-Dist: tqdm >=4.55.0
37
+ Requires-Dist: xarray >=2022.3.0
37
38
  Provides-Extra: notebooks
38
39
  Requires-Dist: odc-stac >=0.3.10 ; extra == 'notebooks'
39
- Requires-Dist: pystac-client ; extra == 'notebooks'
40
- Requires-Dist: folium ; extra == 'notebooks'
41
- Requires-Dist: matplotlib ; extra == 'notebooks'
40
+ Requires-Dist: pystac-client >=0.8.3 ; extra == 'notebooks'
41
+ Requires-Dist: folium >=0.16.0 ; extra == 'notebooks'
42
+ Requires-Dist: planetary-computer >=1.0.0 ; extra == 'notebooks'
42
43
 
43
44
  # `eo-tides`: Tide modelling tools for large-scale satellite earth observation analysis
44
45
 
@@ -0,0 +1,11 @@
1
+ eo_tides/__init__.py,sha256=TWmQNplePCcNAlua5WI_H7SShkWNk-Gd3X70EDEknSo,1557
2
+ eo_tides/eo.py,sha256=K2Ubxvp5yYxVwAOaUZDHCIV_AppRuqMiQRtArhe7YOM,22480
3
+ eo_tides/model.py,sha256=l7RmzRqr_-yrNMVwW0bisCSnxR9NFHrwWda6WvQHZKU,33129
4
+ eo_tides/stats.py,sha256=-piIpX3SmZL8DJntSLORRozyAJ1-od49GU-p56Cs_lc,13663
5
+ eo_tides/utils.py,sha256=l9VXJawQzaRBYaFMsP8VBeaN5VA3rFDdzcvF7Rk04Vc,5620
6
+ eo_tides/validation.py,sha256=yFuIjAxS9qf097_n4DHWG4AOa6n4nt1HGibUkOkJW6o,11437
7
+ eo_tides-0.0.22.dist-info/LICENSE,sha256=owxWsXViCL2J6Ks3XYhot7t4Y93nstmXAT95Zf030Cc,11350
8
+ eo_tides-0.0.22.dist-info/METADATA,sha256=Ae6NT_B98l4eZauFon-YBdb04z02JKRhzI6cxfvMBxc,6452
9
+ eo_tides-0.0.22.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
10
+ eo_tides-0.0.22.dist-info/top_level.txt,sha256=lXZDUUM1DlLdKWHRn8zdmtW8Rx-eQOIWVvt0b8VGiyQ,9
11
+ eo_tides-0.0.22.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- eo_tides/__init__.py,sha256=TWmQNplePCcNAlua5WI_H7SShkWNk-Gd3X70EDEknSo,1557
2
- eo_tides/eo.py,sha256=K2Ubxvp5yYxVwAOaUZDHCIV_AppRuqMiQRtArhe7YOM,22480
3
- eo_tides/model.py,sha256=WC-3gBH-ToREl1RS6CxYJLfXlpy03vtwZKd_464QYcU,32958
4
- eo_tides/stats.py,sha256=gpkewfgM7ixOr_XXepkMHpezmvjlt5-Qo2xbUmzyKhs,10898
5
- eo_tides/utils.py,sha256=l9VXJawQzaRBYaFMsP8VBeaN5VA3rFDdzcvF7Rk04Vc,5620
6
- eo_tides/validation.py,sha256=yFuIjAxS9qf097_n4DHWG4AOa6n4nt1HGibUkOkJW6o,11437
7
- eo_tides-0.0.21.dist-info/LICENSE,sha256=owxWsXViCL2J6Ks3XYhot7t4Y93nstmXAT95Zf030Cc,11350
8
- eo_tides-0.0.21.dist-info/METADATA,sha256=t60AoRbSg9K6GEXlk3TCd9nrEQbaFrHp7rgAmwAda_g,6298
9
- eo_tides-0.0.21.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
10
- eo_tides-0.0.21.dist-info/top_level.txt,sha256=lXZDUUM1DlLdKWHRn8zdmtW8Rx-eQOIWVvt0b8VGiyQ,9
11
- eo_tides-0.0.21.dist-info/RECORD,,