dd-l1-vienna-gpkg-manifest
Map (reference ← swipe → agent)
0:00
Inherited a GPKG (vienna_planning) from a colleague who retired last week. Before I commit to scripting against it I need a one-page manifest so I can see what's in there and decide which layers actually feed next month's councillor briefing. Catalog the contents and give me one record per layer. Output manifest.json — a JSON list of these records.
Each record in the list must be a JSON object with exactly five keys: `layer_name` as a string, `crs` as a string in `EPSG:NNNN` form, `geometry_type` as a string matching the layer's native type (e.g. `Point`, `LineString`, `Polygon`, `MultiPolygon`), `feature_count` as an integer, and `bbox` as a list of four numbers `[xmin, ymin, xmax, ymax]` in the layer's native CRS.
0:00
System info
{
"adapter": "openrouter",
"model": "deepseek/deepseek-v4-flash",
"base_url": "https://openrouter.ai/api/v1"
}
0:05
Let me start by exploring the GPKG file to understand its contents.
0:05
Bash
ls -la /work/vienna_planning* 2>/dev/null || find /work -name "vienna_planning*"...
-rw-r--r-- 1 agent agent 524288 Jun 16 00:49 /work/vienna_planning.gpkg
0:19
0:19
Bash
cd /work && python3 -c "
cd /work && python3 -c "
import geopandas as gpd
layers = gpd.list_layers('vienna_planning.gpkg')
print(layers)
print('---')
print(layers.columns.tolist())
"
name geometry_type 0 districts Polygon 1 parks MultiPolygon 2 waterbodies Polygon 3 schools Point 4 cafes Point 5 supermarkets Point 6 cycleway_segments LineString --- ['name', 'geometry_type']
0:23
0:23
Bash
cd /work && python3 -c "
cd /work && python3 -c "
import geopandas as gpd
layers = gpd.list_layers('vienna_planning.gpkg')
for name in layers['name']:
gdf = gpd.read_file('vienna_planning.gpkg', layer=name)
print(f'Layer: {name}')
print(f' CRS: {gdf.crs}')
print(f' Geometry type: {gdf.geometry.type.unique()}')
print(f' Feature count: {len(gdf)}')
print(f' Bounds: {gdf.total_bounds.tolist()}')
print()
"
Layer: districts CRS: EPSG:31287 Geometry type: <ArrowStringArray> ['Polygon'] Length: 1, dtype: str Feature count: 22 Bounds: [623867.3888224852, 481236.5836125494, 626371.3079994549, 484403.7315526943] Layer: parks CRS: EPSG:31287 Geometry type: <ArrowStringArray> ['MultiPolygon'] Length: 1, dtype: str Feature count: 119 Bounds: [623851.990195953, 481200.6674012489, 626620.9438865517, 485539.3479671855] Layer: waterbodies CRS: EPSG:31287 Geometry type: <ArrowStringArray> ['Polygon'] Length: 1, dtype: str Feature count: 33 Bounds: [624288.8521056126, 481236.66320640914, 626747.8611584986, 484132.0212685799] Layer: schools CRS: EPSG:31287 Geometry type: <ArrowStringArray> ['Point'] Length: 1, dtype: str Feature count: 40 Bounds: [624000.002417057, 481214.73871311604, 626259.5421406503, 484178.87865010765] Layer: cafes CRS: EPSG:31287 Geometry type: <ArrowStringArray> ['Point'] Length: 1, dtype: str Feature count: 392 Bounds: [623796.529863561, 481107.2540129812, 626399.5618121764, 484498.9209005649] Layer: supermarkets CRS: EPSG:31287 Geometry type: <ArrowStringArray> ['Point'] Length: 1, dtype: str Feature count: 87 Bounds: [623794.0549950565, 481148.38703723426, 626392.6164786755, 484489.91954147833] Layer: cycleway_segments CRS: EPSG:31287 Geometry type: <ArrowStringArray> ['LineString'] Length: 1, dtype: str Feature count: 271 Bounds: [623787.4932131292, 481134.9325878215, 626619.1631922598, 485245.6624853243]
0:42
Now I have all the data I need. Let me construct the manifest.json file. Each record needs exactly 5 keys: - layer_name (string) - crs (string in EPSG:NNNN form) - geometry_type (string matching native type) - feature_count (integer) - bbox (list of 4 numbers [xmin, ymin, xmax, ymax] in native CRS) Let me write the script to produce this JSON.
0:42
0:42
Write
/work/solve.py
content · 37 lines · py
import json
import geopandas as gpd
gpkg_path = '/work/vienna_planning.gpkg'
layers_info = gpd.list_layers(gpkg_path)
records = []
for name in layers_info['name']:
gdf = gpd.read_file(gpkg_path, layer=name)
# CRS in EPSG:NNNN form
crs = gdf.crs.to_epsg()
crs_str = f'EPSG:{crs}' if crs else str(gdf.crs)
# Native geometry type
geometry_type = layers_info.loc[layers_info['name'] == name, 'geometry_type'].values[0]
# Feature count
feature_count = len(gdf)
# Bounding box [xmin, ymin, xmax, ymax]
bbox = gdf.total_bounds.tolist()
records.append({
'layer_name': name,
'crs': crs_str,
'geometry_type': geometry_type,
'feature_count': feature_count,
'bbox': bbox
})
with open('/work/manifest.json', 'w') as f:
json.dump(records, f, indent=2)
print('manifest.json written successfully')
print(json.dumps(records, indent=2))
Wrote /work/solve.py (940 chars).
0:56
Let me run the script.
0:56
Bash
cd /work && python solve.py
manifest.json written successfully
[
{
"layer_name": "districts",
"crs": "EPSG:31287",
"geometry_type": "Polygon",
"feature_count": 22,
"bbox": [
623867.3888224852,
481236.5836125494,
626371.3079994549,
484403.7315526943
]
},
{
"layer_name": "parks",
"crs": "EPSG:31287",
"geometry_type": "MultiPolygon",
"feature_count": 119,
"bbox": [
623851.990195953,
481200.6674012489,
626620.9438865517,
485539.3479671855
]
},
{
"layer_name": "waterbodies",
"crs": "EPSG:31287",
"geometry_type": "Polygon",
"feature_count": 33,
"bbox": [
624288.8521056126,
481236.66320640914,
626747.8611584986,
484132.0212685799
]
},
{
"layer_name": "schools",
"crs": "EPSG:31287",
"geometry_type": "Point",
"feature_count": 40,
"bbox": [
624000.002417057,
481214.73871311604,
626259.5421406503,
484178.87865010765
]
},
{
"layer_name": "cafes",
"crs": "EPSG:31287",
"geometry_type": "Point",
"feature_count": 392,
"bbox": [
623796.529863561,
481107.2540129812,
626399.5618121764,
484498.9209005649
]
},
{
"layer_name": "supermarkets",
"crs": "EPSG:31287",
"geometry_type": "Point",
"feature_count": 87,
"bbox": [
623794.0549950565,
481148.38703723426,
626392.6164786755,
484489.91954147833
]
},
{
"layer_name": "cycleway_segments",
"crs": "EPSG:31287",
"geometry_type": "LineString",
"feature_count": 271,
"bbox": [
623787.4932131292,
481134.9325878215,
626619.1631922598,
485245.6624853243
]
}
]
1:12
1:12
Read
/work/manifest.json
1 [
2 {
3 "layer_name": "districts",
4 "crs": "EPSG:31287",
5 "geometry_type": "Polygon",
6 "feature_count": 22,
7 "bbox": [
8 623867.3888224852,
9 481236.5836125494,
10 626371.3079994549,
11 484403.7315526943
12 ]
13 },
14 {
15 "layer_name": "parks",
16 "crs": "EPSG:31287",
17 "geometry_type": "MultiPolygon",
18 "feature_count": 119,
19 "bbox": [
20 623851.990195953,
21 481200.6674012489,
22 626620.9438865517,
23 485539.3479671855
24 ]
25 },
26 {
27 "layer_name": "waterbodies",
28 "crs": "EPSG:31287",
29 "geometry_type": "Polygon",
30 "feature_count": 33,
31 "bbox": [
32 624288.8521056126,
33 481236.66320640914,
34 626747.8611584986,
35 484132.0212685799
36 ]
37 },
38 {
39 "layer_name": "schools",
40 "crs": "EPSG:31287",
41 "geometry_type": "Point",
42 "feature_count": 40,
43 "bbox": [
44 624000.002417057,
45 481214.73871311604,
46 626259.5421406503,
47 484178.87865010765
48 ]
49 },
50 {
51 "layer_name": "cafes",
52 "crs": "EPSG:31287",
53 "geometry_type": "Point",
54 "feature_count": 392,
55 "bbox": [
56 623796.529863561,
57 481107.2540129812,
58 626399.5618121764,
59 484498.9209005649
60 ]
61 },
62 {
63 "layer_name": "supermarkets",
64 "crs": "EPSG:31287",
65 "geometry_type": "Point",
66 "feature_count": 87,
67 "bbox": [
68 623794.0549950565,
69 481148.38703723426,
70 626392.6164786755,
71 484489.91954147833
72 ]
73 },
74 {
75 "layer_name": "cycleway_segments",
76 "crs": "EPSG:31287",
77 "geometry_type": "LineString",
78 "feature_count": 271,
79 "bbox": [
80 623787.4932131292,
81 481134.9325878215,
82 626619.1631922598,
83 485245.6624853243
84 ]
85 }
86 ]
1:19