dd-l1-vienna-gpkg-manifest

Status: done
Score: 1.000
Duration: 1:28
Cost: 0.44¢
Model: google/gemma-4-26b-a4b-it

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:00
Bash ls -F
vienna_planning.gpkg
0:23
Write /work/solve.py
content · 65 lines · py
# /// script
# requires-python = ">=3.12"
# dependencies = ["geopandas", "pyogrio"]
# ///

import geopandas as gpd
import json

def main():
    gpkg_path = 'vienna_planning.gpkg'
    
    # Get layer names using pyogrio/fiona via geopandas
    import fiona
    layers = fiona.listlayers(gpkg_path)
    
    manifest = []
    
    for layer_name in layers:
        # Read the layer
        gdf = gpd.read_file(gpkg_path, layer=layer_name)
        
        # layer_name
        # crs: EPSG:NNNN
        crs_string = gdf.crs.to_epsg()
        if crs_string is None:
            # Fallback to string if EPSG not found, but prompt asks for EPSG:NNNN
            # Let's try to get it more robustly
            epsg = gdf.crs.to_string() 
            # If it's something like 'EPSG:32633', it might work. 
            # If it's 'urn:ogc:def:crs:EPSG::32633', we need to clean it.
            if "EPSG::" in epsg:
                epsg = epsg.replace("urn:ogc:def:crs:EPSG::", "EPSG:")
            elif "EPSG:" not in epsg:
                 # This is a fallback. If we can't find EPSG, we'll try to find the code.
                 pass
            crs_val = epsg
        else:
            crs_val = f"EPSG:{crs_string}"
            
        # geometry_type: Point, LineString, Polygon, MultiPolygon
        # Note: geopandas/shapely types might be slightly different (e.g. MultiPolygon vs Polygon)
        # We want the "native" type.
        if gdf.empty:
            geom_type = "None"
            bbox = [0.0, 0.0, 0.0, 0.0]
        else:
            geom_type = gdf.geometry.type.iloc[0]
            bbox = list(gdf.total_bounds)
            
        feature_count = len(gdf)
        
        manifest.append({
            "layer_name": layer_name,
            "crs": crs_val,
            "geometry_type": geom_type,
            "feature_count": feature_count,
            "bbox": bbox
        })
        
    with open('manifest.json', 'w') as f:
        json.dump(manifest, f, indent=4)

if __name__ == "__main__":
    main()
Wrote /work/solve.py (2024 chars).
0:24
Bash uv run /work/solve.py
Installed 10 packages in 223ms
Traceback (most recent call last):
  File "/work/solve.py", line 64, in <module>
    main()
    ~~~~^^
  File "/work/solve.py", line 13, in main
    import fiona
ModuleNotFoundError: No module named 'fiona'
0:42
Write /work/solve.py
content · 69 lines · py
# /// script
# requires-python = ">=3.12"
# dependencies = ["geopandas", "pyogrio", "fiona"]
# ///

import geopandas as gpd
import json
import fiona

def get_epsg_string(crs):
    if crs is None:
        return "Unknown"
    # Try to get EPSG code
    epsg_code = crs.to_epsg()
    if epsg_code is not None:
        return f"EPSG:{epsg_code}"
    
    # If to_epsg() fails, try to parse from string
    s = crs.to_string()
    if "EPSG:" in s:
        # Extract part after EPSG:
        parts = s.split("EPSG:")
        if len(parts) > 1:
            # Take the first number-like part
            code = parts[1].split()[0].split(',')[0].split(';')[0]
            return f"EPSG:{code}"
    return s

def main():
    gpkg_path = 'vienna_planning.gpkg'
    
    # Get layer names using fiona
    layers = fiona.listlayers(gpkg_path)
    
    manifest = []
    
    for layer_name in layers:
        # Read the layer
        gdf = gpd.read_file(gpkg_path, layer=layer_name)
        
        # crs
        crs_val = get_epsg_string(gdf.crs)
            
        # geometry_type
        if gdf.empty:
            geom_type = "Unknown"
            bbox = [0.0, 0.0, 0.0, 0.0]
        else:
            # Get the geometry type of the first feature
            # Shapely types: Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon
            geom_type = gdf.geometry.iloc[0].geom_type
            bbox = list(gdf.total_bounds)
            
        feature_count = len(gdf)
        
        manifest.append({
            "layer_name": layer_name,
            "crs": crs_val,
            "geometry_type": geom_type,
            "feature_count": feature_count,
            "bbox": bbox
        })
        
    with open('manifest.json', 'w') as f:
        json.dump(manifest, f, indent=4)

if __name__ == "__main__":
    main()
Wrote /work/solve.py (1835 chars).
0:45
Bash uv run /work/solve.py
Installed 5 packages in 27ms
0:52
Bash ls -F
manifest.json
solve.py
vienna_planning.gpkg
1:02
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:08
Bash ls -F
manifest.json
solve.py
vienna_planning.gpkg
1:27