暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

通过多边形进行线路路由

原创 X丶 2022-10-27
730

我最喜欢的p注浆路由层之一是水层。我感兴趣的是水从哪里来,流向哪里,径流发生在哪里,以及城市发展如何与这种强大的自然力量相互作用。然而,在使用PostGIS和p注浆:Polygons进行路由时,OpenStreetMap水层提出了一个挑战。

为什么多边形是一个挑战?使用p注浆的路由网络是由线(边)构建的。现在,申明一个显而易见的事实:多边形不是直线。

现实世界的水路网络是由直线和多边形组成的。河流、小溪和排水路线主要(但不是全部!)使用线条绘制。这些管线进出池塘、湖泊和水库。下面的动画展示了水多边形对水路网络的影响。当一些非常重要的路径被排除在外时,它们就会消失。
image.png

为了使整个水网络可路由,我们需要创建一个组合线路层。组合线层将包括:

  • 初始osm。water_line输入

  • 来自osm.water_polygon的中轴线

  • 连接初始输入和中间轴的线

这篇文章的篇幅很大,因为其中包含了查询示例和截图。

扩展和数据

本文使用了st_approximate atemedialaxis()函数,该函数需要postgis_sfcgal扩展。创建扩展,以及postgis和p注浆,以便它们在需要时可用。

CREATE EXTENSION postgis; CREATE EXTENSION pgrouting; CREATE EXTENSION postgis_sfcgal;

本文的数据源是使用PgOSM Flex的Docker程序加载的科罗拉多州的OpenStreetMap数据。使用默认层集加载Colorado通常需要不到10分钟的时间来获取.sql,我将其加载到我选择的Postgres实例中。确保使用PgOSM Flex 0.6.0或更高版本。旧版本的PgOSM Flex不包括Standley Lake等水资源关系。

示例湖多边形和中轴

在上面的动画中消失的最大的水体是斯坦德利湖,也被称为3023193关系。可以使用PostGIS函数st_approatemedialaxis()为多个多边形生成合理的移动路径。另一个选项是st_直骨架();这个功能在本文中没有讨论。下面的查询和图像显示了如何计算Standley Lake的中轴。

SELECT osm_id, geom, ST_ApproximateMedialAxis(geom) FROM osm.water_polygon WHERE osm_id = -3023193 ;

image.png

定义小的测试区域

路由的步骤在大型数据集上可能花费很长时间。为了这篇文章的目的,我将科罗拉多的全部数据限制在史坦德利湖大约5公里的重点区域内。我创建了一个名为routing_demo的模式来保存我们的数据,并创建了一个表(routing_demo.focus)来存储焦点的几何形状。在本文的多个查询中使用了焦点区域,以限制使用的数据量。

CREATE SCHEMA routing_demo; COMMENT ON SCHEMA routing_demo IS 'Contains examples for routing through polygons using ST_ApproximateMedialAxis()'; CREATE TABLE routing_demo.focus AS SELECT osm_id, geom, ST_Buffer(geom, 5000) AS geom_buffer FROM osm.water_polygon WHERE osm_id = -3023193 ; CREATE INDEX ON routing_demo.focus USING GIST (geom); COMMENT ON TABLE routing_demo.focus IS 'Focus area for routing';

新创建的routing_demo。焦点表包含建立焦点区域所需的数据。湖泊多边形和计算的缓冲区如下图所示。

SELECT * FROM routing_demo.focus;

image.png

我要做的下一步是检查osm。water_polygon数据连接到routing_demo。焦点表。下面的查询显示,我希望从以下步骤中排除两种类型的水多边形:水坝和船坞。虽然水坝对水流有重大影响,但它们不是我想要实现的路线分析的一部分。

SELECT p.osm_type, p.osm_subtype, COUNT(*) FROM osm.water_polygon p INNER JOIN routing_demo.focus f ON p.geom && f.geom_buffer GROUP BY p.osm_type, p.osm_subtype ORDER BY p.osm_subtype ; osm_type|osm_subtype|count| --------+-----------+-----+ waterway|boatyard | 1| waterway|dam | 2| natural |water | 172| natural |wetland | 38|

为多边形创建中间轴

定义了焦点区域后,下一步是计算和持久化这些多边形的中间轴线。下面的查询创建了routing_demo。Polygon_medial_axis表,其中包含构成每个多边形的中间轴的单独线条。st_similatemedialaxis()函数返回MULTILINESTRINGs,因此还使用ST_Dump()来提取每个多边形的单个线组件。

