Initial commit

This commit is contained in:
2024-04-19 14:29:59 -05:00
parent a109417bbd
commit c46460ec36
44 changed files with 3259 additions and 1 deletions
+34
View File
@@ -0,0 +1,34 @@
from qgis.core import (QgsFeature, QgsField, QgsVectorLayer, QgsProject, QgsSpatialIndex)
print("Associating home points to drop points...")
# Load the NODES and HOME_POINT layers
nodes_layer = QgsProject.instance().mapLayersByName('NODES')[0]
home_points_layer = QgsProject.instance().mapLayersByName('HOME_POINTS')[0]
# Prepare the drop_point_id field for the HOME_POINT layer
home_points_layer.startEditing()
home_points_prov = home_points_layer.dataProvider()
home_points_prov.addAttributes([QgsField("drop_point_id", QVariant.Int)])
home_points_layer.updateFields()
# Build a spatial index for the NODES layer
index = QgsSpatialIndex()
node_features = {f.id(): f for f in nodes_layer.getFeatures()}
index.addFeatures(node_features.values())
# Find the nearest node for each home point and update the drop_point_id attribute
for hp in home_points_layer.getFeatures():
nearest_nodes = index.nearestNeighbor(hp.geometry().asPoint(), 1)
if nearest_nodes:
nearest_node_id = nearest_nodes[0]
nearest_node = node_features[nearest_node_id]
node_id = nearest_node["id"]
home_points_layer.changeAttributeValue(hp.id(),
home_points_layer.fields().indexOf("drop_point_id"),
node_id)
# Stop editing the HOME_POINT layer and save changes
home_points_layer.commitChanges()
print("Done.")
+77
View File
@@ -0,0 +1,77 @@
import osmnx as ox
import geopandas as gpd
from shapely import wkt
from shapely.geometry import Point
from qgis.core import QgsProject, QgsVectorLayer, QgsFeature, QgsGeometry, QgsWkbTypes, QgsField
print("Getting road centerlines...")
# Get the HOME_POINTS layer
home_points_layer = QgsProject.instance().mapLayersByName('HOME_POINTS')[0]
# Check if there's at least one feature in the layer
if home_points_layer.featureCount() > 0:
# Generate a buffer around each point
buffer_distance = 0.01 # change to your desired buffer distance
buffers = []
for feature in home_points_layer.getFeatures():
point = wkt.loads(feature.geometry().asWkt())
buffer_polygon = point.buffer(buffer_distance)
buffers.append(buffer_polygon)
# Combine all buffers into a single polygon
union_polygon = gpd.GeoSeries(buffers).unary_union
# Create PROJECTAREA layer in memory
project_area_layer = QgsVectorLayer("Polygon?crs=epsg:4326", "PROJECTAREA", "memory")
pr = project_area_layer.dataProvider()
# Start editing the layer
project_area_layer.startEditing()
# Create new feature for the layer
f = QgsFeature()
f.setGeometry(QgsGeometry.fromWkt(union_polygon.wkt))
pr.addFeature(f)
# Commit changes
project_area_layer.commitChanges()
# Add the layer to the Layers panel in QGIS
project_area_layer.setOpacity(0.3)
QgsProject.instance().addMapLayer(project_area_layer)
# Use OSMnx to download the street network
G = ox.graph_from_polygon(union_polygon, network_type='drive')
# Convert the graph into GeoDataFrames
gdf_nodes, gdf_edges = ox.graph_to_gdfs(G)
# Convert gdf_edges (GeoDataFrame) to WKT format for QGIS
wkt_list = gdf_edges['geometry'].to_wkt().tolist()
# Create a new layer for the street centerlines
vl = QgsVectorLayer("LineString?crs=epsg:4326", "CENTERLINES", "memory")
# Start editing the layer
vl.startEditing()
# Get the data provider
pr = vl.dataProvider()
# Create new features for the layer
for wkt in wkt_list:
f = QgsFeature()
f.setGeometry(QgsGeometry.fromWkt(wkt))
pr.addFeature(f)
# Commit changes
vl.commitChanges()
# Add the layer to the Layers panel in QGIS
QgsProject.instance().addMapLayer(vl)
else:
print("No points found in the HOME_POINTS layer.")
print("Done.")
+50
View File
@@ -0,0 +1,50 @@
from qgis.PyQt.QtCore import QVariant
from qgis.core import (QgsFeature, QgsField, QgsGeometry, QgsPointXY, QgsVectorLayer, QgsProject)
from geopy.distance import great_circle
# Constants
SEARCH_RADIUS = 300 / 3.28084 # converting feet to meters
COST_PER_METER = 1.5
# Locate the existing layers
home_points_layer = QgsProject.instance().mapLayersByName('HOME_POINTS')[0]
poles_layer = QgsProject.instance().mapLayersByName('POLES')[0]
edges_layer = QgsProject.instance().mapLayersByName('EDGES')[0]
# Define the data provider
pr = edges_layer.dataProvider()
# Iterate over every home point
for home_feature in home_points_layer.getFeatures():
home_point = home_feature.geometry().asPoint()
# Initialize nearest neighbor search
nearest_neighbor = None
nearest_distance = None
# Search for the nearest pole
for pole_feature in poles_layer.getFeatures():
pole_point = pole_feature.geometry().asPoint()
distance = great_circle((home_point.y(), home_point.x()), (pole_point.y(), pole_point.x())).meters
# If the pole is within 300 feet and it's closer than the previous nearest neighbor
if distance < SEARCH_RADIUS and (nearest_neighbor is None or distance < nearest_distance):
nearest_neighbor = pole_feature
nearest_distance = distance
# If a nearest neighbor was found within 300 feet
if nearest_neighbor is not None:
# Create a new edge feature
edge = QgsFeature()
edge.setGeometry(QgsGeometry.fromPolylineXY([home_point, nearest_neighbor.geometry().asPoint()]))
# Calculate cost
cost = nearest_distance * COST_PER_METER
# Set attributes
edge.setAttributes(['Aerial Drop', nearest_distance, cost])
pr.addFeature(edge)
# Update the layer's extent when new features have been added
edges_layer.updateExtents()
print("Done.")
+70
View File
@@ -0,0 +1,70 @@
from geopy.distance import great_circle
from qgis.core import (QgsFeature, QgsField, QgsGeometry, QgsPoint, QgsPointXY, QgsVectorLayer, QgsProject, QgsSpatialIndex)
# Locate the aerial path layer
aerial_path_layer = QgsProject.instance().mapLayersByName('AERIAL_PATH')[0]
# Locate the edges layer
edges_layer = QgsProject.instance().mapLayersByName('EDGES')[0]
# Define the data provider
pr = edges_layer.dataProvider()
# Initialize spatial index
index = QgsSpatialIndex()
# List to hold new features and their IDs
new_edges = []
features_by_id = {}
# Iterate over every feature of the AERIAL_PATH layer
for feature in aerial_path_layer.getFeatures():
# Get the geometry of the feature
geom = feature.geometry()
# Handle both 2D and 3D geometries
if geom.isMultipart():
vertices = [v for part in geom.asMultiPolyline() for v in part]
else:
vertices = geom.asPolyline()
for i in range(1, len(vertices)):
# Calculate the distance between the two points in feet
pt1 = vertices[i - 1]
pt2 = vertices[i]
length = great_circle((pt1.y(), pt1.x()), (pt2.y(), pt2.x())).feet
# Create a new feature in the EDGES layer for each vertex pair
edge = QgsFeature()
new_geom = QgsGeometry.fromPolyline([QgsPoint(pt1), QgsPoint(pt2)])
edge.setGeometry(new_geom)
edge.setAttributes(['Aerial', length, length * 2.5])
new_edges.append(edge)
pr.addFeature(edge)
index.insertFeature(edge)
features_by_id[edge.id()] = edge
# Start editing the edges layer
edges_layer.startEditing()
# Adjust intersecting line endpoints to the average intersection point
for edge_id, edge in features_by_id.items():
endpoints = [QgsPoint(point) for point in edge.geometry().asPolyline()]
for i in [0, -1]: # Check both endpoints
# Find nearby endpoints (within a small threshold)
search_radius = 0.00005
nearby_ids = index.intersects(QgsGeometry.fromPointXY(QgsPointXY(endpoints[i])).buffer(search_radius, 8).boundingBox())
nearby_endpoints = [QgsPoint(point) for id in nearby_ids for point in features_by_id[id].geometry().asPolyline() if QgsPoint(point).distance(endpoints[i]) < search_radius]
if len(nearby_endpoints) > 2:
# Update endpoint to the average nearby endpoint
avg_x = sum(point.x() for point in nearby_endpoints) / len(nearby_endpoints)
avg_y = sum(point.y() for point in nearby_endpoints) / len(nearby_endpoints)
endpoints[i] = QgsPoint(avg_x, avg_y) # Create new QgsPoint object
# Update geometry in the layer
edges_layer.changeGeometry(edge.id(), QgsGeometry.fromPolyline(endpoints))
# Commit the changes and update the layer's extent when new features have been added
edges_layer.commitChanges()
edges_layer.updateExtents()
print("Done.")
+95
View File
@@ -0,0 +1,95 @@
# Import required libraries
import networkx as nx
import time
from qgis.PyQt.QtCore import QVariant
from qgis.core import QgsProject, QgsVectorLayer, QgsFeature, QgsField, QgsGeometry, QgsPoint
import heapq
# Get layers
edges_layer = QgsProject.instance().mapLayersByName('EDGES')[0]
nodes_layer = QgsProject.instance().mapLayersByName('NODES')[0]
home_points_layer = QgsProject.instance().mapLayersByName('HOME_POINTS')[0]
# Build the graph
G = nx.Graph()
nodes_data = {feature['id']: feature.geometry() for feature in nodes_layer.getFeatures()} # store node geometries by id
edges_data = {(feature['start_node'], feature['end_node']): (feature.geometry(), feature['length'], feature['cost']) for feature in edges_layer.getFeatures()} # store edge geometries by node pairs
for feature in nodes_layer.getFeatures():
G.add_node(feature['id'])
for feature in edges_layer.getFeatures():
start_node = feature['start_node'] # assuming edges layer has start_node & end_node fields
end_node = feature['end_node']
G.add_edge(start_node, end_node, weight=feature['cost'], type=feature['type'], length=feature['length'], cost=feature['cost'])
# Identify home nodes
home_nodes = {feature['drop_point_id'] for feature in home_points_layer.getFeatures() if feature['drop_point_id'] in G}
# Create a subgraph of G that only includes the home nodes and any nodes that connect them
subgraph = nx.Graph()
visited = set()
# Start at any 'home' node
start = next(iter(home_nodes))
visited.add(start)
counter = 0
total = len(home_nodes)
process_times = []
while home_nodes.difference(visited):
start_time = time.time()
# Find the nearest 'home' node that is not in our tree yet
next_node = min(
(node for node in home_nodes if node not in visited),
key=lambda node: nx.shortest_path_length(G, start, node, weight='cost')
)
# Create a subgraph excluding other home nodes except start and next_node
G_sub = G.copy()
remove_nodes = home_nodes - {start} - {next_node}
G_sub.remove_nodes_from(remove_nodes)
# Add the shortest path to this node to our tree
shortest_path = nx.shortest_path(G_sub, start, next_node, weight='cost')
subgraph.add_edges_from(zip(shortest_path, shortest_path[1:]))
visited.add(next_node)
start = next_node
end_time = time.time()
process_times.append(end_time - start_time)
counter += 1
avg_time = sum(process_times) / len(process_times)
remaining = (total - counter) * avg_time
hours, remainder = divmod(remaining, 3600)
minutes, seconds = divmod(remainder, 60)
print(f'Processing home node {counter} of {total}.\nEstimated time remaining: {int(hours)} hours, {int(minutes)} minutes, and {int(seconds)} seconds')
# Create a minimum spanning tree from the subgraph
mst = nx.minimum_spanning_tree(subgraph, weight='cost')
# Prepare the NETWORK layer
network_layer = QgsVectorLayer("LineString", "NETWORK", "memory")
network_prov = network_layer.dataProvider()
# Add 'id', 'type', 'length', and 'cost' fields to the NETWORK layer
network_prov.addAttributes([QgsField("id", QVariant.Int), QgsField("type", QVariant.String), QgsField("length", QVariant.Double), QgsField("cost", QVariant.Double)])
network_layer.updateFields()
# Add edges from the minimum spanning tree to the NETWORK layer
for edge in mst.edges():
edge_geometry, edge_length, edge_cost = edges_data[(edge[0], edge[1])] if (edge[0], edge[1]) in edges_data else edges_data[(edge[1], edge[0])] # fetch geometry, length, cost by node pair
edge_type = G.get_edge_data(edge[0], edge[1])['type'] # get edge 'type' from the original graph G
network_feature = QgsFeature()
network_feature.setGeometry(edge_geometry)
network_feature.setAttributes([edge[0], edge_type, edge_length, edge_cost]) # set the type, length, cost attribute from edge data
network_prov.addFeature(network_feature)
# Update NETWORK layer
network_layer.updateExtents()
# Add NETWORK layer to Layers panel
QgsProject.instance().addMapLayer(network_layer)
print("Done.")
+78
View File
@@ -0,0 +1,78 @@
import itertools
from qgis.PyQt.QtCore import QVariant
from qgis.core import (QgsFeature, QgsField, QgsGeometry, QgsPointXY,
QgsVectorLayer, QgsProject, QgsVectorDataProvider)
print("Creating nodes...")
# Load the EDGES layer
edges_layer = QgsProject.instance().mapLayersByName('EDGES')[0]
# Prepare the NODES layer
nodes_layer = QgsVectorLayer("Point", "NODES", "memory")
prov = nodes_layer.dataProvider()
# Add 'id' field to the NODES layer
prov.addAttributes([QgsField("id", QVariant.Int)])
nodes_layer.updateFields()
# Prepare the start_node and end_node fields for the EDGES layer
edges_layer.startEditing()
edges_prov = edges_layer.dataProvider()
edges_prov.addAttributes([QgsField("start_node", QVariant.Int),
QgsField("end_node", QVariant.Int)])
edges_layer.updateFields()
# To store the unique nodes
unique_nodes = []
id_counter = itertools.count(start=1)
node_id_map = {}
total_features = edges_layer.featureCount()
count = 0
for feature in edges_layer.getFeatures():
count += 1
print(f'Processing feature {count} of {total_features} ({(count/total_features)*100:.2f}%)')
# Get the start and end points
start_point = feature.geometry().asPolyline()[0]
end_point = feature.geometry().asPolyline()[-1]
# Check if nodes are unique and add them to NODES layer
if start_point not in unique_nodes:
unique_nodes.append(start_point)
node_id = next(id_counter)
node_feature = QgsFeature()
node_feature.setGeometry(QgsGeometry.fromPointXY(start_point))
node_feature.setAttributes([node_id])
prov.addFeature(node_feature)
node_id_map[start_point] = node_id
if end_point not in unique_nodes:
unique_nodes.append(end_point)
node_id = next(id_counter)
node_feature = QgsFeature()
node_feature.setGeometry(QgsGeometry.fromPointXY(end_point))
node_feature.setAttributes([node_id])
prov.addFeature(node_feature)
node_id_map[end_point] = node_id
# Populate the start_node and end_node fields for the EDGES layer
edges_layer.changeAttributeValue(feature.id(),
edges_layer.fields().indexOf("start_node"),
node_id_map[start_point])
edges_layer.changeAttributeValue(feature.id(),
edges_layer.fields().indexOf("end_node"),
node_id_map[end_point])
# Stop editing the EDGES layer and save changes
edges_layer.commitChanges()
# Add the layer to the Layers panel
QgsProject.instance().addMapLayer(nodes_layer)
print("Done.")
+52
View File
@@ -0,0 +1,52 @@
from qgis.PyQt.QtCore import QVariant
from qgis.core import (QgsFeature, QgsField, QgsGeometry, QgsPointXY, QgsVectorLayer, QgsProject)
# Locate the edges layer
edges_layer = QgsProject.instance().mapLayersByName('EDGES')[0]
# Create a new temporary layer for the poles
poles_layer = QgsVectorLayer('Point?crs=epsg:4326', 'POLES', 'memory')
# Define the data provider
pr = poles_layer.dataProvider()
# Add a new field to the new layer
pr.addAttributes([QgsField('id', QVariant.Int)])
poles_layer.updateFields()
# Store coordinates of existing poles to avoid duplicates
existing_poles = set()
# Iterate over every feature of the EDGES layer
for feature in edges_layer.getFeatures():
# Only consider edges of type 'Aerial'
if feature['type'] == 'Aerial':
# Get the geometry of the feature
geom = feature.geometry()
# Handle both 2D and 3D geometries
if geom.isMultipart():
vertices = [v for part in geom.asMultiPolyline() for v in part]
else:
vertices = geom.asPolyline()
for i, vertex in enumerate(vertices):
# Do not create a new pole if one already exists at this location
vertex_coordinates = (vertex[0], vertex[1])
if vertex_coordinates in existing_poles:
continue
# Create a new feature in the POLES layer for each vertex
pole = QgsFeature()
pole.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(*vertex_coordinates)))
pole.setAttributes([i])
pr.addFeature(pole)
# Remember this location so we don't create a duplicate pole
existing_poles.add(vertex_coordinates)
# Update the layer's extent when new features have been added
poles_layer.updateExtents()
# Add the new layer to the project
QgsProject.instance().addMapLayer(poles_layer)
+55
View File
@@ -0,0 +1,55 @@
from qgis.PyQt.QtCore import QVariant
from qgis.core import (QgsFeature, QgsField, QgsGeometry, QgsPointXY, QgsVectorLayer, QgsProject)
from geopy.distance import great_circle
# Constants
SEARCH_RADIUS = 50
COST_PER_METER = 19.10
# Locate the existing layers
poles_layer = QgsProject.instance().mapLayersByName('POLES')[0]
edges_layer = QgsProject.instance().mapLayersByName('EDGES')[0]
# Define the data provider
pr = edges_layer.dataProvider()
# Iterate over every pole
for pole_feature in poles_layer.getFeatures():
pole_point = pole_feature.geometry().asPoint()
# Initialize nearest neighbor search
nearest_vertex = None
nearest_distance = None
# Search for the nearest vertex in underground edges
for edge_feature in edges_layer.getFeatures():
if edge_feature['type'] == 'Underground':
# Extract vertices
geom = edge_feature.geometry()
vertices = geom.asPolyline() if not geom.isMultipart() else [v for part in geom.asMultiPolyline() for v in part]
for vertex in vertices:
vertex_point = QgsPointXY(vertex[0], vertex[1])
distance = great_circle((pole_point.y(), pole_point.x()), (vertex_point.y(), vertex_point.x())).meters
# If the vertex is within 50 feet and it's closer than the previous nearest neighbor
if distance < SEARCH_RADIUS and (nearest_vertex is None or distance < nearest_distance):
nearest_vertex = vertex_point
nearest_distance = distance
# If a nearest vertex was found within 50 feet
if nearest_vertex is not None:
# Create a new edge feature
edge = QgsFeature()
edge.setGeometry(QgsGeometry.fromPolylineXY([pole_point, nearest_vertex]))
# Calculate cost
cost = nearest_distance * COST_PER_METER
# Set attributes
edge.setAttributes(['Transition', nearest_distance, cost])
pr.addFeature(edge)
# Update the layer's extent when new features have been added
edges_layer.updateExtents()
print("Done.")
+203
View File
@@ -0,0 +1,203 @@
import geopandas as gpd
from shapely.geometry import LineString, Point
from shapely.affinity import translate
from geopy.distance import great_circle
from qgis.core import (
QgsProject, QgsVectorLayer, QgsFeature, QgsGeometry,
QgsWkbTypes, QgsField, QgsFields
)
underground_cpf = 19.00
buried_drop_cpf = 3.00
def extend_line(line, extension_length):
"""Extend a line by a given length."""
start_point = line.coords[0]
end_point = line.coords[-1]
# Calculate the line's direction
dx = end_point[0] - start_point[0]
dy = end_point[1] - start_point[1]
# Calculate the extension length along each axis
length = math.sqrt(dx**2 + dy**2)
extension_dx = dx / length * extension_length
extension_dy = dy / length * extension_length
# Create the extended line by translating the end point
extended_line = LineString([start_point, translate(line, extension_dx, extension_dy).coords[-1]])
return extended_line
def closest_point_on_line(point, lines):
"""Find closest point on the closest line to the given point."""
closest_line = min(lines, key=point.distance)
return closest_line.interpolate(closest_line.project(point))
# Load your layers
homes_layer = QgsProject.instance().mapLayersByName('HOME_POINTS')[0]
centerlines_layer = QgsProject.instance().mapLayersByName('CENTERLINES')[0]
# Convert layers to GeoDataFrames
gdf_homes = gpd.GeoDataFrame.from_features([f for f in homes_layer.getFeatures()], crs=homes_layer.crs().toWkt())
gdf_centerlines = gpd.GeoDataFrame.from_features([f for f in centerlines_layer.getFeatures()], crs=centerlines_layer.crs().toWkt())
num_homes = len(gdf_homes)
print("Number of home points:", num_homes)
# Create an in-memory layer to store the connecting lines
vl = QgsVectorLayer("LineString?crs=epsg:4326", "DROPS", "memory")
vl_ext = QgsVectorLayer("LineString?crs=epsg:4326", "DROPS_EXT", "memory")
pr = vl.dataProvider()
pr_ext = vl_ext.dataProvider()
print("Creating drops...")
# For each home, find the nearest point on the centerlines and create a line
for idx, home in gdf_homes.iterrows():
nearest_point = closest_point_on_line(home.geometry, gdf_centerlines.geometry)
line = LineString([(home.geometry.x, home.geometry.y), (nearest_point.x, nearest_point.y)])
# Add the line to the "DROPS" layer
feat = QgsFeature()
feat.setGeometry(QgsGeometry.fromWkt(line.wkt))
pr.addFeature(feat)
# Extend the line by 5% of its length beyond the nearest point and add it to the "DROPS_EXT" layer
line_ext = extend_line(line, line.length*0.05)
feat_ext = QgsFeature()
feat_ext.setGeometry(QgsGeometry.fromWkt(line_ext.wkt))
pr_ext.addFeature(feat_ext)
# After processing each home, print the progress
progress = (idx + 1) / num_homes * 100 # Progress as percentage
print(f'\rProgress: {progress:.2f}%', end='') # Print the progress
# Update the layer's extent and add it to the project
vl.updateExtents()
QgsProject.instance().addMapLayer(vl)
print("\nSplitting centerlines at drops...")
# Define the splitting function
def split_with_lines(input_layer, split_layer, output):
"""
This function will use native:splitwithlines processing algorithm
from QGIS's processing toolbox to split features in one layer using lines
from another layer.
"""
params = {
'INPUT': input_layer,
'LINES': split_layer,
'OUTPUT': output
}
res = processing.run("native:splitwithlines", params)
return res
# Use the 'DROPS_EXT' layer to split features in the 'CENTERLINES' layer
centerlines_split = split_with_lines(centerlines_layer, vl_ext, 'memory:')
# Add the split layer to the project instance
split_layer = centerlines_split['OUTPUT']
#split_layer.setName('EDGES')
#QgsProject.instance().addMapLayer(split_layer)
print("Deleting duplicate edges...")
# Define the delete duplicates function
def delete_duplicates(input_layer, output):
"""
This function will use qgis:deleteduplicategeometries processing algorithm
from QGIS's processing toolbox to remove duplicate geometries in a layer.
"""
params = {
'INPUT': input_layer,
'OUTPUT': output
}
res = processing.run("qgis:deleteduplicategeometries", params)
return res
# Use the delete_duplicates function on the split layer
split_layer_no_duplicates = delete_duplicates(split_layer, 'memory:')
# Add the split layer without duplicates to the project instance
split_layer_nd = split_layer_no_duplicates['OUTPUT']
# Get the data provider
dp = split_layer_nd.dataProvider()
# Add new fields
dp.addAttributes([
QgsField("type", QVariant.String),
QgsField("length", QVariant.Double),
QgsField("cost", QVariant.Double)
])
# Update to apply the changes
split_layer_nd.updateFields()
# Now you have to update all features to have 'type' as 'Underground'
# Start editing
split_layer_nd.startEditing()
for feature in split_layer_nd.getFeatures():
# set type to 'Underground'
feature.setAttribute('type', 'Underground')
# set length to the great circle distance of the geometry
# convert coordinates to tuples (latitude, longitude)
start_point = feature.geometry().asPolyline()[0]
end_point = feature.geometry().asPolyline()[-1]
start_coordinates = (start_point.y(), start_point.x())
end_coordinates = (end_point.y(), end_point.x())
# calculate great circle distance in feet
length_in_feet = great_circle(start_coordinates, end_coordinates).feet
feature.setAttribute('length', length_in_feet)
# set cost to a computed value, for example length * underground_cpf
feature.setAttribute('cost', length_in_feet * underground_cpf)
# update the feature in the layer
split_layer_nd.updateFeature(feature)
# Convert 'DROPS' layer to GeoDataFrame
gdf_drops = gpd.GeoDataFrame.from_features([f for f in vl.getFeatures()], crs=vl.crs().toWkt())
# Add the 'DROPS' to the 'EDGES'
for idx, drop in gdf_drops.iterrows():
# Create a new feature for the drop
drop_feature = QgsFeature(dp.fields())
# Convert drop's Shapely geometry to QgsGeometry
qgis_geom = QgsGeometry.fromWkt(drop.geometry.wkt)
drop_feature.setGeometry(qgis_geom)
# Add the drop feature to the 'EDGES' layer
dp.addFeature(drop_feature)
# Set 'type' to 'Buried Drop'
drop_feature.setAttribute('type', 'Buried Drop')
# Calculate the great circle length of the drop in feet
start_point = drop.geometry.coords[0]
end_point = drop.geometry.coords[-1]
start_coordinates = (start_point[1], start_point[0]) # Note the reverse order (y, x) for geopy
end_coordinates = (end_point[1], end_point[0]) # Note the reverse order (y, x) for geopy
length_in_feet = great_circle(start_coordinates, end_coordinates).feet
drop_feature.setAttribute('length', length_in_feet)
# Calculate cost as length * buried_drop_cpf
drop_feature.setAttribute('cost', length_in_feet * buried_drop_cpf)
# Update the feature in the layer
split_layer_nd.updateFeature(drop_feature)
# Commit changes
split_layer_nd.commitChanges()
split_layer_nd.setName('EDGES')
QgsProject.instance().addMapLayer(split_layer_nd)
print("Done.")