dd-l1-vienna-gpkg-manifest

Status: done
Score: 1.000
Duration: 0:47
Cost: 0.29¢
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:01
Bash ls -F
vienna_planning.gpkg
0:11
Write solve.py
content · 62 lines · py
# /// script
# requires-python = ">=3.12"
# dependencies = ["geopandas", "fiona", "pyproj"]
# ///

import geopandas as gpd
import fiona
import json

def get_epsg_string(crs):
    if crs is None:
        return "Unknown"
    # Handle if it's a pyproj object or a string
    try:
        # Try to get EPSG code from CRS object
        return f"EPSG:{crs.to_epsg()}"
    except Exception:
        # If it's already a string like 'EPSG:31287'
        if isinstance(crs, str) and crs.startswith("EPSG:"):
            return crs
        # Fallback for other formats if possible
        return str(crs)

def generate_manifest(gpkg_path, output_path):
    layers_info = []
    
    # Use fiona to list layers and metadata without loading all data
    layers = fiona.listlayers(gpkg_path)
    
    for layer_name in layers:
        with fiona.open(gpkg_path, layer=layer_name) as src:
            # Get CRS
            crs_obj = src.crs
            epsg_str = get_epsg_string(crs_obj)
            
            # Get geometry type
            # fiona geometry types are usually 'Polygon', 'LineString', etc.
            # We'll check the first feature or the schema
            geom_type = src.schema['geometry']
            # Note: fiona/geopandas might return 'MultiPolygon' or 'Polygon'
            
            # Get feature count
            feature_count = len(src)
            
            # Get bbox [xmin, ymin, xmax, ymax]
            # fiona provides bounds: (minx, miny, maxx, maxy)
            bbox = list(src.bounds)
            
            layers_info.append({
                "layer_name": layer_name,
                "crs": epsg_str,
                "geometry_type": geom_type,
                "feature_count": feature_count,
                "bbox": bbox
            })

    with open(output_path, 'w') as f:
        json.dump(layers_info, f, indent=4)

if __name__ == "__main__":
    generate_manifest("vienna_planning.gpkg", "manifest.json")
file_path must be inside the working directory (/work): '/home/nhp/project/benchmark/harness/.sessions/4a3d474cf68c46c98801b27bf208dfd2/solve.py'
0:24
Bash uv run solve.py
error: Failed to spawn: `solve.py`
  Caused by: No such file or directory (os error 2)
0:32
Write solve.py
content · 80 lines · py
# /// script
# requires-python = ">=3.12"
# dependencies = ["geopandas", "fiona", "pyproj"]
# ///

import geopandas as gpd
import fiona
import json

def get_epsg_string(crs):
    if crs is None:
        return "Unknown"
    # Fiona CRS is often a dict or a tuple. 
    # If it is a dict, we might need to extract it.
    # If it's a pyproj object or a CRS object, we use to_epsg.
    
    # Let's try a more robust way to get the EPSG code
    try:
        # If it's a dictionary (common in Fiona)
        if isinstance(crs, dict):
            # Check if 'init' or 'name' or something similar is there
            # Or if it's a proj string
            import pyproj
            p = pyproj.CRS(crs)
            epsg = p.to_epsg()
            if epsg:
                return f"EPSG:{epsg}"
            else:
                return str(crs)
        
        # If it's a string
        if isinstance(crs, str):
            return crs
            
        # Try pyproj directly
        import pyproj
        p = pyproj.CRS(crs)
        epsg = p.to_epsg()
        if epsg:
            return f"EPSG:{epsg}"
        else:
            return str(crs)
    except Exception:
        return str(crs)

def generate_manifest(gpkg_path, output_path):
    layers_info = []
    
    layers = fiona.listlayers(gpkg_path)
    
    for layer_name in layers:
        with fiona.open(gpkg_path, layer=layer_name) as src:
            # Get CRS
            crs_obj = src.crs
            epsg_str = get_epsg_string(crs_obj)
            
            # Get geometry type
            # fiona schema geometry type
            geom_type = src.schema['geometry']
            
            # Get feature count
            feature_count = len(src)
            
            # Get bbox [xmin, ymin, xmax, ymax]
            bbox = list(src.bounds)
            
            layers_info.append({
                "layer_name": layer_name,
                "crs": epsg_str,
                "geometry_type": geom_type,
                "feature_count": feature_count,
                "bbox": bbox
            })

    with open(output_path, 'w') as f:
        json.dump(layers_info, f, indent=4)

if __name__ == "__main__":
    generate_manifest("vienna_planning.gpkg", "manifest.json")
Wrote solve.py (2244 chars).
0:33
Bash uv run solve.py
Installed 15 packages in 256ms
0:38
Bash cat manifest.json
[
    {
        "layer_name": "districts",
        "crs": "EPSG:31287",
        "geometry_type": "Polygon",
        "feature_count": 22,
        "bbox": [
            623867.388822485,
            481236.583612549,
            626371.307999455,
            484403.731552694
        ]
    },
    {
        "layer_name": "parks",
        "crs": "EPSG:31287",
        "geometry_type": "MultiPolygon",
        "feature_count": 119,
        "bbox": [
            623851.990195953,
            481200.667401249,
            626620.943886552,
            485539.347967185
        ]
    },
    {
        "layer_name": "waterbodies",
        "crs": "EPSG:31287",
        "geometry_type": "Polygon",
        "feature_count": 33,
        "bbox": [
            624288.852105613,
            481236.663206409,
            626747.861158499,
            484132.02126858
        ]
    },
    {
        "layer_name": "schools",
        "crs": "EPSG:31287",
        "geometry_type": "Point",
        "feature_count": 40,
        "bbox": [
            624000.002417057,
            481214.738713116,
            626259.54214065,
            484178.878650108
        ]
    },
    {
        "layer_name": "cafes",
        "crs": "EPSG:31287",
        "geometry_type": "Point",
        "feature_count": 392,
        "bbox": [
            623796.529863561,
            481107.254012981,
            626399.561812176,
            484498.920900565
        ]
    },
    {
        "layer_name": "supermarkets",
        "crs": "EPSG:31287",
        "geometry_type": "Point",
        "feature_count": 87,
        "bbox": [
            623794.054995056,
            481148.387037234,
            626392.616478676,
            484489.919541478
        ]
    },
    {
        "layer_name": "cycleway_segments",
        "crs": "EPSG:31287",
        "geometry_type": "LineString",
        "feature_count": 271,
        "bbox": [
            623787.493213129,
            481134.932587822,
            626619.16319226,
            485245.662485324
        ]
    }
]
0:39
Bash ls -F
manifest.json
solve.py
vienna_planning.gpkg
0:45