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:04
Bash
ls -F
vienna_planning.gpkg
0:22
Write
/work/solve.py
content · 67 lines · py
import geopandas as gpd
import json
def main():
gpkg_path = 'vienna_planning.gpkg'
try:
import fiona
except ImportError:
print("fiona not installed")
return
layers = fiona.listlayers(gpkg_path)
manifest = []
for layer in layers:
gdf = gpd.read_file(gpkg_path, layer=layer)
# layer_name
layer_name = layer
# crs
# We want EPSG:NNNN format.
# gdf.crs might be a string, a dict, or a pyproj object.
crs_str = str(gdf.crs)
# Try to extract EPSG code if it's a complex CRS
if "EPSG" not in crs_str:
try:
epsg_code = gdf.crs.to_epsg()
if epsg_code:
crs_str = f"EPSG:{epsg_code}"
except Exception:
pass
# geometry_type
# gdf.geometry.type returns a single type if all are same,
# or a collection type. We want the native type.
# Let's check the first geometry or use fiona metadata
layer_info = fiona.info(gpkg_path, layer=layer)
geom_type = layer_info['geometry']
# fiona might return 'Polygon' or 'MultiPolygon'.
# The prompt examples: 'Point', 'LineString', 'Polygon', 'MultiPolygon'
# Usually fiona types match these closely.
# feature_count
feature_count = len(gdf)
# bbox
# bbox is [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=4)
print(f"Manifest created with {len(manifest)} layers.")
if __name__ == "__main__":
main()
Wrote /work/solve.py (1924 chars).
0:24
Bash
python solve.py
Traceback (most recent call last):
File "/work/solve.py", line 66, in <module>
main()
~~~~^^
File "/work/solve.py", line 39, in main
layer_info = fiona.info(gpkg_path, layer=layer)
^^^^^^^^^^
AttributeError: module 'fiona' has no attribute 'info'
0:40
Write
/work/solve.py
content · 65 lines · py
import geopandas as gpd
import json
import fiona
def get_crs_string(gdf):
"""Ensures CRS is in EPSG:NNNN format."""
if gdf.crs is None:
return "Unknown"
try:
epsg = gdf.crs.to_epsg()
if epsg:
return f"EPSG:{epsg}"
else:
# Fallback to string representation if EPSG cannot be determined
return str(gdf.crs)
except Exception:
return str(gdf.crs)
def main():
gpkg_path = 'vienna_planning.gpkg'
layers = fiona.listlayers(gpkg_path)
manifest = []
for layer in layers:
# Use fiona to get metadata without loading the whole dataset if possible,
# but for bbox and feature count we need to inspect it.
# Reading with geopandas is fine for this task.
gdf = gpd.read_file(gpkg_path, layer=layer)
# layer_name
layer_name = layer
# crs
crs_str = get_crs_string(gdf)
# geometry_type
# The prompt asks for the 'native type' and gives examples: 'Point', 'LineString', 'Polygon', 'MultiPolygon'
# gdf.geometry.type returns the type of the geometry series (e.g. 'Polygon')
geom_type = gdf.geometry.type
# feature_count
feature_count = len(gdf)
# bbox
# bbox is [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=4)
print(f"Manifest created with {len(manifest)} layers.")
if __name__ == "__main__":
main()
Wrote /work/solve.py (1809 chars).
0:45
Bash
python solve.py
Traceback (most recent call last):
File "/work/solve.py", line 64, in <module>
main()
~~~~^^
File "/work/solve.py", line 59, in main
json.dump(manifest, f, indent=4)
~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.14/json/__init__.py", line 181, in dump
for chunk in iterable:
^^^^^^^^
File "/usr/local/lib/python3.14/json/encoder.py", line 442, in _iterencode
yield from _iterencode_list(o, _current_indent_level)
File "/usr/local/lib/python3.14/json/encoder.py", line 326, in _iterencode_list
yield from chunks
File "/usr/local/lib/python3.14/json/encoder.py", line 413, in _iterencode_dict
yield from chunks
File "/usr/local/lib/python3.14/json/encoder.py", line 451, in _iterencode
newobj = _default(o)
File "/usr/local/lib/python3.14/json/encoder.py", line 182, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
f'is not JSON serializable')
TypeError: Object of type Series is not JSON serializable
when serializing dict item 'geometry_type'
when serializing list item 0
1:03
Write
/work/solve.py
content · 64 lines · py
import geopandas as gpd
import json
import fiona
def get_crs_string(gdf):
"""Ensures CRS is in EPSG:NNNN format."""
if gdf.crs is None:
return "Unknown"
try:
epsg = gdf.crs.to_epsg()
if epsg:
return f"EPSG:{epsg}"
else:
# Fallback to string representation if EPSG cannot be determined
return str(gdf.crs)
except Exception:
return str(gdf.crs)
def main():
gpkg_path = 'vienna_planning.gpkg'
layers = fiona.listlayers(gpkg_path)
manifest = []
for layer in layers:
gdf = gpd.read_file(gpkg_path, layer=layer)
# layer_name
layer_name = layer
# crs
crs_str = get_crs_string(gdf)
# geometry_type
# gdf.geometry.type returns a single type (e.g. 'Polygon')
# but it's a pandas Series if not handled correctly.
# Actually gdf.geometry.type in recent geopandas returns a string or type.
# Let's be explicit.
geom_type = str(gdf.geometry.type)
# feature_count
feature_count = len(gdf)
# bbox
# bbox is [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=4)
print(f"Manifest created with {len(manifest)} layers.")
if __name__ == "__main__":
main()
Wrote /work/solve.py (1650 chars).
1:06
Bash
python solve.py
Manifest created with 7 layers.
1:08
Bash
cat manifest.json
[
{
"layer_name": "districts",
"crs": "EPSG:31287",
"geometry_type": "0 Polygon\n1 Polygon\n2 Polygon\n3 Polygon\n4 Polygon\n5 Polygon\n6 Polygon\n7 Polygon\n8 Polygon\n9 Polygon\n10 Polygon\n11 Polygon\n12 Polygon\n13 Polygon\n14 Polygon\n15 Polygon\n16 Polygon\n17 Polygon\n18 Polygon\n19 Polygon\n20 Polygon\n21 Polygon\ndtype: str",
"feature_count": 22,
"bbox": [
623867.3888224852,
481236.5836125494,
626371.3079994549,
484403.7315526943
]
},
{
"layer_name": "parks",
"crs": "EPSG:31287",
"geometry_type": "0 MultiPolygon\n1 MultiPolygon\n2 MultiPolygon\n3 MultiPolygon\n4 MultiPolygon\n ... \n114 MultiPolygon\n115 MultiPolygon\n116 MultiPolygon\n117 MultiPolygon\n118 MultiPolygon\nLength: 119, dtype: str",
"feature_count": 119,
"bbox": [
623851.990195953,
481200.6674012489,
626620.9438865517,
485539.3479671855
]
},
{
"layer_name": "waterbodies",
"crs": "EPSG:31287",
"geometry_type": "0 Polygon\n1 Polygon\n2 Polygon\n3 Polygon\n4 Polygon\n5 Polygon\n6 Polygon\n7 Polygon\n8 Polygon\n9 Polygon\n10 Polygon\n11 Polygon\n12 Polygon\n13 Polygon\n14 Polygon\n15 Polygon\n16 Polygon\n17 Polygon\n18 Polygon\n19 Polygon\n20 Polygon\n21 Polygon\n22 Polygon\n23 Polygon\n24 Polygon\n25 Polygon\n26 Polygon\n27 Polygon\n28 Polygon\n29 Polygon\n30 Polygon\n31 Polygon\n32 Polygon\ndtype: str",
"feature_count": 33,
"bbox": [
624288.8521056126,
481236.66320640914,
626747.8611584986,
484132.0212685799
]
},
{
"layer_name": "schools",
"crs": "EPSG:31287",
"geometry_type": "0 Point\n1 Point\n2 Point\n3 Point\n4 Point\n5 Point\n6 Point\n7 Point\n8 Point\n9 Point\n10 Point\n11 Point\n12 Point\n13 Point\n14 Point\n15 Point\n16 Point\n17 Point\n18 Point\n19 Point\n20 Point\n21 Point\n22 Point\n23 Point\n24 Point\n25 Point\n26 Point\n27 Point\n28 Point\n29 Point\n30 Point\n31 Point\n32 Point\n33 Point\n34 Point\n35 Point\n36 Point\n37 Point\n38 Point\n39 Point\ndtype: str",
"feature_count": 40,
"bbox": [
624000.002417057,
481214.73871311604,
626259.5421406503,
484178.87865010765
]
},
{
"layer_name": "cafes",
"crs": "EPSG:31287",
"geometry_type": "0 Point\n1 Point\n2 Point\n3 Point\n4 Point\n ... \n387 Point\n388 Point\n389 Point\n390 Point\n391 Point\nLength: 392, dtype: str",
"feature_count": 392,
"bbox": [
623796.529863561,
481107.2540129812,
626399.5618121764,
484498.9209005649
]
},
{
"layer_name": "supermarkets",
"crs": "EPSG:31287",
"geometry_type": "0 Point\n1 Point\n2 Point\n3 Point\n4 Point\n ... \n82 Point\n83 Point\n84 Point\n85 Point\n86 Point\nLength: 87, dtype: str",
"feature_count": 87,
"bbox": [
623794.0549950565,
481148.38703723426,
626392.6164786755,
484489.91954147833
]
},
{
"layer_name": "cycleway_segments",
"crs": "EPSG:31287",
"geometry_type": "0 LineString\n1 LineString\n2 LineString\n3 LineString\n4 LineString\n ... \n266 LineString\n267 LineString\n268 LineString\n269 LineString\n270 LineString\nLength: 271, dtype: str",
"feature_count": 271,
"bbox": [
623787.4932131292,
481134.9325878215,
626619.1631922598,
485245.6624853243
]
}
]
1:22
Write
/work/solve.py
content · 64 lines · py
import geopandas as gpd
import json
import fiona
def get_crs_string(gdf):
"""Ensures CRS is in EPSG:NNNN format."""
if gdf.crs is None:
return "Unknown"
try:
epsg = gdf.crs.to_epsg()
if epsg:
return f"EPSG:{epsg}"
else:
return str(gdf.crs)
except Exception:
return str(gdf.crs)
def main():
gpkg_path = 'vienna_planning.gpkg'
layers = fiona.listlayers(gpkg_path)
manifest = []
for layer in layers:
gdf = gpd.read_file(gpkg_path, layer=layer)
# layer_name
layer_name = layer
# crs
crs_str = get_crs_string(gdf)
# geometry_type
# Use the most frequent type or the first one to get a clean string
# instead of the Series representation.
if not gdf.empty:
geom_type = gdf.geometry.type.iloc[0]
else:
geom_type = "Unknown"
# feature_count
feature_count = len(gdf)
# bbox
# bbox is [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=4)
print(f"Manifest created with {len(manifest)} layers.")
if __name__ == "__main__":
main()
Wrote /work/solve.py (1538 chars).
1:25
Bash
python solve.py && cat manifest.json
Manifest created with 7 layers.
[
{
"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:28
Bash
ls -l manifest.json
-rw-r--r-- 1 agent agent 2063 Jun 7 13:38 manifest.json
1:55