import networkx as nx
from qgis.core import *
import processing
from shapely.geometry import Polygon, Point, LineString
from shapely.ops import unary_union
def visibility_graph_2(polygon, start, end):
# Create graph
G = nx.Graph()
# Add start and end points
G.add_node((start.x, start.y))
G.add_node((end.x, end.y))
# Add polygon vertices to graph
for vertex in polygon.exterior.coords:
G.add_node(vertex)
# Connect visible nodes
for node1 in G.nodes:
for node2 in G.nodes:
if node1 != node2 and LineString([node1, node2]).intersects(polygon) and not LineString([node1, node2]).crosses(polygon):
G.add_edge(node1, node2, weight=Point(node1).distance(Point(node2)))
return G
def visibility_graph(polygon, start, end, num_steiner_points=10):
# Create graph
G = nx.Graph()
# Convert start and end points to tuples
start = (start.x, start.y)
end = (end.x, end.y)
# Add start and end points
G.add_node(start)
G.add_node(end)
# Add polygon vertices to graph
for vertex in polygon.exterior.coords:
G.add_node(vertex)
# Add Steiner points to graph
coords = list(polygon.exterior.coords[:-1])
for i in range(len(coords) - 1):
for j in range(num_steiner_points):
point = ((1 - j/num_steiner_points) * coords[i][0] + (j/num_steiner_points) * coords[i+1][0],
(1 - j/num_steiner_points) * coords[i][1] + (j/num_steiner_points) * coords[i+1][1])
G.add_node(point)
# Connect visible nodes
for node1 in G.nodes:
for node2 in G.nodes:
if node1 != node2:
line = LineString([node1, node2])
# Only add the edge if the line is entirely inside the polygon
if polygon.contains(line):
G.add_edge(node1, node2, weight=Point(node1).distance(Point(node2)))
return G
def shortest_path(polygon, start, end, path_shortest_path):
# Create visibility graph
print('creating visibility graph')
G = visibility_graph(polygon, start, end)
# Find shortest path
print('Find shortest path')
path = nx.dijkstra_path(G, source=(start.x, start.y), target=(end.x, end.y), weight='weight')
# Create a new memory layer to store the shortest path
print('Create a new memory layer to store the shortest path')
vl = QgsVectorLayer("LineString", "shortest_path", "memory")
pr = vl.dataProvider()
# Create a new feature
seg = QgsFeature()
# Add the shortest path to the feature as a polyline
seg.setGeometry(QgsGeometry.fromPolyline([QgsPoint(pt[0], pt[1]) for pt in path]))
# Add the feature to the layer
pr.addFeatures([seg])
# Update extent of the layer
vl.updateExtents()
# Write layer to a new shapefile
QgsVectorFileWriter.writeAsVectorFormat(vl, path_shortest_path, "utf-8", vl.crs(), "ESRI Shapefile")
return path
path_polygon = '/Users/manish.sahu/Downloads/polygon.shp'
path_point = '/Users/manish.sahu/Downloads/point.shp'
path_shortest_path = '/Users/manish.sahu/Downloads/path_shortest.shp'
polygon_layer = QgsVectorLayer(path_polygon, 'polygon', 'ogr')
points_layer = QgsVectorLayer(path_point, 'point', 'ogr')
if not layer.isValid():
print("Layer failed to load!")
else:
QgsProject.instance().addMapLayer(layer)
Assuming 'polygon_layer' and 'points_layer' are your loaded layers
###################################
Get the first (and only) feature from the polygon layer
polygon_feature = polygon_layer.getFeature(0)
Convert the feature to a Shapely Polygon
polygon = Polygon(polygon_feature.geometry().asMultiPolygon()[0])
polygons = [p for p in polygon_feature.geometry().asMultiPolygon()]
Filter out polygons with less than 3 points
selected_polygon = Polygon(polygons[0][0])
Get the features from the points layer
points_features = points_layer.getFeatures()
Convert the features to Shapely Points
points = [Point(feature.geometry().asPoint()) for feature in points_features]
The start and end points are the first and second points
start, end = points[0], points[1]
Calculate the shortest path and write it to a shapefile
path = shortest_path(selected_polygon, start, end, path_shortest_path)
###################################
Create a new layer from the shapefile
output_layer = QgsVectorLayer(path_shortest_path, "Shortest Path", "ogr")
Check if the layer is valid
if not output_layer.isValid():
print("Layer failed to load!")
else:
# Add the layer to the project
QgsProject.instance().addMapLayer(output_layer)