$npx -y skills add opengeos/geoai-skills --skill process-rasterProcess raster data: clip by bounding box, stack multiple bands, mosaic GeoTIFFs, or convert between raster and vector formats.
| 1 | You are helping the user process geospatial raster data using geoai. |
| 2 | |
| 3 | Input: `$@` |
| 4 | |
| 5 | Follow these steps in order. |
| 6 | |
| 7 | ## Step 1 -- Determine the operation |
| 8 | |
| 9 | Parse `$@` to identify the requested operation: |
| 10 | |
| 11 | | Operation | Triggers | Required inputs | |
| 12 | |---|---|---| |
| 13 | | `clip` | "clip", "crop", "subset", `--bbox` present | input raster + bbox | |
| 14 | | `stack` | "stack", "combine bands" | list of input rasters | |
| 15 | | `mosaic` | "mosaic", "merge" | input directory or list of rasters | |
| 16 | | `raster-to-vector` | "to vector", "vectorize", "polygonize" | input raster | |
| 17 | | `vector-to-raster` | "to raster", "rasterize", "burn" | input vector + pixel size | |
| 18 | |
| 19 | If the operation is unclear from the input, ask the user to specify. |
| 20 | |
| 21 | ## Step 2 -- Resolve input file(s) |
| 22 | |
| 23 | For single-file operations (`clip`, `raster-to-vector`, `vector-to-raster`): |
| 24 | |
| 25 | ```bash |
| 26 | find "$PWD" -name "INPUT_FILENAME" -not -path '*/.git/*' 2>/dev/null |
| 27 | ``` |
| 28 | |
| 29 | For multi-file operations (`stack`, `mosaic`), if a directory is given: |
| 30 | |
| 31 | ```bash |
| 32 | find "INPUT_DIR" -name "*.tif" -o -name "*.tiff" 2>/dev/null | sort |
| 33 | ``` |
| 34 | |
| 35 | If the user recently inspected or downloaded a file and did not specify an input, check the state file for context: |
| 36 | |
| 37 | ```bash |
| 38 | STATE_DIR="" |
| 39 | test -f .geoai-skills/state.json && STATE_DIR=".geoai-skills" |
| 40 | PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")" |
| 41 | PROJECT_ID="$(echo "$PROJECT_ROOT" | tr '/' '-')" |
| 42 | test -f "$HOME/.geoai-skills/$PROJECT_ID/state.json" && STATE_DIR="$HOME/.geoai-skills/$PROJECT_ID" |
| 43 | ``` |
| 44 | |
| 45 | If state exists, read the last inspected or downloaded file: |
| 46 | |
| 47 | ```bash |
| 48 | python3 -c " |
| 49 | import json |
| 50 | with open('STATE_DIR/state.json') as f: |
| 51 | state = json.load(f) |
| 52 | if 'last_inspected' in state: |
| 53 | print(f'Last inspected: {state[\"last_inspected\"][\"path\"]}') |
| 54 | if 'downloaded_files' in state: |
| 55 | for f in state['downloaded_files']: |
| 56 | print(f'Downloaded: {f}') |
| 57 | " |
| 58 | ``` |
| 59 | |
| 60 | ## Step 3 -- Execute the operation |
| 61 | |
| 62 | ### Clip by bounding box |
| 63 | |
| 64 | ```bash |
| 65 | python3 -c " |
| 66 | import geoai |
| 67 | |
| 68 | result = geoai.clip_raster_by_bbox( |
| 69 | input_raster='INPUT_PATH', |
| 70 | output_raster='OUTPUT_PATH', |
| 71 | bbox=[MINX, MINY, MAXX, MAXY], |
| 72 | ) |
| 73 | print(f'Clipped raster saved to: {result}') |
| 74 | info = geoai.get_raster_info(result) |
| 75 | for k, v in info.items(): |
| 76 | print(f'{k}: {v}') |
| 77 | " |
| 78 | ``` |
| 79 | |
| 80 | Default output: `./clipped_<original_name>.tif` |
| 81 | |
| 82 | ### Stack bands |
| 83 | |
| 84 | ```bash |
| 85 | python3 -c " |
| 86 | import geoai |
| 87 | |
| 88 | result = geoai.stack_bands( |
| 89 | input_files=['FILE1', 'FILE2', 'FILE3'], |
| 90 | output_file='OUTPUT_PATH', |
| 91 | ) |
| 92 | print(f'Stacked raster saved to: {result}') |
| 93 | info = geoai.get_raster_info(result) |
| 94 | for k, v in info.items(): |
| 95 | print(f'{k}: {v}') |
| 96 | " |
| 97 | ``` |
| 98 | |
| 99 | ### Mosaic GeoTIFFs |
| 100 | |
| 101 | ```bash |
| 102 | python3 -c " |
| 103 | import geoai |
| 104 | |
| 105 | result = geoai.mosaic_geotiffs( |
| 106 | input_dir='INPUT_DIR', |
| 107 | output_file='OUTPUT_PATH', |
| 108 | ) |
| 109 | print(f'Mosaic saved to: {result}') |
| 110 | info = geoai.get_raster_info(result) |
| 111 | for k, v in info.items(): |
| 112 | print(f'{k}: {v}') |
| 113 | " |
| 114 | ``` |
| 115 | |
| 116 | ### Raster to vector |
| 117 | |
| 118 | ```bash |
| 119 | python3 -c " |
| 120 | import geoai |
| 121 | |
| 122 | gdf = geoai.raster_to_vector( |
| 123 | raster_path='INPUT_PATH', |
| 124 | output_path='OUTPUT_PATH', |
| 125 | ) |
| 126 | print(f'Vectorized: {len(gdf)} features') |
| 127 | print(f'Saved to: OUTPUT_PATH') |
| 128 | print(f'Columns: {list(gdf.columns)}') |
| 129 | " |
| 130 | ``` |
| 131 | |
| 132 | Default output: `./<original_name>.gpkg` |
| 133 | |
| 134 | ### Vector to raster |
| 135 | |
| 136 | ```bash |
| 137 | python3 -c " |
| 138 | import geoai |
| 139 | |
| 140 | result = geoai.vector_to_raster( |
| 141 | vector_path='INPUT_PATH', |
| 142 | output_path='OUTPUT_PATH', |
| 143 | pixel_size=PIXEL_SIZE, |
| 144 | ) |
| 145 | print(f'Rasterized: {result}') |
| 146 | info = geoai.get_raster_info(result) |
| 147 | for k, v in info.items(): |
| 148 | print(f'{k}: {v}') |
| 149 | " |
| 150 | ``` |
| 151 | |
| 152 | Default pixel size: 1.0 (or infer from context). Default output: `./<original_name>.tif` |
| 153 | |
| 154 | Replace all placeholder values with actual paths and parameters before running. |
| 155 | |
| 156 | ## Step 4 -- Update state |
| 157 | |
| 158 | If a state directory exists, update it with the output file path using the same state resolution pattern as Step 2. |
| 159 | |
| 160 | ## Step 5 -- Report and suggest |
| 161 | |
| 162 | Report: |
| 163 | - Operation performed |
| 164 | - Input and output file paths |
| 165 | - Key properties of the output (dimensions, CRS, band count, feature count) |
| 166 | |
| 167 | Then suggest: *"Use `/geoai-skills:inspect-geo` to examine the result in detail."* |
| 168 | |
| 169 | ## Error handling |
| 170 | |
| 171 | - **`import geoai` fails** -> delegate to `/geoai-skills:install-geoai`. |
| 172 | - **File not found** -> use `find` to locate, suggest corrected path. |
| 173 | - **CRS mismatch** (for stack/mosaic) -> report the issue and suggest reprojecting first. |
| 174 | - **Insufficient disk space** -> report the error. |
| 175 | - **Memory error** (very large rasters) -> suggest processing in tiles or using a smaller extent. |