agis-spatial 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. agis_spatial-0.1.0/.github/workflows/ci.yml +50 -0
  2. agis_spatial-0.1.0/.github/workflows/publish.yml +18 -0
  3. agis_spatial-0.1.0/.gitignore +11 -0
  4. agis_spatial-0.1.0/LICENSE +21 -0
  5. agis_spatial-0.1.0/PKG-INFO +145 -0
  6. agis_spatial-0.1.0/README.md +114 -0
  7. agis_spatial-0.1.0/pyproject.toml +50 -0
  8. agis_spatial-0.1.0/scripts/smoke_test.py +41 -0
  9. agis_spatial-0.1.0/src/agis/__init__.py +3 -0
  10. agis_spatial-0.1.0/src/agis/cli.py +349 -0
  11. agis_spatial-0.1.0/src/agis/config.py +44 -0
  12. agis_spatial-0.1.0/src/agis/context.py +307 -0
  13. agis_spatial-0.1.0/src/agis/crs.py +92 -0
  14. agis_spatial-0.1.0/src/agis/errors.py +26 -0
  15. agis_spatial-0.1.0/src/agis/evals.py +224 -0
  16. agis_spatial-0.1.0/src/agis/executor.py +236 -0
  17. agis_spatial-0.1.0/src/agis/llm.py +58 -0
  18. agis_spatial-0.1.0/src/agis/ops/__init__.py +6 -0
  19. agis_spatial-0.1.0/src/agis/ops/attributes.py +175 -0
  20. agis_spatial-0.1.0/src/agis/ops/base.py +75 -0
  21. agis_spatial-0.1.0/src/agis/ops/geoprocessing.py +333 -0
  22. agis_spatial-0.1.0/src/agis/ops/io.py +105 -0
  23. agis_spatial-0.1.0/src/agis/ops/join.py +158 -0
  24. agis_spatial-0.1.0/src/agis/ops/merge.py +83 -0
  25. agis_spatial-0.1.0/src/agis/ops/tiling.py +184 -0
  26. agis_spatial-0.1.0/src/agis/plan.py +69 -0
  27. agis_spatial-0.1.0/src/agis/planner.py +210 -0
  28. agis_spatial-0.1.0/src/agis/sample_data.py +85 -0
  29. agis_spatial-0.1.0/src/agis/session.py +167 -0
  30. agis_spatial-0.1.0/src/agis/setup.py +241 -0
  31. agis_spatial-0.1.0/tests/conftest.py +9 -0
  32. agis_spatial-0.1.0/tests/evals/cases.yaml +166 -0
  33. agis_spatial-0.1.0/tests/test_attributes.py +89 -0
  34. agis_spatial-0.1.0/tests/test_builtins.py +71 -0
  35. agis_spatial-0.1.0/tests/test_cli.py +33 -0
  36. agis_spatial-0.1.0/tests/test_context.py +96 -0
  37. agis_spatial-0.1.0/tests/test_evals.py +56 -0
  38. agis_spatial-0.1.0/tests/test_executor.py +167 -0
  39. agis_spatial-0.1.0/tests/test_join.py +89 -0
  40. agis_spatial-0.1.0/tests/test_ops.py +234 -0
  41. agis_spatial-0.1.0/tests/test_setup.py +29 -0
