Skip to content

Commit 1584817

Browse files
committed
[scripts] Add script to export all user's vector layers
1 parent c78de26 commit 1584817

File tree

1 file changed

+142
-0
lines changed

1 file changed

+142
-0
lines changed

scripts/forge_export_layers.py

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
"""
2+
Export vector layers and metadata using ForgeClient.
3+
4+
For each project in the user account, creates as many folders as the images inside; each
5+
raster folder than contains - for each detection on it - the metadata about the detection
6+
and the geometries.
7+
8+
Usage:
9+
python scripts/forge_export_layers.py output_dir
10+
11+
Requires `PICTERRA_API_KEY` environment variable.
12+
13+
Usage
14+
-----
15+
16+
Set your API key in the environment and run the script:
17+
18+
```bash
19+
export PICTERRA_API_KEY="your_api_key_here"
20+
python scripts/forge_export_layers.py /path/to/output
21+
```
22+
23+
YOu can use the '--limit' option to test downloading only N layers.
24+
25+
Output layout
26+
-------------
27+
28+
For each vector layer a folder `<vector_id>_<safe_name>/` will be created containing:
29+
30+
- `vector.geojson` : downloaded GeoJSON of the vector layer
31+
- `metadata.json` : JSON with folder, raster, vector layer and detector metadata
32+
33+
"""
34+
from __future__ import annotations
35+
36+
import argparse
37+
import json
38+
import os
39+
import re
40+
from typing import Any
41+
42+
from picterra.forge_client import ForgeClient
43+
44+
45+
def safe_name(name: str) -> str:
46+
return re.sub(r"[^0-9A-Za-z._-]", "_", name)[:200]
47+
48+
49+
def drain_results_page(page):
50+
items = []
51+
while page is not None:
52+
items.extend(list(page))
53+
page = page.next()
54+
return items
55+
56+
57+
def main():
58+
p = argparse.ArgumentParser()
59+
p.add_argument("outdir", help="Directory to write outputs")
60+
p.add_argument("--limit", help="Export only a max number of layers")
61+
args = p.parse_args()
62+
63+
outdir = args.outdir
64+
limit = args.limit
65+
os.makedirs(outdir, exist_ok=True)
66+
67+
client = ForgeClient()
68+
69+
# Build detectors map
70+
detectors_map: dict[str, Any] = {}
71+
print("Listing detectors...")
72+
dp = client.list_detectors()
73+
for det in drain_results_page(dp):
74+
detectors_map[det["id"]] = det
75+
# Get folders via internal paginated endpoint
76+
print("Listing folders...")
77+
folders_page = client._return_results_page("folders", None)
78+
folders = drain_results_page(folders_page)
79+
count = 0
80+
for folder in folders:
81+
folder_id = folder.get("id")
82+
folder_name = folder.get("name")
83+
print(f"Folder: {folder_name} ({folder_id})")
84+
# List rasters in folder
85+
rp = client.list_rasters(folder_id=folder_id)
86+
rasters = drain_results_page(rp)
87+
for raster in rasters:
88+
raster_id = raster.get("id")
89+
raster_name = raster.get("name")
90+
print(f" Raster: {raster_name} ({raster_id})")
91+
# List vector layers for raster
92+
vpage = client.list_raster_vector_layers(raster_id=raster_id)
93+
vlayers = drain_results_page(vpage)
94+
for vl in vlayers:
95+
vl_id = vl.get("id")
96+
vl_name = vl.get("name")
97+
detector_id = vl.get("detector_id")
98+
detector_data = detectors_map.get(detector_id)
99+
if detector_data is None:
100+
print(f"Skip {vl_name}")
101+
continue
102+
detector_name = detector_data["name"]
103+
folder_for_vl = os.path.join(
104+
outdir,
105+
safe_name(folder_name),
106+
safe_name(raster_name),
107+
f"{safe_name(detector_name)}_{safe_name(vl_name)}"
108+
)
109+
os.makedirs(folder_for_vl, exist_ok=True)
110+
111+
geojson_path = os.path.join(folder_for_vl, "vector.geojson")
112+
metadata_path = os.path.join(folder_for_vl, "metadata.json")
113+
114+
print(f" Downloading vector layer {vl_name} ({vl_id}) -> {geojson_path}")
115+
try:
116+
client.download_vector_layer_to_file(vl_id, geojson_path)
117+
except Exception as e:
118+
print(f" Error downloading layer {vl_id}: {e}")
119+
continue
120+
metadata = {
121+
"id": vl_id,
122+
"name": vl_name,
123+
"count": vl.get("count"),
124+
"created_at": vl.get("created_at"),
125+
"folder": {"id": folder_id, "name": folder_name},
126+
"raster": {"id": raster_id, "name": raster_name},
127+
"detector": {"id": detector_id, "name": detector_name},
128+
}
129+
with open(metadata_path, "w") as f:
130+
json.dump(metadata, f, indent=2)
131+
count += 1
132+
if limit and count >= int(limit):
133+
break
134+
if limit and count >= int(limit):
135+
break
136+
if limit and count >= int(limit):
137+
break
138+
print(f"Exported {count} vector layers")
139+
140+
141+
if __name__ == "__main__":
142+
main()

0 commit comments

Comments
 (0)