diff --git a/skais/ais/ais_trajectory.py b/skais/ais/ais_trajectory.py index e6ba885dcafca82f0a023eee0196003472ce28b4..7e1307e18352bf3caf025b5d89c6d73f1f02faba 100644 --- a/skais/ais/ais_trajectory.py +++ b/skais/ais/ais_trajectory.py @@ -62,7 +62,7 @@ def apply_time_sequence(dat, time, func): def __get_image_value__(features, bounds): value = [] for f, b in zip(features, bounds): - value.append(1 - (b[1] - f) / b[1]) + value.append(1 - (b[1] - f - b[0]) / (b[1] - b[0])) return value @@ -236,22 +236,42 @@ class AISTrajectory(AISPoints): node_size=0): nb_channels = 1 - if bounding_box != 'fit': - raise ValueError("feature not implemented") positions = self.df[['longitude', 'latitude']].to_numpy() - min_lon, max_lon = (min(positions[:, 0]), max(positions[:, 0])) - min_lat, max_lat = (min(positions[:, 1]), max(positions[:, 1])) - if min_lat == max_lat: - min_lat -= 1 - max_lat += 1 - if min_lon == max_lon: - min_lon -= 1 - max_lon += 1 + if bounding_box == 'fit': + lower_lon, upper_lon = (min(positions[:, 0]), max(positions[:, 0])) + lower_lat, upper_lat = (min(positions[:, 1]), max(positions[:, 1])) + elif bounding_box == 'centered': + center_lon, center_lat = positions[-1] + min_lon, max_lon = (min(positions[:, 0]), max(positions[:, 0])) + min_lat, max_lat = (min(positions[:, 1]), max(positions[:, 1])) + + distance_to_center = max(center_lon - min_lon, max_lon - center_lon, center_lat - min_lat, + max_lat - center_lat) + + upper_lat = center_lat + distance_to_center + lower_lat = center_lat - distance_to_center + upper_lon = center_lon + distance_to_center + lower_lon = center_lon - distance_to_center + elif type(bounding_box) is list: + upper_lon = bounding_box[1][0] + lower_lon = bounding_box[0][0] + upper_lat = bounding_box[1][1] + lower_lat = bounding_box[0][1] + else: + raise ValueError(f"Option not supported: {bounding_box}") + + if lower_lat == upper_lat: + lower_lat -= 1 + upper_lat += 1 + if lower_lon == upper_lon: + lower_lon -= 1 + upper_lon += 1 if features is None: data = np.zeros((height, width, nb_channels), dtype=np.uint8) for longitude, latitude in positions: - x_coord, y_coord = get_coord(latitude, longitude, height, width, min_lat, max_lat, min_lon, max_lon) + x_coord, y_coord = get_coord(latitude, longitude, height, width, lower_lat, upper_lat, lower_lon, + upper_lon) x_lower_bound = max(0, x_coord - node_size) x_upper_bound = min(height - 1, x_coord + node_size) @@ -265,30 +285,38 @@ class AISTrajectory(AISPoints): if link: lon, lat = positions[0, 0], positions[0, 1] for longitude, latitude in positions[1:]: - x_prv, y_prev = get_coord(lat, lon, height, width, min_lat, max_lat, min_lon, max_lon) - x_nxt, y_nxt = get_coord(latitude, longitude, height, width, min_lat, max_lat, min_lon, max_lon) + x_prv, y_prev = get_coord(lat, lon, height, width, lower_lat, upper_lat, lower_lon, upper_lon) + x_nxt, y_nxt = get_coord(latitude, longitude, height, width, lower_lat, upper_lat, lower_lon, + upper_lon) lon, lat = longitude, latitude for x, y in bresenham(x_prv, y_prev, x_nxt, y_nxt): data[x, y] = [1] else: + bounds = [] if type(features) is list: nb_channels = len(features) + features_vectors = self.df[features].to_numpy() + for c in features_vectors.T: + bounds.append((0, max(c))) elif type(features) is str: - features = [features] + features_vectors = self.df[[features]].to_numpy() + for c in features_vectors.T: + bounds.append((0, max(c))) + elif type(features) is dict: + bounds = list(features.values()) + features_vectors = self.df[features.keys()].to_numpy() else: raise TypeError("Type not supported") data = np.zeros((height, width, nb_channels), dtype=np.float) - features_vectors = self.df[features].to_numpy() - bounds = [] - for c in features_vectors.T: - bounds.append((min(c), max(c))) + for pos, f in zip(positions, features_vectors): latitude = pos[1] longitude = pos[0] - x_coord, y_coord = get_coord(latitude, longitude, height, width, min_lat, max_lat, min_lon, max_lon) + x_coord, y_coord = get_coord(latitude, longitude, height, width, lower_lat, upper_lat, lower_lon, + upper_lon) value = __get_image_value__(f, bounds) x_lower_bound = max(0, x_coord - node_size) x_upper_bound = min(height - 1, x_coord + node_size) @@ -307,8 +335,9 @@ class AISTrajectory(AISPoints): for pos, f in zip(positions[1:], features_vectors[1:]): latitude = pos[1] longitude = pos[0] - x_prv, y_prev = get_coord(lat, lon, height, width, min_lat, max_lat, min_lon, max_lon) - x_nxt, y_nxt = get_coord(latitude, longitude, height, width, min_lat, max_lat, min_lon, max_lon) + x_prv, y_prev = get_coord(lat, lon, height, width, lower_lat, upper_lat, lower_lon, upper_lon) + x_nxt, y_nxt = get_coord(latitude, longitude, height, width, lower_lat, upper_lat, lower_lon, + upper_lon) lon, lat = longitude, latitude for x, y in bresenham(x_prv, y_prev, x_nxt, y_nxt): for i, v in enumerate(value): diff --git a/skais/tests/ais/test_ais_trajectory.py b/skais/tests/ais/test_ais_trajectory.py index d5e64a0e87a67f15f19289c2f1d527f215b178db..56aa71cd0dfbc24deed6671584a38f7862a732e8 100644 --- a/skais/tests/ais/test_ais_trajectory.py +++ b/skais/tests/ais/test_ais_trajectory.py @@ -380,8 +380,10 @@ class TestAISTrajectory(unittest.TestCase): self.assertListEqual(result, expected) - def test_generate_array_from_positions(self): - trajectory = AISTrajectory( + +class TestAISTrajectoryImageGeneration(unittest.TestCase): + def setUp(self) -> None: + self.trajectory = AISTrajectory( pd.DataFrame( { "latitude": [0, 10, 0, -10], @@ -391,8 +393,9 @@ class TestAISTrajectory(unittest.TestCase): ) ) - result = trajectory.generate_array_from_positions(height=9, width=9, link=False, bounding_box='fit', - features=None, node_size=0).reshape((9, 9)) + def test_generate_array_from_positions(self): + result = self.trajectory.generate_array_from_positions(height=9, width=9, link=False, bounding_box='fit', + features=None, node_size=0).reshape((9, 9)) expected = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], @@ -406,18 +409,8 @@ class TestAISTrajectory(unittest.TestCase): np.testing.assert_array_equal(result, expected) def test_generate_array_from_positions_node_size(self): - trajectory = AISTrajectory( - pd.DataFrame( - { - "latitude": [0, 10, 0, -10], - "longitude": [0, 10, 10, -10], - "ts_sec": [i for i in range(4)] - } - ) - ) - - result = trajectory.generate_array_from_positions(height=9, width=9, link=False, bounding_box='fit', - features=None, node_size=1).reshape((9, 9)) + result = self.trajectory.generate_array_from_positions(height=9, width=9, link=False, bounding_box='fit', + features=None, node_size=1).reshape((9, 9)) expected = np.array([[0, 0, 0, 0, 0, 0, 0, 1, 1], [0, 0, 0, 0, 0, 0, 0, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0], @@ -512,7 +505,7 @@ class TestAISTrajectory(unittest.TestCase): "latitude": [0, 10, 0, 20], "longitude": [0, 10, 20, 20], "ts_sec": [i for i in range(4)], - "sog": [10,10,20,40] + "sog": [10, 10, 20, 40] } ) ) @@ -538,7 +531,7 @@ class TestAISTrajectory(unittest.TestCase): "latitude": [0, 10, 0, 20], "longitude": [0, 10, 20, 20], "ts_sec": [i for i in range(4)], - "sog": [10,10,20,40], + "sog": [10, 10, 20, 40], "cog": [40, 20, 10, 10] } ) @@ -546,14 +539,80 @@ class TestAISTrajectory(unittest.TestCase): result = trajectory.generate_array_from_positions(height=9, width=18, link=True, bounding_box='fit', features=['sog', 'cog'], node_size=0) - expected = np.array([[[0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0.5,0.25]], - [[0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0.5,0.25]], - [[0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0.5,0.25]], - [[0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0.5,0.25]], - [[0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0.25,1], [0.25,0.5], [0.25,0.5], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0.5,0.25]], - [[0,0], [0,0], [0,0], [0,0], [0,0], [0.25,1], [0.25,1], [0,0], [0,0], [0,0], [0.25,0.5], [0.25,0.5], [0,0], [0,0], [0,0], [0,0], [0,0], [0.5,0.25]], - [[0,0], [0,0], [0,0], [0.25,1], [0.25,1], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0.25,0.5], [0.25,0.5], [0,0], [0,0], [0,0], [0.5,0.25]], - [[0,0], [0.25,1], [0.25,1], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0.25,0.5], [0.25,0.5], [0,0], [0.5,0.25]], - [[0.25,1], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0,0], [0.25,0.5], [0.5,0.25]]]) - - np.testing.assert_array_equal(result, expected) \ No newline at end of file + expected = np.array([[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], + [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0.5, 0.25]], + [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], + [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0.5, 0.25]], + [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], + [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0.5, 0.25]], + [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], + [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0.5, 0.25]], + [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0.25, 1], [0.25, 0.5], + [0.25, 0.5], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0.5, 0.25]], + [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0.25, 1], [0.25, 1], [0, 0], [0, 0], [0, 0], + [0.25, 0.5], [0.25, 0.5], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0.5, 0.25]], + [[0, 0], [0, 0], [0, 0], [0.25, 1], [0.25, 1], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], + [0, 0], [0, 0], [0.25, 0.5], [0.25, 0.5], [0, 0], [0, 0], [0, 0], [0.5, 0.25]], + [[0, 0], [0.25, 1], [0.25, 1], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], + [0, 0], [0, 0], [0, 0], [0, 0], [0.25, 0.5], [0.25, 0.5], [0, 0], [0.5, 0.25]], + [[0.25, 1], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], + [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0.25, 0.5], [0.5, 0.25]]]) + + np.testing.assert_array_equal(result, expected) + + def test_generate_array_centered(self): + result = self.trajectory.generate_array_from_positions(height=9, width=9, link=False, bounding_box='centered', + features=None, node_size=0).reshape((9, 9)) + expected = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0]]) + + np.testing.assert_array_equal(result, expected) + + def test_generate_array_bounding_box(self): + result = self.trajectory.generate_array_from_positions(height=9, width=9, link=False, + bounding_box=[(0, 0), (10, 10)], + features=None, node_size=0).reshape((9, 9)) + expected = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 0, 0, 1]]) + + np.testing.assert_array_equal(result, expected) + + def test_generate_array_feature_bounds(self): + trajectory = AISTrajectory( + pd.DataFrame( + { + "latitude": [0, 10, 0, 20], + "longitude": [0, 10, 20, 20], + "ts_sec": [i for i in range(4)], + "sog": [10, 10, 20, 40] + } + ) + ) + + result = trajectory.generate_array_from_positions(height=9, width=18, link=True, bounding_box='fit', + features={"sog": (0, 80)}, node_size=0).reshape((9, 18)) + expected = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.5], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.5], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.5], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.5], + [0, 0, 0, 0, 0, 0, 0, 0.25, 0.25, 0.25, 0, 0, 0, 0, 0, 0, 0, 0.5], + [0, 0, 0, 0, 0, 0.25, 0.25, 0, 0, 0, 0.25, 0.25, 0, 0, 0, 0, 0, 0.5], + [0, 0, 0, 0.25, 0.25, 0, 0, 0, 0, 0, 0, 0, 0.25, 0.25, 0, 0, 0, 0.5], + [0, 0.25, 0.25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.25, 0.25, 0, 0.5], + [0.25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.25, 0.5]]) / 2 + + np.testing.assert_array_equal(result, expected)