使用起点-目的地矩阵(QGIS3)定位最近的设施

在上一个教程 基本网络可视化和路由(QGIS3) 中,我们学习了如何构建网络并计算两点之间的最短路径。我们可以将该技术应用于许多不同类型的基于网络的分析。一种这样的应用是计算 Origin-Destination MatrixOD Matrix 。给定一组起点和另一组终点,我们可以计算每个起点-终点对之间的最短路径,并找出它们之间的旅行距离/时间。这样的分析对于找到最接近任何给定点的设施很有用。例如,物流公司可以使用此分析来找到距离其客户最近的仓库,以优化交货路线。在这里,我们使用 QGIS Network Analysis Toolbox(QNEAT3) 插件中的距离矩阵算法来查找距城市中每个地址最近的医疗机构。

注解

本教程介绍了如何使用自己的网络数据来计算起点-目的地矩阵。如果您没有自己的网络数据,则可以使用 ORS工具插件ORS Tools ‣ Matrix ‣ Matrix from Layers 算法来使用 OpenStreetMap 数据进行类似的分析。请参阅 使用Openrouteservice(QGIS3)进行服务区分析 以了解如何使用ORS工具插件。

任务概述

我们将为华盛顿特区设置2层-一层代表地址,另一层代表精神卫生设施-找出距每个地址旅行距离最少的设施。

您将会学习的其他技能

  • 从点层提取分层的随机样本。

  • 使用虚拟层在QGIS层上运行SQL查询。

  • 使用Python控制台编辑器运行pyqgis脚本。

获取数据

哥伦比亚特区政府在 Open Data Catalog 上自由共享数百个数据集。

将以下数据层下载为shapefile。

为了方便起见,您可以直接从下面的链接下载数据集的副本:

Street_Centerlines.zip

Address_Points.zip

Adult_Mental_Health_Providers.zip

资料来源 [DCOPENDATA]

设置

请访问 Plugins ‣ Manage and Install plugins 。搜索 QNEAT3 插件并安装。点击 Close

../../_images/setup12.png

程序

  1. Browser 面板中找到下载的 Street_Centerlines.zip 文件。展开并将 Street_Centerlines.shp 文件拖到画布上。同样,找到 Adult_Mental_Health_Providers.zip 文件,将其展开并将 Adult_Mental_Health_Providers.shp 添加到画布。

../../_images/1101.png
  1. 接下来,找到 Address_Points.zip 文件,将其展开并添加 Address_Points.shp 。您会在城市周围看到很多景点。每个点代表一个有效地址。我们不会在每个病房中随机选择 1 个点作为起点。该技术称为分层采样。进入 Processing ‣ Toolbox

../../_images/247.png
  1. 搜索并找到 Vector Selection ‣ Random extract within subsets 算法。

../../_images/332.png
  1. 选择 Address_Points 作为 Input layer 。每个地址点都包含一个名为 WARD_2012 的属性,该属性具有与该地址关联的病房号。由于每个病房只需要1点,因此我们将该属性用作ID字段。将 Number/percentage of selected features 设置为 1

../../_images/417.png
  1. 一个新的层 Extracted (random stratified) 将被添加到 Layers 面板中。

../../_images/517.png
  1. 关闭 Address_Points 层的可见性。右键单击 Extracted (random stratified) 层,然后选择 Rename layer

../../_images/617.png
  1. 让我们将该层重命名为 origin_points 。同样,将代表卫生设施的 Adult_Mental_Health_Providers 层重命名为 destination_points 。通过这种方式命名图层,可以轻松地在后续处理中识别它们。进入 Processing ‣ Toolbox

../../_images/716.png
  1. 找到 QNEAT3 ‣ Distance matrices ‣ OD Matrix from Layers as Table (m:n) 算法。如果在工具箱中没有看到此算法,请确保已安装 QNEAT3 插件。

