1 构建两条道路空间通行关系判断规则
假设有两条道路A和B,道路A为源道路,道路B为目标道路,实验的目标是判断从道路A出发能否到达道路B。首先根据道路类别建立通行规则,然后根据两条道路的空间拓扑关系判断道路A是否能够通往道路B。
道路空间通行关系规则决策树
1.1 实现方法概述
首先从OSM中获取的路网数据存储了道路类型信息,其将道路分为高速路、快速路、主干道、次干道、其他道路等,并且道路是单向通行的。因此不同道路的通行规则是能够基于道路类型属性快速区分。其次,道路的行车方向与空间线条点集顺序一致,即从第一个点前往最后一个点。最后,影响两条道路通行关系的空间拓扑包括三类:连续(Connected)、接触(Touch)和相交(Intersect),需基于一些算法具体判断道路间的空间拓扑关系。
以下WKT格式表示的道路起点、终点和行车方向如图所示。
text
LINESTRING(118.7668063525639 32.05806454085803,118.7667190099296 32.05807233008715,118.7666316672952 32.0580801193163,118.7665443246609 32.05808790854542,118.7664569820266 32.05809569777457,118.7663696393922 32.05810348700371,118.7662822967579 32.05811127623283,118.7661949541236 32.05811906546198,118.7661076114892 32.0581268546911,118.7660202688549 32.05813464392023,118.7659329262206 32.05814243314938,118.7658452646464 32.05814450350625,118.7657576030723 32.05814657386314,118.7656699414982 32.05814864422003,118.765582279924 32.05815071457692,118.7654946183499 32.05815278493378,118.7654069567757 32.05815485529067,118.7653192952016 32.05815692564755,118.7652316336275 32.05815899600444,118.7651439720534 32.05816106636131,118.7650577766283 32.05815226087318,118.7649715812032 32.05814345538508,118.7648853857781 32.05813464989693,118.764799190353 32.05812584440881,118.7647129949279 32.05811703892068,118.7646267995028 32.05810823343255,118.7645406040777 32.05809942794443,118.7644544086526 32.0580906224563,118.7643682132275 32.05808181696818)
1.2 空间拓扑关系判断的特殊情况
- 由于OSM数据本身会存在一些误差,比如现实世界中相连接的道路,OSM数据中两条线是不相交的。经测算,两条道路空间拓扑关系判断的空间容差(Tolerance)不超过100m。对于地理学,使用了0.00001米的距离公差(因此非常接近的点被认为是相交的)。
- 拓扑关系的不确定性。在二维地图中,相交的道路在实际三维世界中并不一定是相交关系,比如高架桥和地下隧道。
2 技术选型与代码示例
-
利用存储Postgresql+PostGIS管理空间数据,neo4j管理道路通行网络知识图谱。
-
通过编写python算法实现道路通行知识图的构建,其中关键用到了python sqlalchemy和geoalchemy2访问空间数据库,python neo4j库访问neo4j数据库。
在两条道路空间通行关系判断规则中,需基于道路空间拓扑关系判断以确定两条道路是否可通行。以下利用Python编程实现了指定"源道路"空间接触计算、空间相交计算和给定距离线查询等三个方法。
- 连接Postgresql数据库
python
from sqlalchemy import create_engine, func
from sqlalchemy.orm import sessionmaker
DATABASE_URI = f'postgresql://{username}:{password}@localhost:5432/{dbname}' # 数据库连接信息
engine = create_engine(DATABASE_URI, echo=False)
# 创建会话
Session = sessionmaker(bind=engine)
session = Session()
-
空间接触计算
-
方法描述:
ST_Touches --- Tests if two geometries have at least one point in common, but their interiors do not intersect.
Description: Returns TRUE if A and B intersect, but their interiors do not intersect. Equivalently, A and B have at least one point in common, and the common points lie in at least one boundary. For Point/Point inputs the relationship is always FALSE, since points do not have a boundary.
2. 示例代码pythondef get_touch_br(source_br: BaseRoad): touch_br_list = [] touch_res = session.query(BaseRoad, func.ST_AsText(BaseRoad.geom)) \ .filter(func.ST_Touches(BaseRoad.geom, source_br.geom)) for br, geom in touch_res: touch_br_list.append(br) br_geom_list.append(wkt.loads(geom)) return touch_br_list
- 示例结果
-
-
空间相交计算
-
方法描述
ST_Intersects --- Tests if two geometries intersect (they have at least one point in common).
Description: Compares two geometries and returns true if they intersect. Geometries intersect if they have any point in common.For geography, a distance tolerance of 0.00001 meters is used (so points that are very close are considered to intersect).
-
示例代码
pythondef get_intersects_br(source_br: BaseRoad): intersects_br_list = [] br_geom_list = [] # 查询与source_br相交的道路 intersects_res = session.query(BaseRoad) \ .filter(func.ST_Intersects(BaseRoad.geom, source_br.geom)) for br in intersects_res: intersects_br_list.append(br) br_geom_list.append(wkb.loads(WKBElement._data_from_desc(br.geom.desc))) return intersects_br_list
- 示例结果
-
-
给定距离线查询
-
方法描述
ST_DWithin --- Tests if two geometries are within a given distance.
Description: The distance is specified in units defined by the spatial reference system of the geometries. For this function to make sense, the source geometries must be in the same coordinate system (have the same SRID).
-
示例代码
pythondef get_dwithin_br(source_br: BaseRoad): dwithin_br_list = [] dwithin_res = session.query(BaseRoad, func.ST_AsText(BaseRoad.geom)) \ .filter(func.ST_DWithin(func.ST_GeomFromEWKB(BaseRoad.geom), func.ST_GeomFromEWKB(source_br.geom), Tolerance)) for br, geom in dwithin_res: dwithin_br_list.append(br) br_geom_list.append(wkt.loads(geom)) return dwithin_br_list
- 示例结果
-