Skip to content

Commit fcd83fd

Browse files
committed
Add features to GUIEddy
1 parent 59adc65 commit fcd83fd

File tree

4 files changed

+131
-69
lines changed

4 files changed

+131
-69
lines changed

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
"src/scripts/EddyTracking",
3131
"src/scripts/EddyFinalTracking",
3232
"src/scripts/EddyMergeCorrespondances",
33-
"src/scripts/GUIEddy",
3433
],
3534
zip_safe=False,
3635
entry_points=dict(
@@ -48,6 +47,7 @@
4847
"EddyNetworkBuildPath = py_eddy_tracker.appli.network:divide_network",
4948
# anim/gui
5049
"EddyAnim = py_eddy_tracker.appli.gui:anim",
50+
"GUIEddy = py_eddy_tracker.appli.gui:guieddy",
5151
# misc
5252
"ZarrDump = py_eddy_tracker.appli.misc:zarrdump",
5353
]

src/py_eddy_tracker/appli/gui.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from ..generic import flatten_line_matrix
3030
from .. import EddyParser
3131
from ..observations.tracking import TrackEddiesObservations
32+
from ..gui import GUI
3233

3334

3435
class Anim:
@@ -213,3 +214,21 @@ def anim():
213214
nb_step=args.keep_step,
214215
)
215216
a.show(infinity_loop=args.infinity_loop)
217+
218+
219+
def gui_parser():
220+
parser = EddyParser("Eddy atlas GUI")
221+
parser.add_argument("atlas", nargs="+")
222+
parser.add_argument("--med", action="store_true")
223+
return parser.parse_args()
224+
225+
226+
def guieddy():
227+
args = gui_parser()
228+
atlas = {
229+
dataset: TrackEddiesObservations.load_file(dataset) for dataset in args.atlas
230+
}
231+
g = GUI(**atlas)
232+
if args.med:
233+
g.med()
234+
g.show()

src/py_eddy_tracker/gui.py

