bim-tile-overlay
v1.0.0
Published
Overlay web map tiles (OSM, aerial imagery, custom XYZ) onto Autodesk APS Viewer as a camera-synced 3D ground plane
Downloads
132
Maintainers
Readme
bim-tile-overlay
Overlay web map tiles (OSM, aerial imagery, custom XYZ) onto an Autodesk APS Viewer as a camera-synced 3D ground plane.
By Infra Plan

The Problem
Placing geographic map tiles under a BIM model in Autodesk Viewer requires:
- Extracting the camera frustum and projecting it onto a ground plane
- Converting between the viewer's internal coordinate system and WGS84
- Figuring out which map tiles cover the visible area at the right zoom level
- Fetching, stitching, and caching those tiles into a GPU-friendly texture
- Positioning a THREE.js plane in 3D space aligned with the geographic bounds
- Updating everything in real-time as the camera moves
This library handles all of that in ~500 lines of code.
Quick Start
npm install bim-tile-overlay proj4import { TileOverlay, CoordinateTransformer } from 'bim-tile-overlay';
// After viewer has loaded a model:
const transformer = CoordinateTransformer.fromAPSViewer(
viewer,
// proj4 definition for your local CRS (this example uses Croatia HTRS96/TM)
'+proj=tmerc +lat_0=0 +lon_0=16.5 +k=0.9999 +x_0=500000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs'
);
const overlay = new TileOverlay(viewer, transformer, {
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
zoomRange: [14, 19],
maxBounds: transformer.getModelBoundsLL84(),
});
await overlay.enable();How It Works
Camera frustum corners
→ Ray-cast to ground plane (Z elevation)
→ Convert hit points to WGS84 lon/lat
→ Determine visible geographic bounds
→ Calculate optimal tile zoom level
→ Fetch XYZ tiles in parallel
→ Stitch into single canvas texture
→ Map onto THREE.js plane in viewer space
→ Update on camera change (debounced)Coordinate pipeline:
WGS84 (lon, lat)
↔ Local CRS (meters) → via proj4
↔ CRS in feet → × 3.28084
↔ BIM internal coords → via refPointTransform rotation + translation
↔ Viewer display coords → minus globalOffsetAPI Reference
TileOverlay
Main class. Creates a map tile overlay on the viewer.
const overlay = new TileOverlay(viewer, transformer, options);Options:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| urlTemplate | string | required | Tile URL with {z}, {x}, {y} placeholders |
| zoomRange | [number, number] | required | Min and max zoom levels |
| maxBounds | GeoBounds | required | Geographic bounds to clip tile fetching |
| groundZ | number | modelBBox.min.z - 5 | Z elevation of the ground plane |
| debounceMs | number | 150 | Camera change debounce delay (ms) |
| maxCacheSize | number | 6 | Max cached stitched tile canvases |
| zoomScaleFactor | number | 12 | Tile detail vs. camera distance. Higher = more detail |
| progressInterval | number | 5 | Texture refresh frequency during tile loading (every N tiles). Always fires on the last tile. Set to 1 for per-tile updates. |
| sceneName | string | 'bim-tile-overlay' | Viewer overlay scene name |
Methods:
| Method | Description |
|--------|-------------|
| enable() | Show the overlay and start tracking camera |
| disable() | Hide the overlay, preserve cache for re-enabling |
| destroy() | Fully dispose all GPU resources and cache |
| update() | Force an immediate tile refresh |
CoordinateTransformer
Converts between WGS84 and the viewer's coordinate system.
// From viewer (recommended):
const transformer = CoordinateTransformer.fromAPSViewer(viewer, crsDefinition);
// Manual config:
const transformer = new CoordinateTransformer({
crs: '+proj=tmerc +lat_0=0 +lon_0=16.5 ...',
refPointTransform: [...], // From model metadata
globalOffset: { x, y, z },
modelBBox: { min: { x, y, z }, max: { x, y, z } },
});Methods:
| Method | Description |
|--------|-------------|
| lonLatToViewer(lon, lat, z?) | WGS84 → viewer coordinates |
| viewerToLonLat(x, y, z?) | Viewer coordinates → WGS84 |
| getModelBoundsLL84() | Model bounding box in WGS84 |
Utility Functions
import { lonLatToTile, tileToLonLat, getViewportBounds, createTileCache } from 'bim-tile-overlay';| Function | Description |
|----------|-------------|
| lonLatToTile(lon, lat, zoom) | WGS84 → tile {x, y} coordinates |
| tileToLonLat(x, y, zoom) | Tile coordinates → WGS84 (NW corner) |
| getViewportBounds(camera, transformer, options) | Camera frustum → geographic bounds + zoom |
| createTileCache(maxSize?) | LRU cache that frees bitmap memory on eviction |
Finding Your CRS
Your BIM model needs a local Coordinate Reference System (CRS) for accurate positioning. Common ones:
| Region | CRS | Code | |--------|-----|------| | Croatia | HTRS96/TM | EPSG:3765 | | UK | British National Grid | EPSG:27700 | | Germany | ETRS89/UTM zone 32N | EPSG:25832 | | US (New York) | NAD83/NY Long Island | EPSG:2263 |
Find your CRS definition at epsg.io and pass the proj4 string.
Requirements
- Autodesk Viewer (APS / Forge) with a loaded, georeferenced model — the Viewer script provides
THREE.jsandAutodeskglobals that this library uses internally; you do not need to install them separately - proj4 (peer dependency) —
npm install proj4 - The BIM model must have
refPointTransformin its metadata (set via Revit's survey/project point)
Contributing
Contributions are welcome! Please open an issue or pull request.
License
MIT - Infra Plan

