Clamping Branch Corners
A route from another source rarely starts and ends exactly on the node symbols. BranchPresentation.between() places both ends on the node symbols, so the branch connects even when the route's own end points are a few pixels off. NodePresentation.clamp_point() does the same for a single point.
Full Example
View the complete code: 12_clamp_branch_corners.py
Why Clamping?
Branch corners must match node positions for correct rendering. In practice, coordinates often have slight errors from:
- External data imports: GIS systems, CAD exports, spreadsheets
- Coordinate transformations: Scaling, rotation, projection changes
- Manual entry: Typos, rounding differences
- Algorithmic generation: Layout algorithms with floating-point precision
The Nodes
network = NetworkLV()
sheet = network.add(SheetLV(SheetLV.General(name="Clamp Example")))
sheet_guid = sheet.general.guid
substation = network.add(
NodeLV(
NodeLV.General(name="Substation"),
presentations=[NodePresentation(sheet=sheet_guid, x=100, y=100)],
)
)
load = network.add(
NodeLV(
NodeLV.General(name="Load"),
presentations=[NodePresentation(sheet=sheet_guid, x=300, y=100)],
)
)A Route That Is a Few Pixels Off
The route should run from (100, 100) to (300, 100), but its ends are off:
imported_route = [(105, 98), (200, 100), (295, 102)]Building the Branch
The first and last points of the route stand for the nodes themselves, so pass only the points in between as via. between() puts the ends on the node symbols:
route_between_nodes = imported_route[1:-1]
feeder = network.add(
LinkLV(
LinkLV.General(name="Feeder", node1=substation.general.guid, node2=load.general.guid),
presentations=[BranchPresentation.between(substation, load, sheet_guid, via=route_between_nodes)],
)
)
feeder.presentations[0].polyline() # [(100, 100), (200, 100), (300, 100)]Passing the whole route would keep (105, 98) and (295, 102) as corners, with a short stub from each node to them.
Clamping a Single Point
clamp_point() returns the point on the node symbol nearest to the one you give:
substation.presentations[0].clamp_point((105, 98)) # (100, 100)For standard point symbols this is always the node's centre:
# Node at (100, 100) with default point symbol
node_pres.clamp_point((105, 98)) # Returns (100, 100)
node_pres.clamp_point((95, 103)) # Returns (100, 100)
node_pres.clamp_point((100, 100)) # Returns (100, 100)For line symbols (busbars), it finds the nearest point on the line segment. See Line Symbol Clamping for details.
Batch Processing
When importing many branches, the same call works in a loop:
for row in imported_data:
node1 = nodes[row.from_id]
node2 = nodes[row.to_id]
network.add(
LinkLV(
LinkLV.General(node1=node1.general.guid, node2=node2.general.guid),
presentations=[BranchPresentation.between(node1, node2, sheet_guid, via=row.route[1:-1])],
)
)Next Steps
- Branch Corners for
between(),symbol_segmentand how the corner lists are stored - Line Symbol Clamping for busbars drawn as a line