CREATE TABLE routing_demo.polygon_medial_axis AS SELECT p.osm_id, (ST_Dump(ST_ApproximateMedialAxis(p.geom))).path[1] AS ma_id, (ST_Dump(ST_ApproximateMedialAxis(p.geom))).geom AS geom FROM osm.water_polygon p INNER JOIN routing_demo.focus f ON p.geom && f.geom_buffer WHERE p.osm_subtype NOT IN ('dam', 'boatyard') ; CREATE INDEX ON routing_demo.polygon_medial_axis USING GIST (geom); COMMENT ON TABLE routing_demo.polygon_medial_axis IS 'Contains calculated medial axis lines created from the osm.water_polygon table.';

210个多边形的输入创建了4415条中轴线,平均每个多边形21条线。

SELECT COUNT(*) AS rows, COUNT(DISTINCT osm_id) AS input_polygons, COUNT(*) / COUNT(DISTINCT osm_id) AS avg_medial_axis_per_input FROM routing_demo.polygon_medial_axis ; rows|input_polygons|avg_medial_axis_per_input| ----+--------------+-------------------------+ 4415| 210| 21|

下面的查询和屏幕截图提供了保存到routing_demo中的中轴数据。polygon_medial_axis表格。

SELECT * FROM routing_demo.polygon_medial_axis;

image.png

确定与水多边形相交的水线

以上步骤已经提供了通过我们的水多边形的基本路线。然而,内侧轴线与水道进出多边形的位置不匹配。为了解决这一挑战,我们需要确定osm的水线在哪里。Water_line与水多边形图层相交。为了实现这一点,我重用了我的帖子中关于在OpenStreetMap数据中寻找缺失的交叉标记的技巧。

在下面的查询中,相交CTE查找与多边形相交的线。第一个查询还连接到焦点表,以将范围限制到我们感兴趣的区域。node_id查询使用ST_PointN()和generate_series()从相交于多边形多个点的直线中提取节点id。组合CTE查询将与一个相交节点(来自交集)的相交线和与多个相交节点(来自node_id)的相交线组合在一起。查询后的截图显示斯坦德利湖周围的相交点为蓝点。

CREATE TABLE routing_demo.line_polygon_intersection AS WITH intersections AS ( SELECT p.osm_id AS water_polygon_osm_id, l.osm_id, l.osm_type, l.osm_subtype, ST_Intersection(p.geom, l.geom) AS geom FROM osm.water_polygon p INNER JOIN routing_demo.focus f ON p.geom && f.geom_buffer INNER JOIN osm.water_line l ON ST_Intersects(p.geom, l.geom) AND l.osm_subtype NOT IN ('dam', 'boatyard') ), node_ids AS ( SELECT osm_id, generate_series(1, ST_Npoints(geom)) AS node_id FROM intersections WHERE ST_NPoints(geom) > 1 ), combined AS ( SELECT DISTINCT i.water_polygon_osm_id, i.osm_id, i.osm_type, i.osm_subtype, CASE WHEN n.osm_id IS NULL THEN i.geom ELSE ST_PointN(i.geom, n.node_id) END AS geom FROM intersections i LEFT JOIN node_ids n ON i.osm_id = n.osm_id ) SELECT * FROM combined

image.png

状态检查

为了查看到目前为止所取得的进展,我从routing_demo中提取了数据。polygon_medial_axis routing_demo。line_polygon_intersection表导入QGIS。下面的截图显示了一条水线与斯丹利湖多边形相交的蓝色点。中间轴有一个分支,它靠近但不接触多边形边界的交点。要创建完整的路由,我们需要将各个部分连接起来。
image.png

连接片

下面的查询使用st_closepoint()和ST_MakeLine()将水道与多边形内部的中间轴线相交的点连接起来。INNER JOIN中的子查询使用ST_Collect()将中间轴的各个线折叠为每个多边形的单个几何图形。跳过此步骤将导致计算到每一行的最近点的行。

CREATE TABLE routing_demo.water_line_to_polygon AS SELECT i.osm_id, i.water_polygon_osm_id, ST_MakeLine(i.geom, ST_ClosestPoint(ma.geom, i.geom)) AS geom FROM routing_demo.line_polygon_intersection i INNER JOIN ( SELECT osm_id, ST_Collect(geom) AS geom FROM routing_demo.polygon_medial_axis GROUP BY osm_id ) ma ON i.water_polygon_osm_id = ma.osm_id ;

image.png

把它放在一起上述步骤创建了我们需要缝合在一起的组件。重新查看文章开头的项目列表,下表显示了数据元素当前的位置。
image.png