Lines changed: 111 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,10 @@
2222
"""
2323

2424
import numpy as np
25-
from datetime import datetime
2625
import matplotlib.pyplot as plt
26+
from datetime import datetime, timedelta
2727
from matplotlib.projections import register_projection
28-
import py_eddy_tracker_sample as sample
2928
from .generic import flatten_line_matrix, split_line
30-
from .observations.tracking import TrackEddiesObservations
3129

3230

3331
try:
@@ -65,10 +63,6 @@ def end_pan(self, *args, **kwargs):
6563
register_projection(GUIAxes)
6664

6765

68-
class A(TrackEddiesObservations):
69-
pass
70-
71-
7266
def no(*args, **kwargs):
7367
return False
7468

@@ -81,6 +75,7 @@ class GUI:
8175
"time_ax",
8276
"param_ax",
8377
"settings",
78+
"d_indexs",
8479
"m",
8580
"last_event",
8681
)
@@ -89,6 +84,7 @@ class GUI:
8984

9085
def __init__(self, **datasets):
9186
self.datasets = datasets
87+
self.d_indexs = dict()
9288
self.m = dict()
9389
self.set_initial_values()
9490
self.setup()
@@ -140,8 +136,9 @@ def setup(self):
140136
# map
141137
self.map = self.figure.add_axes((0, 0.25, 1, 0.75), projection="full_axes")
142138
self.map.grid()
143-
self.map.tick_params("x", pad=-12)
144-
self.map.tick_params("y", pad=-22)
139+
self.map.tick_params("both", pad=-22)
140+
# self.map.tick_params("y", pad=-22)
141+
self.map.bg_cache = None
145142
# time ax
146143
self.time_ax = self.figure.add_axes((0, 0.15, 1, 0.1), facecolor=".95")
147144
self.time_ax.can_pan
@@ -159,41 +156,65 @@ def setup(self):
159156
self.param_ax = self.figure.add_axes((0, 0, 1, 0.15), facecolor="0.2")
160157

161158
def draw(self):
159+
self.m["mini_ax"] = self.figure.add_axes((0.3, 0.85, 0.4, 0.15), zorder=80)
160+
self.m["mini_ax"].grid()
162161
# map
163162
for i, (name, dataset) in enumerate(self.datasets.items()):
163+
kwargs = dict(color=self.COLORS[i])
164164
self.m[name] = dict(
165-
contour_s=self.map.plot(
166-
[], [], color=self.COLORS[i], lw=0.5, label=name
167-
)[0],
168-
contour_e=self.map.plot([], [], color=self.COLORS[i], lw=0.5)[0],
169-
path_previous=self.map.plot([], [], color=self.COLORS[i], lw=0.5)[0],
170-
path_future=self.map.plot([], [], color=self.COLORS[i], lw=0.2, ls=":")[
171-
0
172-
],
165+
contour_s=self.map.plot([], [], lw=1, label=name, **kwargs)[0],
166+
contour_e=self.map.plot([], [], lw=0.5, ls="-.", **kwargs)[0],
167+
path_previous=self.map.plot([], [], lw=0.5, **kwargs)[0],
168+
path_future=self.map.plot([], [], lw=0.2, ls=":", **kwargs)[0],
169+
mini_line=self.m["mini_ax"].plot([], [], **kwargs, lw=1)[0]
173170
)
174-
self.m["title"] = self.map.set_title("")
175171
# time_ax
172+
self.m["annotate"] = self.map.annotate(
173+
"",
174+
(0, 0),
175+
xycoords="figure pixels",
176+
zorder=100,
177+
fontsize=9,
178+
bbox=dict(boxstyle="round", facecolor="w", edgecolor="0.5", alpha=0.85),
179+
)
180+
self.m["mini_ax"].set_visible(False)
181+
self.m["annotate"].set_visible(False)
182+
176183
self.m["time_vline"] = self.time_ax.axvline(0, color="k", lw=1)
177184
self.m["time_text"] = self.time_ax.text(
178-
0, 0, "", fontsize=8, bbox=dict(facecolor="w", alpha=0.75)
185+
0,
186+
0,
187+
"",
188+
fontsize=8,
189+
bbox=dict(facecolor="w", alpha=0.75),
190+
verticalalignment="bottom",
179191
)
180192

181193
def update(self):
182-
# text = []
194+
time_text = [
195+
(timedelta(days=int(self.now)) + datetime(1950, 1, 1)).strftime("%d/%m/%Y")
196+
]
183197
# map
184198
xs, ys, ns = list(), list(), list()
185199
for j, (name, dataset) in enumerate(self.datasets.items()):
186200
i = self.indexs(dataset)
201+
self.d_indexs[name] = i
187202
self.m[name]["contour_s"].set_label(f"{name} {len(i)} eddies")
188203
if len(i) == 0:
189204
self.m[name]["contour_s"].set_data([], [])
205+
self.m[name]["contour_e"].set_data([], [])
190206
else:
191-
if 'contour_lon_s' in dataset.elements:
207+
if "contour_lon_s" in dataset.elements:
192208
self.m[name]["contour_s"].set_data(
193209
flatten_line_matrix(dataset["contour_lon_s"][i]),
194210
flatten_line_matrix(dataset["contour_lat_s"][i]),
195211
)
196-
# text.append(f"{i.shape[0]}")
212+
if "contour_lon_e" in dataset.elements:
213+
self.m[name]["contour_e"].set_data(
214+
flatten_line_matrix(dataset["contour_lon_e"][i]),
215+
flatten_line_matrix(dataset["contour_lat_e"][i]),
216+
)
217+
time_text.append(f"{i.shape[0]}")
197218
local_path = dataset.extract_ids(dataset["track"][i])
198219
x, y, t, n, tr = (
199220
local_path.longitude,
@@ -229,12 +250,11 @@ def update(self):
229250
self.map.text(x_, y_, n_) for x_, y_, n_ in zip(x, y, n) if n_ >= n_min
230251
]
231252

232-
self.m["title"].set_text(self.now)
233253
self.map.legend()
234254
# time ax
235255
x, y = self.m["time_vline"].get_data()
236256
self.m["time_vline"].set_data(self.now, y)
237-
# self.m["time_text"].set_text("\n".join(text))
257+
self.m["time_text"].set_text("\n".join(time_text))
238258
self.m["time_text"].set_position((self.now, 0))
239259
# force update
240260
self.map.figure.canvas.draw()
@@ -263,6 +283,30 @@ def press(self, event):
263283
self.time_ax.press = True
264284
self.time_ax.bg_cache = self.figure.canvas.copy_from_bbox(self.time_ax.bbox)
265285

286+
def get_infos(self, name, index):
287+
i = self.d_indexs[name][index]
288+
d = self.datasets[name]
289+
now = d.obs[i]
290+
tr = now["track"]
291+
nb = d.nb_obs_by_track[tr]
292+
i_first = d.index_from_track[tr]
293+
track = d.obs[i_first : i_first + nb]
294+
nb -= 1
295+
t0 = timedelta(days=int(track[0]["time"])) + datetime(1950, 1, 1)
296+
t1 = timedelta(days=int(track[-1]["time"])) + datetime(1950, 1, 1)
297+
txt = f"--{name}--\n"
298+
txt += f" {t0} -> {t1}\n"
299+
txt += f" Tracks : {tr} {now['n']}/{nb} ({now['n'] / nb * 100:.2f} %)\n"
300+
for label, n, f, u in (
301+
("Amp.", "amplitude", 100, "cm"),
302+
("S. radius", "radius_s", 1e-3, "km"),
303+
("E. radius", "radius_e", 1e-3, "km"),
304+
):
305+
v = track[n] * f
306+
min_, max_, mean_, std_ = v.min(), v.max(), v.mean(), v.std()
307+
txt += f" {label} : {now[n] * f:.1f} {u} ({min_:.1f} <-{mean_:.1f}+-{std_:.1f}-> {max_:.1f})\n"
308+
return track, txt.strip()
309+
266310
def move(self, event):
267311
if event.inaxes == self.time_ax and self.time_ax.press:
268312
x, y = self.m["time_vline"].get_data()
@@ -271,6 +315,49 @@ def move(self, event):
271315
self.time_ax.draw_artist(self.m["time_vline"])
272316
self.figure.canvas.blit(self.time_ax.bbox)
273317

318+
if event.inaxes == self.map:
319+
touch = dict()
320+
for name in self.datasets.keys():
321+
flag, data = self.m[name]["contour_s"].contains(event)
322+
if flag:
323+
# 51 is for contour on 50 point must be rewrote
324+
touch[name] = data["ind"][0] // 51
325+
a = self.m["annotate"]
326+
ax = self.m["mini_ax"]
327+
if touch:
328+
if not a.get_visible():
329+
self.map.bg_cache = self.figure.canvas.copy_from_bbox(self.map.bbox)
330+
a.set_visible(True)
331+
ax.set_visible(True)
332+
else:
333+
self.figure.canvas.restore_region(self.map.bg_cache)
334+
a.set_x(event.x), a.set_y(event.y)
335+
txt = list()
336+
x0_, x1_, y1_ = list(), list(), list()
337+
for name in self.datasets.keys():
338+
if name in touch:
339+
track, txt_ = self.get_infos(name, touch[name])
340+
txt.append(txt_)
341+
x, y = track["time"], track["radius_s"] / 1e3
342+
self.m[name]["mini_line"].set_data(x, y)
343+
x0_.append(x.min()), x1_.append(x.max()), y1_.append(y.max())
344+
else:
345+
self.m[name]["mini_line"].set_data([], [])
346+
ax.set_xlim(min(x0_), max(x1_)), ax.set_ylim(0, max(y1_))
347+
a.set_text("\n".join(txt))
348+
349+
self.map.draw_artist(a)
350+
self.map.draw_artist(ax)
351+
self.figure.canvas.blit(self.map.bbox)
352+
if not flag and self.map.bg_cache is not None and a.get_visible():
353+
a.set_visible(False)
354+
ax.set_visible(False)
355+
self.figure.canvas.restore_region(self.map.bg_cache)
356+
self.map.draw_artist(a)
357+
self.map.draw_artist(ax)
358+
self.figure.canvas.blit(self.map.bbox)
359+
self.map.bg_cache = None
360+
274361
def release(self, event):
275362
if self.time_ax.press:
276363
self.time_ax.press = False
@@ -293,21 +380,3 @@ def adjust(self, event=None):
293380
def show(self):
294381
self.update()
295382
plt.show()
296-
297-
298-
if __name__ == "__main__":
299-
300-
# a_ = A.load_file(
301-
# "/home/toto/dev/work/pet/20200611_example_dataset/tracking/Anticyclonic_track_too_short.nc"
302-
# )
303-
# c_ = A.load_file(
304-
# "/home/toto/dev/work/pet/20200611_example_dataset/tracking/Cyclonic_track_too_short.nc"
305-
# )
306-
a = A.load_file(sample.get_path("eddies_med_adt_allsat_dt2018/Anticyclonic.zarr"))
307-
# c = A.load_file(sample.get_path("eddies_med_adt_allsat_dt2018/Cyclonic.zarr"))
308-
# g = GUI(Acyc=a, Cyc=c, Acyc_short=a_, Cyc_short=c_)
309-
g = GUI(Acyc=a)
310-
# g = GUI(Acyc_short=a_)
311-
# g = GUI(Acyc_short=a_, Cyc_short=c_)
312-
g.med()
313-
g.show()

src/scripts/GUIEddy

Lines changed: 0 additions & 26 deletions
This file was deleted.

0 commit comments

Comments
 (0)