@@ -0,0 +1,50 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ schedule:
8
+ # Weekly, to catch a dependency release that drops a macOS wheel.
9
+ - cron: "0 9 * * 1"
10
+
11
+ jobs:
12
+ test:
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ os: [macos-14, macos-15, macos-15-intel]
17
+ python: ["3.11", "3.13"]
18
+ runs-on: ${{ matrix.os }}
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ - uses: astral-sh/setup-uv@v6
22
+ - name: Install
23
+ run: |
24
+ uv venv --python ${{ matrix.python }}
25
+ uv pip install -e ".[dev]"
26
+ - run: uv run --no-sync ruff check .
27
+ - run: uv run --no-sync pytest -q
28
+
29
+ clean-install:
30
+ # Mirrors what a user does: install the built wheel as a uv tool on a bare runner,
31
+ # refusing to compile anything, then run the CLI and the smoke test.
32
+ strategy:
33
+ fail-fast: false
34
+ matrix:
35
+ os: [macos-14, macos-15, macos-15-intel]
36
+ runs-on: ${{ matrix.os }}
37
+ steps:
38
+ - uses: actions/checkout@v4
39
+ - uses: astral-sh/setup-uv@v6
40
+ - run: uv build --wheel
41
+ - name: Install the wheel as a tool, with no source builds allowed
42
+ run: uv tool install --no-build --python 3.12 dist/*.whl
43
+ - run: agis --version
44
+ - run: agis --help
45
+ - name: Smoke test with the tool's own Python
46
+ run: |
47
+ TOOL_PY="$(uv tool dir)/agis-spatial/bin/python"
48
+ "$TOOL_PY" scripts/smoke_test.py
49
+ - name: doctor runs (Ollama is absent on CI, so a failing exit is expected)
50
+ run: agis doctor || true
@@ -0,0 +1,18 @@
1
+ name: Publish
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ jobs:
8
+ pypi:
9
+ runs-on: ubuntu-latest
10
+ environment: pypi
11
+ permissions:
12
+ # PyPI trusted publishing; configure the publisher at pypi.org/manage/project/agis-spatial.
13
+ id-token: write
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: astral-sh/setup-uv@v6
17
+ - run: uv build
18
+ - run: uv publish --trusted-publishing always
@@ -0,0 +1,11 @@
1
+ .DS_Store
2
+ __pycache__/
3
+ *.py[cod]
4
+ .venv/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .pytest_cache/
9
+ .ruff_cache/
10
+ /scratch/
11
+ data/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Peter Menzies
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,145 @@
1
+ Metadata-Version: 2.5
2
+ Name: agis-spatial
3
+ Version: 0.1.0
4
+ Summary: A lightweight, local-first natural-language CLI for simple GIS tasks.
5
+ Author: Peter Menzies
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: agent,cli,geospatial,gis,llm,ollama
9
+ Classifier: Environment :: Console
10
+ Classifier: Operating System :: MacOS
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Scientific/Engineering :: GIS
13
+ Requires-Python: >=3.11
14
+ Requires-Dist: geopandas>=1.0
15
+ Requires-Dist: morecantile>=6.0
16
+ Requires-Dist: numpy>=1.26
17
+ Requires-Dist: ollama>=0.4
18
+ Requires-Dist: prompt-toolkit>=3
19
+ Requires-Dist: pydantic>=2.7
20
+ Requires-Dist: pyogrio>=0.9
21
+ Requires-Dist: pyproj>=3.6
22
+ Requires-Dist: pyyaml>=6.0
23
+ Requires-Dist: rasterio>=1.4
24
+ Requires-Dist: rich>=13
25
+ Requires-Dist: shapely>=2.0
26
+ Requires-Dist: typer>=0.12
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=8; extra == 'dev'
29
+ Requires-Dist: ruff>=0.5; extra == 'dev'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # agis
33
+
34
+ Run simple GIS tasks by describing them in plain English. Everything runs on your machine with a local model; no API keys, and your data never leaves your computer.
35
+
36
+ ```console
37
+ $ agis "clip roads.gpkg to county.shp and reproject to web mercator"
38
+ Clip roads to the county and reproject to Web Mercator (EPSG:3857).
39
+ # operation details output
40
+ 1 clip input=roads.gpkg mask=county.shp (intermediate)
41
+ 2 reproject input=$step1 crs=EPSG:3857 roads_clipped_3857.gpkg
42
+ Run this plan? [y/n] (y):
43
+ Wrote roads_clipped_3857.gpkg reprojected to EPSG:3857 (WGS 84 / Pseudo-Mercator)
44
+ ```
45
+
46
+ ## Install (macOS)
47
+
48
+ You need a Mac with Apple Silicon and at least 8 GB of memory (16 GB recommended). Intel Macs work, but models run on the CPU and are noticeably slower.
49
+
50
+ ```bash
51
+ brew install uv ollama # or: curl -LsSf https://astral.sh/uv/install.sh | sh, and the Ollama app from ollama.com
52
+ uv tool install agis-spatial # or: pipx install agis-spatial
53
+ agis setup
54
+ ```
55
+
56
+ `agis setup` checks that Ollama is running (and offers to start it), downloads `qwen2.5:3b`, and saves that choice to `~/.config/agis/config.toml`. Pass `--model` to use a different one.
57
+
58
+ You don't need Conda, a system GDAL, or any particular Python: `uv` installs agis in its own environment with its own Python, and the geospatial libraries ship with GDAL, PROJ and GEOS built in.
59
+
60
+ If something isn't working, run `agis doctor` and include its output in bug reports.
61
+
62
+ ## Usage
63
+
64
+ ```bash
65
+ agis # interactive prompt; later requests can say "now buffer that"
66
+ agis "reproject parcels.shp to EPSG:2227"
67
+ agis "buffer the streams by 30 m and remove that from parcels, save as buildable.gpkg"
68
+ agis "make an MBTiles file from ortho.tif up to zoom 16"
69
+ agis "what projection is dem.tif in?"
70
+ ```
71
+
72
+ `agis` on its own opens the prompt. Arrow keys move through the line you are typing, and up and down walk requests from this session and from earlier ones (`~/.config/agis/history`). Tab completes dataset names in the working folder. `ls`, `cd`, and `pwd` run locally and are not sent to the model. `exit` or Ctrl-D quits. A quoted request still runs once and exits.
73
+
74
+ agis reads the files in the current folder (CRS, geometry type, fields, raster bands) so the model knows what it's working with. It always shows the plan before running it.
75
+
76
+ | Option | Effect |
77
+ | --- | --- |
78
+ | `-n`, `--dry-run` | Show the plan without running it |
79
+ | `-y`, `--yes` | Run without asking |
80
+ | `-f`, `--force` | Allow overwriting existing outputs |
81
+ | `-m`, `--model` | Use a different Ollama model |
82
+ | `-C`, `--dir` | Work in another folder |
83
+ | `-v`, `--verbose` | Show the file context and raw plans |
84
+
85
+ Outputs are named after their inputs and steps (`roads_clipped_3857.gpkg`) unless you name them. agis never overwrites an input. If the output name is already taken, it appends a number (`roads_clipped_3857_2.gpkg`); `--force` overwrites that file instead. Intermediate results of multi-step plans go to a temporary folder that is deleted afterwards.
86
+
87
+ ### Operations
88
+
89
+ | Operation | Works on |
90
+ | --- | --- |
91
+ | reproject (EPSG codes, common names like "web mercator", "utm", or "match county.shp") | vector, raster |
92
+ | clip (to a mask layer or a bounding box) | vector, raster |
93
+ | difference, union | vector |
94
+ | merge (append features from other vector files; columns line up by name) | vector |
95
+ | buffer (meters, kilometers, feet, miles; correct in any CRS) | vector |
96
+ | dissolve (all, or by a field) | vector |
97
+ | filter (keep rows by attribute: equals, comparisons, contains, in, null) | vector |
98
+ | select (keep named columns; geometry is always kept) | vector |
99
+ | join (attach a CSV, TSV, or another layer's columns on a shared field) | vector + table |
100
+ | info | vector, raster, table |
101
+ | convert (gpkg, shp, geojson, fgb, kml, tif, Cloud Optimized GeoTIFF) | vector, raster |
102
+ | tile_raster (MBTiles of 256px PNG tiles) | raster |
103
+
104
+ Requests that need anything else (slope, contours, and so on) get a short explanation instead of a guess.
105
+
106
+ ## How it works
107
+
108
+ agis uses a plan-then-execute design built for small local models:
109
+
110
+ 1. **Context:** summarize the files in the folder and any the request mentions.
111
+ 2. **Plan:** one call to the model through Ollama, with output constrained to a JSON schema generated from the operation definitions, so the model can only produce well-formed plans.
112
+ 3. **Validate:** check every file, `$stepN` reference, CRS and output path. If the plan can't run, the problems go back to the model for one corrected attempt.
113
+ 4. **Preview and confirm**, then **execute** with GeoPandas, pyogrio, shapely, pyproj and rasterio. If a step fails, files from that run are removed and the error goes back to the model for one re-plan.
114
+
115
+ The model never writes or runs code; it only fills in arguments for a fixed set of operations.
116
+
117
+ ```
118
+ src/agis/
119
+ cli.py command line (run, repl, setup, doctor, eval)
120
+ session.py plan -> validate -> preview -> confirm -> run, with re-planning
121
+ context.py file summaries given to the model
122
+ planner.py system prompt, few-shot examples, model call
123
+ plan.py Plan schema built from the operation registry
124
+ executor.py validation, output naming, running steps
125
+ llm.py model backend (Ollama)
126
+ ops/ one Pydantic model per operation, with its implementation
127
+ setup.py agis setup / agis doctor
128
+ evals.py agis eval
129
+ ```
130
+
131
+ To add an operation, subclass `OpArgs` in `agis/ops/` with an `op: Literal["name"]` field, describe its arguments with `Field(description=...)`, and implement `run`. It's registered automatically and shows up in the schema and prompt.
132
+
133
+ ## Development
134
+
135
+ ```bash
136
+ uv venv && uv pip install -e ".[dev]"
137
+ .venv/bin/pytest # unit tests, no model needed
138
+ .venv/bin/agis eval -m qwen2.5:3b # plan accuracy against tests/evals/cases.yaml
139
+ ```
140
+
141
+ `agis eval` runs each case in `tests/evals/cases.yaml` against generated sample data and compares the plan to the expected one (CRSs, units and file names are normalized). Use it to compare models or check prompt changes; `--only` filters cases and `--show` prints every plan.
142
+
143
+ ## License
144
+
145
+ MIT
@@ -0,0 +1,114 @@
1
+ # agis
2
+
3
+ Run simple GIS tasks by describing them in plain English. Everything runs on your machine with a local model; no API keys, and your data never leaves your computer.
4
+
5
+ ```console
6
+ $ agis "clip roads.gpkg to county.shp and reproject to web mercator"
7
+ Clip roads to the county and reproject to Web Mercator (EPSG:3857).
8
+ # operation details output
9
+ 1 clip input=roads.gpkg mask=county.shp (intermediate)
10
+ 2 reproject input=$step1 crs=EPSG:3857 roads_clipped_3857.gpkg
11
+ Run this plan? [y/n] (y):
12
+ Wrote roads_clipped_3857.gpkg reprojected to EPSG:3857 (WGS 84 / Pseudo-Mercator)
13
+ ```
14
+
15
+ ## Install (macOS)
16
+
17
+ You need a Mac with Apple Silicon and at least 8 GB of memory (16 GB recommended). Intel Macs work, but models run on the CPU and are noticeably slower.
18
+
19
+ ```bash
20
+ brew install uv ollama # or: curl -LsSf https://astral.sh/uv/install.sh | sh, and the Ollama app from ollama.com
21
+ uv tool install agis-spatial # or: pipx install agis-spatial
22
+ agis setup
23
+ ```
24
+
25
+ `agis setup` checks that Ollama is running (and offers to start it), downloads `qwen2.5:3b`, and saves that choice to `~/.config/agis/config.toml`. Pass `--model` to use a different one.
26
+
27
+ You don't need Conda, a system GDAL, or any particular Python: `uv` installs agis in its own environment with its own Python, and the geospatial libraries ship with GDAL, PROJ and GEOS built in.
28
+
29
+ If something isn't working, run `agis doctor` and include its output in bug reports.
30
+
31
+ ## Usage
32
+
33
+ ```bash
34
+ agis # interactive prompt; later requests can say "now buffer that"
35
+ agis "reproject parcels.shp to EPSG:2227"
36
+ agis "buffer the streams by 30 m and remove that from parcels, save as buildable.gpkg"
37
+ agis "make an MBTiles file from ortho.tif up to zoom 16"
38
+ agis "what projection is dem.tif in?"
39
+ ```
40
+
41
+ `agis` on its own opens the prompt. Arrow keys move through the line you are typing, and up and down walk requests from this session and from earlier ones (`~/.config/agis/history`). Tab completes dataset names in the working folder. `ls`, `cd`, and `pwd` run locally and are not sent to the model. `exit` or Ctrl-D quits. A quoted request still runs once and exits.
42
+
43
+ agis reads the files in the current folder (CRS, geometry type, fields, raster bands) so the model knows what it's working with. It always shows the plan before running it.
44
+
45
+ | Option | Effect |
46
+ | --- | --- |
47
+ | `-n`, `--dry-run` | Show the plan without running it |
48
+ | `-y`, `--yes` | Run without asking |
49
+ | `-f`, `--force` | Allow overwriting existing outputs |
50
+ | `-m`, `--model` | Use a different Ollama model |
51
+ | `-C`, `--dir` | Work in another folder |
52
+ | `-v`, `--verbose` | Show the file context and raw plans |
53
+
54
+ Outputs are named after their inputs and steps (`roads_clipped_3857.gpkg`) unless you name them. agis never overwrites an input. If the output name is already taken, it appends a number (`roads_clipped_3857_2.gpkg`); `--force` overwrites that file instead. Intermediate results of multi-step plans go to a temporary folder that is deleted afterwards.
55
+
56
+ ### Operations
57
+
58
+ | Operation | Works on |
59
+ | --- | --- |
60
+ | reproject (EPSG codes, common names like "web mercator", "utm", or "match county.shp") | vector, raster |
61
+ | clip (to a mask layer or a bounding box) | vector, raster |
62
+ | difference, union | vector |
63
+ | merge (append features from other vector files; columns line up by name) | vector |
64
+ | buffer (meters, kilometers, feet, miles; correct in any CRS) | vector |
65
+ | dissolve (all, or by a field) | vector |
66
+ | filter (keep rows by attribute: equals, comparisons, contains, in, null) | vector |
67
+ | select (keep named columns; geometry is always kept) | vector |
68
+ | join (attach a CSV, TSV, or another layer's columns on a shared field) | vector + table |
69
+ | info | vector, raster, table |
70
+ | convert (gpkg, shp, geojson, fgb, kml, tif, Cloud Optimized GeoTIFF) | vector, raster |
71
+ | tile_raster (MBTiles of 256px PNG tiles) | raster |
72
+
73
+ Requests that need anything else (slope, contours, and so on) get a short explanation instead of a guess.
74
+
75
+ ## How it works
76
+
77
+ agis uses a plan-then-execute design built for small local models:
78
+
79
+ 1. **Context:** summarize the files in the folder and any the request mentions.
80
+ 2. **Plan:** one call to the model through Ollama, with output constrained to a JSON schema generated from the operation definitions, so the model can only produce well-formed plans.
81
+ 3. **Validate:** check every file, `$stepN` reference, CRS and output path. If the plan can't run, the problems go back to the model for one corrected attempt.
82
+ 4. **Preview and confirm**, then **execute** with GeoPandas, pyogrio, shapely, pyproj and rasterio. If a step fails, files from that run are removed and the error goes back to the model for one re-plan.
83
+
84
+ The model never writes or runs code; it only fills in arguments for a fixed set of operations.
85
+
86
+ ```
87
+ src/agis/
88
+ cli.py command line (run, repl, setup, doctor, eval)
89
+ session.py plan -> validate -> preview -> confirm -> run, with re-planning
90
+ context.py file summaries given to the model
91
+ planner.py system prompt, few-shot examples, model call
92
+ plan.py Plan schema built from the operation registry
93
+ executor.py validation, output naming, running steps
94
+ llm.py model backend (Ollama)
95
+ ops/ one Pydantic model per operation, with its implementation
96
+ setup.py agis setup / agis doctor
97
+ evals.py agis eval
98
+ ```
99
+
100
+ To add an operation, subclass `OpArgs` in `agis/ops/` with an `op: Literal["name"]` field, describe its arguments with `Field(description=...)`, and implement `run`. It's registered automatically and shows up in the schema and prompt.
101
+
102
+ ## Development
103
+
104
+ ```bash
105
+ uv venv && uv pip install -e ".[dev]"
106
+ .venv/bin/pytest # unit tests, no model needed
107
+ .venv/bin/agis eval -m qwen2.5:3b # plan accuracy against tests/evals/cases.yaml
108
+ ```
109
+
110
+ `agis eval` runs each case in `tests/evals/cases.yaml` against generated sample data and compares the plan to the expected one (CRSs, units and file names are normalized). Use it to compare models or check prompt changes; `--only` filters cases and `--show` prints every plan.
111
+
112
+ ## License
113
+
114
+ MIT
@@ -0,0 +1,50 @@
1
+ [project]
2
+ name = "agis-spatial"
3
+ version = "0.1.0"
4
+ description = "A lightweight, local-first natural-language CLI for simple GIS tasks."
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = "MIT"
8
+ authors = [{ name = "Peter Menzies" }]
9
+ keywords = ["gis", "geospatial", "llm", "agent", "ollama", "cli"]
10
+ classifiers = [
11
+ "Environment :: Console",
12
+ "Operating System :: MacOS",
13
+ "Programming Language :: Python :: 3",
14
+ "Topic :: Scientific/Engineering :: GIS",
15
+ ]
16
+ dependencies = [
17
+ "typer>=0.12",
18
+ "rich>=13",
19
+ "pydantic>=2.7",
20
+ "ollama>=0.4",
21
+ "geopandas>=1.0",
22
+ "pyogrio>=0.9",
23
+ "shapely>=2.0",
24
+ "pyproj>=3.6",
25
+ "rasterio>=1.4",
26
+ "morecantile>=6.0",
27
+ "numpy>=1.26",
28
+ "pyyaml>=6.0",
29
+ "prompt_toolkit>=3",
30
+ ]
31
+
32
+ [project.optional-dependencies]
33
+ dev = ["pytest>=8", "ruff>=0.5"]
34
+
35
+ [project.scripts]
36
+ agis = "agis.cli:main"
37
+
38
+ [build-system]
39
+ requires = ["hatchling"]
40
+ build-backend = "hatchling.build"
41
+
42
+ [tool.hatch.build.targets.wheel]
43
+ packages = ["src/agis"]
44
+
45
+ [tool.pytest.ini_options]
46
+ testpaths = ["tests"]
47
+
48
+ [tool.ruff]
49
+ line-length = 100
50
+ target-version = "py311"
@@ -0,0 +1,41 @@
1
+ """Post-install check that the geospatial stack works without any system GDAL.
2
+
3
+ Run with the Python from a fresh install, e.g. the one `uv tool install agis-spatial` created.
4
+ It doesn't need Ollama: it exercises the operations directly.
5
+ """
6
+
7
+ import sys
8
+ import tempfile
9
+ from pathlib import Path
10
+
11
+ from agis.executor import resolve, run
12
+ from agis.ops.geoprocessing import Buffer, Clip, Convert, Reproject
13
+ from agis.ops.tiling import TileRaster
14
+ from agis.plan import Plan, plan_schema
15
+ from agis.sample_data import write_sample_data
16
+
17
+
18
+ def main() -> int:
19
+ with tempfile.TemporaryDirectory() as tmp:
20
+ root = Path(tmp)
21
+ write_sample_data(root)
22
+ plan = Plan(steps=[
23
+ Clip(input="roads.gpkg", mask="county.shp"),
24
+ Buffer(input="$step1", distance=50),
25
+ Reproject(input="$step2", crs="EPSG:3857"),
26
+ Convert(input="dem.tif", format="cog"),
27
+ TileRaster(input="ortho.tif", min_zoom=11, max_zoom=12),
28
+ ])
29
+ steps = resolve(plan, root, root / "work")
30
+ results = run(steps)
31
+ for step, result in zip(steps, results):
32
+ print(f"{step.args.op}: {result.message}")
33
+ assert (root / "roads_clipped_buffer50m_3857.gpkg").exists()
34
+ assert (root / "ortho_tiles.mbtiles").read_bytes().startswith(b"SQLite format 3")
35
+ assert plan_schema()["$defs"]
36
+ print("smoke test passed")
37
+ return 0
38
+
39
+
40
+ if __name__ == "__main__":
41
+ sys.exit(main())
@@ -0,0 +1,3 @@
1
+ """agis: natural-language GIS tasks on your local machine."""
2
+
3
+ __version__ = "0.1.0"