Skip to main content

Layers That Load When Asked

The master WebMap carries around a hundred operational layers. The <arcgis-map> component, given an item-id, loads the WebMap and creates the view in one go, and the view then creates a layer view for every layer in the map. Creating a layer view loads the layer: its service metadata and, for the ~55 layers referenced by portal item, a portal-item lookup first. Visibility does not matter. An invisible layer's layer view is merely suspended after it has loaded. Measured on 2026-09-03: 110 of 113 layers loaded at startup for a map on which the operator typically has a handful turned on.

The mechanism​

hooks/featureMap/usePreparedWebMap.ts loads the WebMap before the view exists. WebMap.load() fetches the item and instantiates the layer objects but does not load them; loading is the view's doing. With the layer objects in hand:

  1. util/map/webMapLayerCatalog.ts snapshots the whole tree — every layer, its group, its position — into a catalog, registered against the WebMap in a WeakMap.
  2. Every parkable layer is parked: removed from its parent collection. Parkable means everything except the topo and imagery layers (TOPO_LABELS / IMAGERY_LABELS) and group layers themselves. Those are never touched.
  3. The prepared WebMap is handed to <arcgis-map map={…}> instead of an item id. The view is created from a map that only contains the always-on layers, so that is all it loads.
  4. The existing visibility sync in usePageUpdates runs as before once the map is ready. For each layer it calls onLayerVisibilityChange, which now restores a parked layer when it is on (inserting it at its original position relative to whatever siblings are currently on the map, then loading it) and parks it when it is off. Parking is also what closes a StreamLayer's WebSocket and drops the layer view, so toggling off gives the memory back.

Nothing about the operator's saved preferences changed: what was on stays on, it just loads after the view exists instead of before, and everything that was off never loads at all.

Parked layers are still real layers​

A parked layer is an ordinary layer object that happens not to be in a collection. It can be found, configured, and queried — a queryFeatures on it loads it on demand. So every piece of code that used to find a layer by walking map.allLayers now goes through the catalog (listWebMapLayers / findWebMapLayer / getLayerCatalog) instead, otherwise a toggled-off layer would silently vanish from:

  • the Layers panel tree (buildLayerTree and getLayerDataFromWebMap read the catalog);
  • the Alerts panel and alert modals, which query the "Alerts" feature layer whether or not it is drawn;
  • the area filter — its boundary-layer picker, its saved scopes (a scope whose layer cannot be found is dropped as unresolvable), and the popup action it attaches to each boundary layer. The picker has one more wrinkle: a parked layer has never fetched its service metadata and the WebMap JSON carries no geometry type, so it cannot be classified as a polygon source. useAreaBoundaryLayerList therefore primes the unresolved candidates — metadata only, four at a time — when the Camera Options drawer opens, and the list fills in as they resolve. The popup action re-attaches when a boundary layer finishes loading;
  • Velocity layer discovery, which also takes a layer's original parent from the catalog, since a parked layer's parent is null;
  • StreamLayer token injection, so a parked stream already carries the token when restored.

Where the point is to draw or hit-test, map.allLayers is still right, and isWebMapLayerOnMap answers "is this parked?" — the area map-pick uses it to tell the operator to turn a boundary layer on rather than tapping an empty basemap.

The row tells you what is happening​

Toggling a layer on is now the moment it loads, so the Layers panel row shows it: a spinner while the service answers, and the grey label + "!" marker (with its tooltip) if it never does. onLayerVisibilityChange feeds the pre-existing layerLoadingStore on restore (loading → loaded / failed) and clears it on park. The SDK's load() is one-shot per layer instance — a layer that failed stays failed until the page reloads — so toggling it on again shows the failed marker straight away rather than issuing another request. The store is persisted; usePreparedWebMap resets it whenever a WebMap is prepared, so a status left over from a previous page never disables or spins a row before anything has been asked of it.

Interactions worth knowing​

  • Velocity camera layers are parkable like any other, and ensureVelocityLayersOnMap restores the claimed set through the catalog once the view is ready, parking the unclaimed candidates (another environment's set) for good. See Cameras From the Stream.
  • The legacy camera feature service (ALERTCalifornia Cameras) is parked at startup and never restored: cameras come from the Velocity layers, and this service was the single largest source of ArcGIS 429s.
  • Legend. The legend's hide/allow list is built from the catalog rather than from the layers on the map, so a layer restored later is legended like one that was on from the start. The legend is behind its own flag.
  • The three layers that always failed (CAL FIRE Facilities, NPS Ownership, California Land Ownership) now fail only if someone turns them on.

What it does not fix​

The 1 MB WebMap JSON, the portal-item lookups for the layers that are on, the basemap chain, and the camera feature service queries are unchanged. Trimming the WebMap and referencing layers by URL rather than item id remain AGOL-side work.