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": "google/gemma-4-26b-a4b-it",
"base_url": "https://openrouter.ai/api/v1"
}
0:03
Bash
ls -F
vienna_planning.gpkg
0:51
Write
/work/solve.py
content · 56 lines · py
import geopandas as gpd
import json
def main():
gpkg_path = 'vienna_planning.gpkg'
manifest = []
# Get layer names using fiona (via geopandas engine)
import fiona
layers = fiona.listlayers(gpkg_path)
for layer_name in layers:
# Read layer metadata
gdf = gpd.read_file(gpkg_path, layer=layer_name)
# Get CRS in EPSG:NNNN format
# Note: epsg code might be in gdf.crs.to_epsg()
epsg_code = gdf.crs.to_epsg()
crs_str = f"EPSG:{epsg_code}" if epsg_code else gdf.crs.to_string()
# Geometry type
# gdf.geometry.type returns a string like 'Polygon' or 'MultiPolygon'
# We want the most representative type if it's a GeoSeries
# gdf.geometry.geom_type is more reliable for the whole series
geom_type = gdf.geometry.geom_type
# In some versions of geopandas, geom_type returns a single value for the series
# Let's check the first element to be sure of the "native" type if it's mixed,
# but usually it's consistent.
if hasattr(geom_type, 'name'):
geom_type = geom_type.name
elif isinstance(geom_type, str):
geom_type = geom_type
else:
# Fallback for different versions
geom_type = str(gdf.geometry.iloc[0].geom_type) if not gdf.empty else "Unknown"
# Feature count
feature_count = len(gdf)
# BBOX [xmin, ymin, xmax, ymax]
bbox = list(gdf.total_bounds)
manifest.append({
"layer_name": layer_name,
"crs": crs_str,
"geometry_type": geom_type,
"feature_count": feature_count,
"bbox": bbox
})
with open('manifest.json', 'w') as f:
json.dump(manifest, f, indent=2)
if __name__ == "__main__":
main()
Wrote /work/solve.py (1879 chars).
1:10
Edit
/work/solve.py
old_string not found in /work/solve.py
1:12
Read
/work/solve.py
1 import geopandas as gpd
2 import json
3
4 def main():
5 gpkg_path = 'vienna_planning.gpkg'
6 manifest = []
7
8 # Get layer names using fiona (via geopandas engine)
9 import fiona
10 layers = fiona.listlayers(gpkg_path)
11
12 for layer_name in layers:
13 # Read layer metadata
14 gdf = gpd.read_file(gpkg_path, layer=layer_name)
15
16 # Get CRS in EPSG:NNNN format
17 # Note: epsg code might be in gdf.crs.to_epsg()
18 epsg_code = gdf.crs.to_epsg()
19 crs_str = f"EPSG:{epsg_code}" if epsg_code else gdf.crs.to_string()
20
21 # Geometry type
22 # gdf.geometry.type returns a string like 'Polygon' or 'MultiPolygon'
23 # We want the most representative type if it's a GeoSeries
24 # gdf.geometry.geom_type is more reliable for the whole series
25 geom_type = gdf.geometry.geom_type
26 # In some versions of geopandas, geom_type returns a single value for the series
27 # Let's check the first element to be sure of the "native" type if it's mixed,
28 # but usually it's consistent.
29 if hasattr(geom_type, 'name'):
30 geom_type = geom_type.name
31 elif isinstance(geom_type, str):
32 geom_type = geom_type
33 else:
34 # Fallback for different versions
35 geom_type = str(gdf.geometry.iloc[0].geom_type) if not gdf.empty else "Unknown"
36
37 # Feature count
38 feature_count = len(gdf)
39
40 # BBOX [xmin, ymin, xmax, ymax]
41 bbox = list(gdf.total_bounds)
42
43 manifest.append({
44 "layer_name": layer_name,
45 "crs": crs_str,
46 "geometry_type": geom_type,
47 "feature_count": feature_count,
48 "bbox": bbox
49 })
50
51 with open('manifest.json', 'w') as f:
52 json.dump(manifest, f, indent=2)
53
54 if __name__ == "__main__":
55 main()
1:30
Write
/work/solve.py
content · 50 lines · py
import geopandas as gpd
import json
import fiona
def main():
gpkg_path = 'vienna_planning.gpkg'
manifest = []
layers = fiona.listlayers(gpkg_path)
for layer_name in layers:
gdf = gpd.read_file(gpkg_path, layer=layer_name)
# Get CRS in EPSG:NNNN format
if gdf.crs is not None:
epsg_code = gdf.crs.to_epsg()
if epsg_code:
crs_str = f"EPSG:{epsg_code}"
else:
crs_str = gdf.crs.to_string()
else:
crs_str = "None"
# Geometry type
# We use the geom_type of the first element if the layer is not empty
if not gdf.empty:
geom_type = gdf.geometry.iloc[0].geom_type
else:
geom_type = "Unknown"
# Feature count
feature_count = len(gdf)
# BBOX [xmin, ymin, xmax, ymax]
bbox = list(gdf.total_bounds)
manifest.append({
"layer_name": layer_name,
"crs": crs_str,
"geometry_type": geom_type,
"feature_count": feature_count,
"bbox": bbox
})
with open('manifest.json', 'w') as f:
json.dump(manifest, f, indent=2)
if __name__ == "__main__":
main()
Wrote /work/solve.py (1294 chars).
1:33
Bash
python solve.py
(no output)
1:36
Read
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 ]
2:19