Modeling Structural Attachments for Poles and Vaults
Structural attachment is the association most often left out of a utility network, because geometry appears to express it already: the transformer is drawn on the pole, so surely the model knows. It does not. Without an explicit attachment association, retiring the pole leaves the transformer floating with no support recorded, a structure-based work order cannot list what is mounted on the asset it is about, and a load calculation that walks the pole finds nothing attached to it. This guide covers how attachment differs from containment, how to derive candidate attachments from survey geometry without inventing them, and how to keep the association honest as structures are replaced. It extends the containment discipline in asset hierarchy design for water and electric.
Environment Prerequisites
- ArcGIS Pro 3.2+ with a Standard or Advanced licence and
arcpyimportable from Python 3.11, since attachment associations are utility network objects. - Structure feature classes populated — poles, skids, risers, cabinets — with asset types that the rule set permits as supports.
- A structure tolerance, tighter than the connectivity tolerance, expressing how far a mounted device may be drawn from its support. A device is on its structure, not near it.
- The permitted support pairs as data: which structure asset types may support which device asset types, held in version control alongside the connectivity rules.
- A review table for staged candidates, because ambiguous pairs must be decided rather than resolved by distance.
- A versioned workspace for the writes, following the isolation policy in branch versioning and conflict resolution.
Schema-Aware Validation Protocol — Run Before Creating Associations
- Confirm the structure classes are complete. An estate that has mapped devices but not the poles they sit on cannot build attachments at all, and the gap is usually discovered here.
- Check the tolerance against capture practice. If devices were digitised offset from their structures by convention — a common habit to keep symbols readable — the tolerance must reflect the convention or every pair will be rejected.
- Verify the permitted-support table covers every device class present. A class with no permitted support will silently produce no attachments, and its absence looks identical to having none.
- Look for devices already contained. A device inside a vault is contained, not attached, and a candidate generator that ignores existing containment will propose both.
- Count devices with two candidate structures. In dense corridors this is common, and the count tells you how much review the run will need before you start it.
Minimal Reproducible Implementation
from __future__ import annotations
import logging
from dataclasses import dataclass, field
import geopandas as gpd
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
LOG = logging.getLogger("attachments")
@dataclass
class Candidate:
device_id: str
structure_id: str | None
distance_m: float
status: str # STAGED, AMBIGUOUS, NO_SUPPORT, ALREADY_CONTAINED
reason: str = ""
@dataclass
class AttachRun:
candidates: list[Candidate] = field(default_factory=list)
@property
def staged(self) -> list[Candidate]:
return [c for c in self.candidates if c.status == "STAGED"]
def build_candidates(
devices: gpd.GeoDataFrame,
structures: gpd.GeoDataFrame,
permitted: dict[str, set[str]],
contained_ids: set[str],
tolerance_m: float = 0.6,
) -> AttachRun:
"""Propose attachment associations from proximity, filtered by the rule set.
``permitted`` maps a structure asset type to the device asset types it may support.
A device within tolerance of two permitted structures is marked AMBIGUOUS rather
than assigned to the nearer one, because the nearer one is frequently the wrong one
in a dense corridor.
"""
run = AttachRun()
sindex = structures.sindex
for _, dev in devices.iterrows():
dev_id = str(dev["asset_id"])
if dev_id in contained_ids:
run.candidates.append(Candidate(dev_id, None, 0.0, "ALREADY_CONTAINED",
"device is contained, not attached"))
continue
nearby_idx = list(sindex.intersection(dev.geometry.buffer(tolerance_m).bounds))
nearby = structures.iloc[nearby_idx]
matches = []
for _, st in nearby.iterrows():
allowed = permitted.get(str(st["asset_type"]), set())
if str(dev["asset_type"]) not in allowed:
continue
d = float(dev.geometry.distance(st.geometry))
if d <= tolerance_m:
matches.append((d, str(st["asset_id"])))
if not matches:
run.candidates.append(Candidate(
dev_id, None, float("inf"), "NO_SUPPORT",
f"no permitted structure within {tolerance_m} m"))
elif len(matches) > 1:
matches.sort()
run.candidates.append(Candidate(
dev_id, matches[0][1], matches[0][0], "AMBIGUOUS",
f"{len(matches)} permitted structures in tolerance"))
else:
d, st_id = matches[0]
run.candidates.append(Candidate(dev_id, st_id, d, "STAGED"))
LOG.info("%d device(s): %d staged, %d ambiguous, %d unsupported",
len(run.candidates), len(run.staged),
sum(c.status == "AMBIGUOUS" for c in run.candidates),
sum(c.status == "NO_SUPPORT" for c in run.candidates))
return run
The rule check before the distance comparison is what keeps the run honest. A street light three centimetres from a communications pedestal is closer to it than to the pole it is actually mounted on, and only the permitted-support table knows that a pedestal cannot support a light.
Keeping Attachments Honest Through Replacement
Structures are replaced more often than the devices on them, and a replacement programme is where attachment data usually degrades. The physical work is straightforward — a new pole beside the old one, devices transferred, old pole removed — but the model sees a new feature, a set of associations pointing at a feature about to be retired, and no instruction connecting the two.
The workable pattern is to treat the transfer as part of the replacement rather than as cleanup. The work order that creates the replacement structure carries the identifier of the structure it supersedes; the automation reads that, moves every attachment association across, and only then allows the old structure to be retired. Where the transfer is partial — some devices moved, others scrapped — the difference is explicit in the work order rather than inferred from what is left.
Without that link, the estate accumulates retired poles that still own attachments and new poles that own nothing, and the two are only reconcilable by going back to the field.
Production Deployment Pattern
- Write attachments in a version and post through the normal gate, because creating them changes what a structure retirement will cascade to.
- Work the ambiguous queue before the unsupported one. An ambiguous device has a support and the model does not know which; an unsupported one may genuinely be free-standing.
- Re-run after every structure replacement programme. A pole replacement that creates a new feature and retires the old one leaves the attachments pointing at the retired pole unless the programme moves them.
- Gate structure retirement on empty attachments, the same way container retirement is gated on empty contents in modeling asset retirement workflows.
- Report unsupported devices by class. A class where most devices have no support is usually missing its structure layer rather than genuinely free-standing.
- Persist the run. Candidates, distances, statuses and the tolerance in force explain why a given device is or is not attached, months later.
Conclusion
Attachment is cheap to model and expensive to omit: it is what lets a structure retirement know what it strands, what lets a pole-mounted inventory exist, and what keeps a trace from walking through a support. Deriving candidates from proximity is fine as a hypothesis, provided the rule set filters them and ambiguity is escalated rather than resolved by distance. Once attachments exist, the hierarchy finally answers both of the questions a crew asks in the field: what is inside this, and what is on it.
Related
- Up to the parent topic: Asset Hierarchy Design for Water & Electric
- Up to the section: Core Utility GIS Fundamentals & Network Models
- Step-by-Step Guide to Building Asset Hierarchies for Gas Networks
- Understanding UN vs. Traditional GIS Networks
- Modeling Asset Retirement Workflows in ArcGIS Pro
For authoritative reference, consult the Esri utility network associations documentation and the GeoPandas spatial-index guide.