🧠 地理空间感知系统(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 |
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
gisense-0.0.1.tar.gz
(47.5 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
gisense-0.0.1-py3-none-any.whl
(100.1 kB
view details)
File details
Details for the file gisense-0.0.1.tar.gz.
File metadata
- Download URL: gisense-0.0.1.tar.gz
- Upload date:
- Size: 47.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.1.0 CPython/3.11.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a4a94d8bf722dd4b83cc756ed5fb29f7e14c9057fc13241bf570e99fb0e23e8d
|
|
| MD5 |
cb04fa9a2bd73558364f0f76cb87a98f
|
|
| BLAKE2b-256 |
baeeafc61b01f00710fba1c9625e5b9a305ff3a1cf880caa8e3dabf302cac844
|
File details
Details for the file gisense-0.0.1-py3-none-any.whl.
File metadata
- Download URL: gisense-0.0.1-py3-none-any.whl
- Upload date:
- Size: 100.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.1.0 CPython/3.11.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7da1518aa17955b8ddd9231f731f7e9b1f181bc1f7972f4b78f003bf3090633e
|
|
| MD5 |
e6a0dc2be0d1623e390490e41ddf9847
|
|
| BLAKE2b-256 |
cb54781dc16fa47880b8804d4e0f25fea0ddbff7cfa3a7acb96580321aceb76c
|