geospatial-mcp 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.
- geospatial_mcp-0.1.0/PKG-INFO +319 -0
- geospatial_mcp-0.1.0/README.md +286 -0
- geospatial_mcp-0.1.0/demo.tape +11 -0
- geospatial_mcp-0.1.0/docs/assets/demo.gif +0 -0
- geospatial_mcp-0.1.0/docs/assets/topology_repair_comparison.png +0 -0
- geospatial_mcp-0.1.0/docs/assets/topology_repair_comparison.svg +2986 -0
- geospatial_mcp-0.1.0/pyproject.toml +69 -0
- geospatial_mcp-0.1.0/scripts/render_comparison.py +117 -0
- geospatial_mcp-0.1.0/src/geospatial_mcp/__init__.py +6 -0
- geospatial_mcp-0.1.0/src/geospatial_mcp/core/__init__.py +3 -0
- geospatial_mcp-0.1.0/src/geospatial_mcp/core/geometry.py +524 -0
- geospatial_mcp-0.1.0/src/geospatial_mcp/core/projection.py +63 -0
- geospatial_mcp-0.1.0/src/geospatial_mcp/models.py +166 -0
- geospatial_mcp-0.1.0/src/geospatial_mcp/server.py +147 -0
- geospatial_mcp-0.1.0/tests/__init__.py +3 -0
- geospatial_mcp-0.1.0/tests/test_geometry.py +205 -0
- geospatial_mcp-0.1.0/tests/test_server.py +117 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: geospatial-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight spatial data topology verification and analysis service for AI agents based on FastMCP
|
|
5
|
+
Project-URL: Homepage, https://github.com/geospatial-mcp/geospatial-mcp
|
|
6
|
+
Project-URL: Documentation, https://github.com/geospatial-mcp/geospatial-mcp#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/geospatial-mcp/geospatial-mcp.git
|
|
8
|
+
Project-URL: Issues, https://github.com/geospatial-mcp/geospatial-mcp/issues
|
|
9
|
+
Author-email: GeoSpatial MCP Team <dev@geospatial-mcp.org>
|
|
10
|
+
License: MIT
|
|
11
|
+
Keywords: ai-agent,fastmcp,geojson,geospatial,gis,mcp,pyproj,shapely,spatial-analysis,topology
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: fastmcp>=2.0.0
|
|
25
|
+
Requires-Dist: pydantic>=2.0.0
|
|
26
|
+
Requires-Dist: pyproj>=3.6.0
|
|
27
|
+
Requires-Dist: shapely>=2.0.0
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: matplotlib>=3.8.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: pillow>=10.0.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# ๐ Geospatial MCP Server
|
|
35
|
+
|
|
36
|
+
[](https://pypi.org/project/geospatial-mcp/)
|
|
37
|
+
[](LICENSE)
|
|
38
|
+
[](https://www.python.org/)
|
|
39
|
+
[](https://modelcontextprotocol.io/)
|
|
40
|
+
[](https://pytest.org)
|
|
41
|
+
|
|
42
|
+
A zero-dependency (no GDAL/C++ build hell) Model Context Protocol (MCP) server that equips AI Agents (Claude, Cursor, Antigravity) with deterministic spatial analysis, GeoJSON auto-repair, and accurate projected metric calculations.
|
|
43
|
+
|
|
44
|
+
<!-- Instant Visual Value: Topology Auto-Healing Comparison -->
|
|
45
|
+

|
|
46
|
+
|
|
47
|
+
<!-- Terminal interaction demo -->
|
|
48
|
+

|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## โก The Problem It Solves
|
|
53
|
+
|
|
54
|
+
Large Language Models (LLMs) are notoriously bad at spatial reasoning and Cartesian mathematics:
|
|
55
|
+
- **Geometry Hallucination**: Self-intersecting polygons (bow-tie / figure-8 errors) and unclosed coordinate rings crash downstream GIS pipelines and map renderers.
|
|
56
|
+
- **Scale Distortion**: Treating spherical WGS84 geographic coordinates (latitude/longitude degrees) as flat Euclidean planes results in catastrophic area and perimeter errors (often 30% to 500% off depending on latitude).
|
|
57
|
+
- **Topology Blindness**: Inability to deterministically resolve point-in-polygon containment, spatial intersection surfaces, separation distances, and metric buffering.
|
|
58
|
+
|
|
59
|
+
`geospatial-mcp` bridges this gap by providing a lightweight, high-performance, GEOS-backed C-extension engine without heavy GIS infrastructure overhead.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## ๐ Quickstart (30 Seconds)
|
|
64
|
+
|
|
65
|
+
### Option 1: Run with UVX (Recommended, Zero Install)
|
|
66
|
+
No local repository clone or manual environment setup required:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"mcpServers": {
|
|
71
|
+
"geospatial": {
|
|
72
|
+
"command": "uvx",
|
|
73
|
+
"args": ["geospatial-mcp"]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Option 2: Local Installation & Development
|
|
80
|
+
|
|
81
|
+
1. **Clone and create virtual environment**:
|
|
82
|
+
```bash
|
|
83
|
+
git clone https://github.com/your-org/geospatial-mcp.git
|
|
84
|
+
cd geospatial-mcp
|
|
85
|
+
|
|
86
|
+
python -m venv .venv
|
|
87
|
+
# Windows (PowerShell):
|
|
88
|
+
.\.venv\Scripts\Activate.ps1
|
|
89
|
+
# Linux / macOS:
|
|
90
|
+
source .venv/bin/activate
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
2. **Install package in editable mode**:
|
|
94
|
+
```bash
|
|
95
|
+
pip install -e .
|
|
96
|
+
pip install pytest
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
3. **Configure MCP Client (Claude Desktop / Cursor / Antigravity)**:
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"mcpServers": {
|
|
103
|
+
"geospatial": {
|
|
104
|
+
"command": "python",
|
|
105
|
+
"args": ["-m", "geospatial_mcp.server"],
|
|
106
|
+
"cwd": "/path/to/geospatial-mcp"
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## ๐ ๏ธ Tools Reference
|
|
115
|
+
|
|
116
|
+
All tools are exposed via standard MCP transports (stdio) and feature robust input tolerance: they natively accept standard GeoJSON strings, raw coordinate arrays, GeoJSON dictionaries, Feature / FeatureCollection objects, and Markdown-wrapped JSON (` ```json `) without crashing.
|
|
117
|
+
|
|
118
|
+
### 1. `validate_and_repair_geometry`
|
|
119
|
+
Validates topological compliance against OGC Simple Features standards. If topological defects exist (e.g., self-intersecting bow-tie polygons or unclosed linear rings), it extracts the exact cause of failure and automatically heals the geometry into a compliant GeoJSON object with RFC 7946 normalized ring winding.
|
|
120
|
+
|
|
121
|
+
- **Parameters**:
|
|
122
|
+
- `geojson` (`str | dict | list`, required): GeoJSON geometry, Feature, FeatureCollection, or raw coordinate array.
|
|
123
|
+
- **Returns**:
|
|
124
|
+
- `is_valid` (`bool`): Whether the original geometry was topologically valid.
|
|
125
|
+
- `error_reason` (`str | null`): Exact topological defect description if invalid (e.g. `Self-intersection[1 1]` or `Points of LinearRing do not form a closed linestring`).
|
|
126
|
+
- `repaired_geojson` (`dict | null`): Compliant, self-healed GeoJSON geometry.
|
|
127
|
+
|
|
128
|
+
#### Example Call
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"name": "validate_and_repair_geometry",
|
|
132
|
+
"arguments": {
|
|
133
|
+
"geojson": {
|
|
134
|
+
"type": "Polygon",
|
|
135
|
+
"coordinates": [[[0, 0], [2, 2], [2, 0], [0, 2], [0, 0]]]
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
#### Response
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"is_valid": false,
|
|
144
|
+
"error_reason": "Self-intersection[1 1]",
|
|
145
|
+
"repaired_geojson": {
|
|
146
|
+
"type": "MultiPolygon",
|
|
147
|
+
"coordinates": [
|
|
148
|
+
[[[0.0, 0.0], [1.0, 1.0], [0.0, 2.0], [0.0, 0.0]]],
|
|
149
|
+
[[[1.0, 1.0], [2.0, 0.0], [2.0, 2.0], [1.0, 1.0]]]
|
|
150
|
+
]
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
### 2. `calculate_accurate_metrics`
|
|
158
|
+
Eliminates spherical degree distortion by deriving the optimal local UTM projection zone (`EPSG:32601-32660` for Northern hemisphere or `EPSG:32701-32760` for Southern hemisphere) from the polygon centroid, projecting to a planar Cartesian coordinate system via PyProj, and calculating distortion-free surface area and perimeter.
|
|
159
|
+
|
|
160
|
+
- **Parameters**:
|
|
161
|
+
- `geojson` (`str | dict | list`, required): Polygon or MultiPolygon GeoJSON.
|
|
162
|
+
- `unit` / `target_unit` (`str`, optional, default `"sq_meters"`): Desired unit (`"sq_meters"`, `"sq_kilometers"`, or `"hectares"`). Fully backwards-compatible with dual alias parsing.
|
|
163
|
+
- **Returns**:
|
|
164
|
+
- `area` (`float`): Planar area in the requested unit.
|
|
165
|
+
- `perimeter_meters` (`float`): Total boundary length in meters.
|
|
166
|
+
- `unit` (`str`): Target unit used.
|
|
167
|
+
- `projected_epsg` (`int`): EPSG integer code of the metric UTM projection applied (e.g. `32651`).
|
|
168
|
+
|
|
169
|
+
#### Example Call
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"name": "calculate_accurate_metrics",
|
|
173
|
+
"arguments": {
|
|
174
|
+
"geojson": {
|
|
175
|
+
"type": "Polygon",
|
|
176
|
+
"coordinates": [[[121.47, 31.23], [121.48, 31.23], [121.48, 31.24], [121.47, 31.24], [121.47, 31.23]]]
|
|
177
|
+
},
|
|
178
|
+
"unit": "hectares"
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
#### Response
|
|
183
|
+
```json
|
|
184
|
+
{
|
|
185
|
+
"area": 105.74,
|
|
186
|
+
"perimeter_meters": 4118.62,
|
|
187
|
+
"unit": "hectares",
|
|
188
|
+
"projected_epsg": 32651
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
### 3. `check_spatial_relationship`
|
|
195
|
+
Evaluates topological relationship predicates between two geometries (`contains`, `within`, `intersects`, `disjoint`). Automatically returns the shared intersection area in $m^2$ if overlapping, or the minimum metric separation distance in meters if disjoint.
|
|
196
|
+
|
|
197
|
+
- **Parameters**:
|
|
198
|
+
- `geom_a` (`str | dict | list`, required): First spatial object.
|
|
199
|
+
- `geom_b` (`str | dict | list`, required): Second spatial object.
|
|
200
|
+
- `predicate` (`str`, optional, default `"intersects"`): Predicate to evaluate (`"contains"`, `"within"`, `"intersects"`, `"disjoint"`).
|
|
201
|
+
- **Returns**:
|
|
202
|
+
- `result` (`bool`): Boolean verdict for the requested predicate.
|
|
203
|
+
- `details` (`dict`): Quantitative measurements including `intersection_area_sq_meters`, `distance_meters`, and `projected_epsg`.
|
|
204
|
+
|
|
205
|
+
#### Example Call
|
|
206
|
+
```json
|
|
207
|
+
{
|
|
208
|
+
"name": "check_spatial_relationship",
|
|
209
|
+
"arguments": {
|
|
210
|
+
"geom_a": {"type": "Polygon", "coordinates": [[[0, 0], [2, 0], [2, 2], [0, 2], [0, 0]]]},
|
|
211
|
+
"geom_b": {"type": "Polygon", "coordinates": [[[1, 1], [3, 1], [3, 3], [1, 3], [1, 1]]]},
|
|
212
|
+
"predicate": "intersects"
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
#### Response
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"result": true,
|
|
220
|
+
"details": {
|
|
221
|
+
"predicate": "intersects",
|
|
222
|
+
"projected_epsg": 32631,
|
|
223
|
+
"intersection_area_sq_meters": 12285149372.45,
|
|
224
|
+
"distance_meters": 0.0
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
### 4. `generate_buffer`
|
|
232
|
+
Constructs an accurate metric buffer (expansion or erosion) in the local UTM coordinate system, auto-heals any resulting self-intersections, and projects back to standard EPSG:4326 WGS84 GeoJSON.
|
|
233
|
+
|
|
234
|
+
- **Parameters**:
|
|
235
|
+
- `geojson` (`str | dict | list`, required): Input geometry.
|
|
236
|
+
- `distance_meters` (`float`, required): Buffer radius in meters (positive expands, negative erodes).
|
|
237
|
+
- `quad_segs` (`int`, optional, default `8`): Quadrant segments for circular arc approximation.
|
|
238
|
+
- **Returns**:
|
|
239
|
+
- `buffered_geojson` (`dict | null`): Normalized RFC 7946 GeoJSON dictionary in EPSG:4326.
|
|
240
|
+
|
|
241
|
+
#### Example Call
|
|
242
|
+
```json
|
|
243
|
+
{
|
|
244
|
+
"name": "generate_buffer",
|
|
245
|
+
"arguments": {
|
|
246
|
+
"geojson": {"type": "Point", "coordinates": [121.5, 31.2]},
|
|
247
|
+
"distance_meters": 500.0,
|
|
248
|
+
"quad_segs": 16
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## ๐๏ธ Architecture & Project Structure
|
|
256
|
+
|
|
257
|
+
`geospatial-mcp` is designed to be completely decoupled from bulky C/C++ GIS frameworks:
|
|
258
|
+
|
|
259
|
+
```
|
|
260
|
+
geospatial-mcp/
|
|
261
|
+
โโโ pyproject.toml # Hatchling packaging and dependency constraints
|
|
262
|
+
โโโ README.md # Complete architectural guide and tool references
|
|
263
|
+
โโโ src/
|
|
264
|
+
โ โโโ geospatial_mcp/
|
|
265
|
+
โ โโโ __init__.py # Package root
|
|
266
|
+
โ โโโ server.py # FastMCP service instance & tool entrypoints
|
|
267
|
+
โ โโโ core/ # Pure C-extension geometry engine
|
|
268
|
+
โ โ โโโ __init__.py
|
|
269
|
+
โ โ โโโ projection.py # Dynamic UTM zone deduction & CRS reprojector
|
|
270
|
+
โ โ โโโ geometry.py # Topology validation, metric calculations, relations & buffer
|
|
271
|
+
โ โโโ models.py # Pydantic v2 schemas (unit / target_unit alias support)
|
|
272
|
+
โโโ tests/
|
|
273
|
+
โโโ __init__.py
|
|
274
|
+
โโโ test_geometry.py # Core geometry, projection, bowtie repair, equator tests
|
|
275
|
+
โโโ test_server.py # FastMCP tool invocation, input tolerance, & error tests
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Why Zero GDAL?
|
|
279
|
+
Traditional GIS tools bundle GDAL, PROJ C libraries, and Python wrappers (Fiona, GeoPandas) that create:
|
|
280
|
+
- Massive container image sizes (> 1.5 GB).
|
|
281
|
+
- Platform-dependent C++ build failures and DLL hell on Windows / macOS.
|
|
282
|
+
- Potential process segmentation faults during concurrent execution.
|
|
283
|
+
|
|
284
|
+
`geospatial-mcp` uses pure **`shapely>=2.0`** (which packages standalone GEOS C libraries as pre-compiled wheels) and **`pyproj`**. It installs in seconds via `pip` or `uvx` with zero compilation steps.
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## ๐ก๏ธ Fault Tolerance & Defensive Engineering
|
|
289
|
+
|
|
290
|
+
1. **Unclosed Linear Rings**: In RFC 7946, polygon exterior and interior rings must be closed (first coordinate identical to last coordinate). If an unclosed ring is passed, the engine flags it (`is_valid=False`), describes the error, and automatically closes the ring during repair.
|
|
291
|
+
2. **Malformed JSON / Syntax Errors**: Broken JSON strings, unclosed brackets, or invalid geometry types return structured, friendly JSON error responses with diagnostic hints rather than crashing the MCP server.
|
|
292
|
+
3. **Hemisphere & Longitude Aware**: Automatically maps any coordinate on Earth into its corresponding UTM Zone (1 through 60) and applies EPSG:326xx for $lat \ge 0^\circ$ (North) and EPSG:327xx for $lat < 0^\circ$ (South), with seamless handling for geometries crossing the equator.
|
|
293
|
+
4. **Dual Access Output Models**: All output objects implement `BaseResultModel`, permitting both object attribute access (`res.area`) and dictionary key access (`res['area']`).
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## ๐งช Testing & Verification
|
|
298
|
+
|
|
299
|
+
The suite includes 20 comprehensive unit tests covering critical GIS boundary scenarios:
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
# Run tests with pytest
|
|
303
|
+
pytest -v
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Verified Scenarios
|
|
307
|
+
- [x] **Bow-tie Self-Intersection Repair**: Resolves figure-8 polygons into valid OGC MultiPolygons.
|
|
308
|
+
- [x] **Unclosed Ring Auto-Healing**: Detects unclosed coordinate arrays and normalizes them.
|
|
309
|
+
- [x] **Equator Crossing & Multi-Longitude UTM**: Precision reprojection across latitude $0^\circ$ and longitudinal zones (Shanghai UTM 51N, Rio de Janeiro UTM 23S).
|
|
310
|
+
- [x] **Unit Conversions**: Metric validation across square meters, square kilometers, and hectares.
|
|
311
|
+
- [x] **Spatial Predicates**: Accurate metric distance on separation, intersection surface area on overlap.
|
|
312
|
+
- [x] **Parameter Alias Compatibility**: Identical resolution whether calling with `unit` or `target_unit`.
|
|
313
|
+
- [x] **Stdio Protocol Handshake**: Seamless MCP `initialize` JSON-RPC handshake verification.
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## ๐ License
|
|
318
|
+
|
|
319
|
+
MIT License. Free for open-source and commercial use.
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# ๐ Geospatial MCP Server
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/geospatial-mcp/)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://www.python.org/)
|
|
6
|
+
[](https://modelcontextprotocol.io/)
|
|
7
|
+
[](https://pytest.org)
|
|
8
|
+
|
|
9
|
+
A zero-dependency (no GDAL/C++ build hell) Model Context Protocol (MCP) server that equips AI Agents (Claude, Cursor, Antigravity) with deterministic spatial analysis, GeoJSON auto-repair, and accurate projected metric calculations.
|
|
10
|
+
|
|
11
|
+
<!-- Instant Visual Value: Topology Auto-Healing Comparison -->
|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
<!-- Terminal interaction demo -->
|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## โก The Problem It Solves
|
|
20
|
+
|
|
21
|
+
Large Language Models (LLMs) are notoriously bad at spatial reasoning and Cartesian mathematics:
|
|
22
|
+
- **Geometry Hallucination**: Self-intersecting polygons (bow-tie / figure-8 errors) and unclosed coordinate rings crash downstream GIS pipelines and map renderers.
|
|
23
|
+
- **Scale Distortion**: Treating spherical WGS84 geographic coordinates (latitude/longitude degrees) as flat Euclidean planes results in catastrophic area and perimeter errors (often 30% to 500% off depending on latitude).
|
|
24
|
+
- **Topology Blindness**: Inability to deterministically resolve point-in-polygon containment, spatial intersection surfaces, separation distances, and metric buffering.
|
|
25
|
+
|
|
26
|
+
`geospatial-mcp` bridges this gap by providing a lightweight, high-performance, GEOS-backed C-extension engine without heavy GIS infrastructure overhead.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## ๐ Quickstart (30 Seconds)
|
|
31
|
+
|
|
32
|
+
### Option 1: Run with UVX (Recommended, Zero Install)
|
|
33
|
+
No local repository clone or manual environment setup required:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"mcpServers": {
|
|
38
|
+
"geospatial": {
|
|
39
|
+
"command": "uvx",
|
|
40
|
+
"args": ["geospatial-mcp"]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Option 2: Local Installation & Development
|
|
47
|
+
|
|
48
|
+
1. **Clone and create virtual environment**:
|
|
49
|
+
```bash
|
|
50
|
+
git clone https://github.com/your-org/geospatial-mcp.git
|
|
51
|
+
cd geospatial-mcp
|
|
52
|
+
|
|
53
|
+
python -m venv .venv
|
|
54
|
+
# Windows (PowerShell):
|
|
55
|
+
.\.venv\Scripts\Activate.ps1
|
|
56
|
+
# Linux / macOS:
|
|
57
|
+
source .venv/bin/activate
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
2. **Install package in editable mode**:
|
|
61
|
+
```bash
|
|
62
|
+
pip install -e .
|
|
63
|
+
pip install pytest
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
3. **Configure MCP Client (Claude Desktop / Cursor / Antigravity)**:
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"mcpServers": {
|
|
70
|
+
"geospatial": {
|
|
71
|
+
"command": "python",
|
|
72
|
+
"args": ["-m", "geospatial_mcp.server"],
|
|
73
|
+
"cwd": "/path/to/geospatial-mcp"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## ๐ ๏ธ Tools Reference
|
|
82
|
+
|
|
83
|
+
All tools are exposed via standard MCP transports (stdio) and feature robust input tolerance: they natively accept standard GeoJSON strings, raw coordinate arrays, GeoJSON dictionaries, Feature / FeatureCollection objects, and Markdown-wrapped JSON (` ```json `) without crashing.
|
|
84
|
+
|
|
85
|
+
### 1. `validate_and_repair_geometry`
|
|
86
|
+
Validates topological compliance against OGC Simple Features standards. If topological defects exist (e.g., self-intersecting bow-tie polygons or unclosed linear rings), it extracts the exact cause of failure and automatically heals the geometry into a compliant GeoJSON object with RFC 7946 normalized ring winding.
|
|
87
|
+
|
|
88
|
+
- **Parameters**:
|
|
89
|
+
- `geojson` (`str | dict | list`, required): GeoJSON geometry, Feature, FeatureCollection, or raw coordinate array.
|
|
90
|
+
- **Returns**:
|
|
91
|
+
- `is_valid` (`bool`): Whether the original geometry was topologically valid.
|
|
92
|
+
- `error_reason` (`str | null`): Exact topological defect description if invalid (e.g. `Self-intersection[1 1]` or `Points of LinearRing do not form a closed linestring`).
|
|
93
|
+
- `repaired_geojson` (`dict | null`): Compliant, self-healed GeoJSON geometry.
|
|
94
|
+
|
|
95
|
+
#### Example Call
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"name": "validate_and_repair_geometry",
|
|
99
|
+
"arguments": {
|
|
100
|
+
"geojson": {
|
|
101
|
+
"type": "Polygon",
|
|
102
|
+
"coordinates": [[[0, 0], [2, 2], [2, 0], [0, 2], [0, 0]]]
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
#### Response
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"is_valid": false,
|
|
111
|
+
"error_reason": "Self-intersection[1 1]",
|
|
112
|
+
"repaired_geojson": {
|
|
113
|
+
"type": "MultiPolygon",
|
|
114
|
+
"coordinates": [
|
|
115
|
+
[[[0.0, 0.0], [1.0, 1.0], [0.0, 2.0], [0.0, 0.0]]],
|
|
116
|
+
[[[1.0, 1.0], [2.0, 0.0], [2.0, 2.0], [1.0, 1.0]]]
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
### 2. `calculate_accurate_metrics`
|
|
125
|
+
Eliminates spherical degree distortion by deriving the optimal local UTM projection zone (`EPSG:32601-32660` for Northern hemisphere or `EPSG:32701-32760` for Southern hemisphere) from the polygon centroid, projecting to a planar Cartesian coordinate system via PyProj, and calculating distortion-free surface area and perimeter.
|
|
126
|
+
|
|
127
|
+
- **Parameters**:
|
|
128
|
+
- `geojson` (`str | dict | list`, required): Polygon or MultiPolygon GeoJSON.
|
|
129
|
+
- `unit` / `target_unit` (`str`, optional, default `"sq_meters"`): Desired unit (`"sq_meters"`, `"sq_kilometers"`, or `"hectares"`). Fully backwards-compatible with dual alias parsing.
|
|
130
|
+
- **Returns**:
|
|
131
|
+
- `area` (`float`): Planar area in the requested unit.
|
|
132
|
+
- `perimeter_meters` (`float`): Total boundary length in meters.
|
|
133
|
+
- `unit` (`str`): Target unit used.
|
|
134
|
+
- `projected_epsg` (`int`): EPSG integer code of the metric UTM projection applied (e.g. `32651`).
|
|
135
|
+
|
|
136
|
+
#### Example Call
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"name": "calculate_accurate_metrics",
|
|
140
|
+
"arguments": {
|
|
141
|
+
"geojson": {
|
|
142
|
+
"type": "Polygon",
|
|
143
|
+
"coordinates": [[[121.47, 31.23], [121.48, 31.23], [121.48, 31.24], [121.47, 31.24], [121.47, 31.23]]]
|
|
144
|
+
},
|
|
145
|
+
"unit": "hectares"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
#### Response
|
|
150
|
+
```json
|
|
151
|
+
{
|
|
152
|
+
"area": 105.74,
|
|
153
|
+
"perimeter_meters": 4118.62,
|
|
154
|
+
"unit": "hectares",
|
|
155
|
+
"projected_epsg": 32651
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
### 3. `check_spatial_relationship`
|
|
162
|
+
Evaluates topological relationship predicates between two geometries (`contains`, `within`, `intersects`, `disjoint`). Automatically returns the shared intersection area in $m^2$ if overlapping, or the minimum metric separation distance in meters if disjoint.
|
|
163
|
+
|
|
164
|
+
- **Parameters**:
|
|
165
|
+
- `geom_a` (`str | dict | list`, required): First spatial object.
|
|
166
|
+
- `geom_b` (`str | dict | list`, required): Second spatial object.
|
|
167
|
+
- `predicate` (`str`, optional, default `"intersects"`): Predicate to evaluate (`"contains"`, `"within"`, `"intersects"`, `"disjoint"`).
|
|
168
|
+
- **Returns**:
|
|
169
|
+
- `result` (`bool`): Boolean verdict for the requested predicate.
|
|
170
|
+
- `details` (`dict`): Quantitative measurements including `intersection_area_sq_meters`, `distance_meters`, and `projected_epsg`.
|
|
171
|
+
|
|
172
|
+
#### Example Call
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"name": "check_spatial_relationship",
|
|
176
|
+
"arguments": {
|
|
177
|
+
"geom_a": {"type": "Polygon", "coordinates": [[[0, 0], [2, 0], [2, 2], [0, 2], [0, 0]]]},
|
|
178
|
+
"geom_b": {"type": "Polygon", "coordinates": [[[1, 1], [3, 1], [3, 3], [1, 3], [1, 1]]]},
|
|
179
|
+
"predicate": "intersects"
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
#### Response
|
|
184
|
+
```json
|
|
185
|
+
{
|
|
186
|
+
"result": true,
|
|
187
|
+
"details": {
|
|
188
|
+
"predicate": "intersects",
|
|
189
|
+
"projected_epsg": 32631,
|
|
190
|
+
"intersection_area_sq_meters": 12285149372.45,
|
|
191
|
+
"distance_meters": 0.0
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
### 4. `generate_buffer`
|
|
199
|
+
Constructs an accurate metric buffer (expansion or erosion) in the local UTM coordinate system, auto-heals any resulting self-intersections, and projects back to standard EPSG:4326 WGS84 GeoJSON.
|
|
200
|
+
|
|
201
|
+
- **Parameters**:
|
|
202
|
+
- `geojson` (`str | dict | list`, required): Input geometry.
|
|
203
|
+
- `distance_meters` (`float`, required): Buffer radius in meters (positive expands, negative erodes).
|
|
204
|
+
- `quad_segs` (`int`, optional, default `8`): Quadrant segments for circular arc approximation.
|
|
205
|
+
- **Returns**:
|
|
206
|
+
- `buffered_geojson` (`dict | null`): Normalized RFC 7946 GeoJSON dictionary in EPSG:4326.
|
|
207
|
+
|
|
208
|
+
#### Example Call
|
|
209
|
+
```json
|
|
210
|
+
{
|
|
211
|
+
"name": "generate_buffer",
|
|
212
|
+
"arguments": {
|
|
213
|
+
"geojson": {"type": "Point", "coordinates": [121.5, 31.2]},
|
|
214
|
+
"distance_meters": 500.0,
|
|
215
|
+
"quad_segs": 16
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## ๐๏ธ Architecture & Project Structure
|
|
223
|
+
|
|
224
|
+
`geospatial-mcp` is designed to be completely decoupled from bulky C/C++ GIS frameworks:
|
|
225
|
+
|
|
226
|
+
```
|
|
227
|
+
geospatial-mcp/
|
|
228
|
+
โโโ pyproject.toml # Hatchling packaging and dependency constraints
|
|
229
|
+
โโโ README.md # Complete architectural guide and tool references
|
|
230
|
+
โโโ src/
|
|
231
|
+
โ โโโ geospatial_mcp/
|
|
232
|
+
โ โโโ __init__.py # Package root
|
|
233
|
+
โ โโโ server.py # FastMCP service instance & tool entrypoints
|
|
234
|
+
โ โโโ core/ # Pure C-extension geometry engine
|
|
235
|
+
โ โ โโโ __init__.py
|
|
236
|
+
โ โ โโโ projection.py # Dynamic UTM zone deduction & CRS reprojector
|
|
237
|
+
โ โ โโโ geometry.py # Topology validation, metric calculations, relations & buffer
|
|
238
|
+
โ โโโ models.py # Pydantic v2 schemas (unit / target_unit alias support)
|
|
239
|
+
โโโ tests/
|
|
240
|
+
โโโ __init__.py
|
|
241
|
+
โโโ test_geometry.py # Core geometry, projection, bowtie repair, equator tests
|
|
242
|
+
โโโ test_server.py # FastMCP tool invocation, input tolerance, & error tests
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Why Zero GDAL?
|
|
246
|
+
Traditional GIS tools bundle GDAL, PROJ C libraries, and Python wrappers (Fiona, GeoPandas) that create:
|
|
247
|
+
- Massive container image sizes (> 1.5 GB).
|
|
248
|
+
- Platform-dependent C++ build failures and DLL hell on Windows / macOS.
|
|
249
|
+
- Potential process segmentation faults during concurrent execution.
|
|
250
|
+
|
|
251
|
+
`geospatial-mcp` uses pure **`shapely>=2.0`** (which packages standalone GEOS C libraries as pre-compiled wheels) and **`pyproj`**. It installs in seconds via `pip` or `uvx` with zero compilation steps.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## ๐ก๏ธ Fault Tolerance & Defensive Engineering
|
|
256
|
+
|
|
257
|
+
1. **Unclosed Linear Rings**: In RFC 7946, polygon exterior and interior rings must be closed (first coordinate identical to last coordinate). If an unclosed ring is passed, the engine flags it (`is_valid=False`), describes the error, and automatically closes the ring during repair.
|
|
258
|
+
2. **Malformed JSON / Syntax Errors**: Broken JSON strings, unclosed brackets, or invalid geometry types return structured, friendly JSON error responses with diagnostic hints rather than crashing the MCP server.
|
|
259
|
+
3. **Hemisphere & Longitude Aware**: Automatically maps any coordinate on Earth into its corresponding UTM Zone (1 through 60) and applies EPSG:326xx for $lat \ge 0^\circ$ (North) and EPSG:327xx for $lat < 0^\circ$ (South), with seamless handling for geometries crossing the equator.
|
|
260
|
+
4. **Dual Access Output Models**: All output objects implement `BaseResultModel`, permitting both object attribute access (`res.area`) and dictionary key access (`res['area']`).
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## ๐งช Testing & Verification
|
|
265
|
+
|
|
266
|
+
The suite includes 20 comprehensive unit tests covering critical GIS boundary scenarios:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
# Run tests with pytest
|
|
270
|
+
pytest -v
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Verified Scenarios
|
|
274
|
+
- [x] **Bow-tie Self-Intersection Repair**: Resolves figure-8 polygons into valid OGC MultiPolygons.
|
|
275
|
+
- [x] **Unclosed Ring Auto-Healing**: Detects unclosed coordinate arrays and normalizes them.
|
|
276
|
+
- [x] **Equator Crossing & Multi-Longitude UTM**: Precision reprojection across latitude $0^\circ$ and longitudinal zones (Shanghai UTM 51N, Rio de Janeiro UTM 23S).
|
|
277
|
+
- [x] **Unit Conversions**: Metric validation across square meters, square kilometers, and hectares.
|
|
278
|
+
- [x] **Spatial Predicates**: Accurate metric distance on separation, intersection surface area on overlap.
|
|
279
|
+
- [x] **Parameter Alias Compatibility**: Identical resolution whether calling with `unit` or `target_unit`.
|
|
280
|
+
- [x] **Stdio Protocol Handshake**: Seamless MCP `initialize` JSON-RPC handshake verification.
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## ๐ License
|
|
285
|
+
|
|
286
|
+
MIT License. Free for open-source and commercial use.
|
|
Binary file
|
|
Binary file
|