Skip to content

Commit 4ab528d

Browse files
committed
feat: add spatial sampling to street project
1 parent 984d821 commit 4ab528d

File tree

9 files changed

+64
-35
lines changed

9 files changed

+64
-35
lines changed

mapswipe_workers/mapswipe_workers/project_types/street/project.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def __init__(self, project_draft):
5454
start_time=project_draft.get("startTimestamp", None),
5555
end_time=project_draft.get("endTimestamp", None),
5656
organization_id=project_draft.get("organizationId", None),
57+
sampling_threshold=project_draft.get("samplingThreshold", None),
5758
)
5859

5960

mapswipe_workers/mapswipe_workers/utils/process_mapillary.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from concurrent.futures import ThreadPoolExecutor, as_completed
1919
from mapswipe_workers.definitions import MAPILLARY_API_LINK, MAPILLARY_API_KEY
2020
from mapswipe_workers.definitions import logger
21+
from mapswipe_workers.utils.spatial_sampling import spatial_sampling
2122

2223

2324
def create_tiles(polygon, level):
@@ -219,14 +220,21 @@ def get_image_metadata(
219220
organization_id: str = None,
220221
start_time: str = None,
221222
end_time: str = None,
223+
sampling_threshold = None,
222224
):
223225
aoi_polygon = geojson_to_polygon(aoi_geojson)
224226
downloaded_metadata, failed_tiles = coordinate_download(
225227
aoi_polygon, level, attempt_limit
226228
)
229+
downloaded_metadata = downloaded_metadata[
230+
downloaded_metadata['geometry'].apply(lambda geom: isinstance(geom, Point))
231+
]
232+
227233
downloaded_metadata = filter_results(
228234
downloaded_metadata, is_pano, organization_id, start_time, end_time
229235
)
236+
if sampling_threshold is not None:
237+
downloaded_metadata = spatial_sampling(downloaded_metadata, sampling_threshold)
230238
if downloaded_metadata.isna().all().all() == False:
231239
return {
232240
"ids": downloaded_metadata["id"].tolist(),

mapswipe_workers/mapswipe_workers/utils/spatial_sampling.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,8 @@ def filter_points(df, threshold_distance):
5858
road_length = 0
5959
mask = np.zeros(len(df), dtype=bool)
6060
mask[0] = True
61-
lat = np.array([wkt.loads(point).y for point in df['data']])
62-
long = np.array([wkt.loads(point).x for point in df['data']])
63-
64-
df['lat'] = lat
65-
df['long'] = long
61+
lat = df["lat"].to_numpy()
62+
long = df["long"].to_numpy()
6663

6764

6865
distances = distance_on_sphere([long[1:],lat[1:]],
@@ -92,7 +89,7 @@ def filter_points(df, threshold_distance):
9289
return to_be_returned_df
9390

9491

95-
def calculate_spacing(df, interval_length):
92+
def spatial_sampling(df, interval_length):
9693
"""
9794
Calculate spacing between points in a GeoDataFrame.
9895
@@ -109,9 +106,22 @@ def calculate_spacing(df, interval_length):
109106
then filters points using the filter_points function. The function returns the filtered
110107
GeoDataFrame along with the total road length.
111108
"""
112-
road_length = 0
113109
if len(df) == 1:
114110
return df
115-
sorted_sub_df = df.sort_values(by=['timestamp'])
116-
filtered_sorted_sub_df = filter_points(sorted_sub_df,interval_length)
117-
return filtered_sorted_sub_df
111+
112+
df['long'] = df['geometry'].apply(lambda geom: geom.x if geom.geom_type == 'Point' else None)
113+
df['lat'] = df['geometry'].apply(lambda geom: geom.y if geom.geom_type == 'Point' else None)
114+
sorted_df = df.sort_values(by=['captured_at'])
115+
116+
sampled_sequence_df = pd.DataFrame()
117+
118+
# loop through each sequence
119+
for sequence in sorted_df['sequence_id'].unique():
120+
sequence_df = sorted_df[sorted_df['sequence_id'] == sequence]
121+
122+
filtered_sorted_sub_df = filter_points(sequence_df,interval_length)
123+
sampled_sequence_df = pd.concat([sampled_sequence_df,filtered_sorted_sub_df],axis=0)
124+
125+
126+
127+
return sampled_sequence_df

mapswipe_workers/tests/fixtures/mapillary_sequence.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
,data,captured_at,creator_id,id,image_id,is_pano,compass_angle,sequence_id,organization_id
1+
,geometry,captured_at,creator_id,id,image_id,is_pano,compass_angle,sequence_id,organization_id
22
9,POINT (38.995129466056824 -6.785243670271996),1453463352000.0,102506575322825.0,371897427508205,,True,292.36693993283,ywMkSP_5PaJzcbIDa5v1aQ,
33
12,POINT (38.9906769990921 -6.783315348346505),1453463400000.0,102506575322825.0,503298877423056,,True,286.12725090647,ywMkSP_5PaJzcbIDa5v1aQ,
44
14,POINT (39.00127172470093 -6.787981661065601),1453463294000.0,102506575322825.0,2897708763800777,,True,296.09895739855,ywMkSP_5PaJzcbIDa5v1aQ,

mapswipe_workers/tests/fixtures/projectDrafts/street.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,6 @@
4646
"requestingOrganisation": "test",
4747
"verificationNumber": 3,
4848
"groupSize": 25,
49-
"isPano": false,
50-
"startTimestamp": "2019-07-01T00:00:00.000Z"
49+
"startTimestamp": "2019-07-01T00:00:00.000Z",
50+
"samplingThreshold": 0.1
5151
}

mapswipe_workers/tests/integration/fixtures/street/projectDrafts/street.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,5 @@
4646
"requestingOrganisation": "test",
4747
"verificationNumber": 3,
4848
"groupSize": 25,
49-
"isPano": false,
50-
"startTimestamp": "2019-07-01T00:00:00.000Z",
51-
"endTimestamp": null,
52-
"organisationId": "1",
5349
"customOptions": [{ "color": "", "label": "", "value": -999 }, { "color": "#008000", "label": "yes", "value": 1 }, { "color": "#FF0000", "label": "no", "value": 2 }, { "color": "#FFA500", "label": "maybe", "value": 3 }]
5450
}

