fio-l2-cairo-mixedgeom-split

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

Map (reference ← swipe → agent)

0:00
Heritage analyst here. I've put together `heritage_sites.geojson` from my own field work, and every site in it is several features sharing a `site_id`. I need to load the data into our desktop tool, which only ingests typed GPKG layers, no mixed-geometry files, and every feature has to be single-part. Can you give me back `heritage.gpkg` with three layers named `points`, `lines`, and `polygons`, keeping the `site_id` on every feature so I don't lose the cross-layer link? Use Egypt's national grid.
0:00
System info
{
  "adapter": "openrouter",
  "model": "google/gemma-4-26b-a4b-it",
  "base_url": "https://openrouter.ai/api/v1"
}
0:05
Bash ls -F
ls -F
heritage_sites.geojson
0:09
Write /work/check_data.py
content · 13 lines · py
import geopandas as gpd

# Load the data
gdf = gpd.read_file('heritage_sites.geojson')

# Print info to understand the structure
print("Original GeoDataFrame info:")
print(gdf.info())
print("\nFirst few rows:")
print(gdf.head())
print("\nGeometry types present:")
print(gdf.geometry.type.unique())
Wrote /work/check_data.py (298 chars).
0:13
Bash python /work/check_data.py
python /work/check_data.py
Original GeoDataFrame info:
<class 'geopandas.geodataframe.GeoDataFrame'>
RangeIndex: 50 entries, 0 to 49
Data columns (total 5 columns):
 #   Column        Non-Null Count  Dtype   
---  ------        --------------  -----   
 0   site_id       50 non-null     str     
 1   feature_kind  50 non-null     str     
 2   name_en       50 non-null     str     
 3   name_ar       50 non-null     str     
 4   geometry      50 non-null     geometry
dtypes: geometry(1), str(4)
memory usage: 5.4 KB
None

First few rows:
      site_id  ...                                           geometry
0  EG-CAI-001  ...  POLYGON ((31.256 30.0316, 31.2572 30.0316, 31....
1  EG-CAI-001  ...  LINESTRING (31.2556 30.0321, 31.2566 30.0322, ...
2  EG-CAI-001  ...  LINESTRING (31.2567 30.0312, 31.2566 30.0322, ...
3  EG-CAI-001  ...                            POINT (31.2566 30.0326)
4  EG-CAI-001  ...                             POINT (31.257 30.0322)

[5 rows x 5 columns]

Geometry types present:
<ArrowStringArray>
['Polygon', 'LineString', 'Point', 'MultiPolygon']
Length: 4, dtype: str
0:42
Write /work/solve.py
content · 65 lines · py
import geopandas as gpd

def process_heritage_data(input_file, output_file):
    # Load the data
    gdf = gpd.read_file(input_file)
    
    # Egypt's national grid: EPSG:22992 (Egypt Red Belt) 
    # or EPSG:32636 (WGS 84 / UTM zone 36N) is commonly used.
    # Since the prompt mentions "Egypt's national grid", 
    # let's try to find a standard one. EPSG:22992 is a common choice for Egypt.
    # However, if the input is WGS84, we'll transform it.
    
    target_crs = "EPSG:22992" # Egypt Red Belt
    
    # Ensure we are working with a CRS
    if gdf.crs is None:
        # Assuming WGS84 if not specified
        gdf.set_crs("EPSG:4326", inplace=True)
    
    # Transform to target CRS
    gdf = gdf.to_crs(target_crs)
    
    # Separate geometries and ensure they are single-part
    # We create three separate DataFrames: points, lines, polygons
    
    # 1. Polygons (including MultiPolygons, but exploded to single part)
    # We explode multi-geometries to ensure single-part
    gdf_exploded = gdf.explode(index_parts=False)
    
    polygons = gdf_exploded[gdf_exploded.geometry.type.isin(['Polygon'])]
    lines = gdf_exploded[gdf_exploded.geometry.type.isin(['LineString'])]
    points = gdf_exploded[gdf_exploded.geometry.type.isin(['Point'])]
    
    # If there are MultiLineStrings or MultiPoints, explode them too if they weren't caught
    # But explode(index_parts=False) usually handles that.
    
    # Let's double check the types after explode
    # Sometimes LineString becomes MultiLineString if not careful, but explode handles it.
    
    # Create the GeoPackage
    # We need to write them to different layers
    
    # First, clear/create the file? gpd.to_file with mode='w' creates/overwrites.
    # To write multiple layers, we use mode='a' for subsequent layers or just write one by one.
    
    # Write Polygons
    if not polygons.empty:
        polygons.to_file(output_file, layer='polygons', driver="GPKG")
    
    # Write Lines (append to existing GPKG)
    if not lines.empty:
        lines.to_file(output_file, layer='lines', driver="GPKG", mode='a')
        
    # Write Points (append to existing GPKG)
    if not points.empty:
        points.to_file(output_file, layer='points', driver="GPKG", mode='a')

    print(f"Successfully created {output_file}")
    print(f"Polygons: {len(polygons)} features")
    print(f"Lines: {len(lines)} features")
    print(f"Points: {len(points)} features")

if __name__ == "__main__":
    process_heritage_data('heritage_sites.geojson', 'heritage.gpkg')
Wrote /work/solve.py (2546 chars).
0:45
Bash python /work/solve.py
python /work/solve.py
Successfully created heritage.gpkg
Polygons: 15 features
Lines: 15 features
Points: 25 features
1:02
Write /work/verify.py
content · 23 lines · py
import geopandas as gpd

def verify_output(output_file):
    import fiona
    
    print(f"Verifying {output_file}:")
    layers = fiona.listlayers(output_file)
    print(f"Layers found: {layers}")
    
    for layer in layers:
        gdf = gpd.read_file(output_file, layer=layer)
        print(f"\nLayer: {layer}")
        print(f"CRS: {gdf.crs}")
        print(f"Count: {len(gdf)}")
        print(f"Geometry types: {gdf.geometry.type.unique()}")
        print(f"Sample site_id: {gdf['site_id'].head().tolist()}")
        # Check if any are multi-part
        multi_types = gdf.geometry.type.value_counts()
        print(f"Geometry type distribution:\n{multi_types}")

if __name__ == "__main__":
    verify_output('heritage.gpkg')
Wrote /work/verify.py (733 chars).
1:04
Bash python /work/verify.py
python /work/verify.py
Verifying heritage.gpkg:
Layers found: ['polygons', 'lines', 'points']

Layer: polygons
CRS: EPSG:22992
Count: 15
Geometry types: <ArrowStringArray>
['Polygon']
Length: 1, dtype: str
Sample site_id: ['EG-CAI-001', 'EG-CAI-002', 'EG-CAI-002', 'EG-CAI-003', 'EG-CAI-003']
Geometry type distribution:
Polygon    15
Name: count, dtype: int64

Layer: lines
CRS: EPSG:22992
Count: 15
Geometry types: <ArrowStringArray>
['LineString']
Length: 1, dtype: str
Sample site_id: ['EG-CAI-001', 'EG-CAI-001', 'EG-CAI-002', 'EG-CAI-003', 'EG-CAI-003']
Geometry type distribution:
LineString    15
Name: count, dtype: int64

Layer: points
CRS: EPSG:22992
Count: 25
Geometry types: <ArrowStringArray>
['Point']
Length: 1, dtype: str
Sample site_id: ['EG-CAI-001', 'EG-CAI-001', 'EG-CAI-001', 'EG-CAI-002', 'EG-CAI-002']
Geometry type distribution:
Point    25
Name: count, dtype: int64
1:30