Create the layer. No network happens until it is added to a map; loading starts in onAdd.
See I3sMeshLayerOptions. Only id + url are required.
ReadonlyidA unique layer id.
ReadonlyrenderingEither "2d" or "3d". Defaults to "2d".
ReadonlytypeThe layer's type. Must be "custom".
The MEASURED datum repair currently applied, in metres (see I3sMeshLayerOptions.datumRepair).
0 — the overwhelmingly common case — means the layer is placed exactly where the service says,
either because the declaration checked out or because the repair never ran. A non-zero value means
the service's declared vertical datum was proven false against the 3D terrain and the mesh was
shifted by this much to seat on its own ground (Vienna: +156.3). Read it to verify placement, or
to surface the correction in your own UI; it never changes after the one-shot measurement settles.
Revert to the default (no-sun) lighting — the clearSun half of SunLightable (used when the
geo-sun system is disabled). Clearing lastSun also gates off the cast-shadow pass.
Representative ellipsoidal elevation (m) of the rendered data — for framing the camera at the data's height WITHOUT terrain (the data is placed absolutely). null when nothing is rendered yet.
The service's published attribute fields, for building a symbology UI.
The service's own statistics for one attribute — what a symbology UI needs to offer anything honest about a field it has never seen.
The attribute name, as returned by getAttributeFields.
The statistics, or null when the service publishes none for this field (or the
fetch fails — a symbology panel degrades to "no range known", it does not break).
I3S publishes statisticsInfo per attribute, and what comes back DIFFERS BY TYPE: a numeric
field summarises to { min, max, count, avg, stddev }, a string field to a list of values with
counts. That is exactly the difference between the two symbology renderers — class breaks over
a range, or a colour per value — so the shape of the answer tells a caller which to build,
without anyone hardcoding a field name.
Not every attribute has statistics. Object-id columns typically publish none, and a field the
service never summarised returns null rather than a guessed range: inventing 0..100 for a
column of parcel ids produces five class breaks that all contain everything.
The dataset's attribution string, if any. Wire it into MapLibre's AttributionControl
(e.g. new AttributionControl({ customAttribution })) or your UI — this layer does NOT
auto-register a geojson attribution source (an empty source destabilizes MapLibre's
source-cache / tile-coverage when multiple custom layers coexist over 3D terrain).
The dataset's camera target [lng, lat] (degrees) — handy for driving your own flyTo.
[lng, lat] in degrees, or null until the root is known.
The dataset's geographic footprint as [west, south, east, north] in degrees, read from the
service document and normalised out of whatever CRS it was published in.
[west, south, east, north] in degrees, or null before the service document
resolves — or after, if the service published no extent (or one in a CRS the reader cannot
invert; a missing footprint is reported rather than guessed).
The difference from getCenter is what you can DECIDE with it. A centre answers "where do I point the camera"; a footprint answers "is this dataset on screen at all", which is what lets a caller holding many layers order them by distance, frame them together, and give resident memory to the ones actually in view instead of dividing it evenly and starving the one being looked at.
The currently selected key value, or null.
The selection style in force, after defaults and any runtime overrides.
Whether this layer extracted hard edges at all — false when constructed without edges,
in which case I3sMeshLayer.setEdges has nothing to draw.
MapLibre CustomLayerInterface hook — invoked when the layer is added with map.addLayer;
initializes GL resources and begins streaming.
MapLibre CustomLayerInterface hook — invoked when the layer is removed with map.removeLayer;
releases GL resources and stops streaming.
Pick the 3DObject feature under canvas pixel (x, y) (top-left origin, e.g. from a MapLibre
click event's e.point). Renders an offscreen feature-id pass over the visible tiles, reads
back the hit, then fetches + decodes that feature's per-feature attributes. Returns null on a
miss (no mesh under the cursor). Async: the attribute blobs are fetched on demand.
Resolve a canvas point to the 3D geographic position of the frontmost rendered mesh surface — the screen→scene primitive behind measurement and slice placement. GPU pick pass + window-depth readback, reconstructed through the inverse frame matrix.
Canvas X in CSS pixels, top-left origin (e.g. a MapLibre click event's e.point.x).
Canvas Y in CSS pixels, top-left origin.
Snap radius in CSS pixels (default 0 = exact pixel): the nearest mesh hit within the radius wins, so a click slightly off an edge still lands on the mesh.
The picked position (lng/lat degrees, height metres above the rendered ground datum,
plus the cross-layer-comparable depth01), or null on a miss / before the first frame.
Optional method called during a render frame to allow a layer to prepare resources or render into a texture.
The layer cannot make any assumptions about the current GL state and must bind a framebuffer before rendering.
Nearest obstruction distance per height row through a forward corridor — ONE synthetic forward-looking orthographic pick pass over the walker's cross-section. The ObstructionProbeable contract; see it for why an overhead height probe cannot answer this.
The corridor; see ObstructionProbeOptions.
Distance in metres per row (index 0 = lowest), NaN where clear, null when this
layer cannot answer.
Working-set load fraction (0–1): drawn tiles over drawn plus still-pending (loading/in-flight/queued). 1 once the current view is fully resolved.
The fraction, or 0 before the engine exists.
Called during a render frame allowing the layer to draw into the GL context.
The layer can assume blending and depth state is set to allow the layer to properly blend and clip other layers. The layer cannot make any other assumptions about the current GL state.
If the layer needs to render to a texture, it should implement the prerender method
to do this and only use the render method for drawing directly into the main framebuffer.
The blend function is set to gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA). This expects
colors to be provided in premultiplied alpha form where the r, g and b values are already
multiplied by the a value. If you are unable to provide colors in premultiplied form you
may want to change the blend function to
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA).
Sample the height of this layer's topmost rendered surface at each position — ONE synthetic
top-down orthographic pick pass over the stations' bbox, then a per-station grid lookup (the
"buildings" line on an elevation profile). Samples the currently-RESIDENT tiles; positions in
unloaded areas return null.
lng/lat in degrees.
Optionalopts: SurfaceSampleOptionsOptional: gridPx sets the overhead pass's long-axis resolution (default 1024;
small values like 64 for cheap point probes), marginM the padding around the stations, and
maxHeightM a CUT so the reading is the highest surface BELOW it rather than the topmost
(see SurfaceSampleOptions.maxHeightM — this is what lets a walker read the floor of an
interior instead of the roof over it).
Height in metres above the rendered ground datum per point, or null where this
layer has no surface.
Highlight one feature — the building the user clicked.
The key value to select, or null to clear. Pass a picked feature's
objectId straight through.
Resolves once every resident tile has been re-styled.
Keyed on I3sMeshLayerOptions.selectionField (the OBJECTID by default) rather than on the picked feature ordinal, so the highlight follows the building through LOD changes instead of jumping to whichever feature happens to hold that ordinal in the next tile.
The highlight overrides any symbology colour, and never resurrects a feature the filter excluded.
Toggle the DEBUG tile bounding-volume (OBB) wireframe overlay live (placement QA).
Resize the resident-memory budget live (the runtime equivalent of the budget ctor option).
maxBytes = resident VRAM budget in bytes. Shrinking evicts far/out-of-view tiles
on the next frame; growing lets the far LOD refill.
Replace this layer's clipping planes at runtime. Planes are geographic
(ClippingPlanesOptions) and survive re-anchors; null (or an empty or
disabled set) removes clipping and restores byte-identical rendering.
The new plane set, or null to clear.
Switch the vertical-datum placement live (rigid vertical shift; no re-stream).
Restyle the hard-edge overlay, or turn it off.
The new look, or null to hide the overlay.
Colour, width, opacity and overshoot are uniforms, so this is instant and costs no re-decode.
creaseAngle and maxEdgesPerTile are NOT re-read here: they decide what geometry gets
extracted, which happened when each tile decoded. Change those in the constructor.
Passing null stops drawing edges. It does not reclaim the extracted line geometry — the
tiles keep it so turning edges back on is instant.
This only ever draws what was extracted, so it has no effect on a layer constructed without I3sMeshLayerOptions.edges.
Show only the features matching a SQL expression over the service's attributes.
The expression, or null/'' to clear it.
Optionalopts: { mode?: FilterMode }Optionalmode?: FilterMode'hide' removes non-matching features; 'xray' dims them so the
surrounding context stays legible. Default 'hide'.
Resolves once every resident tile has been re-styled.
The same shape as Esri's definitionExpression, e.g.
"Type_Toit = 'plat' AND H_MAX <= 20". Composes with I3sMeshLayer.setSymbology:
the filter decides WHICH features are visible, the symbology decides what the visible ones
look like.
Only the columns the expression references are fetched, per node, and cached — so a filter over one field never downloads the rest of the attribute table.
Fade the mesh over the basemap.
Opacity in [0, 1] (clamped); 0 = transparent, 1 = opaque.
Restyle the selection — fill colour and strength, halo colour, width and opacity.
The fields to change (SelectionStyleOptions).
Resolves once the resident tiles have been re-styled.
Toggle cast shadows at runtime (the setShadows half of SunLightable).
true to cast shadows (needs a sun via I3sMeshLayer.setSunState), false to disable.
Adopt (or clear) the shared vector-drape atlas — satisfies the DrapeReceiver contract, so
enableVectorDrape drives this layer directly. Stashed until the renderer exists.
Colour features by one of the service's published attributes.
The symbology, or null to clear it and return every feature to its own colour.
Resolves once every resident tile has been re-styled.
Applies to every resident tile immediately and to each new tile as it streams, so panning into fresh nodes does not reveal unstyled buildings.
Only the referenced column is fetched, per node, and it is cached — changing colours or switching between symbologies on the same field costs no network. A city-scale service has attributes for every building, so pulling the whole table to colour by one field would be both slow and wasteful.
Switch how each building seats on the terrain it's draped over (basemap datum) — 'min'/'median'/
'max'/'average' over its footprint. Re-clamps the resident tiles off-frame (re-armed ftTick,
texture kept until the new offsets land → no flash); no re-stream, no terrain re-sample cost change.
Show/hide without removing the layer (streaming continues).
true to show, false to hide.
Live streaming stats for a debug HUD / perf overlay.
The engine's I3sMeshEngineStats (rendered / resident / loading / in-flight counts and
bytes), or null until the engine is up.
Streams an Esri I3S IntegratedMesh or 3DObject SceneServer and renders it georeferenced — Draco geometry plus jpg/png/dds/ktx2 textures, decoded in a worker pool by default and seated against the map's basemap or 3D terrain. Takes a SceneServer (or
…/SceneServer/layers/{id}) URL; onlyidandurlare required, and no network happens untilmap.addLayerruns it.Mercator only, and WebGL2 only — on a WebGL1 context the layer goes inert and draws nothing rather than throwing every frame. Prefer the type-specific entry point when you know which scene layer type you have: I3sIntegratedMeshLayer for a continuous photogrammetric surface, I3s3dObjectLayer for discrete feature-bearing models. This class serves either.
Example