Edge Networks
An edge network is a single object representing multiple discontinuous lines, such as contour lines. An edge network is represented in the SDK with the EdgeNetwork class. Unlike polygons or polylines, edge networks use both point and edge arrays to describe how disparate lines are connected.
Edge network examples
The following examples demonstrate various ways to create, manipulate, and analyse edge networks using the SDK.
Some examples on this page use non-standard external libraries. You may need to install these separately. External libraries include the following:
Creating an edge network
This example demonstrates how to create an EdgeNetwork with a specific arrangement of points and edges. The edges in this example are disjoint, meaning that no two edges share the same endpoint.
from mapteksdk.project import Project from mapteksdk.data import EdgeNetwork import numpy as np project = Project() # Connect to default project with project.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 an edge network using the same points as in the previous example, but with all edges joined to form a continuous loop. This approach effectively creates a shape that could be more easily represented using a Polygon object.
from mapteksdk.project import Project from mapteksdk.data import EdgeNetwork import numpy as np project = Project() # Connect to default project with project.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 demonstrates how to import a DXF file containing several polylines (open) and polygons (closed) and create a single EdgeNetwork object with the data.
Note: To run this example script, first download the example DXF file contours.dxfto a folder on your system. The script references the folder location F:\Python SDK Help\data\. You will need to update this location in the script to match the folder where you extracted the file to.
Note: This example uses the ezdxf library, which is not included by default with Python. You may need to install it using pip install ezdxf before running 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]) project = Project() # Connect to default project with project.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 how to extract points from an EdgeNetwork object and create a PointSet object with those points. The same approach can be used to extract points from other objects, such as surfaces.
from mapteksdk.project import Project from mapteksdk.data import PointSet project = Project() # Connect to default project input_path = "/cad/dxf file" output_path = "/cad/dxf file - points" if project.find_object(input_path): with project.read(input_path) as edge_network: with project.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 demonstrates how to assign attributes to edge and point primitives in order to facilitate colouring based on these attributes. Specifically, it assigns the X, Y, and Z coordinates of each point as attributes and calculates the distance of each edge, which is also assigned as an attribute.
from mapteksdk.project import Project import math project = Project() # Connect to default project path = "/cad/dxf file" edge_distances = [] for obj in project.get_selected(): with project.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)
The animation below illustrates how to access and use these attributes in the PointStudio. In the SDK, you can use colour maps (called legends in the application) to colour the object by these attributes (see Colour Maps).
Removing points below a given height
This example demonstrates how to remove points from an EdgeNetwork object that are below a given height.
from mapteksdk.project import Project import numpy as np project = Project() # Connect to default project path = "/cad/dxf file" if project.find_object(path): with project.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(path))
Removing edges by length
This example demonstrates how to remove edges (rather than points) from an EdgeNetwork object that are longer than a specified length.
from mapteksdk.project import Project import numpy as np import math project = Project() # Connect to default project path = "/cad/dxf file" edge_distances = [] if project.find_object(path): with project.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(path))
Deleting selected edges
This example demonstrates how to delete edges selected by the user from an edge network.
from mapteksdk.project import Project from mapteksdk.data import EdgeNetwork import numpy as np project = Project() # Connect to default project for item in project.get_selected(): if item.is_a(EdgeNetwork): with project.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 colours edges in selected edge network, polyline, and polygon objects based on the percentage grade (or slope) calculated between the two endpoints of each edge. It uses predefined minimum and maximum grade thresholds to determine the colour.
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__": project = 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 = project.get_selected() for item in selection: with project.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 edge network into a set of polylines
This example demonstrates how to convert an EdgeNetwork into a collection of Polyline objects by tracing connected lines throughout the network. The process involves recursively tracing from a starting edge, identifying connected edges, and storing them to avoid revisits. Once all edges have been processed, the results are returned, and the polylines are constructed.
This approach is effective for linework created manually, such as haul road networks, but may not be suitable for all edge networks, particularly those generated by certain software tools (such as like contour linework).
Note: You can download the data used in this example 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__": project = Project() input("Select EdgeNetwork and press any key to continue..") selection = project.get_selected() for item in selection: if item.is_a(EdgeNetwork): with project.read(item) as edges: explode_edgenetwork(edges, project)