关于 GeoSpark
GeoSpark 为 LLM 提供可验证的地理空间计算工具,适用于需要空间分析的开发者和 Agent。它用几何引擎计算距离、拓扑和坐标系转换,并为结果附上来源、单位与可复现记录。
主要功能
- 01计算测地距离与几何关系
- 02转换坐标参考系
- 03提供地理编码、路线与等时圈
- 04查询 Overture Maps 与环境数据
- 05输出带来源和单位的结构化结果
适用场景
- 01核验两地距离或点面关系
- 02按出行时间分析可达范围
- 03比较地点并筛选设施选址
- 04处理本地地理数据图层
MCP 相关
提供本地及托管 MCP 服务,向客户端开放只读空间计算、地点数据和分析工具;本地服务还支持本地文件图层工具。
配置
远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。
https://geospark.terrascout.app/mcpclaude mcp add --transport http maz2580-geospark https://geospark.terrascout.app/mcp{"mcpServers":{"maz2580-geospark":{"type":"http","url":"https://geospark.terrascout.app/mcp"}}}在 Cursor 中打开{"servers":{"maz2580-geospark":{"type":"http","url":"https://geospark.terrascout.app/mcp"}}}在 VS Code 中打开{"mcpServers":{"maz2580-geospark":{"type":"http","url":"https://geospark.terrascout.app/mcp"}}}{"mcpServers":{"maz2580-geospark":{"serverUrl":"https://geospark.terrascout.app/mcp"}}}命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。
工具
详细介绍
GeoSpark
为任意大模型提供可验证的空间推理能力。
距离、拓扑和 CRS 由几何引擎计算,而非由语言模型猜测。开源,可在任何环境中运行,适用于任何能够调用工具的模型。
向大模型提问 “埃菲尔铁塔距离卢浮宫有多远?” 或 “这个点位于那个多边形内部吗?” 答案取决于你选择了哪个模型。在我们测试的 9 个模型中,小型开放权重模型(3.8B–9B)在数值距离问题上的错误率约为 95%。前沿 API 的正确率则从 25%(Gemini 2.5 Pro)到 95%(GPT-5.4)不等。
GeoSpark 为模型提供能够计算答案的工具:在 WGS84 椭球上计算大地距离、精确拓扑关系、CRS 转换、地理编码、高程和实时环境数据。借助这些工具,所有成功发出有效工具调用的模型,对同一批问题的正确率都达到 76–96%。 因此,在自己的 CPU 上运行的 7B 模型也能达到付费前沿 API 的表现,而且每个答案都附带可供核验的确定性追踪记录。
试用
无需安装:网站。 geospark.terrascout.app 可让你提问、绘制可达区域,并探索任意地点周边的 Overture Maps 数据。每个结果都会显示其来源、注意事项和单位,并可下载为 GeoJSON。
无需安装:托管 API(开放使用,无需密钥;API 参考)
curl -X POST https://geospark.terrascout.app/api/v1/distance \
-H "Content-Type: application/json" \
-d '{"lat_a": 48.8584, "lon_a": 2.2945, "lat_b": 48.8606, "lon_b": 2.3376}'
# {"distance_m": 3172.01, "distance_km": 3.172, "note": "Geodesic distance on WGS84 ellipsoid"}
在 Claude 或任何 MCP 客户端中使用,无需安装:托管 MCP 服务
使用此 URL 添加自定义连接器(在 Claude 中:Settings → Connectors → Add custom connector):
https://geospark.terrascout.app/mcp
该服务开放使用,无需密钥,提供与下方本地服务相同的 15 个工具,速率限制也与托管 API 相同。它绝不会读取文件;如需处理自己的文件,请在本地运行服务。
在 Claude Desktop 或任何运行于你电脑上的 MCP 客户端中使用
pip install "geospark-ai[mcp]"
将 GeoSpark 添加到 MCP 客户端的配置中。对于 Claude Desktop,在 macOS 上,配置文件为
~/Library/Application Support/Claude/claude_desktop_config.json;在 Windows 上,则为
%APPDATA%\Claude\claude_desktop_config.json:
{ "mcpServers": { "geospark": { "command": "geospark-mcp" } } }
这会为模型提供 15 个只读工具。计算: calculate_distance(计算地点名称之间的距离,服务会对地点名称进行地理编码;也可使用坐标),
check_spatial_relationship 和 spatial_operation(缓冲区、面积、长度、质心、凸包)。
地点和数据: geocode、reverse_geocode、get_elevation、route、get_weather、
get_air_quality、get_active_fires 和 overture_features。分析: isochrone、travel_time_matrix、location_report 和 select_site。
结果以结构化内容返回,并带有输出架构,通常为 GeoJSON。结果包含来源信息(来源、许可、署名)、单位和警告。若 GeoSpark 无法可靠地回答(几何无效、地点未知或数据源不可用),工具会返回说明原因的错误,以便模型告知用户,而不是自行估算。该服务使用 MCP SDK 2.x(协议 2026-07-28),也兼容使用较早协议版本的客户端。
作为 Python 库使用
pip install geospark-ai # core
pip install "geospark-ai[api,mcp]" # + REST server and MCP server
pip install "geospark-ai[all]" # everything
使用 Docker 运行自己的服务
docker run -p 8000:8000 ghcr.io/maz2580/geospark:latest
# then open http://localhost:8000
这会以开发模式运行,运维路由处于开放状态:在自己的机器上运行时没有问题。若要搭建公开服务,请添加 -e GEOSPARK_ENV=production -e GEOSPARK_ADMIN_TOKEN=<secret>(参见
自托管)。
功能
| 空间引擎 | 测地距离、面积和长度(基于 WGS84 的 pyproj);通过 Shapely 进行拓扑检查(contains、within、intersects、touches、crosses、overlaps、disjoint);保形缓冲区;并集、交集、差集、质心、凸包;转换每个输入几何的 CRS |
| 地点和地形 | 地理编码和反向地理编码(OpenStreetMap Nominatim,遵守其使用政策:每秒 ≤ 1 个请求并缓存);Copernicus GLO-30 高程数据,无法获取时回退到 Open Elevation,并识别垂直基准(NAVD88 / EGM96 / WGS84);路线规划(OSRM);等时圈(步行、骑行或驾车 N 分钟内可到达哪些地方)和行程时间矩阵(Valhalla) |
| 实时数据 | Overture Maps 地点、建筑、道路和行政区(无需密钥,通过 DuckDB 获取);天气和预报(Open-Meteo,无需密钥);空气质量(OpenAQ v3,免费密钥);活跃火点(NASA FIRMS,免费密钥);卫星影像搜索(STAC) |
| Agent | GeoAgent(根据纯英文目标进行多步分析)、SpatialReport(地点档案:便利设施、可达性、高程、天气)、SiteSelector(多标准地点排名),以及一个将目标分派给合适 Agent 的协调器 |
| 接口 | Python 库、CLI(geospark …)、REST API(52 项已记录操作,参考文档见 /docs)、MCP 服务(geospark-mcp)、网页体验环境 |
| 记忆和工作流 | 包含事实、事件和矛盾检测的空间记忆;分层上下文存储;Flows(DAG 工作流,可根据自然语言目标构建) |
| GeoSpark Bench | 535 个可复现问题,涵盖拓扑、距离、多步推理、变化和多模态任务,并提供计算得出的真实值 |
**已规划,尚未实现:**坡度、视域、栅格波段运算和变化检测。这些操作已存在于协议中,但目前会返回明确的“尚未实现”错误,而不是给出答案。请参阅路线图。
快速开始
真实几何数据
from geospark.engine.spatial_reasoner import SpatialReasoner
park = {"type": "Polygon", "coordinates": [[[2.29, 48.85], [2.30, 48.85], [2.30, 48.86], [2.29, 48.86], [2.29, 48.85]]]}
point = {"type": "Point", "coordinates": [2.295, 48.855]} # GeoJSON order: [lon, lat]
SpatialReasoner.check_relationship(park, point, "contains") # True
SpatialReasoner.calculate_distance(
{"type": "Point", "coordinates": [2.2945, 48.8584]}, # Eiffel Tower
{"type": "Point", "coordinates": [2.3376, 48.8606]}, # Louvre
) # 3172.01 (metres, geodesic)
通过引擎查询
from geospark import Engine
from geospark.protocol import SpatialOperation, SpatialQuery
engine = Engine(tools=["geocoder", "terrain"])
place = engine.execute(SpatialQuery(operation=SpatialOperation.GEOCODE,
metadata={"query": "Federation Square, Melbourne"}))
print(place.features[0].geometry) # {'type': 'Point', 'coordinates': [144.969..., -37.817...]}
# Inputs in another CRS are transformed to WGS84 first, including the second geometry
buffered = engine.execute(SpatialQuery(operation=SpatialOperation.BUFFER,
geometry={"type": "Point", "coordinates": [2.2945, 48.8584]},
radius_m=100))
print(buffered.features[0].properties["area_sq_m"]) # ≈ 31,365
每个答案都可核验,并可在 QGIS 中打开
result = engine.execute(SpatialQuery(operation=SpatialOperation.GEOCODE, metadata={"query": "Springfield"}))
result.status # "ok" | "partial" | "cannot_answer"
result.warnings # ["Ambiguous place name: 'Springfield, …, Illinois…'; '…Massachusetts…' …"]
result.provenance # source, licence (ODbL-1.0), attribution, retrieved_at, method
result.spatial_context.units # {"distance_m": "metre", ...}
import json
json.dump(result.to_geojson(), open("springfield.geojson", "w")) # RFC 7946, drag into QGIS
GeoSpark 宁可拒绝回答,也不会猜测。对于自相交多边形、超出范围的坐标或很可能发生的 [lat, lon] 顺序颠倒,GeoSpark 会返回 cannot_answer 和原因,而不是给出看似肯定但错误的数字。使用 API 时,添加 ?format=geojson(或 Accept: application/geo+json)。使用 CLI 时,在 geocode、elevation、query 或 report 命令后添加 --geojson out.geojson。地点报告会变成地图图层:包含该地点及所有附近的便利设施,并标注距离。
无需 API 密钥的开放地图数据:Overture Maps
from geospark.tools.overture.client import query # pip install "geospark-ai[overture]"
query("divisions", 48.8606, 2.3376) # the Louvre is in: France › Île-de-France › Paris › Paris 1er Arrondissement
query("places", -37.8180, 144.9691, radius_m=200, category="cafe") # nearest first, geodesic metres
query("buildings", -37.8180, 144.9691, radius_m=150) # footprints, height, geodesic footprint area
query("transportation", -37.8180, 144.9691, radius_m=150) # road/path segments, geodesic length
GeoSpark 使用 DuckDB 直接从 S3 查询 Overture Maps GeoParquet。它利用该版本的 STAC 索引仅读取覆盖你所在区域的文件,因此无需下载,也无需密钥。结果会报告 Overture 版本以及返回记录的许可证。同一数据也可通过 CLI(geospark overture places -37.818 144.969 --category cafe)、REST API(POST /api/v1/overture/{theme})和 MCP 工具 overture_features 获取。
你自己的数据
pip install "geospark-ai[io]"
geospark layer info suburbs.gpkg # features, fields, CRS, geodesic area
geospark layer count-within crashes.csv suburbs.gpkg --geojson counts.geojson
geospark layer nearest schools.shp hospitals.geojson
geospark layer intersect parcels.gpkg flood_zones.geojson
支持的格式:GeoJSON、GeoPackage、Shapefile(或 .zip)、GeoParquet、包含经度/纬度列的 CSV、FlatGeobuf、KML、GML、File Geodatabase。绝不会猜测 CRS:
- 投影文件(例如 MGA zone 55)会重新投影,并报告其原始 CRS;
- 没有
.prj文件的 Shapefile 会被拒绝,除非你传入--crs EPSG:28355; - 无效形状会被拒绝,除非你传入
--repair。
结果会附带每个文件的 SHA-256,因此分析可以针对完全相同的数据进行复现。在 Python 中,使用 geospark.io.layers(load_layer、count_within、nearest、intersect)。本地 MCP 服务(geospark-mcp)提供相同的 layer_* 工具;托管的 /mcp 端点不会读取文件。
可复现分析
geospark record report "Federation Square, Melbourne" --out fedsq.json
geospark replay fedsq.json
# REPRODUCED: Same result as recorded.
# ... or DATA CHANGED: openstreetmap-overpass returned different data for the same request (step 6 ...)
# ... or CODE CHANGED: versions changed: {'shapely': ('2.0.0', '2.2.0')}
一次运行记录会列出:
- 环境:GeoSpark、Python、pyproj/PROJ 和 Shapely/GEOS 的版本
- 每一项数据请求(Nominatim、Overpass、Overture、Valhalla、天气、海拔)和每一次引擎 计算,并分别记录其输入和输出的指纹
- 最终结果的指纹
geospark replay 会重新运行分析,并说明是否复现成功。如果没有,它会指出哪个数据源返回了不同的数据,或代码、库版本发生了变化。在 Python 中,使用
geospark.runs.record_run(...) 和 replay(...)。
自然语言问题
result = engine.ask("How far is the Eiffel Tower from Big Ben?")
print(result.spatial_context.summary)
print(result.sources) # the model, then each tool it called
ask 会优先使用本地 Ollama 模型;如果设置了 OPENROUTER_API_KEY,则会回退到免费的 OpenRouter 模型。
Agent
from geospark.agents import AgentCoordinator, GeoAgent, SiteSelector, SpatialReport
GeoAgent().run("Find all hospitals within 2km of the Eiffel Tower").findings["nearby"]
report = SpatialReport().analyze("Federation Square, Melbourne", radius_m=1000)
print(report.weather, report.accessibility, report.data_gaps)
best = SiteSelector().find(within="Zurich", near=["hospital", "school"], facility_type="pharmacy").best
AgentCoordinator().run("Find the best location for a cafe in Melbourne CBD near schools")
Agent 使用大模型读取目标。即使没有大模型,基于规则的解析器也能处理类似上文的请求:包含多个单词的地点名称、类似 "2km" 的半径,以及设施类型。
记忆、上下文和流程
from geospark.memory import SpatialIntelligence
intel = SpatialIntelligence()
intel.remember_fact("Eiffel Tower is at 48.8584 N, 2.2945 E", source="user")
intel.remember_episode("PM2.5 was 120 in Delhi", importance=0.8, source="tool:air_quality")
intel.recall("Where is the Eiffel Tower?", limit=5) # semantic with Ollama embeddings, keyword match without
intel.find_contradictions()
from geospark.flows import ChatFlowSession, FlowRunner, make_ollama_chat_fn
session = ChatFlowSession(llm_fn=make_ollama_chat_fn("qwen2.5:7b"))
result = session.run("Geocode Valencia, Spain then check its elevation")
if result.flow:
FlowRunner().run(result.flow) # a validated DAG, built one tool call at a time
命令行
geospark distance -37.8136 144.9631 -33.8688 151.2093 # Melbourne → Sydney: 713.86 km
geospark check contains '<geojson A>' '<geojson B>'
geospark geocode "Tokyo Tower, Japan"
geospark elevation -37.8177 144.9691
geospark isochrone -37.8180 144.9691 --minutes 5,10,15 --mode walk
geospark ask "Is Tokyo closer to Seoul or Beijing?"
geospark agent "Find all parks within 2km of Big Ben"
geospark report "Federation Square, Melbourne"
geospark site-select --within "Paris" --near "school,hospital" --facility pharmacy
geospark multi-agent "Analyze Federation Square" --stream
geospark data weather "Melbourne, Australia"
geospark data air-quality "Delhi" # needs OPENAQ_API_KEY
geospark data fires "Amazon Rainforest" # needs FIRMS_MAP_KEY
geospark flow list
geospark flow build "Geocode Valencia then check its elevation" --run
geospark info # versions and optional extras
基准测试结果
GeoSpark Bench v2 在三种协议下测试了 9 个模型:直接提问、逐步推理(CoT),以及附带 GeoSpark MCP 工具的提问。模型包括五个开放权重 ≤9B 模型(Qwen 2.5 7B、Llama 3.1 8B、Gemma 2 9B、Mistral 7B、Phi-3.5 3.8B)、gpt-oss 20B,以及三个前沿 API(GPT-5.4、Claude Sonnet 4.5、Gemini 2.5 Pro)。置信区间为 Wilson 95%,每次调用的费用账目(约 $41)已提交到代码库。完整分析收录于一篇目前正在同行评审的论文中。
数值距离(distance_absolute,每个模型 n = 80;正确 = 与 pyproj.Geod.inv() 测得的测地线距离相差不超过 10%):
| 模型 | 层级 | 纯大模型 | 逐步推理 | + GeoSpark 工具 |
|---|---|---|---|---|
| Qwen 2.5 7B | open ≤9B | 5.0% | 20.0% | 76.2% |
| Llama 3.1 8B | open ≤9B | 5.0% | 21.2% | 7.5% ᵃ |
| Gemma 2 9B | open ≤9B | 37.5% ᵇ | 6.2% | — |
| Mistral 7B | open ≤9B | 2.5% | 2.5% | 0.0% ᵃ |
| Phi-3.5 3.8B | open ≤9B | 1.2% | 6.2% | — |
| gpt-oss 20B | open mid | 93.8% | 93.8% | 91.2% |
| GPT-5.4 | frontier | 95.0% | 90.0% | 95.0% |
| Claude Sonnet 4.5 | frontier | 73.8% | 92.5% | 96.2% |
| Gemini 2.5 Pro | frontier | 25.0% | 55.0% | 85.0% |
ᵃ 工具可用,但未被正确使用:Llama 混淆了返回坐标中的 km 和 m,Mistral 则用纯文本作答。ᵇ Gemma 答对的题目是广为人知的远距离地点对,体现的是对训练数据的记忆,而非计算。— 表示未使用工具进行评估。
我们的发现
- 工具能拉平差距。 纯大模型的得分范围为 1–95%。使用工具后,所有能有效调用工具的模型得分都在 76% 到 96% 之间。Qwen 提升了 71 个百分点;GPT-5.4 没有提升,因为它原本就达到 95%。
- 汇总得分会掩盖偏差。 Qwen 在拓扑上的 56% 得分,来自它总是回答“是”:它在
intersects上得分 93%(数据中真值占多数),在disjoint上则只有 3%。GPT-5.4 在touches上得分为 0%。使用 GeoSpark 后,每种谓词都达到 100%,因为这些结果由引擎计算。 - 逐步推理提示并非放之四海皆准的解决方案。 它对某些模型有帮助,对另一些则有害。
- 知识类问题不需要工具。 “2015 年到 2023 年间,亚马逊雨林的森林面积减少了吗?”这类问题在各个模型规模下都能在不使用工具的情况下得到良好回答。
运行 python -m geospark.bench run,亲自重新执行基准测试。数据集可通过 python -m geospark.bench.generate_datasets 完全一致地重新生成;数据来源请参阅
数据集 README,每张表格请参阅
基准测试报告。
自托管
git clone https://github.com/Maz2580/geospark && cd geospark
cp .env.example .env # set GEOSPARK_ENV=production and GEOSPARK_ADMIN_TOKEN
docker compose -f docker-compose.prod.yml up -d
托管 API 的设计目标是在无需密钥的情况下保持开放,同时设置安全防护:
- 管理员令牌保护运维路由(审计、用量、流程存储)。LLM 网关需要该令牌,或可撤销的合作伙伴密钥。
- 公共调用者只能选择本地模型或免费模型,因此无人能消耗你的付费 API 额度。
- 每位访客都会获得一个私有、大小受限且自动过期的沙盒,用于记忆和上下文演示。
- 速率限制使用真实客户端 IP,且 LLM 任务设有并发上限。
- 输入均有长度限制,审计日志中存储的是经过哈希处理的 IP。
所有状态都存储在 GEOSPARK_DATA_DIR 下。要解除 Nominatim 的公共服务限制,可将
GEOSPARK_NOMINATIM_URL 指向你自己的服务。详情请参阅
deploy/DEPLOY.md 和
.env.example。
路线图
- v0.7(已发布):可验证的答案。
- 每个结果都包含其来源、许可、时间戳、CRS 和单位。
- 无法回答时会明确告知,而不是猜测。
- 数据质量标记,例如地理编码器的置信度和 DEM 精度。
- 可直接加载到 QGIS 中的 GeoJSON FeatureCollection。
- MCP 服务已更新至 2026 版规范,支持结构化 GeoJSON 输出和托管 HTTP 端点。
- v0.8(已发布):更多数据,更多分析。
- 通过 DuckDB 使用 Overture Maps 的建筑物、地点和道路数据,无需密钥。
- 真实的等时圈和行程时间矩阵。
- 支持导入自己的 GeoJSON、GeoPackage 或 GeoParquet。
- 可复现的运行清单。
- 重新设计的网站,每个结果都附带 Proof 面板(来源、注意事项、单位、GeoJSON 下载),且 Ask 的回答绝不会使用模型凭记忆写出的坐标。
- **持续进行:**外部基准测试(GPSBench、GeoBenchX)、QGIS 桥接工具,以及更多 MCP 工具。
数据署名
GeoSpark 在本地计算几何数据,但会从公共服务获取数据。基于这些数据生成的结果仍受提供方许可约束,因此发布时请注明来源:
- OpenStreetMap(地理编码、配套设施、地图图块):© OpenStreetMap 贡献者,ODbL
- Copernicus DEM GLO-30(高程):© DLR e.V. 2010-2014 和 © Airbus Defence and Space GmbH 2014-2018,由欧盟和 ESA 根据 COPERNICUS 提供
- Open-Meteo(天气):CC BY 4.0
- OpenAQ(空气质量)和 NASA FIRMS(活跃火灾):请参阅各提供方的条款。空气质量结果会注明各站点的许可。
高程结果包含一个可直接复制的 attribution 字段。完整列表请参阅
NOTICE。
开发
git clone https://github.com/Maz2580/geospark && cd geospark
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev,api,mcp]"
pytest # 1003 tests; offline, never touch ~/.geospark
ruff check geospark tests
欢迎贡献。请参阅 CONTRIBUTING.md,并按照 SECURITY.md 中的说明私下报告安全问题。架构概览请参阅 docs/ARCHITECTURE.md, 发行说明请参阅 CHANGELOG.md。
引用
如果 GeoSpark 或 GeoSpark Bench 对你的研究有所帮助,请引用它。GitHub 的**“引用此仓库”**按钮会使用 CITATION.cff。
作者
由 Mazdak Ghasemi Tootkaboni 创建(University of Melbourne) · ORCID 0000-0001-8084-5270 · GitHub @Maz2580
许可证
Apache 2.0。版权所有 2024-2026 Mazdak Ghasemi Tootkaboni。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。