下面的查询将这三(3)个数据集合并为一个行表。这一步的目标是为pgRouting步骤做好一切准备。

DROP TABLE IF EXISTS routing_demo.waterways_to_route; CREATE TABLE routing_demo.waterways_to_route AS WITH ww_selected AS ( SELECT l.osm_id, l.osm_type, l.osm_subtype, NULL::BIGINT AS water_polygon_osm_id, l.layer, -- This will simplify some/most... ST_LineMerge(l.geom) AS the_geom FROM routing_demo.focus f INNER JOIN osm.water_line l ON f.geom_buffer && l.geom AND l.osm_subtype NOT IN ('dam', 'boatyard') ), ww_extra_cleanup AS ( -- Split the complex lines that made it through ST_LineMerge SELECT osm_id, osm_type, osm_subtype, water_polygon_osm_id, layer, (ST_Dump(the_geom)).geom AS the_geom FROM ww_selected WHERE ST_GeometryType(the_geom) = 'ST_MultiLineString' ), combined AS ( SELECT osm_id, osm_type, osm_subtype, water_polygon_osm_id, layer, the_geom FROM ww_selected WHERE ST_GeometryType(the_geom) != 'ST_MultiLineString' UNION SELECT osm_id, osm_type, osm_subtype, water_polygon_osm_id, layer, the_geom FROM ww_extra_cleanup WHERE ST_GeometryType(the_geom) != 'ST_MultiLineString' UNION SELECT NULL AS osm_id, 'line-to-polygon' AS osm_type, '' AS osm_subtype, water_polygon_osm_id, 0 AS layer, geom AS the_geom FROM routing_demo.water_line_to_polygon UNION SELECT NULL AS osm_id, 'medial-axis' AS osm_type, '' AS osm_subtype, osm_id AS water_polygon_osm_id, 0 AS layer, geom AS the_geom FROM routing_demo.polygon_medial_axis ) SELECT ROW_NUMBER() OVER () AS id, * FROM combined WHERE the_geom IS NOT NULL ;

路由网络

以下步骤创建路由所需的结构和数据。在这篇文章中没有足够的篇幅来深入讨论这些步骤。

我的《精通PostGIS和OpenStreetMap》一书的第14、15、16和18章都是关于p注浆的,并详细解释了这个过程。

SELECT pgr_nodeNetwork('routing_demo.waterways_to_route', .1); -- Establish route costs ALTER TABLE routing_demo.waterways_to_route_noded ADD cost NUMERIC GENERATED ALWAYS AS ( ST_Length(the_geom) ) STORED ; ALTER TABLE routing_demo.waterways_to_route_noded ADD reverse_cost NUMERIC NOT NULL DEFAULT -1 ; UPDATE routing_demo.waterways_to_route_noded n SET reverse_cost = ST_Length(n.the_geom) FROM routing_demo.waterways_to_route r WHERE n.old_id = r.id AND r.osm_type IN ('line-to-polygon', 'medial-axis') ; -- Topology and Analyze SELECT pgr_createTopology('routing_demo.waterways_to_route_noded', 0.1); SELECT pgr_analyzeGraph('routing_demo.waterways_to_route_noded', 0.1);

创建一条穿过湖泊的路线

唷,我们成功了。是时候创建一条路线了!下面的查询从所选的开始和结束节点创建一条从西到东穿过Standley Lake的路线。

SELECT * FROM pgr_dijkstra(' SELECT rn.id, rn.source, rn.target, rn.cost, rn.reverse_cost FROM routing_demo.waterways_to_route_noded rn WHERE source IS NOT NULL ', 235, -- start node 5930, -- end node True -- Enforce one-way restrictions ) d INNER JOIN routing_demo.waterways_to_route_noded x ON d.edge = x.id ;

image.png

总结

这篇文章深入介绍了如何使用OpenStreetMap数据创建一个能够穿越多边形的水路路由网络。虽然st_approatemedialaxis()是涉及的一个关键函数,但这篇文章展示了十几个函数的使用!该技术为使用该地层进行更完整、更复杂的水文分析奠定了基础。

在水路层之外,这种方法还可以使用OpenStreetMap中的其他层数据来改进路由。行人路线是另一个需要包含多边形的地方。要关闭这篇文章,再一个查询和一个截图。坚持了下来,值得称赞!

SELECT osm_id, osm_type, route_foot, geom FROM osm.road_polygon WHERE osm_id = 323692586 ;

image.png

原文标题:Routing with Lines through Polygons
原文作者: Ryan Lambert
原文地址:https://blog.rustprooflabs.com/2022/10/pgrouting-lines-through-polygons

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论