Skip to main content

The Parking Lot

Give <arcgis-map> an item-id and it does something reasonable and expensive: it loads the WebMap, creates the view, and then creates a layer view for every layer in the map. Creating a layer view loads the layer: its service metadata, and for the fifty-odd layers referenced by portal item, a portal lookup first. Visibility does not matter; an invisible layer's view is merely suspended after it has loaded. On 2026-09-03 we measured it: 110 of 113 layers loaded at startup for a map on which the operator typically has a handful turned on.

The fix is a parking lot. Layers the operator has not asked for are kept out of the map's collections entirely, and put back, at their original position, when a checkbox turns them on.

The catalog​

util/map/webMapLayerCatalog.ts is a snapshot of the WebMap's full tree, taken by usePreparedWebMap right after WebMap.load() and before any view exists. It remembers every layer, its parent (group or map) and its index among its siblings, and it is registered against the WebMap in a WeakMap so a discarded map takes its catalog with it.

The parking lotReload the toy WebMap both ways, then turn layers on and off and watch the request count, the load state and the draw stack.
0 ArcGIS requests0 layer views0 WebSockets0 parked
Basemap
Cameras
Operations
Fire
Boundaries
Aviation
Weather
on the map · top → bottom
  1. Camera Arrowsours
  2. Cursor Tracking LOSours
  3. Line of Sightours
  4. Field of Viewours
  5. basemap

Press reload to boot the map in the selected mode.

Three things are never parked: the topo and imagery layers (TOPO_LABELS, IMAGERY_LABELS), group layers themselves (their children park individually), and the camera feature service. Everything else leaves the map at prepare time. restore() puts a layer back after every original sibling that precedes it and is currently on the map, which keeps WebMap order intact no matter which subset is on.

A parked layer is still a real layer object. It can be found, configured and queried, and a query loads it on demand. That is what lets the Alerts panel query the "Alerts" layer whether or not it is drawn, and what lets a saved area scope resolve against a boundary layer the operator has switched off.

Gotcha: find layers through the catalog, never through map.allLayers

map.allLayers lists only what is on the map, so a toggled-off layer silently vanishes from any code that walks it. Every finder goes through listWebMapLayers, findWebMapLayer or getLayerCatalog. The Layers Menu tree, the alert modals, area-filter discovery, Velocity discovery and stream-token injection all do. Where the point is to draw or hit-test, map.allLayers is still correct, and isWebMapLayerOnMap answers "is this one parked right now?"

One pipeline for every visibility change​

Nothing sets layer.visible directly. Visibility lives in one place, the layersOnMap array in mapCameraInteractiveStore, and every change flows through the same four steps:

onLayerVisibilityChange in useFeatureMapAGOLLayers is the only place that touches the collections. For a parkable layer turning on it restores and calls load() with the layerLoadingStore set to loading, then loaded or failed. Turning off parks and resets the status to idle. For the topo and imagery layers it flips visible, re-adding them to the root collection if a group had swallowed them, imagery above topo. For a StreamLayer without a catalog it removes and re-adds the layer, since hiding one leaves its WebSocket open.

The sync effect in usePageUpdates also decides what to persist: every layer except the ignore list and the Velocity set is written to layerVisibilityStore, and the Layers Menu's optimistic toggle calls useUpdateLayerVisibilityMutation so the operator's layer setup follows them across devices. Basemap state is not persisted; the basemap button owns it, and on load processLayers forces topo on and imagery off.

Basemaps are layers wearing a trench coat​

In this WebMap the "basemap" an operator sees is the portal's hillshade basemap plus an operational vector tile layer titled ALERTCalifornia World Topographic Map, with an imagery alternative in a group. data/basemaps.ts defines two options, Topographic and Imagery, each naming the layer titles it toggles. The basemap button in the map's FAB tray cycles through them and flips those titles through the exact pipeline above. There is no basemap-specific machinery, which is why it is listed here rather than on its own page.

What the menu is allowed to show​

processLayers builds the tree from the catalog's roots (parked layers included, at their WebMap positions) and hands it to the store, which also flattens it into layersOnMap. Before anything reaches the screen, three filters apply:

  • The ignore list. LAYERS_TO_IGNORE is the topo and imagery titles plus CLIENT_LAYERS. Operators cannot switch off the arrows, and the basemap pair is the basemap button's job.
  • The legacy camera service. LEGACY_CAMERA_LAYER_TITLES keeps the parked camera feature service out of the menu; it is always on.
  • Roles. hooks/useCanAccessLayer.ts reads two maps in data/layers.ts: layersToAccessRequirements (Alerts needs Notification.Alert; Raw AI, AI Hits and AI Ray Lines need AI.Access) and layersRequiringAuth (CAL FIRE Battalion Boundaries needs any signed-in session). An inaccessible node renders nothing and is excluded from the accordion counts, so an operator without the role never learns the layer exists.

The Velocity layers have one more rule, covered on their own page: while the real-time source is active their checkboxes toggle plain visibility; while it is inactive they are off the map and therefore out of the menu.

Registering a new client-side layer​

If you create a layer in layerFactories.ts and forget to add its title to CLIENT_LAYERS, three things go wrong at once: it appears in the Layers Menu as a toggle, the catalog refuses to park it (it is not in the snapshot, which is correct) but the visibility handler will still flip its visible, and its state gets persisted to the server as an operator preference. Add the title before you add the layer.

Wrapping up​

The WebMap is loaded once, before the view, and every toggleable layer is parked out of the collections so the view only creates views for what the operator has on. A restore puts a layer back in WebMap order, loads it then and there, and reports the load on the menu row. Every visibility change, basemaps included, flows through layersOnMap and one handler. Finders use the catalog; drawers use map.allLayers.

Next: Icons Without Image Files, how the layers we own decide what they look like.