SDK JS
    Preparing search index...

    Streams an Esri I3S Building Scene Layer (BSL) — a BIM model published as a tree of discipline sublayers (Walls / Doors / Floors / …) — and draws every renderable 3DObject sublayer through one shared renderer, one anchor and one decoder pool in a single pass. On load it applies the service's own default view: the detailed disciplines, minus the coarse Overview shell and minus any leaf the publisher switched off (I3sBuildingLayer.sublayerStates reports what is drawn and why). Only id and url are required, and no network happens until map.addLayer runs it.

    Mercator only, and WebGL2 only — on a WebGL1 context the layer goes inert and draws nothing. This is the modern I3S 1.7+ nodePages path: a legacy 1.4–1.6 BSL has no sublayers it can open and reports that through I3sBuildingLayerOptions.onError. For an IntegratedMesh or 3DObject scene layer use I3sMeshLayer instead.

    import { I3sBuildingLayer } from '@bitruvius/sdk-maplibre';

    map.addLayer(new I3sBuildingLayer({ id: 'bsl', url: 'https://…/SceneServer/layers/0' }));

    Implements

    • CustomLayerInterface
    Index

    Constructors

    Properties

    id: string

    A unique layer id.

    renderingMode: "3d" = ...

    Either "2d" or "3d". Defaults to "2d".

    type: "custom" = ...

    The layer's type. Must be "custom".

    Methods

    • The distinct filterable BIM attribute fields across all sublayers (e.g. for a filter dropdown). numeric distinguishes BldgLevel<=2 from Discipline='Architectural' in the value formatting.

      Returns { name: string; numeric: boolean }[]

    • 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.

      Returns void

    • Sample DISTINCT values of field from currently-resident tiles (to populate a value dropdown). Capped at max; reuses the per-node attribute-column cache.

      Parameters

      • field: string
      • max: number = 40

      Returns Promise<(string | number)[]>

    • The dataset's attribution string, if any.

      Returns string | undefined

      The attribution option, or undefined if none was set.

    • The building's parsed tree (for a discipline/sublayer UI), or null until loaded.

      Returns BuildingSceneLayer | null

    • The building's camera target [lng, lat] (degrees) — handy for driving your own flyTo.

      Returns [number, number] | null

      [lng, lat] in degrees, or null until the building's anchor is known.

    • MapLibre CustomLayerInterface hook — invoked when the layer is added with map.addLayer; initializes GL resources and begins streaming.

      Parameters

      Returns void

    • MapLibre CustomLayerInterface hook — invoked when the layer is removed with map.removeLayer; releases GL resources and stops streaming.

      Returns void

    • Resolve a canvas point to the 3D geographic position of the frontmost rendered BSL surface — the screen→scene primitive behind measurement and slice placement (all visible sublayers are queried in one GPU pick pass, exactly the merged set render() draws).

      Parameters

      • x: number

        Canvas X in CSS pixels, top-left origin (e.g. a MapLibre click event's e.point.x).

      • y: number

        Canvas Y in CSS pixels, top-left origin.

      • snapRadiusPx: number = 0

        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.

      Returns PickedPosition | null

      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.

      Parameters

      Returns void

    • Nearest obstruction distance per height row through a forward corridor — ONE synthetic forward-looking orthographic pick pass over the walker's cross-section, merged across the visible sublayers. The ObstructionProbeable contract; see it for why an overhead height probe cannot tell a doorway from a wall.

      DOOR LEAVES AND RAILINGS ARE EXCLUDED — see DOOR_SUBLAYER_RE and RAILING_SUBLAYER_RE. Both are modelled as solid geometry filling the very opening a walker has to use (a leaf across the doorway, a rail overhanging the treads of the flight), so a probe that honoured them refuses every door and every staircase in the building. Matched by sublayer ROLE (a name test), because the discipline names differ between services; everything else visible counts, glazing included.

      Parameters

      Returns Float32Array<ArrayBufferLike> | null

      Distance in metres per row (index 0 = lowest), NaN where clear, null when this layer cannot answer.

    • Working-set load fraction (0–1) across every VISIBLE sublayer: drawn tiles over drawn plus still-pending. 1 once the current view is fully resolved.

      Returns number

      The fraction, or 0 before any sublayer is open.

    • 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).

      Parameters

      Returns void

    • Sample the height of the building'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; the surface probe behind first-person navigation). The SurfaceSampleable contract, like the mesh layers.

      All VISIBLE sublayers are sampled in one merged pass — exactly the set render() draws, so sublayers hidden via I3sBuildingLayer.setSublayerVisible (including the auto-hidden Overview shell) contribute nothing. Samples the currently-RESIDENT tiles; positions in unloaded areas return null. Note: individual features hidden by an attribute filter (I3sBuildingLayer.setFilter) still count as surface — the overhead pick pass, like I3sBuildingLayer.pickPosition, does not evaluate per-feature styles.

      Parameters

      • points: readonly { lat: number; lng: number }[]

        lng/lat in degrees.

      • Optionalopts: SurfaceSampleOptions

        Optional: 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 — the difference between "the roof of this building" and "the floor I am standing on", and the reason a walker can read one storey of a BSL at all (see SurfaceSampleOptions.maxHeightM).

      Returns (number | null)[]

      Height in metres above the rendered ground datum per point, or null where this layer has no surface.

    • Toggle the DEBUG tile bounding-volume (OBB) wireframe overlay live (placement QA).

      Parameters

      • on: boolean

      Returns void

    • 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.

      Parameters

      Returns void

    • Filter the building by a BSL SQL predicate over per-feature BIM attributes — e.g. "Discipline='Architectural'", "BldgLevel<=2", "Category IN ('Walls','Doors')". Features that DON'T match are hidden (mode:'hide', default) or dimmed to a dark ghost (mode:'xray'). Compiled once; evaluated per feature only on change; the GPU pays one texture lookup at draw time.

      Parameters

      • expr: string | null

        A BSL SQL predicate string, or null / '' to clear the filter (show everything).

      • Optionalopts: { mode?: "hide" | "xray" }

        Filter display mode. mode:'hide' (default) removes non-matching features; mode:'xray' dims them so the surrounding context stays legible. Omit to keep the current mode.

      Returns Promise<void>

      A promise that resolves once every resident tile has been re-styled.

      X-ray DIMS; it does not make anything see-through. The per-feature style carries an alpha, but the building draws in the opaque pass (written unblended), so that alpha lands as a darkening multiply on the colour instead of compositing what is behind — the excluded parts read as a dark ghost of the model, still legible as context. Documented as-is rather than as-intended: the see-through version needs a blended pass (mesh KNOWN-GAPS §2), which this layer does not have.

    • Fade the whole building over the basemap (all sublayers together).

      Parameters

      • opacity: number

        0 (invisible) – 1 (opaque); clamped.

      Returns void

    • Toggle cast shadows at runtime (the setShadows half of SunLightable).

      Parameters

      Returns void

    • Adopt (or clear) the shared vector-drape atlas — satisfies the DrapeReceiver contract, so enableVectorDrape drives this layer directly. Stashed until the renderer exists.

      Parameters

      • shared: SharedDrape | null

      Returns void

    • Show/hide one BIM sublayer by id (re-renders without re-streaming).

      Parameters

      Returns void

    • Light this layer from a SunState — satisfies @bitruvius/sdk-maplibre's SunLightable, so a GeoSunSystem / RendererSunController drives it directly.

      Parameters

      • sun: SunState

        The sun state (direction in ENU, altitude in degrees) to light + (if enabled) cast shadows from.

      Returns void

    • Show/hide the whole building without removing the layer.

      Parameters

      • visible: boolean

        true to show, false to hide.

      Returns void

    • Wireframe render mode for the whole building (a BIM structure view). Edges come from the mesh triangles in-shader (no extra geometry); applies to the I3S PAA (non-indexed) sublayers these BSLs use — enable the I3sBuildingLayerOptions.wireframe option to make it work on Draco-served sublayers too.

      Parameters

      • mode: 0 | 1 | 2

        0 = off (solid), 1 = edges drawn over the solid, 2 = wireframe only.

      Returns void

    • Aggregate streaming stats across visible sublayers — drive a debug HUD / load indicator from it.

      Returns {
          bytes: number;
          inFlight: number;
          loading: number;
          queued: number;
          rendered: number;
          resident: number;
          sublayers: number;
          visible: number;
      }

      Counts of sublayers (total) and visible sublayers, plus tile totals across the visible ones: rendered (drawn this frame), resident (in VRAM), loading (decoding), inFlight (network), queued (waiting to load) and resident bytes.

    • Every renderable sublayer with the state this layer ACTUALLY applied, and — when one is not drawn — why. Built for a sublayer show/hide UI: BslSublayer.visibility alone is the service's declared flag, which is only one of the three reasons a sublayer can be off, so a checklist seeded from it claims sublayers are on that the layer is correctly hiding.

      Distinguishing them matters: a service-hidden discipline and a user-hidden one look identical on screen, and reading "the publisher switched this off" as "the SDK lost my geometry" is how a correctly-rendered BSL gets reported as broken.

      Returns BslSublayerState[]

      One entry per renderable leaf in tree order (including non-3DObject leaves, which report opened: false / hiddenReason: 'not-renderable'). Empty until the building document has loaded — poll, or call it from your onLoad.

      for (const s of layer.sublayerStates()) {
      const badge = s.hiddenReason && s.hiddenReason !== 'user' ? ` — ${s.hiddenReason}` : '';
      console.log(`${s.visible ? '☑' : '☐'} ${s.name}${badge}`);
      }