../../_images/816.png
  1. 该算法有助于找到选定起点和终点之间沿网络的距离。选择 Street_Centerlines 作为网络层的标签。选择 origin_points 作为 From-Points layer ,并选择 OBJECTID 作为 Unique Point ID field 。类似地,将 destination_points 设置为 To-Points Layer ,将 OBJECTID 设置为 Unique Point ID field 。将 Optimization Criterion 设置为 Shortest Path

../../_images/916.png
  1. 由于网络中的许多街道都是单向的,因此我们需要 Advanced parameters 来指定方向。有关如何构造这些属性的更多详细信息,请参见 基本网络可视化和路由(QGIS3) 。 选择 DIRECTIONA 作为 Direction field 。输入 One Way (Digitizing direction) 作为 Value for forward direction ,并输入 One way (Against digitizing direction) 作为 Value for backward direction 。将其他选项保留为其默认值,然后单击 Run

../../_images/1016.png
  1. 一个名为 Output OD Matrix 的新表层将被添加到 Layers 面板中。右键单击并选择 Open Attributes Table 。您将看到该表包含 117 行。我们有9个起点和13个终点-因此输出包含 9x13 = 117 对起点和终点。 total_cost 列包含每个起点到每个目的地点之间的距离(以米为单位)。

../../_images/1119.png
  1. 对于本教程,我们仅对距离最短的目标点感兴趣。我们可以创建一个SQL查询来选择所有目的地中 total_cost 最少的目的地。进入 Database ‣ DB Manager..

../../_images/1217.png
  1. DB Manager 对话框中,从左侧面板中选择 Virtual Layers ‣ Project layers ‣ Output OD Matrix 。请参阅 Virtual layers 文档以了解更多信息。点击 SQL Window 按钮。

../../_images/1316.png
  1. 输入以下查询,然后单击 Execute 。结果将显示在下面的面板中。正如预期的那样,结果中有9行-每个起点的最短路径目标。检查并选择 Column with unique values 列作为 origin_id 。输入 nearest_destinations 作为 Layer name (prefix) 。点击 Load

select origin_id, destination_id, min(total_cost) as shortest_distance
from 'Output OD Matrix' group by origin_id
../../_images/1415.png
  1. 一个新的虚拟层 nearest_destinations 将被添加到 Layers 面板中。该表是我们分析的结果。9个起点中的每个起点最近的精神保健中心。让我们尝试几种不同的方法来可视化和验证这些结果。进入 Processing ‣ Toolbox 。搜索并找到 Join attributes by field value 算法。 双击以启动它。

../../_images/1514.png
  1. 选择 origin_points 作为输入层的 gui_points ,并选择 OBJECTID 作为表字段的 GUIJID 。 将 nearest_destinations 设置为输入层2的 guilabel ,将 origin_id 设置为表字段2的 label 。 单击要复制的第2层字段旁边的 按钮,然后选择 destination_idshortest_distance 。 点击 Run

../../_images/1613.png
  1. 一个新的 Joined layer 将被添加到 Layers 面板中。该图层具有每个起点的最近目的地ID属性。现在,我们可以使用此层创建中心轮辐可视化。搜索 Vector analysis ‣ Join by lines (hub lines) 算法。右键单击以启动它。

../../_images/1714.png
  1. 选择 destination_points 作为 Hub layer ,并选择 OBJECTID 作为 Hub ID field 。选择 Joined layer 作为 Spoke layer ,并选择 destination_id 作为 Spoke ID field 。 点击 Run

../../_images/1813.png
  1. 处理完成后,新图层 Hub lines 将添加到 Layers 面板中。该层显示了连接每个起点和最近目的地的线。

../../_images/1911.png
  1. 请注意,即使连接起点和终点的线是一条直线,也可以使用沿网络的距离找到终点。这对于显示每个起点与终点之间的实际最短路径将很有用。到目前为止,还没有一种简单的方法可以像生成距离矩阵那样在多个起点-终点对之间生成最短路径。但是,我将演示一种使用一些python脚本生成此可视化效果的方法。首先,让我们在1对上运行最短路径算法。找到 QNEAT3 ‣ Routing ‣ Shortest path (point to point) 算法,然后启动它。

