Skip to content

Commit bb97c63

Browse files
authored
Merge pull request #21 from kili-technology/feature/lab-3719-aau-i-can-upload-geojson-labels-directly-without-conversion
feat(LAB-3719): apply same mid to identical parts in `MultiPolygon`
2 parents 409fe21 + d2cc372 commit bb97c63

File tree

3 files changed

+106
-7
lines changed

3 files changed

+106
-7
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "kili-formats"
3-
version = "0.2.7"
3+
version = "0.2.8"
44
description = ""
55
authors = [{ name = "Kili Technology", email = "contact@kili-technology.com" }]
66
license = { file = "LICENSE.txt" }

src/kili_formats/format/geojson/segmentation.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Geojson segmentation utilities."""
22

3+
import uuid
34
from typing import Any, Dict, List, Optional
45

56

@@ -287,6 +288,7 @@ def geojson_polygon_feature_to_kili_segmentation_annotation(
287288
If not provided, the children are taken from the `kili` key of the geojson feature properties.
288289
mid: The mid of the annotation.
289290
If not provided, the mid is taken from the `id` key of the geojson feature.
291+
If no id is available, a new UUID is generated.
290292
291293
Returns:
292294
A list of Kili segmentation annotations. Each annotation has a flat boundingPoly structure.
@@ -386,6 +388,8 @@ def geojson_polygon_feature_to_kili_segmentation_annotation(
386388
annotation_mid = str(mid)
387389
elif "id" in polygon:
388390
annotation_mid = str(polygon["id"])
391+
else:
392+
annotation_mid = str(uuid.uuid4())
389393

390394
coords = polygon["geometry"]["coordinates"]
391395
annotations = []
@@ -400,11 +404,9 @@ def geojson_polygon_feature_to_kili_segmentation_annotation(
400404
{"normalizedVertices": [{"x": coord[0], "y": coord[1]} for coord in ring[:-1]]}
401405
for ring in coords
402406
],
407+
"mid": annotation_mid,
403408
}
404409

405-
if annotation_mid is not None:
406-
ret["mid"] = annotation_mid
407-
408410
annotations.append(ret)
409411

410412
else:
@@ -418,11 +420,9 @@ def geojson_polygon_feature_to_kili_segmentation_annotation(
418420
{"normalizedVertices": [{"x": coord[0], "y": coord[1]} for coord in ring[:-1]]}
419421
for ring in polygon_coords
420422
],
423+
"mid": annotation_mid,
421424
}
422425

423-
if annotation_mid is not None:
424-
ret["mid"] = annotation_mid
425-
426426
annotations.append(ret)
427427

428428
return annotations

tests/test_geojson.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,3 +1093,102 @@ def test_semantic_segmentation_multipolygon_workflow(self):
10931093
kili_result = geojson_feature_collection_to_kili_json_response(geojson_result)
10941094

10951095
assert kili_result == original_kili_response
1096+
1097+
def test_geojson_multipolygon_feature_same_mid_for_all_parts(self):
1098+
"""Test that all parts of a MultiPolygon get the same mid."""
1099+
multipolygon = {
1100+
"type": "Feature",
1101+
"geometry": {
1102+
"type": "MultiPolygon",
1103+
"coordinates": [
1104+
[[[0, 0], [1, 0], [1, 1], [0, 0]]], # First polygon
1105+
[[[2, 2], [3, 2], [3, 3], [2, 2]]], # Second polygon
1106+
[[[4, 4], [5, 4], [5, 5], [4, 4]]], # Third polygon
1107+
],
1108+
},
1109+
"id": "forest_multipart_001",
1110+
"properties": {
1111+
"kili": {"categories": [{"name": "forest"}], "children": {}, "type": "semantic"}
1112+
},
1113+
}
1114+
1115+
result = geojson_polygon_feature_to_kili_segmentation_annotation(multipolygon)
1116+
1117+
# Should return 3 annotations (one for each polygon part)
1118+
assert len(result) == 3
1119+
1120+
# All annotations should have the same mid
1121+
mids = [ann["mid"] for ann in result]
1122+
assert all(mid == "forest_multipart_001" for mid in mids)
1123+
1124+
# Each annotation should have the correct structure
1125+
for i, annotation in enumerate(result):
1126+
assert annotation["type"] == "semantic"
1127+
assert annotation["categories"] == [{"name": "forest"}]
1128+
assert annotation["children"] == {}
1129+
assert annotation["mid"] == "forest_multipart_001"
1130+
assert len(annotation["boundingPoly"]) == 1 # Single ring per part
1131+
1132+
# Check coordinates match the expected polygon part
1133+
expected_coords = [
1134+
[{"x": 0, "y": 0}, {"x": 1, "y": 0}, {"x": 1, "y": 1}],
1135+
[{"x": 2, "y": 2}, {"x": 3, "y": 2}, {"x": 3, "y": 3}],
1136+
[{"x": 4, "y": 4}, {"x": 5, "y": 4}, {"x": 5, "y": 5}],
1137+
]
1138+
assert annotation["boundingPoly"][0]["normalizedVertices"] == expected_coords[i]
1139+
1140+
def test_geojson_multipolygon_feature_custom_mid_override(self):
1141+
"""Test that custom mid parameter overrides the feature id."""
1142+
multipolygon = {
1143+
"type": "Feature",
1144+
"geometry": {
1145+
"type": "MultiPolygon",
1146+
"coordinates": [
1147+
[[[0, 0], [1, 0], [1, 1], [0, 0]]],
1148+
[[[2, 2], [3, 2], [3, 3], [2, 2]]],
1149+
],
1150+
},
1151+
"id": "original_id",
1152+
"properties": {
1153+
"kili": {"categories": [{"name": "water"}], "children": {}, "type": "semantic"}
1154+
},
1155+
}
1156+
1157+
custom_mid = "custom_water_id_123"
1158+
result = geojson_polygon_feature_to_kili_segmentation_annotation(
1159+
multipolygon, mid=custom_mid
1160+
)
1161+
1162+
# Should return 2 annotations
1163+
assert len(result) == 2
1164+
1165+
# Both should have the custom mid, not the original feature id
1166+
for annotation in result:
1167+
assert annotation["mid"] == custom_mid
1168+
assert annotation["mid"] != "original_id"
1169+
1170+
def test_geojson_multipolygon_feature_no_id_generates_uuid(self):
1171+
"""Test that when no feature id is provided, a mid is generated and used for all parts."""
1172+
multipolygon = {
1173+
"type": "Feature",
1174+
"geometry": {
1175+
"type": "MultiPolygon",
1176+
"coordinates": [
1177+
[[[0, 0], [1, 0], [1, 1], [0, 0]]],
1178+
[[[2, 2], [3, 2], [3, 3], [2, 2]]],
1179+
],
1180+
},
1181+
"properties": {
1182+
"kili": {"categories": [{"name": "building"}], "children": {}, "type": "semantic"}
1183+
},
1184+
}
1185+
1186+
result = geojson_polygon_feature_to_kili_segmentation_annotation(multipolygon)
1187+
1188+
# Should return 2 annotations
1189+
assert len(result) == 2
1190+
1191+
# Both should have the same generated UUID
1192+
mid_1 = result[0]["mid"]
1193+
mid_2 = result[1]["mid"]
1194+
assert mid_1 == mid_2

0 commit comments

Comments
 (0)