Skip to content

Commit acabe0d

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

File tree

2 files changed

+131
-0
lines changed

2 files changed

+131
-0
lines changed

scripts/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Forge export layers script
2+
3+
Usage
4+
-----
5+
6+
Set your API key in the environment and run the script:
7+
8+
```bash
9+
export PICTERRA_API_KEY="your_api_key_here"
10+
python scripts/forge_export_layers.py --outdir /path/to/output --include-detectors
11+
```
12+
13+
- `--outdir` : directory where per-vector-layer folders will be created
14+
- `--include-detectors` : writes `detectors_map.json` in the output directory
15+
16+
Output layout
17+
-------------
18+
19+
For each vector layer a folder `<vector_id>_<safe_name>/` will be created containing:
20+
21+
- `vector.geojson` : downloaded GeoJSON of the vector layer
22+
- `metadata.json` : JSON with folder, raster, vector layer and detector metadata

scripts/forge_export_layers.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
"""
2+
Export vector layers and metadata using ForgeClient.
3+
4+
For each project in the user account, creates a folder for each detection
5+
on every project raster containing metadata about the detection (raster id and
6+
name, project id and name) 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+
from __future__ import annotations
14+
15+
import argparse
16+
import json
17+
import os
18+
import re
19+
from typing import Any
20+
21+
from picterra.forge_client import ForgeClient
22+
23+
24+
def safe_name(name: str) -> str:
25+
return re.sub(r"[^0-9A-Za-z._-]", "_", name)[:200]
26+
27+
28+
def drain_results_page(page):
29+
items = []
30+
while page is not None:
31+
items.extend(list(page))
32+
page = page.next()
33+
return items
34+
35+
36+
def main():
37+
p = argparse.ArgumentParser()
38+
p.add_argument("outdir", help="Directory to write outputs")
39+
args = p.parse_args()
40+
41+
outdir = args.outdir
42+
os.makedirs(outdir, exist_ok=True)
43+
44+
client = ForgeClient()
45+
46+
# Build detectors map
47+
detectors_map: dict[str, Any] = {}
48+
print("Listing detectors...")
49+
dp = client.list_detectors()
50+
for det in drain_results_page(dp):
51+
detectors_map[det["id"]] = det
52+
53+
with open(os.path.join(outdir, "detectors_map.json"), "w") as f:
54+
json.dump(detectors_map, f, indent=2)
55+
56+
# Get folders via internal paginated endpoint
57+
print("Listing folders...")
58+
folders_page = client._return_results_page("folders", None)
59+
folders = drain_results_page(folders_page)
60+
61+
for folder in folders:
62+
folder_id = folder.get("id")
63+
folder_name = folder.get("name")
64+
print(f"Folder: {folder_name} ({folder_id})")
65+
66+
# List rasters in folder
67+
rp = client.list_rasters(folder_id=folder_id)
68+
rasters = drain_results_page(rp)
69+
for raster in rasters:
70+
raster_id = raster.get("id")
71+
raster_name = raster.get("name")
72+
print(f" Raster: {raster_name} ({raster_id})")
73+
# List vector layers for raster
74+
vpage = client.list_raster_vector_layers(raster_id=raster_id)
75+
vlayers = drain_results_page(vpage)
76+
for vl in vlayers:
77+
vl_id = vl.get("id")
78+
vl_name = vl.get("name")
79+
detector_id = vl.get("detector_id")
80+
81+
folder_for_vl = os.path.join(outdir, f"{vl_id}_{safe_name(vl_name)}")
82+
os.makedirs(folder_for_vl, exist_ok=True)
83+
84+
geojson_path = os.path.join(folder_for_vl, "vector.geojson")
85+
metadata_path = os.path.join(folder_for_vl, "metadata.json")
86+
87+
print(f" Downloading vector layer {vl_name} ({vl_id}) -> {geojson_path}")
88+
try:
89+
client.download_vector_layer_to_file(vl_id, geojson_path)
90+
except Exception as e:
91+
print(f" Error downloading layer {vl_id}: {e}")
92+
continue
93+
metadata = {
94+
"id": vl_id,
95+
"name": vl_name,
96+
"count": vl.get("count"),
97+
"created_at": vl.get("created_at"),
98+
"folder": {"id": folder_id, "name": folder_name},
99+
"raster": {"id": raster_id, "name": raster_name},
100+
"detector": {"id": detector_id},
101+
}
102+
if detector_id and detector_id in detectors_map:
103+
metadata["detector"]["id"] = detectors_map[detector_id]
104+
with open(metadata_path, "w") as f:
105+
json.dump(metadata, f, indent=2)
106+
107+
108+
if __name__ == "__main__":
109+
main()

0 commit comments

Comments
 (0)