The ArcGIS Maps SDK for JavaScript
Examples on this page assume the UtilityNetwork instance is created using a dataset which includes a telecom domain network.
If you run into issues while implementing telecom capabilities, share feedback through Esri support channels.
Getting started
The UtilityNetwork class is the entry point to most telecom-specific workflows. From an instance of UtilityNetwork loaded with a telecom domain network, we create a CircuitManager for circuit management workflows, and a UnitIdentifierManager for unit identifier management workflows.
const [UtilityNetwork] = await $arcgis.import([ "@arcgis/core/networks/UtilityNetwork.js", ]);
const utilityNetwork = new UtilityNetwork({ layerUrl: "https://host.com/arcgis/rest/services/Test/FeatureServer/0",});
await utilityNetwork.load();
const domainNetworks = utilityNetwork.dataElement?.domainNetworks;const telecomDomainNetwork = domainNetworks!.find((dn) => dn.isTelecomNetwork);
const circuitManager = await utilityNetwork.getCircuitManager( telecomDomainNetwork.domainNetworkName);const unitIdentifierManager = await utilityNetwork.getUnitIdentifierManager();Telecom-specific classes and capabilities
The following classes have telecom-specific capabilities or exist for use with telecom domain networks.
- UtilityNetwork: Telecom-specific metadata and helper methods used across telecom editing and tracing workflows.
- TelecomNetworkElement: Extends NetworkElement with grouping properties firstUnit and lastUnit. Used for associations and traces in a telecom domain network.
- CircuitPath: Represents a path in a telecom domain network.
- CircuitPathConnectivityElement: A single connectivity hop in a path, pairing a junction element and edge element.
- CircuitManager: Exposes circuit lifecycle operations for a telecom domain network, including create, alter, query, verify, export, and delete.
- Circuit: Represents a circuit in a telecom domain network with start/stop location or section definitions and user attributes.
- CircuitSection: Represents a physical or virtual section of a circuit, defined by locations or a consumed subcircuit.
- Subcircuit: Represents a subcircuit, a reusable portion of a circuit that can be used to define another circuit.
- CircuitLocation: Represents start or stop locations for circuits and circuit sections.
- UnitIdentifierManager: Exposes unit identifier operations for a telecom domain network, including reserve, reset, resize, query, divide, and combine.
The relationship between these telecom-specific classes in the Maps SDK for JavaScript is visualized in the Telecom Object Model Diagram (OMD).
UtilityNetwork class telecom capabilities
The UtilityNetwork class is the entry point for telecom workflows in a utility network. The UtilityNetwork class exposes telecom-specific properties and methods used for editing and tracing scenarios in a telecom domain network.
- Domain network metadata is exposed through
dataElement.domainNetworks, typed as DomainNetworkJSON. This type has telecom-specific properties such as telecom status, circuit sources, circuit properties, color schemes, divide policies, and combine policies. - Property hasTelecomNetwork returns whether the utility network’s data element includes a telecom domain network.
- Method getCircuitManager returns a CircuitManager instance for circuit management of a specific telecom domain network.
- Method getUnitIdentifierManager returns a UnitIdentifierManager for unit identifier management of telecom domain networks.
- Method trace supports telecom path and circuit trace workflows and telecom-specific trace results.
- Method generateAddAssociations supports generating FeatureService.applyEdits payloads for creating associations between grouped elements in a telecom domain network.
- Method generateDeleteAssociations supports generating FeatureService.applyEdits payloads for deleting associations between grouped elements in a telecom domain network.
To create an instance of CircuitManager for circuit management of a telecom domain network:
const circuitManager = await utilityNetwork.getCircuitManager( telecomDomainNetwork.domainNetworkName);To create an instance of UnitIdentifierManager for unit identifier management of telecom domain networks:
const unitIdentifierManager = await utilityNetwork.getUnitIdentifierManager();Circuit management
The CircuitManager class handles circuit lifecycle operations for a specific telecom domain network in a utility network. It supports creating, altering, querying, verifying, exporting, and deleting circuits. It also provides references to circuit-related tables (circuits, sections, and subcircuits).
For background on the telecom data model with respect to circuits, see Interacting with Circuits.
Circuit management workflows may involve the following:
- Create or modify a circuit definition (nonsectioned or sectioned).
- Validate or verify the circuit to confirm expected path behavior.
- Query circuits as data changes over time.
Circuits are modeled in the Maps SDK for JavaScript with the following classes:
- Circuit: Represents a circuit in a telecom domain network and is used to manage circuits with
CircuitManager. ACircuitinstance can have start and stop locations or sections, subcircuits, and user attributes. Circuit instances also have methods to help with setting and reading the values of these properties. Circuits must be instantiated using either start/stop locations or sections; combining both is invalid. Circuits can also persist a global ID by specifying a globalId property during circuit creation. - CircuitSection: Represents a circuit section in a telecom domain network. Sections can be physical or virtual, can either use start and stop locations or consume a subcircuit, and support user attributes. The section will also have a path if returned as the result of a circuit trace on a sectioned circuit. A
CircuitSectionmust be instantiated using either start/stop locations or a subcircuit; combining both is invalid. Circuit sections can also persist a global ID by specifying a globalId property during circuit creation. - Subcircuit: Represents a circuit subcircuit in a telecom domain network. A
Subcircuitcan be reserved and can have user attributes. Subcircuits can also persist a global ID by specifying a globalId property during circuit creation. - CircuitLocation: Represents a circuit start or stop location in a telecom domain network. Instances of this class are used for start and stop locations on
CircuitandCircuitSectioninstances, and to query circuits with queryCircuits.
Example: create and verify a non-sectioned circuit
To create a non-sectioned circuit with user-defined attributes, then verify the circuit:
const circuit = new Circuit({ name: "CIR-1001", circuitType: "physical", startLocation: new CircuitLocation({ sourceId: 27, globalId: "{A3F2B9D1-7C4E-4A8D-91F3-2E7B6C5D9A10}", firstUnit: 1, lastUnit: 1, }), stopLocation: new CircuitLocation({ sourceId: 27, globalId: "{B4C1E8F0-2D6A-43BE-8F19-6A2D3E9C7B55}", firstUnit: 1, lastUnit: 1, }), attributes: { serviceType: "business-data", },});
await circuitManager.create(circuit);
const verifyResult = await circuitManager.verify({ circuitNames: ["CIR-1001"],});Use verify after create or alter operations to check the validity of circuits in a telecom domain network before moving to downstream analysis and tracing. See CircuitVerifyResult for the shape of verifyResult.
Example: create and verify a sectioned circuit
To create a sectioned circuit, provide a Map to represent the logical connectivity between sections in a circuit. This map should represent a directed adjacency list where each key is a CircuitSection that connects to other CircuitSection instances indicated by the key’s value. The map is type Map<CircuitSection, CircuitSection[]>.
In this example, section 1 connects to 2 and 3 (where sections 2 and 3 are in parallel), 2 connects to 4, 3 connects to 4, and 4 connects to nothing (is the end of the circuit).
const sectionOne = new CircuitSection({ sectionId: 1, sectionType: "physical", startLocation: new CircuitLocation({ sourceId: 20, globalId: "{6B0A0E4E-80EF-4688-B463-253063A5B4B2}", firstUnit: 1, lastUnit: 4, }), stopLocation: new CircuitLocation({ sourceId: 20, globalId: "{3A557124-4243-4615-96DF-51991E6B9766}", firstUnit: 1, lastUnit: 4, }),});
const sectionTwo = new CircuitSection({ sectionId: 2, sectionType: "physical", subcircuit: new Subcircuit({ name: "SUBCIRCUITVALUE2" }),});
const sectionThree = new CircuitSection({ sectionId: 3, sectionType: "physical", startLocation: new CircuitLocation({ sourceId: 20, globalId: "{7C874691-D659-4311-B19C-D33027B6F48A}", firstUnit: 1, lastUnit: 4, }), stopLocation: new CircuitLocation({ sourceId: 20, globalId: "{BF34887C-359E-4E00-A695-8D92BF41FE37}", firstUnit: 1, lastUnit: 4, }),});
const sectionFour = new CircuitSection({ sectionId: 4, sectionType: "physical", startLocation: new CircuitLocation({ sourceId: 20, globalId: "{E4ECA4B4-F9F7-4AB2-B2FD-6CDAB1A9506B}", firstUnit: 1, lastUnit: 4, }), stopLocation: new CircuitLocation({ sourceId: 20, globalId: "{18DE7259-FDC6-4788-B13B-3F6210CCCB56}", firstUnit: 1, lastUnit: 4, }),});
const sections = new Map([ [sectionOne, [sectionTwo, sectionThree]], [sectionTwo, [sectionFour]], [sectionThree, [sectionFour]], [sectionFour, []],]);
const sectionedCircuit = new Circuit({ name: "SectionedCircuit", circuitType: "physical", sections: sections,});
await circuitManager.create(sectionedCircuit);
const verifyResult = await circuitManager.verify({ circuitNames: ["SectionedCircuit"],});Example: query circuits
CircuitManager supports multiple query patterns depending on what identifiers you already have in your workflow.
// Query circuits by name; returns `Circuit` class instancesconst circuitsByName = await circuitManager.queryCircuits({ names: ["CIR-1001"],});
// Query circuits by global ID; returns `Circuit` class instancesconst circuitsByIds = await circuitManager.queryCircuits({ globalIds: ["{8A1B5A33-3EBA-42CC-9085-31B22DAFE5DC}"],});
// Query circuits that begin or end at the given `locations`// `locationType` specifies the type of circuit location to query ("start", "stop", or "all")// Returns `Circuit` class instancesconst circuitsByLocations = await circuitManager.queryCircuits({ locationType: "all", locations: [ new CircuitLocation({ sourceId: 20, globalId: "{8A1B5A33-3EBA-42CC-9085-31B22DAFE5DC}", firstUnit: 1, lastUnit: 1, }), ],});
// Query circuit names; returns an array of stringsconst circuitNames = await circuitManager.queryCircuitNames({ locationType: "all", locations: [ new CircuitLocation({ sourceId: 20, globalId: "{8A1B5A33-3EBA-42CC-9085-31B22DAFE5DC}", firstUnit: 1, lastUnit: 1, }), ],});Example: alter a circuit
Query an existing circuit, alter the circuit, then verify.
const circuits = await circuitManager.queryCircuits({ names: ["CIR-1001"],});
const circuitToAlter = circuits[0];
circuitToAlter.name = "CIR-1001-UPDATED";
await circuitManager.alter(circuitToAlter);
const verifyResult = await circuitManager.verify({ circuitNames: ["CIR-1001-UPDATED"],});Example: export circuits
Use export to export feature and connectivity information about circuits into JSON files for consumption by external systems. Export operations are commonly used after circuits have been verified and before sharing or downstream processing.
const exportResult = await circuitManager.export({ circuitNames: ["CIR-1001", "CIR-1002"], exportAcknowledgement: true, outSpatialReference: { wkid: 4326 }, resultTypes: [ { type: "features", includeGeometry: true, includeDomainDescriptions: true, networkAttributeNames: ["Asset group", "Asset type"], diagramTemplateName: "", resultTypeFields: [ { networkSourceId: 20, fieldName: "FIRSTUNIT" }, { networkSourceId: 20, fieldName: "LASTUNIT" }, ], }, ],});See CircuitExportResult for the shape of exportResult.
Example: delete circuits
Use delete to logically remove one or more circuits by name.
In this context, logically deleted means the circuit is marked with status: "deleted" in the circuit management tables, but the record is still present in the Circuit table until a follow-up export workflow acknowledges and removes it. See Circuit management tables and Circuits.
await circuitManager.delete(["CIR-1001", "CIR-1002"]);To fully remove a logically deleted circuit record (physical delete), perform the following:
- Logically delete with
CircuitManager.delete(...). - Optionally, confirm the circuit is now deleted by querying with
status: "deleted". - Export with
exportAcknowledgement: truefor the deleted circuit name(s).
// Optionally, confirm that the circuit was logically deletedconst deletedCircuits = await circuitManager.queryCircuits({ names: ["CIR-1001"], status: "deleted",});
// Acknowledged export physically removes deleted circuit rowsawait circuitManager.export({ circuitNames: deletedCircuits.map((circuit) => circuit.name), exportAcknowledgement: true, outSpatialReference: { wkid: 4326 }, resultTypes: [ { type: "features", includeGeometry: false, includeDomainDescriptions: false, networkAttributeNames: [], diagramTemplateName: "", resultTypeFields: [], }, ],});In production workflows, query and verify target circuits before deleting to avoid removing active operational circuits.
For asynchronous execution patterns, use submitVerifyJob and submitExportJob.
Unit identifier management
The UnitIdentifierManager class provides unit identifier management functionality for telecom domain networks in a utility network. It supports querying, reserving, resizing, and resetting ranges for grouped elements, e.g., fibers in a cable or ports in a panel. This class can also generate FeatureService.applyEdits payloads for dividing and combining grouped elements.
The concept of grouping is central to the telecom domain network data model. Instead of storing one row per unit in a table, grouped objects represent a contiguous range with firstUnit and lastUnit. Unit identifier operations allocate and maintain those ranges.
For background on the telecom data model with respect to unit identifiers and grouping, see Work with unit identifiers and Grouping of objects.
Most unit identifier operations are performed with objects typed as UnitIdentifier, which has a source ID and global ID for the grouped element in the telecom domain network.
Example: query unit identifiers
const containers = [ { sourceId: 16, globalId: "{25B5F11B-70A2-4A61-9284-F0CE4C1E2111}", },];
const queryResult = await unitIdentifierManager.query(containers);Example: query the content of a container and resize the content
Query a unit container and resize its content to change the number of units associated with the content feature to 15 units.
Resizing is useful when the number of units on an existing unit identifiable feature needs to change after initial setup. Use it to increase or decrease the feature’s lastUnit value so the grouped unit range reflects the current design or edited state of the network.
In a typical unit identifier workflow, a TelcoDevice can contain a unit identifiable transceiver (TRX) that starts with units 1 through 8 (8 ports). If the transceiver hardware is replaced with a higher-density model, resize the grouped feature by increasing lastUnit (for example, from 8 to 15) so the unit range reflects the updated equipment design and subsequent associations and traces use the correct unit space.
const containers = [ { sourceId: 16, globalId: "{25B5F11B-70A2-4A61-9284-F0CE4C1E2111}", },];
const queryResult = await unitIdentifierManager.query(containers);const content = queryResult[0].ranges[0].content;
await unitIdentifierManager.resize(content, 15);Example: reserve a unit range
Reserve units 2 through 5 (inclusive) for a unit container.
Reserving a range is useful when you intentionally need a gap in the unit ID sequence. A common case is preparing future slot or port positions so upcoming containment edits receive the expected unit numbers, even when other editors are adding content at the same time.
const container = { sourceId: 16, globalId: "{25B5F11B-70A2-4A61-9284-F0CE4C1E2111}",};
await unitIdentifierManager.reserve(container, 2, 5);Example: reset containers
Resets the unit identifiers associated with the container.
Reset is useful when you need to condense the unit ID space by removing gaps that accumulate over time (for example, after card or port replacement), or when unit identifiable records become out of sync with the container and you need to restore a consistent sequence.
const containers = [ { sourceId: 16, globalId: "{25B5F11B-70A2-4A61-9284-F0CE4C1E2111}", },];
await unitIdentifierManager.reset(containers);Tracing in a telecom domain network
Telecom domain networks extend utility network tracing with path and circuit traces. Path traces find valid paths that exist between one or more start and stop locations; in a telecom domain network, those start and stop locations may be grouped elements. Circuit traces use network topology and circuit management tables to identify the elements which comprise a circuit.
The Maps SDK for JavaScript supports path and circuit traces via the following:
- TraceParameters.traceType supports
pathandcircuit. - TraceLocation includes grouping properties firstUnit and lastUnit, and its type can be set as
stopping-point. - UNTraceConfiguration includes telecom-specific options including circuitName, inferConnectivity, maxHops, and numPaths.
- TraceParameters.resultTypes supports
pathsandcircuitsfor ResultType.type. - TraceResult may return telecom-specific
paths,circuits, and TelecomNetworkElement results. - CircuitPath and CircuitTraceResult represent the output of path and circuit traces, respectively.
Example: run a path trace
const unTraceConfiguration = new UNTraceConfiguration({ numPaths: 2, maxHops: 100, domainNetworkName: "Telco",});
const traceParameters: TraceProperties = { traceType: "path", traceLocations: [ { type: "starting-point", globalId: "{49FCE773-6C0D-4029-B680-394E591CD002}", percentAlong: 0.5, firstUnit: 1, lastUnit: 1, }, { type: "stopping-point", globalId: "{FFEC062E-E871-491C-BA70-031F8628C7A1}", firstUnit: 1, lastUnit: 1, }, ], traceConfiguration: unTraceConfiguration, resultTypes: [ { type: "paths", includeGeometry: false, includePropagatedValues: false, networkAttributeNames: [], diagramTemplateName: "", resultTypeFields: [], }, ],};
const traceResult = await utilityNetwork.trace(traceParameters);The result of the path trace includes paths instead of elements in TraceResult.paths.
Example: run a circuit trace
Running a circuit trace on a physical, non-sectioned circuit:
const unTraceConfiguration = new UNTraceConfiguration({ circuitName: "CIR-1001", domainNetworkName: "Telco",});
const traceParameters: TraceProperties = { traceType: "circuit", traceConfiguration: unTraceConfiguration, resultTypes: [ { type: "circuits", includeGeometry: false, includePropagatedValues: false, networkAttributeNames: [], diagramTemplateName: "", resultTypeFields: [], }, ],};
const traceResult = await utilityNetwork.trace(traceParameters);The result of the circuit trace includes circuits instead of elements in TraceResult.circuits.
Associations in a telecom domain network
In a telecom domain network, associations can be modeled using foreign-key fields on domain network classes. To support foreign key-based associations in the Maps SDK for JavaScript, Association allows its globalId property to be null. If an Association represents a foreign key-based relationship, its globalId property will be null, as there is no explicit association record to populate globalId.
The associations table also includes attributes for grouping. To support workflows involving grouped elements, fromNetworkElement and toNetworkElement on Association can be TelecomNetworkElement, which extends NetworkElement with grouping properties firstUnit and lastUnit.
Example: add associations between grouped elements
The FeatureService.applyEdits payload for adding associations supports grouped unit ranges. The UtilityNetwork.generateAddAssociations helper can generate this payload.
To add an association between grouped elements:
const association = new Association({ fromNetworkElement: new TelecomNetworkElement({ globalId: "{931A1CD0-BF9B-43B1-B4B9-5BE4157DAA96}", networkSourceId: 27, firstUnit: 1, lastUnit: 3, }), toNetworkElement: new TelecomNetworkElement({ globalId: "{E26E101E-92EA-4195-A686-CFD69D0E8CAB}", networkSourceId: 27, firstUnit: 4, lastUnit: 6, }), associationType: "connectivity",});
const addAssociationPayload = utilityNetwork.generateAddAssociations([association]);
await featureService.applyEdits([addAssociationPayload]);Example: delete associations between grouped elements
The FeatureService.applyEdits payload supports deleting both traditional and foreign-key associations with the deleteAssociations field. The UtilityNetwork.generateDeleteAssociations helper supports creating payloads to delete either type of association.
To delete a foreign key-based association:
const association = new Association({ fromNetworkElement: new TelecomNetworkElement({ globalId: "{931A1CD0-BF9B-43B1-B4B9-5BE4157DAA96}", networkSourceId: 27, firstUnit: 1, lastUnit: 3, }), toNetworkElement: new TelecomNetworkElement({ globalId: "{E26E101E-92EA-4195-A686-CFD69D0E8CAB}", networkSourceId: 27, firstUnit: 4, lastUnit: 6, }), associationType: "connectivity",});
const deleteAssociationPayload = utilityNetwork.generateDeleteAssociations([association]);
await featureService.applyEdits([deleteAssociationPayload]);Example: delete a traditional association with generateDeleteAssociations
To delete a traditional association with an explicit association record, include the association globalId in addition to fromNetworkElement and toNetworkElement.
const association = new Association({ globalId: "{210A7912-46F5-4FB3-A46B-D000014BDE43}", fromNetworkElement: new TelecomNetworkElement({ globalId: "{931A1CD0-BF9B-43B1-B4B9-5BE4157DAA96}", networkSourceId: 27, firstUnit: 1, lastUnit: 3, }), toNetworkElement: new TelecomNetworkElement({ globalId: "{E26E101E-92EA-4195-A686-CFD69D0E8CAB}", networkSourceId: 27, firstUnit: 4, lastUnit: 6, }), associationType: "connectivity",});
const deleteAssociationPayload = utilityNetwork.generateDeleteAssociations([association]);
await featureService.applyEdits([deleteAssociationPayload]);Example: delete traditional association by GUID (legacy)
For backwards compatibility, deleting traditional associations by association GUID to the deleteFeatures field is still supported. This method requires specifying the globalIdUsed option when calling applyEdits.
const deleteTraditionalAssociationPayload = { deleteFeatures: [ { globalId: "{B8192CAA-98D5-4D42-BCF4-870CDD04B48F}", }, ], id: 500001, identifierFields: { globalIdField: "globalid", objectIdField: "objectid" },};
await featureService.applyEdits([deleteTraditionalAssociationPayload], { globalIdUsed: true,});Combine and divide workflows
The concept of grouping in the telecom domain network allows high-cardinality assets like fibers and ports to be represented efficiently. Editing workflows may require dividing grouped objects into smaller ranges or combining compatible ranges into a larger grouped object.
The FeatureService.applyEdits payload supports these operations with:
- divideGroupedObjects for divide workflows.
- combineGroupedObjects for combine workflows.
Unlike standard feature split/merge edits, which are geometry-driven edits on features, divide/combine workflows operate on grouped telecom objects and their unit identifier ranges. Use divide/combine when you need to repartition or consolidate logical unit capacity while preserving telecom grouping rules.
Example: divide grouped objects
Use UnitIdentifierManager.generateDivideNetworkElements to produce applyEdits payloads for dividing grouped objects.
In this example, the object with GUID 931A is divided into three grouped objects, with 2, 4, and 6 units respectively.
const dividePayload = unitIdentifierManager.generateDivideNetworkElements( new NetworkElement({ globalId: "{931A1CD0-BF9B-43B1-B4B9-5BE4157DAA96}", networkSourceId: 27, }), [2, 3, 4],);
const divideResult = await featureService.applyEdits([dividePayload]);See DivideGroupedObjectResult for the shape of divideResult.
Example: combine grouped objects
Use UnitIdentifierManager.generateCombineNetworkElements to produce applyEdits payloads for combining grouped objects.
const combinePayload = unitIdentifierManager.generateCombineNetworkElements([ new NetworkElement({ globalId: "{C5D8A7B2-1E4F-4C96-9B23-5F1A8D3E7C64}", networkSourceId: 27, }), new NetworkElement({ globalId: "{D6E9C4F1-3A5B-4D7E-8C92-4B7D1E2F9A33}", networkSourceId: 27, }),]);
const combineResult = await featureService.applyEdits([combinePayload]);See CombineGroupedObjectResult for the shape of combineResult.