Skip to content

路网优化

gotrackit提供了以下几种方式来对标准路网做一些优化,你需要使用NetReverse类,该类包含了以下几个与路网优化有关的函数:

清洗路网线层数据

如果你已经有了路网线层数据(从OSM或者其他任何途径获取的), 使用create_node_from_link函数来生产拓扑关联可能会报错,因为你的路网线层数据可能存在以下问题:

我们可以使用clean_link_geo函数对线层做一次清洗,示例代码如下:

import geopandas as gpd
import gotrackit.netreverse.NetGen as ng

if __name__ == '__main__':
    # 读取数据
    df = gpd.read_file(r'./data/output/request/0304/道路双线20230131_84.shp')

    # 处理geometry
    # l_threshold表示将线型中距离小于l_threshold米的折点进行合并,简化路网,同时消除重叠折点
    # l_threshold推荐 1m ~ 5m,过大会导致线型细节失真
    # plain_crs是要使用的平面投影坐标系
    link_gdf = ng.NetReverse.clean_link_geo(gdf=df, plain_crs='EPSG:32649', l_threshold=1.0)

修复联通性

如果你已经有了标准路网,但是路网可能存在联通性问题,你可以使用modify_conn函数来修复路网的联通性,修复联通性示例代码如下:

import geopandas as gpd
import gotrackit.netreverse.NetGen as ng

if __name__ == '__main__':
    link_gdf = gpd.read_file(r'./data/input/net/test/sz/FinalLink.shp')
    node_gdf = gpd.read_file(r'./data/input/net/test/sz/FinalNode.shp')

    # net_file_type指的是输出路网文件的类型
    nv = ng.NetReverse(net_file_type='shp', conn_buffer=0.8, net_out_fldr=r'./data/input/net/test/sz/')
    new_link_gdf,  new_node_gdf = nv.modify_conn(link_gdf=link_gdf, node_gdf=node_gdf, book_mark_name='sz_conn_test', generate_mark=True)
    print(new_link_gdf)
    print(new_node_gdf)

net_out_fldr下会生成联通性修复完成后的路网文件以及xml空间书签文件,将xml文件导入到QGIS可以查看修复的点位情况,以便排查是否所有修复都是合理的

Image title

Image title

ID重映射

如果你的线层文件link_id或者点层文件node_id是一个非常大的整数, 使用这样的路网存在风险,你可以使用静态方法remapping_link_node_id函数进行ID重映射

import geopandas as gpd
import gotrackit.netreverse.NetGen as ng

if __name__ == '__main__':
    l = gpd.read_file(r'./data/input/net/xian/modifiedConn_link.shp')
    n = gpd.read_file(r'./data/input/net/xian/modifiedConn_node.shp')
    print(l[['link_id', 'from_node', 'to_node']])
    print(n[['node_id']])
    nv = ng.NetReverse()
    nv.remapping_link_node_id(l, n)
    print(l[['link_id', 'from_node', 'to_node']])
    print(n[['node_id']])

路段划分

你已经有了一套标准路网, 你希望对link层进行路段划分,即将长度大于L(m)的路段都进打断,并且同步更新点层数据,你可以使用divide_links函数进行路段划分

import geopandas as gpd
import gotrackit.netreverse.NetGen as ng

if __name__ == '__main__':
    link = gpd.read_file(r'./data/input/net/test/0317/link1.geojson')
    node = gpd.read_file(r'./data/input/net/test/0317/node1.geojson')

    nv = ng.NetReverse()
    # 执行划分路网
    # divide_l: 所有长度大于divide_l的路段都将按照divide_l进行划分
    # min_l: 划分后如果剩下的路段长度小于min_l, 那么此次划分将不被允许
    new_link, new_node = nv.divide_links(link_gdf=link, node_gdf=node, divide_l=50, min_l=5.0)

    new_link.to_file(r'./data/input/net/test/0317/divide_link.geojson', driver='GeoJSON', encoding='gbk')
    new_node.to_file(r'./data/input/net/test/0317/divide_node.geojson', driver='GeoJSON', encoding='gbk')

Image title

Image title

  • 划分后,会生产一个新的字段:_parent_link,用于记录此路段在划分前所属的link_id,如果为空值,说明该路段没有发生划分
  • 关于路段划分函数的参数详解见:divide_links

处理环路

gotrackit不允许路网出现以下情况:

  • 环路(from_node == to_node)
  • 具有相同的拓扑起终点(from_node,to_node)的link

Image title

Image title

在构建Net时会自动识别这些link并且进行删除, 如果你想保留这些路段请使用circle_process函数进行处理

import geopandas as gpd
import gotrackit.netreverse.NetGen as ng

