Skip to main content

Tuning the Map

Unlike the lessons before it, this page is a reference sheet — every tuning knob the map exposes, its current value, and which file to edit. Read How the Map Thinks first: changing a value here without that context is how symbology quietly breaks. Come back whenever a knob needs turning or the symptom table at the bottom needs consulting.

Almost everything lives in three files. Nothing here is runtime-configurable; these are source constants, and a change means a deploy.

FileOwns
util/map/featureMap/layerFactories.tsLayer definitions, renderers, every colour and scale
util/map/featureMap/geometryBuilders.tsFOV wedge and LOS line geometry
data/constants.tsLOS length, portal item ID, camera layer name, pulse duration

Arrows​

Arrow SVGs are generated at module load, base64-encoded into data URLs, and cached — one per state. There is no arrow asset file to swap. (public/images/arrow-triangle.svg exists but nothing on this map reads it; ignore it.) To change the shape, edit the path inside getFilledArrowSvgUrl / getUnfilledArrowSvgUrl.

| Knob | Current value | Where | | ----------------------------------- | ------------------------ | ---------------------- | ---------- | ------------------- | | AI-detection colour | #ff0000 | ARROW_COLORS.RED | | Online colour | #000000 | ARROW_COLORS.BLACK | | Offline colour (filled and outline) | #8f8f8f | ARROW_COLORS.GREY | | Symbol footprint | 20×20 px, yoffset 14px | createArrowsRenderer | | Visibility scale threshold | 3000000 | Layer minScale | 10000000 | createArrowsLayer | | Rotation source | field p, geographic | rotationVariable |

Arrow size is a stepped visual variable against $view.scale, not a smooth ramp, and it is not monotonic — the 2.5M step is smaller than the 5M step above it:

$view.scale50 000100 000500 0001 000 0002 500 0005 000 000
size (px)1111101068

Line of sight and field of view​

LOS colour is driven by the losColorCode attribute, bucketed from image age by getLosColorCodeFromDate. Edit LOS_COLOR_MAP for the colours, or the thresholds in that function for the buckets — but change both together, since the code names encode the buckets.

Image agelosColorCodeColour
under 5 minunder5min#CB181D
5–30 minunder30min#FA6A4A
30–60 minunder60min#FDAE91
60–120 minunder120min#FEE5D9
over 120 minover120min#808080
no timestamp(unset)#808080 — renderer default
KnobCurrent valueWhere
LOS line width1.25, with a 2.25 grey #5F6369 halo stroke behind itLOS_WIDTH, createLosLineSymbol
Cursor-tracking LOS colourblack, [0, 0, 0, 1]CURSOR_LOS_COLOR
LOS length40233.6 m (25 miles)LOS_LENGTH in data/constants.ts
FOV fill and outline[87, 89, 132, 0.5], outline width 0.5FOV_COLOR
FOV wedge anglederived from camera model + zoomcalculateFovAngle

FOV width is not a constant: calculateFovAngle(cameraModel, zoomValue) looks the angle up per model, and falls back to 'Q6075-E' when the model is missing rather than failing. A camera with an unknown model draws a plausible, wrong wedge. If zoomValue is 0 or less it returns DEFAULT_FOV_ANGLE (60°) instead.

Clustering​

Removed in October 2026 with the legacy camera feature service; the Velocity stream layers have no feature reduction.

Timing and scale constants​

ConstantValueFile
EXTENT_CHANGE_DEBOUNCE_MS200useFeatureMapExtent.ts
EXTENT_PERSIST_DEBOUNCE_MS500useFeatureMapExtent.ts
AI_DETECTION_PULSE_DURATION_MS10_000data/constants.ts
AI pulse store clear delay10600 msuseFeatureMapPolling.ts

Environment​

VariableConstantDefault
NEXT_PUBLIC_MASTER_MAP_PORTAL_ITEM_IDMASTER_MAP_PORTAL_ITEM_IDdb68c79771164142a90466c0cb64d712

Cameras come from the WebMap's ArcGIS Velocity layers, discovered by service name (see Cameras From the Stream). A WebMap without a Velocity Locations service draws no cameras and logs one error naming the item. Check that first when the basemap renders but cameras don't.

LAYERS_TO_IGNORE (topo labels + imagery labels + CLIENT_LAYERS) is what keeps basemaps and the cursor-line layer out of the Layers Menu. Add a client-side layer and you must add its title to CLIENT_LAYERS, or it shows up as a user-toggleable layer.

Adding a layer​

Prefer the WebMap portal: add the layer in ArcGIS Online, save, and it loads automatically with no code change. Only build a FeatureLayer in layerFactories.ts when the geometry is computed client-side from camera state — that is the whole distinction between our four layers and everything else on the map.

When it looks wrong​

SymptomMost likely cause
No cameras, basemap fineThe WebMap has no Velocity Locations layer (one error line in the console names the item)
Arrow state lags a stale imageTimestamp-only changes under 60 s are deliberately ignored
No red arrows despite AI hitsUser lacks the AI role; getArrowStateCode gates on hasAiAccess
Arrows vanish when zooming outWorking as designed past scale 3 000 000, unless isRecentlyMoved is set
Clusters never disappear when zooming inClustering was applied after layer.when() resolved, so maxScale was dropped
Cursor LOS blinksDelete and add issued as two applyEdits calls instead of one