Skip to content

Commit 67213fe

Browse files
committed
add a function to get close detection between two dataset, and improve display
1 parent 73f754c commit 67213fe

File tree

2 files changed

+89
-24
lines changed

2 files changed

+89
-24
lines changed

src/py_eddy_tracker/observations/observation.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
===========================================================================
2828
2929
"""
30+
import logging
3031
import zarr
3132
from numpy import (
3233
zeros,
@@ -47,15 +48,15 @@
4748
ceil,
4849
)
4950
from netCDF4 import Dataset
50-
from ..generic import distance_grid, distance, flatten_line_matrix
51-
from .. import VAR_DESCR, VAR_DESCR_inv
52-
import logging
5351
from datetime import datetime
5452
from numba import njit, types as numba_types
5553
from Polygon import Polygon
5654
from pint import UnitRegistry
5755
from pint.errors import UndefinedUnitError
5856
from tokenize import TokenError
57+
from .. import VAR_DESCR, VAR_DESCR_inv
58+
from ..generic import distance_grid, distance, flatten_line_matrix
59+
from ..poly import bbox_intersection, common_area
5960

6061
logger = logging.getLogger("pet")
6162

@@ -712,11 +713,21 @@ def propagate(
712713
return next_obs
713714

714715
def match(self, other, intern=False):
716+
"""return index and score compute with area
717+
"""
715718
x_name, y_name = (
716719
("contour_lon_s", "contour_lat_s")
717720
if intern
718721
else ("contour_lon_e", "contour_lat_e")
719722
)
723+
i, j = bbox_intersection(
724+
self[x_name], self[y_name], other[x_name], other[y_name]
725+
)
726+
c = common_area(
727+
self[x_name][i], self[y_name][i], other[x_name][j], other[y_name][j]
728+
)
729+
m = c > 0
730+
return i[m], j[m], c[m]
720731

721732
@staticmethod
722733
def cost_function_common_area(xy_in, xy_out, distance, intern=False):
@@ -1228,21 +1239,25 @@ def set_global_attr_netcdf(self, h_nc):
12281239
for key, item in self.global_attr.items():
12291240
h_nc.setncattr(key, item)
12301241

1231-
def display(self, ax, ref=None, **kwargs):
1232-
lon_s = flatten_line_matrix(self.obs["contour_lon_s"])
1233-
lat_s = flatten_line_matrix(self.obs["contour_lat_s"])
1242+
def display(self, ax, ref=None, extern_only=False, **kwargs):
1243+
if not extern_only:
1244+
lon_s = flatten_line_matrix(self.obs["contour_lon_s"])
1245+
lat_s = flatten_line_matrix(self.obs["contour_lat_s"])
12341246
lon_e = flatten_line_matrix(self.obs["contour_lon_e"])
12351247
lat_e = flatten_line_matrix(self.obs["contour_lat_e"])
1236-
kwargs_e = kwargs.copy()
12371248
if "label" in kwargs:
1238-
kwargs["label"] += " (%s eddies)" % self.obs["contour_lon_s"].shape[0]
1249+
kwargs["label"] += " (%s eddies)" % len(self)
1250+
kwargs_e = kwargs.copy()
1251+
if not extern_only:
12391252
kwargs_e.pop("label", None)
12401253
if ref is None:
1241-
ax.plot(lon_s, lat_s, **kwargs)
1254+
if not extern_only:
1255+
ax.plot(lon_s, lat_s, **kwargs)
12421256
ax.plot(lon_e, lat_e, linestyle="-.", **kwargs_e)
12431257
else:
12441258
# FIXME : ref could split eddies
1245-
ax.plot((lon_s - ref) % 360 + ref, lat_s, **kwargs)
1259+
if not extern_only:
1260+
ax.plot((lon_s - ref) % 360 + ref, lat_s, **kwargs)
12461261
ax.plot((lon_e - ref) % 360 + ref, lat_e, linestyle="-.", **kwargs_e)
12471262

12481263

src/py_eddy_tracker/poly.py

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# -*- coding: utf-8 -*-
22
"""
33
"""
4-
from numpy import empty, where
4+
from numpy import empty, where, array
55
from numba import njit, prange, types as numba_types
6+
from Polygon import Polygon
67

78

89
@njit(cache=True)
@@ -17,7 +18,9 @@ def is_left(x_line_0, y_line_0, x_line_1, y_line_1, x_test, y_test):
1718
See: Algorithm 1 "Area of Triangles and Polygons"
1819
"""
1920
# Vector product
20-
product = (x_line_1 - x_line_0) * (y_test - y_line_0) - (x_test - x_line_0) * (y_line_1 - y_line_0)
21+
product = (x_line_1 - x_line_0) * (y_test - y_line_0) - (x_test - x_line_0) * (
22+
y_line_1 - y_line_0
23+
)
2124
return product > 0
2225

