Why FeatureMap replaced MapComponent
The map renders thousands of camera points that update every few seconds. The original
MapComponent did that with an ArcGIS GraphicsLayer and a scale watcher. At fleet size it
dropped to 15–30fps while panning and burned 5–10% CPU sitting idle.
FeatureMap replaced it with a FeatureLayer plus declarative renderers. Same visual
result, roughly an order of magnitude less work per frame.
You mostly need this page for one reason: so you don't reintroduce the old pattern. The rules at the bottom are the part that matters day to day.
What actually changed
Three decisions carry nearly all the gain.
Declarative visual variables instead of scale watchers. The old code subscribed to zoom changes and recomputed every symbol in JavaScript on the main thread. A FeatureLayer's renderer expresses "size varies with scale" as configuration, and ArcGIS applies it on the GPU without a round trip through our code.
One shared symbol instead of one per graphic. A GraphicsLayer allocates a symbol object, a JS geometry object, and a JS attribute object per point. A FeatureLayer stores geometry in a binary buffer, attributes in typed arrays, and points every feature at a single shared symbol — with an R-tree spatial index on top.
Diff-based updates instead of full redraws. The old layer cleared and rebuilt on every
poll. FeatureMap compares incoming camera state against current state and applies only the
delta, so a poll that changed 3 cameras touches 3 features rather than 5,000.
The numbers
Measured at 5,000 camera points:
| Legacy MapComponent | FeatureMap | |
|---|---|---|
| Initial render | ~3000ms | ~800ms |
| Zoom frame time | 50–100ms | ~16ms |
| Pan | 30–45fps | 60fps |
| Idle CPU | 5–10% | <1% |
| Memory | ~150MB | ~80MB |
| Features touched per poll | all 5,000 | 1–10 changed |
The idle-CPU line is the one that mattered most operationally. Dispatchers leave this app open for an entire shift, often on modest hardware, sometimes on battery in a vehicle.
The rules this leaves you with
These are the ones people break when adding to the map:
- Never
add/removeto redraw. Update the attribute and let the renderer react. A delete-then-add produces a visible blink and defeats the diff. - Never subscribe to zoom to resize things. Use the renderer's visual variables.
- Never set a symbol on an individual feature. That's what put the old implementation on the main thread.
- Don't duplicate camera state into a second store. One source of truth is what makes the diff correct; a second copy is what made the old version race.
If you find yourself writing a loop over every camera on every poll, stop — that's the shape of the problem this migration removed.
Next: FeatureMap internals for how the diff and hook lifecycle actually work.