Skip to content

Commit aa54056

Browse files
committed
feat(lab-4089): update geojson format to be gis friendly
1 parent c63e9ca commit aa54056

File tree

4 files changed

+69
-44
lines changed

4 files changed

+69
-44
lines changed

pyproject.toml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ dependencies = [
4444
"filelock >= 3.0.0, < 4.0.0",
4545
"pip-system-certs >= 4.0.0, < 5.0.0; platform_system=='Windows'",
4646
"pyrate-limiter >= 3, < 4",
47-
"kili-formats == 1.0.0"
47+
"kili-formats == 1.0.1"
4848
]
4949
urls = { homepage = "https://github.com/kili-technology/kili-python-sdk" }
5050

@@ -79,7 +79,7 @@ dev = [
7979
"nbconvert",
8080
"ipykernel",
8181
# optional dependencies
82-
"kili-formats[all] == 1.0.0",
82+
"kili-formats[all] == 1.0.1",
8383
"opencv-python >= 4.0.0, < 5.0.0",
8484
"azure-storage-blob >= 12.0.0, < 13.0.0",
8585
# optional dependencies gis
@@ -95,7 +95,7 @@ dev = [
9595
all = [
9696
# aggregate all optional deps without dev
9797
"azure-storage-blob >= 12.0.0, < 13.0.0",
98-
"kili-formats[all] == 1.0.0",
98+
"kili-formats[all] == 1.0.1",
9999
"opencv-python >= 4.0.0, < 5.0.0",
100100
"Pillow >=9.0.0, <11.0.0",
101101
"pyproj == 3.7.1",
@@ -108,20 +108,20 @@ cli = [
108108
"tabulate >= 0.9.0, < 0.10.0"
109109
]
110110
coco = [
111-
"kili-formats[coco] == 1.0.0"
111+
"kili-formats[coco] == 1.0.1"
112112
]
113113
gis = [
114114
"pyproj == 3.7.1",
115115
"shapely >= 1.8, < 3"
116116
]
117117
image = [
118118
"Pillow >=9.0.0, <11.0.0",
119-
"kili-formats[image] == 1.0.0"
119+
"kili-formats[image] == 1.0.1"
120120
]
121121
image-utils = ["opencv-python >= 4.0.0, < 5.0.0"]
122122

123123
video = [
124-
"kili-formats[video] == 1.0.0"
124+
"kili-formats[video] == 1.0.1"
125125
]
126126
yolo = [
127127
"pyyaml >= 6.0, < 7.0"

src/kili/services/export/format/geojson/__init__.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,20 +81,28 @@ def process_and_save(self, assets: list[dict], output_filename: Path) -> None:
8181
" will be exported."
8282
)
8383

84+
# Get json_interface for GIS-friendly property names
85+
json_interface = self.project.get("jsonInterface")
86+
8487
for asset in tqdm(geotiff_assets, disable=self.disable_tqdm):
85-
_process_asset(asset, labels_folder)
88+
_process_asset(asset, labels_folder, json_interface, flatten_properties=True)
8689

8790
self.create_readme_kili_file(self.export_root_folder)
8891
self.make_archive(self.export_root_folder, output_filename)
8992

9093
self.logger.warning(output_filename)
9194

9295

93-
def _process_asset(asset: dict, labels_folder: Path) -> None:
96+
def _process_asset(
97+
asset: dict,
98+
labels_folder: Path,
99+
json_interface: dict | None = None,
100+
flatten_properties: bool = False,
101+
) -> None:
94102
geojson_feature_collection = convert_from_kili_to_geojson_format(
95-
asset["latestLabel"]["jsonResponse"]
103+
asset["latestLabel"]["jsonResponse"], json_interface, flatten_properties
96104
)
97-
filepath = labels_folder / f'{asset["externalId"]}.geojson'
105+
filepath = labels_folder / f"{asset['externalId']}.geojson"
98106
filepath.parent.mkdir(parents=True, exist_ok=True)
99107
with open(filepath, "w", encoding="utf-8") as file:
100108
json.dump(geojson_feature_collection, file)

tests/unit/services/export/expected/geojson_project_assets_export.py

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515
},
1616
"id": "20230713142445256-28071",
1717
"properties": {
18+
"class": "BBox A",
19+
"BBox job": "BBox A",
1820
"kili": {
21+
"type": "rectangle",
1922
"categories": [{"name": "B_BOX_A"}],
2023
"children": {},
2124
"job": "BBOX_DETECTION_JOB",
22-
"type": "rectangle",
23-
}
25+
},
2426
},
2527
"type": "Feature",
2628
},
@@ -39,51 +41,59 @@
3941
},
4042
"id": "20230713142448596-95775",
4143
"properties": {
44+
"class": "BBox A",
45+
"BBox job": "BBox A",
4246
"kili": {
47+
"type": "rectangle",
4348
"categories": [{"name": "B_BOX_A"}],
4449
"children": {},
4550
"job": "BBOX_DETECTION_JOB",
46-
"type": "rectangle",
47-
}
51+
},
4852
},
4953
"type": "Feature",
5054
},
5155
{
5256
"geometry": {"coordinates": [4.398223125643078, 52.24806039693592], "type": "Point"},
5357
"id": "20230713142454008-23856",
5458
"properties": {
59+
"class": "Point A",
60+
"Point job": "Point A",
5561
"kili": {
62+
"type": "marker",
5663
"categories": [{"name": "POINT_A"}],
5764
"children": {},
5865
"job": "POINT_DETECTION_JOB",
59-
"type": "marker",
60-
}
66+
},
6167
},
6268
"type": "Feature",
6369
},
6470
{
6571
"geometry": {"coordinates": [4.3710296361584255, 52.241662846365564], "type": "Point"},
6672
"id": "20230713142503281-53174",
6773
"properties": {
74+
"class": "Point A",
75+
"Point job": "Point A",
6876
"kili": {
77+
"type": "marker",
6978
"categories": [{"name": "POINT_A"}],
7079
"children": {},
7180
"job": "POINT_DETECTION_JOB",
72-
"type": "marker",
73-
}
81+
},
7482
},
7583
"type": "Feature",
7684
},
7785
{
7886
"geometry": {"coordinates": [4.344001960634017, 52.24602952843428], "type": "Point"},
7987
"id": "20230713142504544-36453",
8088
"properties": {
89+
"class": "Point A",
90+
"Point job": "Point A",
8191
"kili": {
92+
"type": "marker",
8293
"categories": [{"name": "POINT_A"}],
8394
"children": {},
8495
"job": "POINT_DETECTION_JOB",
85-
"type": "marker",
86-
}
96+
},
8797
},
8898
"type": "Feature",
8999
},
@@ -107,12 +117,14 @@
107117
},
108118
"id": "20230713142514505-569",
109119
"properties": {
120+
"class": "Polygon A",
121+
"Polygon job": "Polygon A",
110122
"kili": {
123+
"type": "polygon",
111124
"categories": [{"name": "POLYGON_A"}],
112125
"children": {},
113126
"job": "POLYGON_DETECTION_JOB",
114-
"type": "polygon",
115-
}
127+
},
116128
},
117129
"type": "Feature",
118130
},
@@ -131,12 +143,14 @@
131143
},
132144
"id": "20230713142520574-39224",
133145
"properties": {
146+
"class": "Polygon A",
147+
"Polygon job": "Polygon A",
134148
"kili": {
149+
"type": "polygon",
135150
"categories": [{"name": "POLYGON_A"}],
136151
"children": {},
137152
"job": "POLYGON_DETECTION_JOB",
138-
"type": "polygon",
139-
}
153+
},
140154
},
141155
"type": "Feature",
142156
},
@@ -165,12 +179,14 @@
165179
},
166180
"id": "20230713142557676-43208",
167181
"properties": {
182+
"class": "Line A",
183+
"Line job": "Line A",
168184
"kili": {
185+
"type": "polyline",
169186
"categories": [{"name": "LINE_A"}],
170187
"children": {},
171188
"job": "LINE_DETECTION_JOB",
172-
"type": "polyline",
173-
}
189+
},
174190
},
175191
"type": "Feature",
176192
},
@@ -188,12 +204,14 @@
188204
},
189205
"id": "20230713142603466-75106",
190206
"properties": {
207+
"class": "Line A",
208+
"Line job": "Line A",
191209
"kili": {
210+
"type": "polyline",
192211
"categories": [{"name": "LINE_A"}],
193212
"children": {},
194213
"job": "LINE_DETECTION_JOB",
195-
"type": "polyline",
196-
}
214+
},
197215
},
198216
"type": "Feature",
199217
},
@@ -917,12 +935,14 @@
917935
},
918936
"id": "20230713142654407-69669",
919937
"properties": {
938+
"class": "Segmentation A",
939+
"Segmentation job": "Segmentation A",
920940
"kili": {
941+
"type": "semantic",
921942
"categories": [{"name": "SEGMENTATION_A"}],
922943
"children": {},
923944
"job": "SEGMENTATION_JOB",
924-
"type": "semantic",
925-
}
945+
},
926946
},
927947
"type": "Feature",
928948
},