2326

@@ -50,21 +53,13 @@ def winding_number_poly(x, y, xy_poly):
5053
y_next = xy_poly[i_elt + 1, 1]
5154
if xy_poly[i_elt, 1] <= y:
5255
if y_next > y:
53-
if is_left(xy_poly[i_elt, 0],
54-
xy_poly[i_elt, 1],
55-
x_next,
56-
y_next,
57-
x, y
58-
):
56+
if is_left(xy_poly[i_elt, 0], xy_poly[i_elt, 1], x_next, y_next, x, y):
5957
wn += 1
6058
else:
6159
if y_next <= y:
62-
if not is_left(xy_poly[i_elt, 0],
63-
xy_poly[i_elt, 1],
64-
x_next,
65-
y_next,
66-
x, y
67-
):
60+
if not is_left(
61+
xy_poly[i_elt, 0], xy_poly[i_elt, 1], x_next, y_next, x, y
62+
):
6863
wn -= 1
6964
return wn
7065

@@ -92,3 +87,58 @@ def winding_number_grid_in_poly(x_1d, y_1d, i_x0, i_x1, x_size, i_y0, xy_poly):
9287
if i_x1 < i_x0:
9388
i_x %= x_size
9489
return i_x, i_y
90+
91+
92+
@njit(cache=True, fastmath=True)
93+
def bbox_intersection(x0, y0, x1, y1):
94+
"""compute bbox to check if there are a bbox intersection
95+
"""
96+
nb0 = x0.shape[0]
97+
nb1 = x1.shape[0]
98+
x1_min, y1_min = empty(nb1), empty(nb1)
99+
x1_max, y1_max = empty(nb1), empty(nb1)
100+
for i1 in range(nb1):
101+
x1_min[i1], y1_min[i1] = x1[i1].min(), y1[i1].min()
102+
x1_max[i1], y1_max[i1] = x1[i1].max(), y1[i1].max()
103+
104+
i, j = list(), list()
105+
for i0 in range(nb0):
106+
x_in_min, y_in_min = x0[i0].min(), y0[i0].min()
107+
x_in_max, y_in_max = x0[i0].max(), y0[i0].max()
108+
for i1 in range(nb1):
109+
if y_in_max < y1_min[i1] or y_in_min > y1_max[i1]:
110+
continue
111+
x1_min_ = x1_min[i1]
112+
x1_max_ = x1_max[i1]
113+
if abs(x_in_min - x1_min_) > 180:
114+
ref = x_in_min - 180
115+
x1_min_ = (x1_min_ - ref) % 360 + ref
116+
x1_max_ = (x1_max_ - ref) % 360 + ref
117+
if x_in_max < x1_min_ or x_in_min > x1_max_:
118+
continue
119+
i.append(i0)
120+
j.append(i1)
121+
return array(i), array(j)
122+
123+
124+
@njit(cache=True, fastmath=True)
125+
def create_vertice(x, y):
126+
nb = x.shape[0]
127+
v = empty((nb, 2))
128+
for i in range(nb):
129+
v[i, 0] = x[i]
130+
v[i, 1] = y[i]
131+
return v
132+
133+
134+
def common_area(x0, y0, x1, y1):
135+
nb, _ = x0.shape
136+
cost = empty((nb))
137+
for i in range(nb):
138+
x0_, x1_ = x0[i], x1[i]
139+
if abs(x0_[0] - x1_[0]) > 180:
140+
x1_ = (x1[i] - (x0_[0] - 180)) % 360 + x0_[0] - 180
141+
p0 = Polygon(create_vertice(x0_, y0[i]))
142+
p1 = Polygon(create_vertice(x1_, y1[i]))
143+
cost[i] = (p0 & p1).area() / (p0 + p1).area()
144+
return cost

0 commit comments

Comments
 (0)