Upload 3 files
Browse files- network_analysis.py +141 -0
- shortest_path.gpkg +0 -0
- symbology-style.db +0 -0
network_analysis.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#Libraries To Import For Doing Network Analysis using QGis
|
| 2 |
+
#Using The Core QGIS functions and we're using PyQT classes for graph building and layer manipulation to learn 2 points to another
|
| 3 |
+
import sys
|
| 4 |
+
import os
|
| 5 |
+
from qgis.analysis import QgsGraphBuilder #Used To analyze two points
|
| 6 |
+
from qgis.analysis import QgsGraphAnalyzer #Uses Djikstra's Algorithm to find the most optimal path between 2 points
|
| 7 |
+
|
| 8 |
+
from qgis.core import (
|
| 9 |
+
QgsApplication,
|
| 10 |
+
QgsWkbTypes,
|
| 11 |
+
QgsProject, #Project Manager
|
| 12 |
+
QgsVectorLayer, #To Load Shapefiles and geopackages
|
| 13 |
+
QgsFeature, #Single row in a vector layer
|
| 14 |
+
QgsGeometry, #For mathematical geometry operations
|
| 15 |
+
QgsPointXY, #Makes a 2D point
|
| 16 |
+
QgsPoint,
|
| 17 |
+
QgsFields,
|
| 18 |
+
QgsField, #For multple attribute schemas and individual attribute field
|
| 19 |
+
QgsVectorFileWriter #For Package Export
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
from qgis.PyQt.QtCore import QVariant # Used to define field data types
|
| 23 |
+
|
| 24 |
+
#Now we load the Philippine roads dataset from a non-profit organization called Humanitarian Open Street map team
|
| 25 |
+
#This will give us an open dataset about roads in NCR and give us the shortest path between two points
|
| 26 |
+
#Setup QGIS Paths
|
| 27 |
+
QGIS_PREFIX_PATH = "/usr" # Common for Ubuntu QGIS installs
|
| 28 |
+
QGIS_PLUGIN_PATH = "/usr/lib/qgis/plugins"
|
| 29 |
+
|
| 30 |
+
qgs = QgsApplication([], False)
|
| 31 |
+
qgs.setPrefixPath(QGIS_PREFIX_PATH, True)
|
| 32 |
+
qgs.initQgis()
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
layer = QgsVectorLayer("phl_roads_lines.gpkg", "roads", "ogr")
|
| 36 |
+
|
| 37 |
+
#print("Layer loaded:", layer.name())
|
| 38 |
+
|
| 39 |
+
#We are going to find shortest path for Quezon city
|
| 40 |
+
start_point = QgsPointXY(121.0365, 14.6760)
|
| 41 |
+
end_point = QgsPointXY(121.0000, 14.5550)
|
| 42 |
+
|
| 43 |
+
tolerance = 0.001 #to snap nearest vortex
|
| 44 |
+
builder = QgsGraphBuilder(layer.sourceCrs())
|
| 45 |
+
|
| 46 |
+
points = [start_point, end_point]
|
| 47 |
+
|
| 48 |
+
graph = builder.graph()
|
| 49 |
+
|
| 50 |
+
#getting the feature graphs
|
| 51 |
+
for feature in layer.getFeatures():
|
| 52 |
+
geom = feature.geometry()
|
| 53 |
+
if geom.isMultipart():
|
| 54 |
+
lines = geom.asMultiPolyline()
|
| 55 |
+
else:
|
| 56 |
+
lines = [geom.asPolyline()]
|
| 57 |
+
for line in lines:
|
| 58 |
+
for i in range(len(line) - 1):
|
| 59 |
+
#Then we convert it to a QgisPoint
|
| 60 |
+
p1 = QgsPointXY(line[i].x(), line[i].y())
|
| 61 |
+
p2 = QgsPointXY(line[i + 1].x(), line[i + 1].y())
|
| 62 |
+
|
| 63 |
+
builder.addVertex(-1, p1)
|
| 64 |
+
builder.addVertex(-1, p2)
|
| 65 |
+
|
| 66 |
+
id1 = builder.graph().findVertex(p1)
|
| 67 |
+
id2 = builder.graph().findVertex(p2)
|
| 68 |
+
|
| 69 |
+
distance = p1.distance(p2)
|
| 70 |
+
|
| 71 |
+
builder.addEdge(id1, p1, id2, p2, [distance])
|
| 72 |
+
|
| 73 |
+
graph = builder.graph()
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
# Convert the coordinates (start_point and end_point) into node indexes in the graph
|
| 77 |
+
start_idx = graph.findVertex(start_point)
|
| 78 |
+
end_idx = graph.findVertex(end_point)
|
| 79 |
+
|
| 80 |
+
# Use Dijkstra's algorithm to compute the shortest path tree from the start node
|
| 81 |
+
# 0 is the index for cost (in our case: distance)
|
| 82 |
+
tree, cost = QgsGraphAnalyzer.dijkstra(graph, start_idx, 0)
|
| 83 |
+
|
| 84 |
+
# If cost is infinite, it means there’s no possible path between start and end
|
| 85 |
+
if cost[end_idx] == float('inf'):
|
| 86 |
+
print("No Path Found")
|
| 87 |
+
sys.exit(1)
|
| 88 |
+
|
| 89 |
+
# Backtrack from end point to start to extract the shortest path
|
| 90 |
+
route = []
|
| 91 |
+
cur_pos = end_idx
|
| 92 |
+
while cur_pos != start_idx:
|
| 93 |
+
incoming_edge = tree[cur_pos] # The edge that leads to current vertex
|
| 94 |
+
if incoming_edge == -1:
|
| 95 |
+
print("No Route!")
|
| 96 |
+
sys.exit(1)
|
| 97 |
+
|
| 98 |
+
# Get the source vertex of this edge, and retrieve its point
|
| 99 |
+
from_vertex = graph.edge(incoming_edge).fromVertex()
|
| 100 |
+
point = graph.vertex(from_vertex).point()
|
| 101 |
+
route.append(point)
|
| 102 |
+
|
| 103 |
+
# Move to the previous vertex
|
| 104 |
+
cur_pos = from_vertex
|
| 105 |
+
|
| 106 |
+
# Add the final starting point to complete the route
|
| 107 |
+
route.append(start_point)
|
| 108 |
+
route.reverse() # Reverse because we built the route from end to start
|
| 109 |
+
|
| 110 |
+
# Define fields for the output layer
|
| 111 |
+
fields = QgsFields()
|
| 112 |
+
fields.append(QgsField("id", QVariant.Int)) # We'll assign an ID = 1 to this feature
|
| 113 |
+
|
| 114 |
+
# Define the GeoPackage file path for saving the shortest path result
|
| 115 |
+
output_path = "shortest_path.gpkg"
|
| 116 |
+
|
| 117 |
+
# Create the output vector layer writer (LineString geometry, same CRS as roads layer)
|
| 118 |
+
output_layer = QgsVectorFileWriter.create(
|
| 119 |
+
output_path, # File to save
|
| 120 |
+
fields, # Attribute schema
|
| 121 |
+
QgsWkbTypes.LineString, # Geometry type
|
| 122 |
+
layer.sourceCrs(), # Coordinate reference system
|
| 123 |
+
"GPKG" # Format
|
| 124 |
+
)
|
| 125 |
+
|
| 126 |
+
# Create a feature from the route geometry
|
| 127 |
+
feat = QgsFeature()
|
| 128 |
+
feat.setFields(fields)
|
| 129 |
+
feat.setAttribute("id", 1)
|
| 130 |
+
feat.setGeometry(QgsGeometry.fromPolylineXY(route))
|
| 131 |
+
|
| 132 |
+
# Add the feature to the output file
|
| 133 |
+
output_layer.addFeature(feat)
|
| 134 |
+
|
| 135 |
+
# Close and save the layer to disk
|
| 136 |
+
del output_layer
|
| 137 |
+
|
| 138 |
+
print(f"✅ Shortest path successfully saved to: {output_path}")
|
| 139 |
+
|
| 140 |
+
# Exit QGIS to clean up resources and avoid memory leaks
|
| 141 |
+
qgs.exitQgis()
|
shortest_path.gpkg
ADDED
|
Binary file (98.3 kB). View file
|
|
|
symbology-style.db
ADDED
|
Binary file (86 kB). View file
|
|
|