tests/unit/utils/labels/test_geojson.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ def test_polygon_geojson():
270270
{"x": 9.519223671685653, "y": 54.52470311233283},
271271
]
272272

273-
first_polygon_vertices = first_annotation["boundingPoly"][0]["normalizedVertices"]
273+
first_polygon_vertices = first_annotation["boundingPoly"][0][0]["normalizedVertices"]
274274
assert len(first_polygon_vertices) == len(expected_first_polygon)
275275

276276
for i, point in enumerate(first_polygon_vertices):
@@ -336,19 +336,16 @@ def test_multipolygon_geojson():
336336
assert "annotations" in response["multipolygons_job"]
337337

338338
annotations = response["multipolygons_job"]["annotations"]
339-
# MultiPolygon should create multiple annotations with the same mid
340-
assert len(annotations) == 2
341-
342-
# Check that both annotations have the same mid (indicating they're parts of the same multipolygon)
343-
mids = [ann.get("mid") for ann in annotations]
344-
assert len(set(mids)) == 1 # All should have the same mid
345-
346-
for annotation in annotations:
347-
assert annotation["type"] == "semantic"
348-
assert "boundingPoly" in annotation
349-
assert "categories" in annotation
350-
assert len(annotation["categories"]) == 1
351-
assert annotation["categories"][0]["name"] == "multipolygon_category"
339+
# MultiPolygon should create a single annotation with multiple polygons in boundingPoly
340+
assert len(annotations) == 1
341+
342+
annotation = annotations[0]
343+
assert annotation["type"] == "semantic"
344+
assert "boundingPoly" in annotation
345+
assert len(annotation["boundingPoly"]) == 2
346+
assert "categories" in annotation
347+
assert len(annotation["categories"]) == 1
348+
assert annotation["categories"][0]["name"] == "multipolygon_category"
352349

353350
shutil.rmtree(temp_dir)
354351

0 commit comments

Comments
 (0)