if __name__ == '__main__':
    l = gpd.read_file('./data/input/net/test/0506yg/link.shp')
    n = gpd.read_file('./data/input/net/test/0506yg/node.shp')

    # 处理环路和相同from_node - to_node的link
    new_link, new_node = ng.NetReverse.circle_process(link_gdf=l, node_gdf=n)

    # circle_process处理后输出的路网是平面投影坐标系, 需要转化为EPSG:4326
    new_link = new_link.to_crs('EPSG:4326')
    new_node = new_node.to_crs('EPSG:4326')

    new_link.to_file('./data/input/net/test/0506yg/new_link.shp')
    new_node.to_file('./data/input/net/test/0506yg/new_node.shp')

circle_process处理后如图:

Image title

Image title

拓扑优化

拓扑优化可以帮助你按照一定的规则对路段进行合并,我们以一个简单的示例来说明,下图是源路网:

Image title

Image title

合并后的长度限制

我们使用拓扑优化接口-topology_optimization对源路网进行路段合并,我们设置合并后的路段长度不能超过320米

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import geopandas as gpd
import gotrackit.netreverse.NetGen as ng
from gotrackit.netreverse.RoadNet.Tools.process import merge_double_link

if __name__ == '__main__':
    link = gpd.read_file(r'./data/input/net/test/tp_opt/LinkAfterModify.shp')
    # link必须为双向表示的路网(可用函数merge_double_link处理)
    link = merge_double_link(link_gdf=link)
    node = gpd.read_file(r'./data/input/net/test/tp_opt/NodeAfterModify.shp')
    nv = ng.NetReverse(restrict_angle=False, # 不启用转角限制
                       restrict_length=True, accu_l_threshold=320,  # 启用长度限制, 合并后的路段长度不能超过320米
                       ignore_dir=False, allow_ring=False,  # 不忽略路段行车方向, 不允许合并后出现环
                       net_out_fldr=r'./data/input/net/test/tp_opt/')
    nv.topology_optimization(link_gdf=link, node_gdf=node)

执行上述代码后,会在net_out_fldr下生成新的路网文件,可视化后如下:

Image title

由合并后的路网可以发现:

  • 节点15没有被合并,因为其连接的两条link的行车方向相反,ignore_dir=False表明了不能忽略行车方向
  • 节点5没有被合并,因为该节点一旦被合并,合并后的路段长度= 198 + 219 > 320,不满足长度限制条件
  • 节点10没有被合并,不满足长度限制条件
  • 节点12、13、4没有被合并,因为一旦合并后将形成环,不满足allow_ring=False的限制

路段转角限制

我们使用拓扑优化接口-topology_optimization对源路网进行路段合并,我们设置转角大于的节点不能被合并

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import geopandas as gpd
import gotrackit.netreverse.NetGen as ng
from gotrackit.netreverse.RoadNet.Tools.process import merge_double_link

if __name__ == '__main__':
    link = gpd.read_file(r'./data/input/net/test/tp_opt/LinkAfterModify.shp')
    # link必须为双向表示的路网(可用函数merge_double_link处理)
    link = merge_double_link(link_gdf=link)
    node = gpd.read_file(r'./data/input/net/test/tp_opt/NodeAfterModify.shp')
    nv = ng.NetReverse(restrict_angle=True, angle_threshold=5, # 启用转角限制, 转角大于5°的节点不能被合并
                       restrict_length=False,  # 不启用长度限制
                       ignore_dir=False, allow_ring=False,  # 不忽略路段行车方向, 不允许合并后出现环
                       net_out_fldr=r'./data/input/net/test/tp_opt/')
    nv.topology_optimization(link_gdf=link, node_gdf=node)

执行上述代码后,会在net_out_fldr下生成新的路网文件,可视化后如下:

Image title

由合并后的路网可以发现:

  • 节点15没有被合并,因为其连接的两条link的行车方向相反,ignore_dir=False表明了不能忽略行车方向
  • 节点5、3、8、7没有被合并,因为该节点的转向角超过了angle_threshold
  • 节点12、13、4没有被合并,因为一旦合并后将形成环,不满足allow_ring=False的限制

路段属性限制

我们使用拓扑优化接口-topology_optimization对源路网进行路段合并,我们设置限制字段为link层的road_name字段

Image title
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import geopandas as gpd
import gotrackit.netreverse.NetGen as ng
from gotrackit.netreverse.RoadNet.Tools.process import merge_double_link

