Skip to main content

Every Layer Has an Owner

Open the Layers Menu and you will count somewhere around a hundred entries. Open map.allLayers in the console and you will count a few more that the menu never shows. Each of those layers was created by one of six mechanisms, is filtered by one of two properties, and sits at a position in the draw stack that something, somewhere, deliberately chose. When a change to one layer makes another misbehave, it is nearly always because two pieces of code believed they owned the same property. This page is the ownership map.

Six kinds of layer​

KindExamplesCreated byClass
Portal operational layersFire perimeters, battalions, aircraft, weather, basemap-style VTThe WebMap, authored in ArcGIS OnlineWhatever the portal says
The legacy camera serviceALERTCalifornia Cameras (LEGACY_CAMERA_LAYER_TITLES)The WebMap; parked at init, never loadedFeatureLayer (hosted)
Client graphics layersCursor Tracking LOS, area highlight, alert drawing, what3wordsHooks and on-demand featuresGraphicsLayer
The view's own graphicsRecenter marker, coordinate crosshair, sightings, companion marksview.graphics.add(...)Graphics on MapView
Velocity setLocations, Viewshed, Proboscis, Camera Feed + persisted companionsThe WebMap; discovered by service nameStreamLayer or FeatureLayer

The first two kinds arrive with the WebMap. The next three we construct. The last is a hybrid: the layers come from the WebMap, but the app decides which of them are on the map at any moment, because a StreamLayer only disconnects its WebSocket when it is removed, not when it is hidden.

The camera feature service​

The legacy camera service deserves a paragraph only to say what it no longer does. Until October 2026 it drew the site circles, answered the extent query that fed the tile grid, was hit-tested by hover and click, carried clustering, and was refreshed every 60 seconds — and it was the single largest source of ArcGIS 429s. Cameras now come from the Velocity set, so the catalog parks this layer at startup like any other toggled-off layer, usePageUpdates ignores its stored preference, and the Layers menu and the area-filter picker hide it. Nothing reads NEXT_PUBLIC_CAMERA_LAYER_NAME any more.

The one client layer​

initializeLayers adds a single GraphicsLayer, "Cursor Tracking LOS", holding one graphic whose geometry is moved in place as the operator's cursor crosses the camera image. Its title is listed in CLIENT_LAYERS so the Layers Menu never offers it as a toggle and the catalog never parks it. Everything else a camera needs — point, heading arrow, field-of-view wedge, line-of-sight beam — is a Velocity layer; see Streams, Beams, and the Handoff.

Where things draw​

From the top down: the view's own graphics (markers, sightings, companion marks) sit above everything; graphics layers added later by features (area highlight, alert drawing, what3words) come next; then the cursor line, added at init; then every WebMap operational layer in WebMap order, which is also where a restored layer lands — the Velocity camera layers among them, each persisted companion ordered just beneath its stream; and the basemap underneath it all.

Two forces decide this stack. The WebMap order comes first, as authored; webmap.add appends the cursor line above it. Then ensureVelocityLayersOnMap keeps each companion below its stream so a live beam covers the last-known one. If a portal layer must draw above the camera beams, that is a WebMap ordering decision, authored in the portal. Graphics layers that features add later use map.add(layer), which appends to the top, and view.graphics is above all layers by construction. A claimed Velocity layer inside a WebMap group that the GIS team left hidden gets its ancestors made visible so it can draw at all.

Who owns which property​

This is the part that prevents the next regression. Each property on a camera layer has exactly one writer.

PropertyWriterMeaning
definitionExpressionapplyVelocityLayerFilters (feature layers)The Type and spotlight filters (and the beam gate), as deviceId IN (…)
layerView.filter (features)useFeatureMapAreaFilterThe area scope, ANDed client-side, evaluated after definitionExpression
layerView.filter (streams)applyVelocityLayerFilters onlyCamera filter AND beam set AND handoff AND area scope, composed into one clause
layerView.featureEffectpulseAiDetectionHitThe AI pulse bloom on the point layers
visibleThe Layers Menu checkbox (usePageUpdates)Plain visibility; never persisted for the Velocity layers
rendererThe portal; borrowStreamRenderer for Locations onlyAppearance; never a symbol on a feature
Membership in map.layersThe catalog (park / restore) via ensureVelocityLayersOnMapLoaded-or-not, connected-or-not

The split between the two filter properties is not cosmetic. A layer-view filter applies with no network request and composes with the definition expression instead of competing for it. A StreamLayer in SDK 5 has no filter of its own (the 4.x connection filter became definitionExpression and geometryDefinition, which go to the Velocity service), which is why the area hook publishes its clause on the store and lets the stream's single writer compose it. Writing layer.filter on a stream is a silent no-op; it was one for several months.

Legacy you will find in the tree​

data/layers.ts exports a layers array describing WFIGS incident and perimeter services with custom renderers. Nothing on the current map reads it; those layers come from the WebMap now. The file is still imported for layersToAccessRequirements and layersRequiringAuth, which gate menu rows by role. Likewise data/basemaps.ts and util/map/getBasemapByKey.ts describe basemaps the map no longer constructs; only basemapOptions (the Topographic/Imagery pair and the layer titles each one toggles) is live. Treat both as inventory, not as entry points.

Wrapping up​

Six kinds of layer, one owner each. Portal layers are curated, not authored; the Velocity set is discovered by service name and is where every camera comes from; the legacy camera service is parked; graphics layers and view.graphics are for small, mutable things. Draw order is WebMap, then ours. And every filtering property has exactly one writer, which is the rule to check before adding a fifth.

Next: The Parking Lot, how a hundred portal layers load only when asked.