mapswipe_workers/tests/unittests/test_process_mapillary.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
MultiLineString,
1010
GeometryCollection,
1111
)
12+
from shapely import wkt
1213
import pandas as pd
1314
from unittest.mock import patch, MagicMock
1415
from mapswipe_workers.utils.process_mapillary import (
@@ -45,7 +46,9 @@ def setUpClass(cls):
4546
),
4647
"r",
4748
) as file:
48-
cls.fixture_df = pd.read_csv(file)
49+
df = pd.read_csv(file)
50+
df['geometry'] = df['geometry'].apply(wkt.loads)
51+
cls.fixture_df = df
4952

5053
def setUp(self):
5154
self.level = 14

mapswipe_workers/tests/unittests/test_project_type_street.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import os
33
import unittest
44
from unittest.mock import patch
5+
from shapely import wkt
56
import pandas as pd
67

78
from mapswipe_workers.project_types import StreetProject
@@ -30,7 +31,10 @@ def setUp(self) -> None:
3031
),
3132
"r",
3233
) as file:
33-
mock_get.return_value = (pd.read_csv(file), None)
34+
df = pd.read_csv(file)
35+
df['geometry'] = df['geometry'].apply(wkt.loads)
36+
37+
mock_get.return_value = (df, None)
3438
self.project = StreetProject(project_draft)
3539

3640
def test_init(self):

mapswipe_workers/tests/unittests/test_spatial_sampling.py

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from shapely import wkt
77
from shapely.geometry import Point
88

