Large data sets
The editor redraws the whole scene on every change it shows — a hover that moves to another element, a selection, a pan or zoom. How expensive that is was measured before anything was changed. This page gives the numbers, then the two measures built because of them.
What was measured#
There is no real data set to measure on, so the benchmark generates one
(packages/editor/bench/viele-punkte.bench.test.ts): 50,000 free-standing points and, next to
them, a 150 × 150 grid of adjoining square parcels (22,500 faces, 22,801 nodes, 45,300 edges),
in one partition — 72,801 nodes in total. Run it with:
./node_modules/.bin/vitest run --config vitest.bench.config.ts --reporter=verbose packages/editor/bench/viele-punkte.bench.test.ts
Measured on 13.10.2026 on an Apple M4 Pro with Node 25, as the median of nine runs. Your machine will differ; the ratios are what matters. Times are for building the scene in the editor only — what the map library then spends drawing it is not included, because the benchmark runs without a browser.
buildScene, the whole partition, no viewport:
| Data | without handles | with handles |
|---|---|---|
| 50,000 points | 5–7 ms | 9 ms |
| 150 × 150 grid | 41–48 ms | 64–70 ms |
| both | 52–64 ms | 83–85 ms |
One frame at 60 Hz has 16.7 ms. The points alone fit; the parcel grid does not — it costs three to four frames before the map library has drawn a single pixel. A pointer move that changes the hovered element took 59 ms (drawing tools) and 99–104 ms (editing tools, which show handles) through the whole editor.
Measure 1: only what is in view#
If the adapter reports the visible map area, the editor builds the scene only from elements whose
bounding box reaches into that area, plus a margin of half the view's width and height on every
side — the map libraries report a view change only when a pan has ended, and the margin keeps half
a screen of panning from showing an empty border. The candidates come from the partition's spatial
index (SpatialIndex, the same one snapping uses), which gained a face query (facesInBox) for
this: a view deep inside one large parcel touches none of its edges and is still filled by it.
Selected elements always stay in the scene, wherever they lie: a selection never vanishes, and a selected node keeps its handle.
With a 20 × 20 window into the same grid — 484 parcels, roughly what zoom 17 shows of real parcels
of that size — buildScene takes 1.2 ms instead of 52 ms, and a pointer move through the whole
editor 5–6 ms instead of 59–104 ms.
It does not help when everything is in view. Zoomed out over the whole grid there is nothing to leave out: the scene still costs 45–58 ms. Half the grid in view costs 40 ms, not half of the whole — the index queries and the extra bookkeeping eat part of the saving. A view that covers the partition's whole extent skips the queries altogether, so it costs the same as no viewport at all.
For adapter authors#
Adapter.viewport?(): Viewport | undefined is optional. The three bundled adapters implement it;
an adapter without it, or one that returns undefined, gets the full scene exactly as before. A
Viewport is the axis-aligned box around the visible area in world coordinates (on a rotated map,
the box around all four rotated corners) and, where the map runs on Web Mercator, the zoom level on
256-pixel tiles. sichtfeldAusEcken from @vectorstream/adapter builds one from the container size
and the adapter's own unproject. It returns undefined while the container has no size, because
a box of zero area would filter everything out.
If you build scenes yourself, buildScene takes the same as options: viewport and, to share an
index you already keep, index.
Measure 2: no handles when zoomed far out#
Below zoom 15, node and midpoint handles stay out of the scene, except selected ones.
Faces, edges, labels and free-standing points are never hidden by zoom. The threshold is fixed
(GRIFFE_AB_ZOOM in @vectorstream/editor), not a setting — at zoom 15 a pixel covers about five
meters at the equator, so a 20-meter parcel is four pixels wide and its corner handles lie on top
of each other.
Points stay visible at every zoom level; only handles are hidden below the fixed threshold —
there is no toggle (user ruling of 13.10.2026). The first version of this measure also hid
free-standing points, modeled too closely on Geoman's limitMarkersToZoom (which hides vertex
markers, never point features) — but the benchmark above already showed points are cheap
(5–9 ms for 50,000 of them), so hiding them was never backed by a measurement, only assumed. The
fix removed the point-hiding half of the threshold and re-ran the benchmark to confirm the
performance case still holds with only handles hidden — it does, see the numbers below.
In the measurement, hiding handles alone saves about 13 ms of the 58 ms (no handles) for the whole data set: the 50,000 points are cheap to put into a scene regardless of whether handles are shown. The larger saving is in drawing them, which this benchmark does not measure.
What this changes, and what it doesn't:
- Snapping is unaffected. It works on the partition and its spatial index, never on the scene; a new point still snaps onto a corner whose handle is hidden.
- Free-standing points are unaffected. A point is a data object, like a face, not a control — it keeps drawing, and the PNG export (gap no. 1) keeps including it, at every zoom.
- Editing logic is unaffected, but a real adapter's hit test searches the scene: below zoom 15
only selected nodes have a handle to grab. A click near where an unselected corner's handle would
sit does nothing — the editor suppresses the edge grab that would otherwise fire there instead
(see Modes,
EditMode), rather than silently moving a whole boundary line. Zoom in, or select the node first, to grab that corner. - Only on Web Mercator. On a map without a meaningful zoom number — Leaflet's
L.CRS.Simple, an OpenLayers view in another projection — the adapter reports no zoom, and handles are never hidden. MapLibre counts zoom on 512-pixel tiles; its adapter adds one so the threshold means the same scale on all three libraries.