../../_images/209.png
  1. Shortest Path (Point to Point) 对话框中,选择 Street_Centerlines 作为 Vector layer representing network 。保持 Path type to calculateShortest 。接下来,我们需要选择一个起点和终点。您可以单击 Start point 旁边的 按钮,然后单击画布中的起点。类似地,选择终点作为 End point 。展开 Advanced parameter 部分。选择 DIRECTIONA 作为 Direction field 。输入 One Way (Digitizing direction) 作为 Value for forward direction ,并输入 One way (Against digitizing direction) 作为 Value for backward direction 。将其他选项保留为其默认值,然后单击 Run

../../_images/2114.png
  1. 一个新的 Shortest Path Layer 层将被添加到 Layers 面板中。您将看到此路径遵循网络,而不是用直线连接起点和终点。我们在1对上运行该算法的原因是为了轻松识别可在脚本中使用的参数值。选择 Hub linesShortest Path layer ,右键单击并选择 Remove Layer 。点击 Processing Toolbox 中的 History 按钮。

../../_images/2212.png
  1. 选择最上面的算法,您将在下面的面板中看到完整的命令。复制命令,然后单击 Close

../../_images/2311.png
  1. 进入 Plugins ‣ Python Console

../../_images/248.png
  1. Python Console 中,单击 Show Editor 按钮。

../../_images/256.png
  1. 在编辑器窗口中,复制/粘贴以下脚本。该脚本使用了我们之前看到的处理历史记录中的参数值。单击 Run Script 按钮以开始执行。

../../_images/266.png
origin_layer =  QgsProject.instance().mapLayersByName('origin_points')[0]
destination_layer =  QgsProject.instance().mapLayersByName('destination_points')[0]
matrix =  QgsProject.instance().mapLayersByName('nearest_destinations')[0]

for f in matrix.getFeatures():
    origin_expr = QgsExpression('OBJECTID={}'.format(f['origin_id']))
    destination_expr = QgsExpression('OBJECTID={}'.format(f['destination_id']))
    origin_feature = origin_layer.getFeatures(QgsFeatureRequest(origin_expr))
    origin_coords =  [(f.geometry().asPoint().x(), f.geometry().asPoint().y())
        for f in origin_feature]
    destination_feature = destination_layer.getFeatures(QgsFeatureRequest(destination_expr))
    destination_coords =  [(f.geometry().asPoint().x(), f.geometry().asPoint().y())
        for f in destination_feature]
    params = {
        'INPUT':'Street_Centerlines',
        'START_POINT':'{},{}'.format(origin_coords[0][0], origin_coords[0][1]),
        'END_POINT':'{},{}'.format(destination_coords[0][0], destination_coords[0][1]),
        'STRATEGY':0,
        'ENTRY_COST_CALCULATION_METHOD':0,
        'DIRECTION_FIELD':'DIRECTIONA',
        'VALUE_FORWARD':'One Way (Digitizing direction)\n',
        'VALUE_BACKWARD':'One way (Against digitizing direction)\n',
        'VALUE_BOTH':'',
        'DEFAULT_DIRECTION':2,
        'SPEED_FIELD':None,
        'DEFAULT_SPEED':5,
        'TOLERANCE':0,
        'OUTPUT':'memory:'}
    print('Executing analysis')
    processing.runAndLoadResults("qneat3:shortestpathpointtopoint", params)
  1. 该脚本将需要几分钟来运行。完成后,您将看到9个名为 Shortest Path layer 的新层。让我们将这些路径合并到单个层中。找到 Vector general ‣ Merge vector layers 算法,然后启动它。

../../_images/276.png
  1. 选择所有9个 Shortest Path layer 作为 Input layers 。点击 Run

../../_images/286.png
  1. 将创建一个新的 Merged 层,其中将包含我们的起点和终点之间的最短路径。

../../_images/295.png