Skip to content

Commit f60fa8b

Browse files
authored
Merge pull request #1937 from kili-technology/feature/lab-3824-aau-i-see-a-sdk-tutorial-to-import-labels-with-geojson
feat(LAB-3824): add tutorial `import_labels_from_geojson`
2 parents f224377 + 05f25d9 commit f60fa8b

File tree

4 files changed

+687
-0
lines changed

4 files changed

+687
-0
lines changed
Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
<!-- FILE AUTO GENERATED BY docs/utils.py DO NOT EDIT DIRECTLY -->
2+
# Importing Labels from GeoJSON
3+
4+
<a href="https://colab.research.google.com/github/kili-technology/kili-python-sdk/blob/main/recipes/import_labels_from_geojson.ipynb" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>
5+
6+
This tutorial explains how to use the `kili.append_labels_from_geojson_files` function in the Kili SDK to import geometries from GeoJSON files and convert them to annotations in your Kili projects.
7+
8+
## Introduction
9+
10+
GeoJSON is a widely-used open standard format for representing simple geographical features along with their non-spatial attributes. Unlike shapefiles, GeoJSON is human-readable, supports web applications natively, and always uses the WGS84 coordinate system (EPSG:4326).
11+
12+
The `append_labels_from_geojson_files` function provides three flexible modes to convert GeoJSON features into Kili annotations, making it easy to import existing geographic data into your annotation projects.
13+
14+
## Prerequisites
15+
16+
Before using this feature, ensure you have:
17+
18+
- A Kili project of type `IMAGE` or `GEOSPATIAL`
19+
- One or more GeoJSON files (`.geojson` or `.json`)
20+
- Understanding of your project's job structure and categories
21+
22+
## Function Structure
23+
24+
The `append_labels_from_geojson_files` function accepts the following parameters:
25+
26+
| Parameter | Type |
27+
|---------------------|----------------------|
28+
| `project_id` | str |
29+
| `asset_external_id` | str |
30+
| `geojson_file_paths`| List[str] |
31+
| `job_names` | Optional[List[str]] |
32+
| `category_names` | Optional[List[str]] |
33+
34+
## Supported Geometry Types
35+
36+
The function supports the following GeoJSON geometry types and their Kili annotation mappings:
37+
38+
| GeoJSON Geometry | Kili Annotation Type | Job Tool Required |
39+
|------------------|---------------------|-------------------|
40+
| Point | marker | marker |
41+
| LineString | polyline | polyline |
42+
| Polygon | polygon or semantic | polygon/semantic |
43+
| MultiPolygon | semantic | semantic |
44+
45+
## Import Modes
46+
47+
The function supports three different import modes, providing flexibility for various use cases:
48+
49+
### Mode 1: GeoJSON with Kili Properties
50+
51+
In this mode, your GeoJSON features contain `kili` metadata in their properties, specifying the job, annotation type, and categories.
52+
53+
**GeoJSON Structure:**
54+
```json
55+
{
56+
"type": "FeatureCollection",
57+
"features": [
58+
{
59+
"type": "Feature",
60+
"geometry": {
61+
"type": "Point",
62+
"coordinates": [2.3522, 48.8566]
63+
},
64+
"properties": {
65+
"name": "Eiffel Tower",
66+
"kili": {
67+
"job": "LANDMARKS",
68+
"type": "marker",
69+
"categories": [{"name": "TOWER"}]
70+
}
71+
}
72+
}
73+
]
74+
}
75+
```
76+
77+
**Python Code:**
78+
```python
79+
from kili.client import Kili
80+
81+
kili = Kili(api_key="your_api_key")
82+
83+
# Mode 1: Import with kili properties already in GeoJSON
84+
kili.append_labels_from_geojson_files(
85+
project_id="your_project_id",
86+
asset_external_id="paris_satellite.tif",
87+
geojson_file_paths=["landmarks_with_kili_props.geojson"]
88+
)
89+
```
90+
91+
### Mode 2: Specific Job/Category Mapping
92+
93+
In this mode, you explicitly specify which job and category to use for all compatible geometries in each file.
94+
95+
**Python Code:**
96+
```python
97+
# Mode 2: Map all features to specific jobs and categories
98+
kili.append_labels_from_geojson_files(
99+
project_id="your_project_id",
100+
asset_external_id="urban_area.jp2",
101+
geojson_file_paths=["roads.geojson", "buildings.geojson", "parks.geojson"],
102+
job_names=["INFRASTRUCTURE", "BUILDINGS", "VEGETATION"],
103+
category_names=["ROAD", "RESIDENTIAL", "PARK"]
104+
)
105+
```
106+
107+
### Mode 3: Automatic Mapping
108+
109+
When neither kili properties nor specific mappings are provided, the function automatically maps geometries based on their type and available jobs in your project.
110+
111+
**Automatic Mapping Priority:**
112+
113+
- `Point` → First available job with 'marker' tool
114+
- `LineString` → First available job with 'polyline' tool
115+
- `Polygon` → First available job with 'polygon' tool (fallback to 'semantic')
116+
- `MultiPolygon` → First available job with 'semantic' tool
117+
118+
**Python Code:**
119+
```python
120+
# Mode 3: Automatic mapping based on geometry types
121+
kili.append_labels_from_geojson_files(
122+
project_id="your_project_id",
123+
asset_external_id="geographic_data.tif",
124+
geojson_file_paths=["mixed_features.geojson"]
125+
)
126+
```
127+
128+
## Detailed Examples
129+
130+
### Example 1: Importing Multiple Annotation Types
131+
132+
This example shows how to import a GeoJSON file containing different geometry types with kili properties:
133+
134+
```python
135+
from kili.client import Kili
136+
137+
kili = Kili(api_key="your_api_key")
138+
139+
# Create a sample GeoJSON with mixed geometries
140+
import json
141+
142+
geojson_data = {
143+
"type": "FeatureCollection",
144+
"features": [
145+
# Point annotation
146+
{
147+
"type": "Feature",
148+
"geometry": {
149+
"type": "Point",
150+
"coordinates": [-73.985428, 40.758896]
151+
},
152+
"properties": {
153+
"kili": {
154+
"job": "POI_DETECTION",
155+
"type": "marker",
156+
"categories": [{"name": "LANDMARK"}]
157+
}
158+
}
159+
},
160+
# LineString annotation
161+
{
162+
"type": "Feature",
163+
"geometry": {
164+
"type": "LineString",
165+
"coordinates": [
166+
[-73.985, 40.758],
167+
[-73.983, 40.760],
168+
[-73.981, 40.762]
169+
]
170+
},
171+
"properties": {
172+
"kili": {
173+
"job": "ROAD_MAPPING",
174+
"type": "polyline",
175+
"categories": [{"name": "HIGHWAY"}]
176+
}
177+
}
178+
},
179+
# Polygon annotation
180+
{
181+
"type": "Feature",
182+
"geometry": {
183+
"type": "Polygon",
184+
"coordinates": [[
185+
[-73.980, 40.755],
186+
[-73.978, 40.755],
187+
[-73.978, 40.757],
188+
[-73.980, 40.757],
189+
[-73.980, 40.755]
190+
]]
191+
},
192+
"properties": {
193+
"kili": {
194+
"job": "ZONE_DETECTION",
195+
"type": "semantic",
196+
"categories": [{"name": "COMMERCIAL_AREA"}]
197+
}
198+
}
199+
}
200+
]
201+
}
202+
203+
# Save to file
204+
with open("mixed_annotations.geojson", "w") as f:
205+
json.dump(geojson_data, f)
206+
207+
# Import into Kili
208+
kili.append_labels_from_geojson_files(
209+
project_id="your_project_id",
210+
asset_external_id="manhattan_satellite.tif",
211+
geojson_file_paths=["mixed_annotations.geojson"]
212+
)
213+
```
214+
215+
### Example 2: Batch Import with Specific Mapping
216+
217+
This example demonstrates importing multiple GeoJSON files with specific job/category mappings:
218+
219+
```python
220+
# Import different infrastructure types
221+
kili.append_labels_from_geojson_files(
222+
project_id="city_planning_project",
223+
asset_external_id="city_orthophoto_2024.tif",
224+
geojson_file_paths=[
225+
"water_infrastructure.geojson",
226+
"power_lines.geojson",
227+
"green_spaces.geojson"
228+
],
229+
job_names=[
230+
"WATER_INFRASTRUCTURE",
231+
"ELECTRICAL_GRID",
232+
"URBAN_VEGETATION"
233+
],
234+
category_names=[
235+
"WATER_PIPELINE",
236+
"HIGH_VOLTAGE_LINE",
237+
"PUBLIC_PARK"
238+
]
239+
)
240+
```
241+
242+
### Example 3: Working with MultiPolygon Features
243+
244+
MultiPolygon features are useful for representing discontinuous areas:
245+
246+
```python
247+
# Create a GeoJSON with MultiPolygon for forest patches
248+
forest_patches = {
249+
"type": "FeatureCollection",
250+
"features": [
251+
{
252+
"type": "Feature",
253+
"geometry": {
254+
"type": "MultiPolygon",
255+
"coordinates": [
256+
# First forest patch
257+
[[
258+
[2.10, 48.80],
259+
[2.12, 48.80],
260+
[2.12, 48.82],
261+
[2.10, 48.82],
262+
[2.10, 48.80]
263+
]],
264+
# Second forest patch
265+
[[
266+
[2.15, 48.81],
267+
[2.17, 48.81],
268+
[2.17, 48.83],
269+
[2.15, 48.83],
270+
[2.15, 48.81]
271+
]]
272+
]
273+
},
274+
"properties": {
275+
"kili": {
276+
"job": "LAND_COVER",
277+
"type": "semantic",
278+
"categories": [{"name": "DECIDUOUS_FOREST"}]
279+
}
280+
}
281+
}
282+
]
283+
}
284+
285+
# Save and import
286+
with open("forest_patches.geojson", "w") as f:
287+
json.dump(forest_patches, f)
288+
289+
kili.append_labels_from_geojson_files(
290+
project_id="environmental_monitoring",
291+
asset_external_id="sentinel2_composite.tif",
292+
geojson_file_paths=["forest_patches.geojson"]
293+
)
294+
```
295+
296+
297+
## Best Practices
298+
299+
1. **Validate Your GeoJSON**: Use online validators (see [geojson.io](https://geojson.io/)) or the `geojson` Python library to ensure your files are valid before import.
300+
301+
2. **Check Job Compatibility**: Ensure your Kili project has jobs with the appropriate tools for your geometry types:
302+
```python
303+
# Check project structure
304+
project = kili.projects(project_id="your_project_id", fields=["jsonInterface"])[0]
305+
print(json.dumps(project["jsonInterface"]["jobs"], indent=2))
306+
```
307+
308+
309+
## Troubleshooting
310+
311+
### Problem: Features Not Appearing
312+
313+
**Possible causes and solutions:**
314+
315+
1. **Incompatible geometry-job combination**:
316+
```python
317+
# Check which tools are available for each job
318+
project = kili.projects(project_id="your_project_id", fields=["jsonInterface"])[0]
319+
for job_name, job_config in project["jsonInterface"]["jobs"].items():
320+
print(f"{job_name}: {job_config.get('tools', [])}")
321+
```
322+
323+
2. **Coordinates outside image bounds**: Verify your coordinates match the geographic extent of your image.
324+
325+
326+
### Problem: ValueError for Missing Job or Category
327+
328+
**Solution**: Verify exact names in your project:
329+
```python
330+
# List all jobs and categories
331+
project = kili.projects(project_id="your_project_id", fields=["jsonInterface"])[0]
332+
for job_name, job_config in project["jsonInterface"]["jobs"].items():
333+
categories = job_config.get("content", {}).get("categories", {})
334+
```

docs/tutorials.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ For other specific use cases, see these tutorials:
3434
- [Importing segmentation pre-annotations](https://python-sdk-docs.kili-technology.com/latest/sdk/tutorials/pixel_level_masks/)
3535
- [Importing DINOv2 classification pre-annotations](https://python-sdk-docs.kili-technology.com/latest/sdk/tutorials/finetuning_dinov2/)
3636
- [Importing labels from shapefiles](https://python-sdk-docs.kili-technology.com/latest/sdk/tutorials/import_labels_from_shapefiles/)
37+
- [Importing labels from GeoJSON](https://python-sdk-docs.kili-technology.com/latest/sdk/tutorials/import_labels_from_geojson/)
3738

3839
Additionally, we’ve devoted one [tutorial](https://python-sdk-docs.kili-technology.com/latest/sdk/tutorials/inference_labels/) to explaining the most common use cases for importing and using model-generated labels: actively monitoring the quality of a model currently deployed to production to detect issues like data drift, and using a model to speed up the process of label creation.
3940

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ nav:
5252
- Inference Labels: sdk/tutorials/inference_labels.md
5353
- DINOv2 Classification Pre-annotations: sdk/tutorials/finetuning_dinov2.md
5454
- Import labels from shapefiles (GIS): sdk/tutorials/import_labels_from_shapefiles.md
55+
- Import labels from GeoJSON (GIS): sdk/tutorials/import_labels_from_geojson.md
5556
- Converting Labels:
5657
- DICOM: sdk/tutorials/medical_imaging.md
5758
- Tagtog: sdk/tutorials/tagtog_to_kili.md

0 commit comments

Comments
 (0)