EdgeNetwork
An EdgeNetwork is a single object that contains multiple discontinuous lines. A common example is a single object representing a series of contour lines. Unlike a Polygon or a Polyline, you need to consider both point and edge arrays (as opposed to points only) to describe how disparate line-work within an EdgeNetwork joins together.
Some examples in this page use non-standard external libraries.
Libraries referenced:
EdgeNetwork examples
Creating an edge network
This example demonstrates creation of an EdgeNetwork with five metre separation between line joins.
from mapteksdk.project import Project
from mapteksdk.data import EdgeNetwork
import numpy as np
proj = Project() # Connect to default project
with proj.new("/cad/edge network", EdgeNetwork, overwrite=True) as edgenetwork:
# Create a green flat rectangle with 5m spacing between joins
edgenetwork.points = np.array([[0, 0, 0], [0, 50, 0],
[0, 55, 0], [100, 55, 0],
[100, 50, 0], [100, 0, 0],
[95, -5, 0], [0, -5, 0]])
edgenetwork.edges = np.array([[0, 1], [2, 3], [4, 5], [6, 7]])
edgenetwork.point_colours[0:2] = [0,255,0,255] # Green
edgenetwork.point_colours[2:4] = [255,0,0,255] # Red
edgenetwork.point_colours[4:6] = [0,0,255,255] # Blue
edgenetwork.point_colours[6:8] = [255,255,255,255] # White
Creating a joined edge network
This example creates the same points as above but with all edges joined (which could more easily be represented as a Polygon object).
from mapteksdk.project import Project
from mapteksdk.data import EdgeNetwork
import numpy as np
proj = Project() # Connect to default project
with proj.new("/cad/edge network (joined)", EdgeNetwork, overwrite=True) as edgenetwork:
# Create a green flat rectangle with 5m spacing between joins
edgenetwork.points = np.array([[0, 0, 0], [0, 50, 0],
[0, 55, 0], [100, 55, 0],
[100, 50, 0], [100, 0, 0],
[95, -5, 0], [0, -5, 0]])
edgenetwork.edges = np.array([[0, 1], [1, 2], [2, 3], [3, 4],
[4, 5], [5, 6], [6, 7], [7, 0]])
edgenetwork.point_colours[0:2] = [0,255,0,255] # Green
edgenetwork.point_colours[2:4] = [255,0,0,255] # Red
edgenetwork.point_colours[4:6] = [0,0,255,255] # Blue
edgenetwork.point_colours[6:8] = [255,255,255,255] # White
Creating an edge network from a DXF file
This example will import a DXF file containing several polylines (open) and polygons (closed) and create a single EdgeNetwork object with the data.
Note: The following example refers to a dxf file that can be downloaded here: contours.dxf
Important! This makes use of the ezdxf library which is not included by default with Python. You may need to install it before trying the script.
from mapteksdk.project import Project
from mapteksdk.data import EdgeNetwork
import ezdxf
# Use the path you have saved the example shp file:
file_path = "F:\\Python SDK Help\\data\\contours.dxf"
dxf = ezdxf.readfile(file_path)
# Temporary storage for data to go into our EdgeNetwork
points, edges, colours = [], [], []
# Read the dxf modelspace data
for item in dxf.modelspace():
# We're only interested in polyline objects within the file
if item.dxftype() == 'POLYLINE':
# Note the index this line started at for polygon closure
this_line_start = len(points)
# Iterate each of the points within the polyline
for i in range(len(item.vertices)):
vert = item.vertices[i]
points.append([vert.dxf.location.x, vert.dxf.location.y, vert.dxf.location.z])
# Convert RGB provided tuple to RGBA list with 255 alpha value
rgb = list(vert.rgb)
rgb.append(255)
colours.append(rgb)
# For an EdgeNetwork we don't want to join every single point to the next.
# Once we're up to the last point, we don't need to append an edge join for it,
# or we join it back to the start of the line to make it a polygon.
if i < len(item.vertices)-1:
edges.append([len(points)-1, len(points)])
else:
# If the polyline is closed, then join it back to the start,
# otherwise leave a break for the next line
if item.is_closed:
edges.append([len(points)-1, this_line_start])
proj = Project() # Connect to default project
with proj.new("/cad/dxf file", EdgeNetwork, overwrite=True) as edge_network:
edge_network.points = points
edge_network.edges = edges
edge_network.point_colours = colours
Converting an EdgeNetwork into a PointSet
This example demonstrates extracting the points out of the object created above and putting them into a PointSet. The methodology is identical for other objects, such as extracting points from a surface.
from mapteksdk.project import Project
from mapteksdk.data import EdgeNetwork, PointSet
import numpy as np
proj = Project() # Connect to default project
input_path = "/cad/dxf file"
output_path = "/cad/dxf file - points"
if proj.find_object(input_path):
with proj.read(input_path) as edge_network:
with proj.new(output_path, PointSet, overwrite=True) as points:
points.points = edge_network.points
points.point_colours = edge_network.point_colours
else:
print("Couldn't find existing object '{}'".format(input_path))
Assigning attributes to edges and points for colouring
This example shows how to apply attributes to edge and point primitives. The attributes applied are the X, Y, Z value of each point (to allow colouring by X, Y, or Z) as well as calculating the distance of every edge and applying that as an attribute to the edges.
The animation below illustrates how to access and use these attributes in the software. In the SDK to do similar you will need to use colour maps (represented as Legends in the software).
from mapteksdk.project import Project
from mapteksdk.data import EdgeNetwork
import math
proj = Project() # Connect to default project
path = "/cad/dxf file"
edge_distances = []
for obj in proj.get_selected():
with proj.edit(obj) as edge_network:
# Store each of the coordinate components for every point as an attribute
edge_network.point_attributes.save_attribute("z values", edge_network.points[:,2])
edge_network.point_attributes.save_attribute("y values", edge_network.points[:,1])
edge_network.point_attributes.save_attribute("x values", edge_network.points[:,0])
# Calculate the distance for every edge
for edge in edge_network.edges: # [i1, i2]
p1 = edge_network.points[edge[0]] # [x, y, z]
p2 = edge_network.points[edge[1]] # [x, y, z]
dist = math.sqrt(((p1[0]-p2[0])**2) + ((p1[1]-p2[1])**2) + ((p1[2]-p2[2])**2))
edge_distances.append(dist)
# Save each length as an attribute of the associated edge
edge_network.edge_attributes.save_attribute("edge_len", edge_distances)
Removing points below a height
This example will remove all points from the above object that are below a specified height.
from mapteksdk.project import Project
from mapteksdk.data import EdgeNetwork
import numpy as np
proj = Project() # Connect to default project
path = "/cad/dxf file"
if proj.find_object(path):
with proj.edit(path) as edge_network:
# Get index of all points with a z value below 220
height_filter = 220
# np.where will provide the indices of all items matching the criteria
points_below = np.where(edge_network.points[:,2] < height_filter)
# Built in remove_points function will allow removal of one or more points
# The parameter uptdate_immediately defaults to true and will refresh
# related arrays to reflect the changes
edge_network.remove_points(points_below)
else:
print("Couldn't find existing object '{}'".format(input_path))
Removing edges by length
Similar to the above example, this will remove edges (rather than points) that are longer than a specified length.
from mapteksdk.project import Project
from mapteksdk.data import EdgeNetwork
import numpy as np
import math
proj = Project() # Connect to default project
path = "/cad/dxf file"
edge_distances = []
if proj.find_object(path):
with proj.edit(path) as edge_network:
# Calculate the distance for every edge
for edge in edge_network.edges: # [i1, i2]
p1 = edge_network.points[edge[0]] # [x, y, z]
p2 = edge_network.points[edge[1]] # [x, y, z]
dist = math.sqrt(((p1[0]-p2[0])**2) + ((p1[1]-p2[1])**2) + ((p1[2]-p2[2])**2))
edge_distances.append(dist)
# Get index of all edges with a length above 20
length_filter = 20
# np.where will provide the indices of all items matching the criteria
edge_distances = np.array(edge_distances)
long_edges = np.where(edge_distances > length_filter)
# Built in remove_edges function will allow removal of one or more edges.
# The parameter uptdate_immediately defaults to true and will refresh
# related arrays to reflect the changes
edge_network.remove_edges(long_edges)
else:
print("Couldn't find existing object '{}'".format(input_path))
Deleting selected edges
from mapteksdk.project import Project
from mapteksdk.data import EdgeNetwork
import numpy as np
proj = Project() # Connect to default project
for item in proj.get_selected():
if item.is_a(EdgeNetwork):
with proj.edit(item) as edge_network:
# Get list of indices for selected edges
selected_edges = np.where(edge_network.edge_selection)
# Remove selected edge indices
edge_network.remove_edges(selected_edges)
Colouring edges by grade thresholds
This example shows how to colour each edge segment throughout an EdgeNetwork, a Polyline or a Polygon, based on the grade between the first and second point of the edge and minimum/maximum grade thresholds defined.
from mapteksdk.project import Project
import numpy as np
def calculate_percentage_grade(start:np.ndarray, end:np.ndarray):
"""Calculate the percentage grade of the line between points start and end.
Parameters
----------
start (ndarray): Numpy array with three elements representing the start point.
end (ndarray): Numpy array with three elements representing the end point.
Returns
-------
float: The percentage grade between the two points.
"""
# Make sure end is always the higher point.
#if start[2] > end[2]:
## temp = start
# start = end
# end = temp
# Rise is just difference in heights of the two points.
rise = end[2] - start[2]
# Run is distance between start and stop but ignoring the z component.
run_vector = start - end
run = np.linalg.norm(run_vector[:2])
return 100 * (rise / run)
if __name__ == "__main__":
proj = Project()
input("Select EdgeNetwork and press any key to continue..")
colour_above_max_grade = [255, 0, 0, 255] # Red
colour_below_min_grade = [0, 0, 255, 255] # Blue
colour_within_threshold = [0, 255, 0, 255] # Green
min_grade, max_grade = 0., 20. # min % and max % for colour ranges
selection = proj.get_selected()
for item in selection:
with proj.edit(item) as edges:
# By checking if the object has an 'edges' attribute,
# we can use this on EdgeNetwork, Polyline, Polygon and Surface
if hasattr(edges, 'edges'):
for i in range(edges.edge_count):
this_edge = edges.edges[i]
p1 = edges.points[this_edge[0]]
p2 = edges.points[this_edge[1]]
grade = calculate_percentage_grade(p1, p2)
# Note: If creating a new EdgeNetwork, Polyline or Polygon, you will
# need to call edges.save() to populate the edge array in the database
# before assigning edge colours.
if grade > max_grade:
# Colouring edge_colours instead of points will provide
# a solid transition between colour changes.
edges.edge_colours[i] = colour_above_max_grade
elif grade < min_grade:
edges.edge_colours[i] = colour_below_min_grade
else:
edges.edge_colours[i] = colour_within_threshold
Exploding an EdgeNetwork into a set of polylines
This example takes an EdgeNetwork and traces connected lines throughout it to generate a set of output Polyline objects. It works by recursively tracing from a start edge, finding edges with connected indices and storing them in a set so that they aren't revisited. Once all edges have been visited, results are returned and the polylines are built.
This works well for linework drawn by humans (such as haul road networks), but may not work in all cases or all edge networks generated by certain software tools (such as contour linework).
Note: Data used for this example can be found here: EdgeNetworkExample.dxf
from mapteksdk.data import EdgeNetwork, Polyline
from mapteksdk.project import Project
import numpy as np
def explode_edgenetwork(edgenetwork, proj=None):
""" Converts EdgeNetwork (single object with several disconnected lines) into
a series of Polylines.
The results are stored under /{EdgeNetwork path}_exploded/Line x
For each start edge this will recursively trace it until it has no more neighbours.
While tracing it, it will mark traced edges as seen so they aren't revisited later.
Returns:
dict[id]=ndarray(points)
"""
def trace_from(this_edge, edges, points, seen_edges, result):
"""Recursively trace edges from this_edge that has not yet been seen
Args:
this_edge (int): index of current edge to trace from
edges (ndarray): array of edges
seen_edges (Set): list of visited edges
result (ndarray): array of edges to join for this line
Returns:
tuple(ndarray(edges), set(seen_edges))
"""
# Neighbour = start of next edge is end of this edge
neighbour = np.where(edges[:,0] == edges[this_edge][1])
if len(neighbour) > 0:
for val, j in np.ndenumerate(neighbour):
if j in seen_edges: continue
seen_edges.add(j)
result = np.hstack((result, edges[j]))
return trace_from(j, edges, points, seen_edges, result)
return (result, seen_edges)
# Store individual lines in results
results = {}
# Store set of seen edge indices to prevent re-tracing
seen_edges = set()
line_id = 0
for i in range(edgenetwork.edge_count):
if i in seen_edges: continue
line_id += 1
seen_edges.add(i)
this_edges, seen_edges = trace_from(i,
edgenetwork.edges,
edgenetwork.points,
seen_edges,
edgenetwork.edges[i])
# As edges will create duplicates on subsequent edges,
# we will remove consecutive duplicates [0,1,1,2,2,3,1,1,0] = [0,1,2,3,1,0]
selection = np.ones(this_edges.shape[0], dtype=bool)
selection[1:] = this_edges[1:] != this_edges[:-1]
this_edges = this_edges[selection]
# Convert the edge list to a line of x,y,z coordinates
this_line = edgenetwork.points[this_edges]
results[f"Line_{line_id}"] = this_line
if proj:
with proj.new_or_edit(f"{edgenetwork.id.path}_exploded/Line {line_id}", Polyline) as test:
test.points = this_line
print(f"Saved Polyline {edgenetwork.id.path}_exploded/Line {line_id}")
return results
if __name__ == "__main__":
proj = Project()
input("Select EdgeNetwork and press any key to continue..")
selection = proj.get_selected()
for item in selection:
if item.is_a(EdgeNetwork):
with proj.read(item) as edges:
explode_edgenetwork(edges, proj)