if __name__ == '__main__':
    link = gpd.read_file(r'./data/input/net/test/tp_opt/LinkAfterModify.shp')
    # link必须为双向表示的路网(可用函数merge_double_link处理)
    link = merge_double_link(link_gdf=link)
    node = gpd.read_file(r'./data/input/net/test/tp_opt/NodeAfterModify.shp')
    nv = ng.NetReverse(restrict_angle=False, # 不启用转角限制,
                       restrict_length=False,  # 不启用长度限制
                       ignore_dir=False, allow_ring=False,  # 不忽略路段行车方向, 不允许合并后出现环
                       limit_col_name=r'road_name', # limit_col_name字段不能有空值
                       net_out_fldr=r'./data/input/net/test/tp_opt/')
    nv.topology_optimization(link_gdf=link, node_gdf=node)

执行上述代码后,会在net_out_fldr下生成新的路网文件,可视化后如下:

Image title

由合并后的路网可以发现:

  • 节点5没有被合并,因为其连接的两条link的road_name属性不一致

忽略路段行车方向

我们使用拓扑优化接口-topology_optimization对源路网进行路段合并,我们指定忽略路段的行车方向

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import geopandas as gpd
import gotrackit.netreverse.NetGen as ng
from gotrackit.netreverse.RoadNet.Tools.process import merge_double_link

if __name__ == '__main__':
    link = gpd.read_file(r'./data/input/net/test/tp_opt/LinkAfterModify.shp')
    # link必须为双向表示的路网(可用函数merge_double_link处理)
    link = merge_double_link(link_gdf=link)
    node = gpd.read_file(r'./data/input/net/test/tp_opt/NodeAfterModify.shp')
    nv = ng.NetReverse(restrict_angle=False, # 不启用转角限制,
                       restrict_length=False,  # 不启用长度限制
                       ignore_dir=True, allow_ring=False,  # 忽略路段行车方向, 不允许合并后出现环
                       net_out_fldr=r'./data/input/net/test/tp_opt/')
    nv.topology_optimization(link_gdf=link, node_gdf=node)

执行上述代码后,会在net_out_fldr下生成新的路网文件,可视化后如下:

Image title

由合并后的路网可以发现:

  • 节点12、13、4没有被合并,因为一旦合并后将形成环,不满足allow_ring=False的限制
  • 原路网中的15节点已经被合并

允许合并后出现环

我们使用拓扑优化接口-topology_optimization对源路网进行路段合并,我们指定不忽略路段的行车方向、允许合并后出现环

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import geopandas as gpd
import gotrackit.netreverse.NetGen as ng
from gotrackit.netreverse.RoadNet.Tools.process import merge_double_link

if __name__ == '__main__':
    link = gpd.read_file(r'./data/input/net/test/tp_opt/LinkAfterModify.shp')
    # link必须为双向表示的路网(可用函数merge_double_link处理)
    link = merge_double_link(link_gdf=link)
    node = gpd.read_file(r'./data/input/net/test/tp_opt/NodeAfterModify.shp')
    nv = ng.NetReverse(restrict_angle=False, # 不启用转角限制,
                       restrict_length=False,  # 不启用长度限制
                       ignore_dir=False, allow_ring=True,  # 不忽略路段行车方向, 允许合并后出现环
                       net_out_fldr=r'./data/input/net/test/tp_opt/')
    nv.topology_optimization(link_gdf=link, node_gdf=node)

执行上述代码后,会在net_out_fldr下生成新的路网文件,可视化后如下:

Image title

由合并后的路网可以发现:

  • 节点12、13、4被合并形成环

路网重塑

你已经有了线层文件,但是其存在折点联通性问题,你可以使用redivide_link_node函数进行路网重塑:

import geopandas as gpd
import gotrackit.netreverse.NetGen as ng

if __name__ == '__main__':
    # 读取数据
    origin_link = gpd.read_file(r'./data/input/net/test/0402BUG/load/test_link.geojson')
    print(origin_link)

    # 为防止线层线型有重复点,先做清洗
    origin_link = ng.NetReverse.clean_link_geo(gdf=origin_link, l_threshold=1.0, plain_crs='EPSG:32650')

    # multi_core_merge=True表示启用多进程进行拓扑优化
    # merge_core_num表示启用两个核
    nv = ng.NetReverse(net_out_fldr=r'./data/input/net/test/0402BUG/redivide',
                       plain_crs='EPSG:32650', flag_name='new_divide', multi_core_merge=True,
                       merge_core_num=2)

    # 路段、节点重新划分、联通性修复,新的网络文件在net_out_fldr下生成
    nv.redivide_link_node(link_gdf=origin_link)

Image title

Image title

路网重塑实际上进行了以下几个步骤:

  • 按照折点拆分线层
  • 生产点层
  • 进行拓扑优化
  • 去除重叠路段
  • 修复联通性

Comments