9-
from mapswipe_workers.utils.spatial_sampling import distance_on_sphere, filter_points, calculate_spacing
9+
from mapswipe_workers.utils.spatial_sampling import distance_on_sphere, filter_points, spatial_sampling
1010

1111

1212
class TestDistanceCalculations(unittest.TestCase):
@@ -22,7 +22,10 @@ def setUpClass(cls):
2222
),
2323
"r",
2424
) as file:
25-
cls.fixture_df = pd.read_csv(file)
25+
df = pd.read_csv(file)
26+
df['geometry'] = df['geometry'].apply(wkt.loads)
27+
28+
cls.fixture_df = df
2629

2730
def test_distance_on_sphere(self):
2831
p1 = Point(-74.006, 40.7128)
@@ -35,7 +38,7 @@ def test_distance_on_sphere(self):
3538

3639
def test_filter_points(self):
3740
data = {
38-
"data": [
41+
"geometry": [
3942
"POINT (-74.006 40.7128)",
4043
"POINT (-75.006 41.7128)",
4144
"POINT (-76.006 42.7128)",
@@ -44,43 +47,47 @@ def test_filter_points(self):
4447
}
4548
df = pd.DataFrame(data)
4649

50+
df['geometry'] = df['geometry'].apply(wkt.loads)
51+
52+
df['long'] = df['geometry'].apply(lambda geom: geom.x if geom.geom_type == 'Point' else None)
53+
df['lat'] = df['geometry'].apply(lambda geom: geom.y if geom.geom_type == 'Point' else None)
4754
threshold_distance = 100
4855
filtered_df = filter_points(df, threshold_distance)
4956

5057
self.assertIsInstance(filtered_df, pd.DataFrame)
5158
self.assertLessEqual(len(filtered_df), len(df))
5259

5360

54-
def test_calculate_spacing(self):
61+
def test_spatial_sampling_ordering(self):
5562
data = {
56-
"data": [
63+
"geometry": [
5764
"POINT (-74.006 40.7128)",
5865
"POINT (-75.006 41.7128)",
5966
"POINT (-76.006 42.7128)",
6067
"POINT (-77.006 43.7128)"
6168
],
62-
'timestamp': [1, 2, 3, 4]
69+
'captured_at': [1, 2, 3, 4],
70+
'sequence_id': ['1', '1', '1', '1']
6371
}
6472
df = pd.DataFrame(data)
65-
gdf = pd.DataFrame(df)
73+
df['geometry'] = df['geometry'].apply(wkt.loads)
6674

67-
interval_length = 100
68-
filtered_gdf = calculate_spacing(gdf, interval_length)
75+
interval_length = 0.1
76+
filtered_gdf = spatial_sampling(df, interval_length)
6977

70-
self.assertTrue(filtered_gdf['timestamp'].is_monotonic_increasing)
78+
self.assertTrue(filtered_gdf['captured_at'].is_monotonic_increasing)
7179

7280

73-
def test_calculate_spacing_with_sequence(self):
74-
threshold_distance = 5
75-
filtered_df = filter_points(self.fixture_df, threshold_distance)
81+
def test_spatial_sampling_with_sequence(self):
82+
threshold_distance = 0.01
83+
filtered_df = spatial_sampling(self.fixture_df, threshold_distance)
7684
self.assertIsInstance(filtered_df, pd.DataFrame)
7785
self.assertLess(len(filtered_df), len(self.fixture_df))
7886

7987
filtered_df.reset_index(drop=True, inplace=True)
80-
8188
for i in range(len(filtered_df) - 1):
82-
geom1 = wkt.loads(filtered_df.loc[i, 'data'])
83-
geom2 = wkt.loads(filtered_df.loc[i + 1, 'data'])
89+
geom1 = filtered_df.loc[i, 'geometry']
90+
geom2 = filtered_df.loc[i + 1, 'geometry']
8491

8592
distance = geom1.distance(geom2)
8693

0 commit comments

Comments
 (0)