🧠 地理空间感知系统(Geospatial Sense System)完整项目模板
📌 项目概述
本项目旨在构建一个地理空间感知系统,帮助智能体(如自动驾驶车辆、机器人、无人机等)感知和理解其周围的地理环境。该系统支持从多种数据源(包括手动输入、GeoJSON 文件、Shapefile 文件)加载地理数据,并提供路径规划、可视化、感知等功能。
🧱 一、总体架构设计
✅ 核心目标
- 统一处理地理数据:POI、道路网络、坐标点。
- 多数据源支持:支持
.geojson和.shp文件导入。 - 自动节点合并:避免重复的 POI 节点。
- 路径规划与分析:Dijkstra、A* 等算法。
- 可视化能力:地图展示、路径高亮、热力图等。
- 模块化结构:便于扩展、测试和维护。
📁 二、目录结构(推荐)
geospatial-awareness/
├── src/
│ └── gisense/
│ ├── core/ # 核心类定义
│ │ ├── graph.py # LocationGraph 主类
│ │ ├── exceptions.py # 自定义异常类
│ │ └── observer.py # 智能体观察者
│ ├── entities/ # 数据实体定义
│ │ ├── coordinate.py # 经纬度封装
│ │ ├── poi.py # POI 类
│ │ └── road.py # Road 类
│ ├── io/ # 输入输出模块
│ │ ├── geojson_reader.py # GeoJSON 解析
│ │ └── shapefile_reader.py # Shapefile 解析
│ ├── algorithms/ # 路径规划算法
│ │ ├── pathfinding.py # Dijkstra/A*
│ │ └── perception.py # 地理感知逻辑
│ ├── visualization/ # 可视化模块
│ │ └── map_visualizer.py # 路网/路径可视化
│ └── utils/ # 工具函数
│ ├── distance.py # 距离计算
│ └── geometry.py # 几何操作
├── tests/ # 单元测试
├── docs/ # 文档(Sphinx)
├── examples/ # 使用示例脚本
├── pyproject.toml # 构建配置文件
├── requirements.txt # 第三方依赖
└── README.md # 项目说明文档
🧩 三、核心类与模块设计
1. LocationGraph(src/gisense/core/graph.py)
作为整个系统的主入口,负责管理 POI、Road、邻接表,并提供路径查找、数据导入、感知接口。
from typing import Dict, List, Optional
from gisense.entities.poi import POI
from gisense.entities.road import Road
from gisense.utils.geometry import CoordinateMatcher
class LocationGraph:
def __init__(self):
self.pois: Dict[str, POI] = {}
self.roads: Dict[str, Road] = {}
self.adjacency_list: Dict[str, Dict[str, str]] = {}
self._coordinate_to_poi_id = CoordinateMatcher() # 自动去重相同坐标的POI
def add_poi(self, poi: POI) -> None:
coord_key = (poi.coordinate.latitude, poi.coordinate.longitude)
if coord_key in self._coordinate_to_poi_id:
existing_id = self._coordinate_to_poi_id[coord_key]
print(f"Coordinate conflict: '{poi.id}' merged with '{existing_id}'")
return
if poi.id in self.pois:
raise ValueError(f"POI ID '{poi.id}' already exists")
self.pois[poi.id] = poi
self.adjacency_list[poi.id] = {}
def add_road(self, road: Road) -> None:
start_id = road.start_poi.id
end_id = road.end_poi.id
if start_id not in self.pois or end_id not in self.pois:
raise ValueError("Start or End POI does not exist")
self.roads[road.id] = road
self.adjacency_list[start_id][end_id] = road.id
if road.bidirectional:
self.adjacency_list[end_id][start_id] = road.id
def find_path(self, start_id: str, end_id: str) -> List[str]:
# 调用 Dijkstra 或 A*
from gisense.algorithms.pathfinding import dijkstra_shortest_path
return dijkstra_shortest_path(self, start_id, end_id)
2. CoordinateMatcher(utils/geometry.py)
支持坐标匹配与自动去重。
from typing import Tuple, Dict
class CoordinateMatcher:
def __init__(self, tolerance=1e-5):
self.tolerance = tolerance
self._map = {} # {(lat, lon): poi_id}
def __contains__(self, coord: Tuple[float, float]):
for stored in self._map:
if abs(stored[0] - coord[0]) < self.tolerance and \
abs(stored[1] - coord[1]) < self.tolerance:
return True
return False
def get_closest(self, coord: Tuple[float, float]) -> Optional[Tuple[float, float]]:
for stored in self._map:
if abs(stored[0] - coord[0]) < self.tolerance and \
abs(stored[1] - coord[1]) < self.tolerance:
return stored
return None
def add(self, coord: Tuple[float, float], poi_id: str):
closest = self.get_closest(coord)
if closest:
return self._map[closest]
self._map[coord] = poi_id
return poi_id
3. ShapefileReader(io/shapefile_reader.py)
使用
geopandas加载.shp文件并构建路网结构。
import geopandas as gpd
from shapely.geometry import LineString
from gisense.entities.poi import POI
from gisense.entities.road import Road
from gisense.entities.coordinate import Coordinate
class ShapefileReader:
@staticmethod
def read(shapefile_path: str) -> dict:
gdf = gpd.read_file(shapefile_path)
pois = []
roads = []
for _, row in gdf.iterrows():
geom = row.geometry
if isinstance(geom, LineString):
coords = list(geom.coords)
start_lon, start_lat = coords[0]
end_lon, end_lat = coords[-1]
start_poi = POI(
id=f"poi_{start_lat}_{start_lon}",
name="Node Start",
type="Intersection",
coordinate=Coordinate(start_lat, start_lon)
)
end_poi = POI(
id=f"poi_{end_lat}_{end_lon}",
name="Node End",
type="Intersection",
coordinate=Coordinate(end_lat, end_lon)
)
road = Road(
id=row.get('road_id', f"road_{len(roads)}"),
start_poi=start_poi,
end_poi=end_poi,
road_type=row.get('type', 'unknown'),
speed_limit=row.get('speed', 50),
bidirectional=True
)
pois.append(start_poi)
pois.append(end_poi)
roads.append(road)
return {'pois': pois, 'roads': roads}
4. Observer(core/observer.py)
智能体观察者,可基于不同定位方式感知周围环境。
from typing import Union
from gisense.entities.poi import POI
from gisense.entities.coordinate import Coordinate
from gisense.core.graph import LocationGraph
class Observer:
def __init__(self, position: Union[POI, Coordinate, str]):
self.position = position
def perceive(self, graph: LocationGraph):
from gisense.algorithms.perception import perceive_surroundings
return perceive_surroundings(graph, self.position)
5. perceive_surroundings(algorithms/perception.py)
from typing import Union
from gisense.core.graph import LocationGraph
from gisense.entities.poi import POI
from gisense.entities.coordinate import Coordinate
def perceive_surroundings(
graph: LocationGraph,
position: Union[POI, Coordinate, str]
):
# 实现感知逻辑
nearby_pois = graph.find_nearby_pois(position)
nearby_roads = graph.find_nearby_roads(position)
return {
"nearby_pois": nearby_pois,
"nearby_roads": nearby_roads
}
📦 四、依赖管理(requirements.txt)
geopandas>=0.13.0
shapely>=2.0.0
pyproj>=3.6.0
matplotlib>=3.7.0
networkx>=3.0
🧪 五、使用示例(examples/example_usage.py)
from gisense.core.graph import LocationGraph
from gisense.io.shapefile_reader import ShapefileReader
graph = LocationGraph()
# 从 Shapefile 加载路网
result = ShapefileReader.read("data/roads.shp")
for poi in result['pois']:
graph.add_poi(poi)
for road in result['roads']:
graph.add_road(road)
# 查找路径
path = graph.find_path("poi_1", "poi_2")
print("Path:", path)
📘 六、文档生成(docs/)
使用 Sphinx + MyST Parser 支持 Markdown 编写 API 文档:
sphinx-quickstart
pip install sphinx myst-parser
🧪 七、测试框架(tests/)
使用 pytest 进行单元测试:
pytest tests/test_graph.py
✅ 八、总结
| 功能 | 实现方式 |
|---|---|
| 自动节点合并 | 使用 CoordinateMatcher 去重相同坐标的 POI |
| 支持 GeoJSON 导入 | GeoJsonReader 解析 FeatureCollection |
| 支持 Shapefile 导入 | ShapefileReader 使用 geopandas |
| 路径规划 | Dijkstra, A* 算法实现 |
| 观察者模型 | Observer 抽象类感知周围信息 |
| 模块化设计 | 分为 core/io/entities/algorithms/visualization/utils |
Metadata
Release files for gisense 0.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| gisense-0.0.1.tar.gz | 47.5 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gisense-0.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 147.6 kB
Release files / gisense-0.0.1.tar.gz
| Download URL | gisense-0.0.1.tar.gz |
|---|---|
| Size | 47.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a4a94d8bf722dd4b83cc756ed5fb29f7e14c9057fc13241bf570e99fb0e23e8d
|
|
BLAKE2b-256 checksum How to use checksums |
baeeafc61b01f00710fba1c9625e5b9a305ff3a1cf880caa8e3dabf302cac844
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.11.1
|
Release files / gisense-0.0.1-py3-none-any.whl
| Download URL | gisense-0.0.1-py3-none-any.whl |
|---|---|
| Size | 100.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7da1518aa17955b8ddd9231f731f7e9b1f181bc1f7972f4b78f003bf3090633e
|
|
BLAKE2b-256 checksum How to use checksums |
cb54781dc16fa47880b8804d4e0f25fea0ddbff7cfa3a7acb96580321aceb76c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.11.1
|
Security for gisense 0.0.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.