In the previous tutorial, Basic Network Visualization and Routing (QGIS3), we learnt how to build a network and calculate the shortest path between 2 points. We can apply that technique for many different types of network-based analysis. One such application is to compute Origin-Destination Matrix or OD Matrix. Given a set of origin points and another set of destination points, we can calculate shortest path between each origin-destination pairs and find out the travel distance/time between them. Such analysis is useful to locate the closest facility to any given point. For example, a logistics company may use this analysis to find the closest warehouse to their customers to optimize delivery routes. Here we use Distance Matrix algorithm from QGIS Network Analysis Toolbox (QNEAT3) plugin to find the nearest health facility to each address in the city.
Note
This tutorial shows how to use your own network data to compute an origin-destination matrix. If you do not have your own network data, you can use ORS Tools Plugin and algorithm Service Area Analysis using Openrouteservice (QGIS3) to learn how to use ORS Tools plugin.
to do the similar analysis using OpenStreetMap data. SeeWe will take 2 layers for Washington DC - one with points representing addresses and another with points representing mental health facilities - and find out the facility with the least travel distance from each address.
District of Columbia government freely shares hundreds of datasets on the Open Data Catalog.
Download the following data layers as shapefiles.
For convenience, you may directly download a copy of the datasets from the links below:
Adult_Mental_Health_Providers.zip
Data Source: [DCOPENDATA]
Visit Close.
. Search for QNEAT3 plugin and install it. ClickStreet_Centerlines.zip
file in the Browser panel. Expand it and drag the Street_Centerlines.shp
file to the canvas. Similarly, locate the Adult_Mental_Health_Providers.zip
file, expand it and add Adult_Mental_Health_Providers.shp
to the canvas.Address_Points.zip
file, expand it and add the Address_Points.shp
. You will see a lot of points around the city. Each point represents a valid address. We will not randomly select 1 point in each ward to use as the origin points. This technique is called stratified sampling. Go to .Address_Points
as the Input layer. Each address point contains an attribute called WARD_2012
which has the ward number associated with the address. As we want only 1 point per ward, we use that attribute as the ID field. Set Number/percentage of selected features as 1
.Extracted (random stratified)
will be added to the Layers panel.Address_Points
layer. Right-click on the Extracted (random stratified)
layer and select Rename layer.origin_points
. Similarly rename the Adult_Mental_Health_Providers
layers representing the health facilities as destination_points
. Naming the layers this way makes it easy to identify them in subsequent processing. Go to .Street_Centerlines
as the Network layer. Select origin_points
as the From-Points layer and OBJECTID
as the Unique Point ID field. Similarly, set destination_points
as the To-Points Layer and OBJECTID
as the Unique Point ID field. Set the Optimization Criterion as Shortest Path
.DIRECTIONA
as the Direction field. Enter One Way (Digitizing direction)
as the Value for forward direction and One way (Against digitizing direction)
as the Value for backward direction. Keep other options to their default values and click Run.Output OD Matrix
will be added to the Layers panel. Right-click and select Open Attributes Table. You will see that the table contains 117 rows. We had 9 origin points and 13 destination points - so the output contains 9x13 = 117 pairs of origins and destination. The total_cost
column contains distance in meters between each origin point to every destination point.total_cost
among all destinations. Go to origin_id
. Enter nearest_destinations
as the Layer name (prefix). Click Load.select origin_id, destination_id, min(total_cost) as shortest_distance from 'Output OD Matrix' group by origin_id
nearest_destinations
will be added to the Layers panel. This table has the result of our analysis. Nearest mental health center for each of the 9 origin points. Let’s try a few different ways to visualize and validate these results. Go to . Search for and locate the algorithm. Double-click to launch it.origin_points
as the Input layer and OBJECTID
as the Table field. Set nearest_destinations
as the Input layer 2 and origin_id
as the Table field 2. Click the ... button next to Layer 2 fields to copy and select destination_id
and shortest_distance
. Click Run.Joined layer
will be added to the Layers panel. This layer has the nearest destination id attribute for each origin point. We can now create a hub-spoke visualization using this layer. Search for algorithm. Right-click to launch it.destination_points
as the Hub layer and OBJECTID
as the Hub ID field. Select Joined layer` as the :guilabel:`Spoke layer` and ``destination_id
as the Spoke ID field. Click Run.Hub lines
will be added to the Layers panel. This layer shows the lines connecting each origin with the nearest destination.Street_Centerlines
as the Vector layer representing network. Keep the Path type to calculate as Shortest
. Next we need to pick a start and end point. You can click the ... button next to Start point and click on the origin point in the canvas. Similarly select the destination point as the End point. Expand the Advanced parameter section. Choose DIRECTIONA
as the Direction field. Enter One Way (Digitizing direction)
as the Value for forward direction and One way (Against digitizing direction)
as the Value for backward direction. Keep other options to their default values and click Run.Shortest Path Layer
wll be added to the Layers panel. You will see that this path follows the network rather than connecting the origin and destination with a straight line. The reason we ran the algorithm on 1 pair is to easily identify the parameter values that we can use in our script. Select both Hub lines
and Shortest Path layer
, right-click and select Remove Layer. Click the History button in the Processing Toolbox.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)
Shortest Path layer
. Let’s merge these paths to a single layer. Find the algorithm and launch it.Shortest Path layer
as the Input layers. Click Run.Merged
layer will be created which will contain shortest path between our origins and destinations.This work is licensed under a Creative Commons Attribution 4.0 International License