tsp 1.7.3__py3-none-any.whl → 1.7.7__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.
Potentially problematic release.
This version of tsp might be problematic. Click here for more details.
- tsp/__init__.py +11 -11
- tsp/__meta__.py +1 -1
- tsp/core.py +1035 -1010
- tsp/data/2023-01-06_755-test-Dataset_2031-Constant_Over_Interval-Hourly-Ground_Temperature-Thermistor_Automated.timeserie.csv +4 -4
- tsp/data/2023-01-06_755-test.metadata.txt +208 -208
- tsp/data/NTGS_example_csv.csv +6 -0
- tsp/data/NTGS_example_slash_dates.csv +6 -0
- tsp/data/example_geotop.csv +5240 -5240
- tsp/data/example_gtnp.csv +1298 -1298
- tsp/data/example_permos.csv +8 -0
- tsp/data/test_geotop_has_space.txt +5 -5
- tsp/dataloggers/AbstractReader.py +43 -43
- tsp/dataloggers/FG2.py +110 -110
- tsp/dataloggers/GP5W.py +114 -114
- tsp/dataloggers/Geoprecision.py +34 -34
- tsp/dataloggers/HOBO.py +914 -914
- tsp/dataloggers/RBRXL800.py +190 -190
- tsp/dataloggers/RBRXR420.py +308 -308
- tsp/dataloggers/__init__.py +15 -15
- tsp/dataloggers/logr.py +115 -115
- tsp/dataloggers/test_files/004448.DAT +2543 -2543
- tsp/dataloggers/test_files/004531.DAT +17106 -17106
- tsp/dataloggers/test_files/004531.HEX +3587 -3587
- tsp/dataloggers/test_files/004534.HEX +3587 -3587
- tsp/dataloggers/test_files/010252.dat +1731 -1731
- tsp/dataloggers/test_files/010252.hex +1739 -1739
- tsp/dataloggers/test_files/010274.hex +1291 -1291
- tsp/dataloggers/test_files/010278.hex +3544 -3544
- tsp/dataloggers/test_files/012064.dat +1286 -1286
- tsp/dataloggers/test_files/012064.hex +1294 -1294
- tsp/dataloggers/test_files/012081.hex +3532 -3532
- tsp/dataloggers/test_files/07B1592.DAT +1483 -1483
- tsp/dataloggers/test_files/07B1592.HEX +1806 -1806
- tsp/dataloggers/test_files/07B4450.DAT +2234 -2234
- tsp/dataloggers/test_files/07B4450.HEX +2559 -2559
- tsp/dataloggers/test_files/CSc_CR1000_1.dat +295 -0
- tsp/dataloggers/test_files/FG2_399.csv +9881 -9881
- tsp/dataloggers/test_files/GP5W.csv +1121 -1121
- tsp/dataloggers/test_files/GP5W_260.csv +1884 -1884
- tsp/dataloggers/test_files/GP5W_270.csv +2210 -2210
- tsp/dataloggers/test_files/H08-030-08_HOBOware.csv +998 -998
- tsp/dataloggers/test_files/RBR_01.dat +1046 -1046
- tsp/dataloggers/test_files/RBR_02.dat +2426 -2426
- tsp/dataloggers/test_files/RSTDT2055.csv +2152 -2152
- tsp/dataloggers/test_files/U23-001_HOBOware.csv +1001 -1001
- tsp/dataloggers/test_files/hobo-negative-2.txt +6396 -6396
- tsp/dataloggers/test_files/hobo-negative-3.txt +5593 -5593
- tsp/dataloggers/test_files/hobo-positive-number-1.txt +1000 -1000
- tsp/dataloggers/test_files/hobo-positive-number-2.csv +1003 -1003
- tsp/dataloggers/test_files/hobo-positive-number-3.csv +1133 -1133
- tsp/dataloggers/test_files/hobo-positive-number-4.csv +1209 -1209
- tsp/dataloggers/test_files/hobo2.csv +8702 -8702
- tsp/dataloggers/test_files/hobo_1_AB.csv +21732 -21732
- tsp/dataloggers/test_files/hobo_1_AB_Details.txt +133 -133
- tsp/dataloggers/test_files/hobo_1_AB_classic.csv +4373 -4373
- tsp/dataloggers/test_files/hobo_1_AB_defaults.csv +21732 -21732
- tsp/dataloggers/test_files/hobo_1_AB_minimal.txt +1358 -1358
- tsp/dataloggers/test_files/hobo_1_AB_var2.csv +3189 -3189
- tsp/dataloggers/test_files/hobo_1_AB_var3.csv +2458 -2458
- tsp/dataloggers/test_files/logR_ULogC16-32_1.csv +106 -106
- tsp/dataloggers/test_files/logR_ULogC16-32_2.csv +100 -100
- tsp/dataloggers/test_files/mon_3_Ta_2010-08-18_2013-02-08.txt +21724 -21724
- tsp/dataloggers/test_files/rbr_001.dat +1133 -1133
- tsp/dataloggers/test_files/rbr_001.hex +1139 -1139
- tsp/dataloggers/test_files/rbr_001_no_comment.dat +1132 -1132
- tsp/dataloggers/test_files/rbr_001_no_comment.hex +1138 -1138
- tsp/dataloggers/test_files/rbr_002.dat +1179 -1179
- tsp/dataloggers/test_files/rbr_002.hex +1185 -1185
- tsp/dataloggers/test_files/rbr_003.hex +1292 -1292
- tsp/dataloggers/test_files/rbr_003.xls +0 -0
- tsp/dataloggers/test_files/rbr_xl_001.DAT +1105 -1105
- tsp/dataloggers/test_files/rbr_xl_002.DAT +1126 -1126
- tsp/dataloggers/test_files/rbr_xl_003.DAT +4622 -4622
- tsp/dataloggers/test_files/rbr_xl_003.HEX +3587 -3587
- tsp/gtnp.py +148 -141
- tsp/labels.py +3 -3
- tsp/misc.py +90 -90
- tsp/physics.py +101 -101
- tsp/plots/static.py +374 -305
- tsp/readers.py +548 -536
- tsp/scratch.py +6 -0
- tsp/time.py +45 -45
- tsp/tspwarnings.py +15 -0
- tsp/utils.py +101 -101
- tsp/version.py +1 -1
- {tsp-1.7.3.dist-info → tsp-1.7.7.dist-info}/LICENSE +674 -674
- {tsp-1.7.3.dist-info → tsp-1.7.7.dist-info}/METADATA +9 -5
- tsp-1.7.7.dist-info/RECORD +95 -0
- {tsp-1.7.3.dist-info → tsp-1.7.7.dist-info}/WHEEL +5 -5
- tsp-1.7.3.dist-info/RECORD +0 -89
- {tsp-1.7.3.dist-info → tsp-1.7.7.dist-info}/top_level.txt +0 -0
tsp/plots/static.py
CHANGED
|
@@ -1,305 +1,374 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
import warnings
|
|
3
|
-
|
|
4
|
-
import matplotlib.dates as mdates
|
|
5
|
-
|
|
6
|
-
from matplotlib
|
|
7
|
-
from
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
import
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
A d-length array of
|
|
34
|
-
|
|
35
|
-
A d-length array of temperature values representing the
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Units for the
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
ax1.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
A
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
t.long['depth'].values]).transpose(),
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
Z =
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
fig.
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
1
|
+
import numpy as np
|
|
2
|
+
import warnings
|
|
3
|
+
|
|
4
|
+
import matplotlib.dates as mdates
|
|
5
|
+
import matplotlib.cm as cm
|
|
6
|
+
from matplotlib import pyplot as plt
|
|
7
|
+
from matplotlib.figure import Figure
|
|
8
|
+
from matplotlib.colors import ListedColormap
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from scipy.interpolate import griddata
|
|
13
|
+
except ModuleNotFoundError:
|
|
14
|
+
warnings.warn("Missing scipy module. Some functionality will be limited.")
|
|
15
|
+
|
|
16
|
+
from typing import Union
|
|
17
|
+
|
|
18
|
+
import tsp
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def trumpet_curve(depth, t_max, t_min, t_mean,
|
|
22
|
+
title:str="", max_depth:Optional[float]=None,
|
|
23
|
+
t_units:str=u'\N{DEGREE SIGN} C', d_units:str="m",
|
|
24
|
+
data_completeness=None,
|
|
25
|
+
min_completeness:Optional[float]=None) -> Figure:
|
|
26
|
+
"""Plot a trumpet curve
|
|
27
|
+
|
|
28
|
+
The function returns a matplotlib Figure object. To show the figure, you must call the `show()` method.
|
|
29
|
+
|
|
30
|
+
Parameters
|
|
31
|
+
----------
|
|
32
|
+
depth : numpy.ndarray
|
|
33
|
+
A d-length array of depths at which temperature values are
|
|
34
|
+
t_max : numpy.ndarray
|
|
35
|
+
A d-length array of temperature values representing the maximum temperatures over the period at each of the depths.
|
|
36
|
+
t_min : numpy.ndarray
|
|
37
|
+
A d-length array of temperature values representing the minimum temperatures over the period at each of the depths.
|
|
38
|
+
t_mean : str
|
|
39
|
+
A d-length array of temperature values representing the mean temperatures over the period at each of the depths.
|
|
40
|
+
title : str, optional
|
|
41
|
+
A title for the figure, by default ""
|
|
42
|
+
max_depth : float, optional
|
|
43
|
+
If provided, limits the maximum y-axis extent of the plot, by default None
|
|
44
|
+
t_units : unicode, optional
|
|
45
|
+
Units for the x-axis (assumed to be temperature), by default u'\N{DEGREE SIGN} C'
|
|
46
|
+
d_units : str, optional
|
|
47
|
+
Units for the y axis (depth), by default "m"
|
|
48
|
+
data_completeness : numpy.ndarray
|
|
49
|
+
A d-length array of representing data completeness as a fraction (e.g. 0 to 1) for each of the averaging periods ()
|
|
50
|
+
min_completeness : float
|
|
51
|
+
Minimum data completeness to be included in the temperature envelope
|
|
52
|
+
|
|
53
|
+
Returns
|
|
54
|
+
-------
|
|
55
|
+
Figure
|
|
56
|
+
A matplotlib Figure. Note that to show the figure you must call the `show()` method or `matplotlib.pyplot.show()`.
|
|
57
|
+
|
|
58
|
+
Raises
|
|
59
|
+
------
|
|
60
|
+
ValueError
|
|
61
|
+
_description_
|
|
62
|
+
"""
|
|
63
|
+
## Sanity checks and data
|
|
64
|
+
if data_completeness is None:
|
|
65
|
+
data_completeness = np.ones_like(depth)
|
|
66
|
+
if min_completeness is None:
|
|
67
|
+
min_completeness = 0.001
|
|
68
|
+
|
|
69
|
+
if not len(depth) == len(t_max) == len(t_min) == len(t_mean) == len(data_completeness):
|
|
70
|
+
raise ValueError("Length of input arrays must be equal")
|
|
71
|
+
|
|
72
|
+
depth = - np.abs(depth)
|
|
73
|
+
|
|
74
|
+
## Create figure
|
|
75
|
+
fig, ax1 = plt.subplots()
|
|
76
|
+
|
|
77
|
+
## Create artists
|
|
78
|
+
# TODO: https://stackoverflow.com/questions/45176584/dotted-lines-instead-of-a-missing-value-in-matplotlib
|
|
79
|
+
m = np.where(data_completeness >= min_completeness, True, False)
|
|
80
|
+
if m.any():
|
|
81
|
+
line_max = ax1.plot(t_max[m], depth[m], color='red', gid="ln-max-temperature")
|
|
82
|
+
line_min = ax1.plot(t_min[m], depth[m], color='blue', gid="ln-min-temperature")
|
|
83
|
+
line_mean = ax1.plot(t_mean[m], depth[m], color='black', gid="ln-mean-temperature")
|
|
84
|
+
|
|
85
|
+
alphas = np.where((data_completeness / min_completeness) < 1, 0.1 + (0.7 * data_completeness / min_completeness), 1)
|
|
86
|
+
|
|
87
|
+
marker_max = ax1.scatter(t_max, depth, marker='.', c=alpha([1,0,0], alphas), gid="pt-max-temperature")
|
|
88
|
+
marker_min = ax1.scatter(t_min, depth, marker='.', c=alpha([0,0,1], alphas), gid="pt-min-temperature")
|
|
89
|
+
marker_mean = ax1.scatter(t_mean, depth, marker='.', c=alpha([0,0,0], alphas), gid="pt-mean-temperature")
|
|
90
|
+
|
|
91
|
+
surface = ax1.hlines(y=0.0, xmin=-100, xmax=100, linewidth=0.5, linestyles='dotted', color='grey')
|
|
92
|
+
zero = ax1.vlines(x=0.0, ymin=-100, ymax=100, linewidth=0.5, linestyles='dotted', color='grey')
|
|
93
|
+
|
|
94
|
+
## Set axis properties
|
|
95
|
+
ax1.set_ybound(upper=1, lower=min(depth) - 3)
|
|
96
|
+
|
|
97
|
+
if max_depth:
|
|
98
|
+
ax1.set_ybound(lower=-abs(max_depth))
|
|
99
|
+
|
|
100
|
+
ax1.set_xbound(lower=min(t_min) - 3, upper=max(t_max) + 3)
|
|
101
|
+
|
|
102
|
+
## Set axis labels
|
|
103
|
+
ax1.set_xlabel(f"Temperature [{t_units}]")
|
|
104
|
+
ax1.set_ylabel(f"Depth [{d_units}]")
|
|
105
|
+
ax1.set_title(title)
|
|
106
|
+
|
|
107
|
+
return fig
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def colour_contour(depths, times, values, title="", colours: "Union[str, list]"='symmetric', contour:list=[], label_contour=False, max_depth=None, gap_fill=False,
|
|
111
|
+
d_units="m", **kwargs) -> Figure:
|
|
112
|
+
"""Create a colour-contour plot.
|
|
113
|
+
|
|
114
|
+
The x-axis is time and the y-axis is depth. Data values are interpolated and coloured.
|
|
115
|
+
|
|
116
|
+
Parameters
|
|
117
|
+
----------
|
|
118
|
+
depths : numpy.ndarray
|
|
119
|
+
A d-length array of depths at which measurements are collected.
|
|
120
|
+
times : numpy.ndarray
|
|
121
|
+
A t-length array of python datetimes at which measurements are collected
|
|
122
|
+
values : numpy.ndarray
|
|
123
|
+
An array with shape (t,d) of values at each depth-time coordinate
|
|
124
|
+
title : str, optional
|
|
125
|
+
A title for the figure, by default ""
|
|
126
|
+
colours : Union[str, list], optional
|
|
127
|
+
Either a list of colours to be used for the colour bar, or one of:
|
|
128
|
+
* **symmetric**: ensure colour switch is centered at 0C
|
|
129
|
+
* **dynamic**: Maximize dynamic range
|
|
130
|
+
* **basic**: distinguish unfrozen, warm (>-2C) and
|
|
131
|
+
,by default 'symmetric'
|
|
132
|
+
contour : list, optional
|
|
133
|
+
A list of float values. If provided, draw contours at each of those values, by default []
|
|
134
|
+
label_contour : bool, optional
|
|
135
|
+
Whether or not to label contour lines. Ignored if `contour` is empty, by default False
|
|
136
|
+
max_depth : float, optional
|
|
137
|
+
If provided, limits the maximum y-axis extent of the plot, by default None
|
|
138
|
+
gap_fill : bool, optional
|
|
139
|
+
_description_, by default False
|
|
140
|
+
d_units : str, optional
|
|
141
|
+
Units for the y axis (depth), by default "m"
|
|
142
|
+
|
|
143
|
+
Returns
|
|
144
|
+
-------
|
|
145
|
+
Figure
|
|
146
|
+
A matplotlib Figure. Note that to show the figure you must call the `show()` method or `matplotlib.pyplot.show()`.
|
|
147
|
+
"""
|
|
148
|
+
t = tsp.TSP(times, depths, values)
|
|
149
|
+
|
|
150
|
+
# Extract x, y and z (array) values
|
|
151
|
+
X = t.times
|
|
152
|
+
Y = -abs(t.depths)
|
|
153
|
+
|
|
154
|
+
if gap_fill:
|
|
155
|
+
try:
|
|
156
|
+
smoothed = griddata(points = np.stack([t.long.dropna()['time'].values.astype(float),
|
|
157
|
+
t.long.dropna()['depth'].values]).transpose(),
|
|
158
|
+
values = t.long.dropna()['temperature_in_ground'].values,
|
|
159
|
+
xi = np.stack([t.long['time'].values.astype(float),
|
|
160
|
+
t.long['depth'].values]).transpose(),
|
|
161
|
+
rescale=True, method='linear')
|
|
162
|
+
except NameError:
|
|
163
|
+
warnings.warn("Missing scipy library. Could not do gap filling.")
|
|
164
|
+
gap_fill = False
|
|
165
|
+
Z = np.array(t.wide.drop('time', axis=1)).transpose()
|
|
166
|
+
|
|
167
|
+
Z = smoothed.reshape(len(depths), len(values))
|
|
168
|
+
|
|
169
|
+
else:
|
|
170
|
+
Z = np.array(t.wide.drop('time', axis=1)).transpose()
|
|
171
|
+
|
|
172
|
+
# Set up plot
|
|
173
|
+
fig, ax1 = plt.subplots()
|
|
174
|
+
|
|
175
|
+
clev = contour_levels(Z, colours, step=1)
|
|
176
|
+
|
|
177
|
+
# Add data
|
|
178
|
+
if colours == 'basic':
|
|
179
|
+
co = ["darkblue", "lightblue", "lightgreen", "lightred"]
|
|
180
|
+
cmap = None
|
|
181
|
+
else:
|
|
182
|
+
co = None
|
|
183
|
+
cmap = plt.cm.coolwarm
|
|
184
|
+
|
|
185
|
+
cs = ax1.contourf(X, Y, Z, levels=clev, cmap=cmap, colors=co)
|
|
186
|
+
fig.colorbar(cs, ticks = np.arange(-25,25,5))
|
|
187
|
+
|
|
188
|
+
if len(contour) > 0:
|
|
189
|
+
cs2 = ax1.contour(X, Y, Z, levels = contour, colors='k', linewidths = 1)
|
|
190
|
+
if label_contour:
|
|
191
|
+
plt.clabel(cs2, fontsize=8, inline=1, fmt="%1.0f")
|
|
192
|
+
|
|
193
|
+
# Set axis properties
|
|
194
|
+
if max_depth:
|
|
195
|
+
ax1.set_ybound(lower=-abs(max_depth))
|
|
196
|
+
|
|
197
|
+
ax1.xaxis.set_major_formatter(mdates.ConciseDateFormatter(mdates.AutoDateLocator()))
|
|
198
|
+
fig.autofmt_xdate()
|
|
199
|
+
plt.subplots_adjust(bottom = 0.2, top = 0.95, left = 0.2, right = 0.95)
|
|
200
|
+
|
|
201
|
+
# Set axis labels
|
|
202
|
+
ax1.set_xlabel('Time')
|
|
203
|
+
ax1.set_ylabel(f"Depth [{d_units}]")
|
|
204
|
+
ax1.set_title(title)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
return fig
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def time_series(depths, times, values, title='', d_units='m', t_units=u'\N{DEGREE SIGN} C', legend=True) -> Figure:
|
|
211
|
+
"""Create a time-series plot
|
|
212
|
+
|
|
213
|
+
Using time as the X axis and data values as the y axis. Depths are plotted as their own lines.
|
|
214
|
+
|
|
215
|
+
Parameters
|
|
216
|
+
----------
|
|
217
|
+
depths : numpy.ndarray
|
|
218
|
+
1-d list or array of datetimes with length d.
|
|
219
|
+
times : numpy.ndarray
|
|
220
|
+
1-d list or array of datetimes with length t.
|
|
221
|
+
values : array
|
|
222
|
+
An array of data values with shape (t,d).
|
|
223
|
+
title : str, optional
|
|
224
|
+
A title for the plot, by default ''
|
|
225
|
+
d_units : str, optional
|
|
226
|
+
Units of the depths variable, by default 'm'
|
|
227
|
+
t_units : str, optional
|
|
228
|
+
Units of the temperature variable, by default u'\N{DEGREE SIGN} C'
|
|
229
|
+
|
|
230
|
+
Returns
|
|
231
|
+
-------
|
|
232
|
+
Figure
|
|
233
|
+
A matplotlib Figure. Note that to show the figure you must call the `show()` method or `matplotlib.pyplot.show()`.
|
|
234
|
+
"""
|
|
235
|
+
|
|
236
|
+
# Set up plot
|
|
237
|
+
fig, ax = plt.subplots()
|
|
238
|
+
|
|
239
|
+
# Add data elements
|
|
240
|
+
lines = []
|
|
241
|
+
for i, d in enumerate(depths):
|
|
242
|
+
line_i, = ax.plot(times, values[:,i], lw=1, label=f'{d} {d_units}')
|
|
243
|
+
lines.append(line_i)
|
|
244
|
+
|
|
245
|
+
if legend:
|
|
246
|
+
box = ax.get_position()
|
|
247
|
+
ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
|
|
248
|
+
fig.subplots_adjust(right=0.8) # shrink plot to make space
|
|
249
|
+
|
|
250
|
+
leg = ax.legend(loc='center left', bbox_to_anchor=(1.04, 0.5), fancybox=True, shadow=True)
|
|
251
|
+
|
|
252
|
+
lined = {} # Will map legend lines to original lines.
|
|
253
|
+
for legline, label, origline in zip(leg.get_lines(), leg.get_texts(), lines):
|
|
254
|
+
legline.set_picker(True) # Enable picking on the legend line.
|
|
255
|
+
label.set_picker(True) # Enable picking on the legend label.
|
|
256
|
+
lined[legline] = [origline, [label]]
|
|
257
|
+
lined[label] = [origline, [legline]]
|
|
258
|
+
|
|
259
|
+
on_pick = create_legend_picker(fig, lined)
|
|
260
|
+
on_tilde = create_tilde_toggle(fig, lined)
|
|
261
|
+
fig.canvas.mpl_connect('pick_event', on_pick)
|
|
262
|
+
fig.canvas.mpl_connect('key_press_event', on_tilde)
|
|
263
|
+
|
|
264
|
+
zero = ax.hlines(y=0.0, xmin=min(times), xmax=max(times), linewidth=0.5, linestyles='dotted', color='grey')
|
|
265
|
+
# Set axis properties
|
|
266
|
+
|
|
267
|
+
# Set axis labels
|
|
268
|
+
ax.set_xlabel('Time')
|
|
269
|
+
ax.set_ylabel(f"Temperature [{t_units}]")
|
|
270
|
+
ax.set_title(title)
|
|
271
|
+
|
|
272
|
+
return fig
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def profile_evolution(depths, times, values, P:int=100, n:int=10):
|
|
276
|
+
""" Plot sample of temperature profiles over time
|
|
277
|
+
|
|
278
|
+
Parameters
|
|
279
|
+
----------
|
|
280
|
+
depths : array-like
|
|
281
|
+
Depths of the temperature profile
|
|
282
|
+
times : array-like
|
|
283
|
+
Times of the temperature profile
|
|
284
|
+
values : array-like
|
|
285
|
+
Temperature values of the temperature profile
|
|
286
|
+
P : int, optional
|
|
287
|
+
Percentage of the time series to plot, starting from the end, by default 100
|
|
288
|
+
n : int, optional
|
|
289
|
+
Number of profiles to plot, evenly spaced over time period to plot, by default 10
|
|
290
|
+
"""
|
|
291
|
+
cmap = cm.get_cmap('winter')
|
|
292
|
+
clist = cmap(np.arange(0,1,1/10))
|
|
293
|
+
|
|
294
|
+
fig, ax = plt.subplots()
|
|
295
|
+
|
|
296
|
+
p = 100 - P
|
|
297
|
+
lastP = (p*(len(times) // 100))
|
|
298
|
+
|
|
299
|
+
true_depths = -np.abs(depths)
|
|
300
|
+
plot_times = times[lastP:][::len(times[lastP:]) // n][:n]
|
|
301
|
+
plot_temps = values[lastP:,][::len(times[lastP:]) // n, :][:n,]
|
|
302
|
+
|
|
303
|
+
for i in range(n):
|
|
304
|
+
ax.plot(plot_temps[i,:], true_depths, color=clist[i],
|
|
305
|
+
alpha=0.5, label=f"{plot_times[i].year}")
|
|
306
|
+
|
|
307
|
+
ax.legend(fontsize="8")
|
|
308
|
+
ax.vlines(0, ymin=min(true_depths), ymax=max(true_depths), linewidth=0.5, color='black')
|
|
309
|
+
|
|
310
|
+
return fig
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def alpha(rgb, alpha):
|
|
314
|
+
rgb = np.atleast_1d(rgb)
|
|
315
|
+
alpha = np.atleast_1d(alpha)
|
|
316
|
+
rgba = np.zeros((len(alpha), 4))
|
|
317
|
+
rgba[:,3] = alpha
|
|
318
|
+
rgba[:,0:3] = rgb
|
|
319
|
+
return rgba
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def contour_levels(data, levels: "Union[str,list]", step=1) -> np.ndarray:
|
|
323
|
+
if levels == "dynamic":
|
|
324
|
+
return np.arange(np.nanmin(data), np.nanmax(data), step)
|
|
325
|
+
|
|
326
|
+
elif levels == "symmetric":
|
|
327
|
+
return np.arange(min(np.nanmin(data), -np.nanmax(data) + 1),
|
|
328
|
+
max(-np.nanmin(data) - 1, np.nanmax(data)), step)
|
|
329
|
+
elif levels == 'basic':
|
|
330
|
+
return np.array([min(-5, np.nanmin(data)), -2, 0, max(1, np.nanmax(data))])
|
|
331
|
+
else:
|
|
332
|
+
try:
|
|
333
|
+
lev = np.array(levels, dtype='float')
|
|
334
|
+
return lev
|
|
335
|
+
except Exception:
|
|
336
|
+
raise TypeError("Contour levels not properly specified")
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def create_legend_picker(fig, lined) -> object:
|
|
340
|
+
|
|
341
|
+
def on_pick(event):
|
|
342
|
+
# On the pick event, find the original line corresponding to the legend
|
|
343
|
+
# proxy line, and toggle its visibility.
|
|
344
|
+
legline = event.artist
|
|
345
|
+
origline = lined[legline][0]
|
|
346
|
+
other_toggles = lined[legline][1]
|
|
347
|
+
visible = not origline.get_visible()
|
|
348
|
+
origline.set_visible(visible)
|
|
349
|
+
# Change the alpha on the line in the legend so we can see what lines
|
|
350
|
+
# have been toggled.
|
|
351
|
+
legline.set_alpha(1.0 if visible else 0.2)
|
|
352
|
+
for t in other_toggles:
|
|
353
|
+
t.set_alpha(1.0 if visible else 0.2)
|
|
354
|
+
fig.canvas.draw()
|
|
355
|
+
|
|
356
|
+
return on_pick
|
|
357
|
+
|
|
358
|
+
def create_tilde_toggle(fig, lined) -> object:
|
|
359
|
+
|
|
360
|
+
def on_click(event):
|
|
361
|
+
# on the "`" keypress, toggle lines off if any are on.
|
|
362
|
+
# on the "`" keypress, toggle lines on if all are off.
|
|
363
|
+
if event.key == '`':
|
|
364
|
+
visible = False
|
|
365
|
+
for togglable, [origline, other_toggles] in lined.items():
|
|
366
|
+
visible = visible or origline.get_visible()
|
|
367
|
+
for togglable, [origline, other_toggles] in lined.items():
|
|
368
|
+
origline.set_visible(not visible)
|
|
369
|
+
togglable.set_alpha(1.0 if not visible else 0.2)
|
|
370
|
+
for t in other_toggles:
|
|
371
|
+
t.set_alpha(1.0 if not visible else 0.2)
|
|
372
|
+
fig.canvas.draw()
|
|
373
|
+
|
|
374
|
+
return on_click
|