official page →
Add 3D objects and lighting to your MapTiler SDK JS maps
3D objects on MapTiler maps
With this MapTiler SDK module, you can add 3D objects to your basemap with plenty of customizations from glTF/glb files! Those can be meshes, groups of meshes, point clouds and a mix of all these.
Here are some examples:
Add an airplane 3D model to your map using the MapTiler 3D JS Module
Add multiple 3D models to the map with the MapTiler 3D JS Module
Display a building model based on point cloud data on a map with the MapTiler 3D JS Module
Display a 3D building model generated with photogrammetry software with the MapTiler 3D JS Module
Display a 3D building model generated with photogrammetry software with the MapTiler 3D JS Module
Display a point cloud 3D building model on a map with the MapTiler 3D JS Module
Import and play GLTF animations from GLTF files
Listen for mouse events on 3D objects
Apply declarative UI 'states' to 3D objects
Installation
From NPM and using the ES module, in a terminal, in your project:
npm install @maptiler/3d
Then to import:
import { Layer3D } from "@maptiler/3d";
import * as maptiler3d from "@maptiler/3d";
From CDN and using the UMD bundle, in the <head></head> section of your HTML file:
<script src="https://cdn.maptiler.com/maptiler-3d/v1.0.0/maptiler-3d.umd.js"></script>
To generate the typedoc documentation and serve them locally:
npm run doc && npx http-server docs
With the UMD bundle (on CDN), the namespace for this project is maptiler3d. So the layer3D class is available at maptiler3d.Layer3D.
Basic usage
An instance of Layer3D is a custom type of layer that contain a 3D scene, where multiple 3D meshes and lights can be added. Like any other layer in MapTiler SDK/Maplibre GL JS, it must have an ID and then be added to a Map instance:
const map = new Map({
container: "map",
});
map.on("ready", () => {
const layer3D = new maptiler3d.Layer3D("custom-3D-layer");
map.addLayer(layer3D);
})
Once created and added, a mesh can be added. In this version any glTF and their binary counterpart glb files can be added.
To add a mesh:
const item3D = await layer3D.addMeshFromURL(
"flatiron",
"https://example.com/meshes/flatiron_building.glb",
{
lngLat: {lat: 40.74072950731568, lng: -73.98918779556983},
heading: 91.1,
scale: 39.5,
visible: true,
altitude: 74.38,
altitudeReference: maptiler3d.AltitudeReference.GROUND,
}
);
Here are all the options for meshes:
lngLat location of the center of the 3D object, as longitude and latitude
altitude the altitude in meters above the reference point (to the origin of the mesh, that is not always the bottom)
altitudeReference reference point of altitude (ground or mean sea level)
visible whether the mesh is visible
sourceOrientation applies a correction from the original orientation of the mesh
scale scaling factor applied to the mesh. Can be a single number for uniform scaling, or an array of three numbers [x, y, z] for non-uniform scaling.
heading orientation in degrees (0-360), where 0 and 360 are true north and 90 is east
opacity opacity of the mesh. If the mesh is a group of meshes, this is applied to all the child nodes that can deal with transparency
pointSize applicable only to point clouds, set the size of the points
wireframe applicable only to non-point cloud, applies a wireframe rendering to all the child nodes of the mesh that are compatible with the option
states a set of properties that will be applied on different UI states (hover, active/click). For instance, a mesh could be scaled up on hover.
userData a place to store arbitrary data, that can be retrieved at a later stage.
transform (only for addMeshFromURL and cloneMesh) a set of props allowing for tweaking of the mesh before it's added to the map. This can be useful if a model internally points in the incorrect direction or if it needs a world space offset without having to tweak LngLat. { rotation: { x, y, z }, offset: { x, y, z } }. These can also be applied when cloning a mesh. Please Note: The offset when cloning a mesh is additive not absolute, it will be added to the world position of the mesh you are cloning.
Reference documentation
The constructor of the Layer3D class takes two arguments:
- a layer ID (as in the example above)
- an option object, with TypeScript, this object is of type
Layer3DOptions
Here are more details about the Layer3DOptions type:
type Layer3DOptions = {
minZoom?: number;
maxZoom?: number;
antialias?: boolean;
ambientLightColor?: ColorRepresentation,
ambientLightIntensity?: number,
};
Other important types that are exported:
- About the reference for altitude:
enum AltitudeReference {
GROUND = 1,
MEAN_SEA_LEVEL = 2
};
Example: A mesh that is add with the option altitudeReference being AltitudeReference.GROUND and an altitude of 10 will always "fly" 10 meters above the ground, regardless the terrain or the terrain exaggeration. If the provided altitude were to be a negative number, then it would always be beneath the ground surface by this amount (in meters). This mode is convenient for any item that needs to be positions relatively to the ground: cars, buildings, lap post, etc.
On the other hand, mesh that is add with the option altitudeReference being AltitudeReference.MEAN_SEA_LEVEL and the altitude of 1000 means the item will be at an absolute altitude of 1000 meters (3280 feet) above the mean sea level. If located in a place where the terrain shows mountains higher than 1000 meters, then the mesh will be underneath the ground surface and as such not visible. This mode is more convenient for flying objects such as planes, paraglydings, etc. as those thend to measure altitude with an absolute reference.
- Going from the original 3D space the mesh was created in, to the map 3D space:
enum SourceOrientation {
X_UP = 1,
Y_UP = 2,
Z_UP = 3,
};
Note that regardless of the original up axis, this module as well as MapTiler SDK/Maplibre GL JS only deal with 3D spaces that follow the right-hand rule.
- Generic options that apply to both point lights and meshes:
type GenericObject3DOptions = {
lngLat?: LngLatLike,
altitude?: number,
altitudeReference?: AltitudeReference,
visible?: boolean;
};
- Options for adding meshes specifically:
export type MeshOptions = GenericObject3DOptions & {
sourceOrientation?: SourceOrientation;
scale?: number | [number, number, number];
heading?: number;
opacity?: number;
pointSize?: number;
wireframe?: boolean;
animationMode?: AnimationMode;
states?: Item3DMeshUIStates;
userData?: Record<string, any>;
};
- Options for the UI states of a mesh:
export type Item3DMeshUIStateName = "default" | "hover" | "active";
export type Item3DMeshUIStateProperties = {
opacity?: number;
scale?: number | [number, number, number];
transform?: Item3DTransform;
heading?: number;
altitude?: number;
lngLat?: LngLatLike;
wireframe?: boolean;
pointSize?: number;
elevation?: number;
};
export type Item3DMeshUIStates = {
[key in Item3DMeshUIStateName]?: Item3DMeshUIStateProperties;
};
const item = layer3D.addMesh('mesh-id', mesh, {
opacity: 0.5,
states: {
hover: { opacity: 1 },
active: { scale: [2,2,2] }
}
})
- Additional options for tweaking models once they are loaded. ** only used in
addMeshFromURL **
export type AddMeshFromURLOptions = MeshOptions & {
transform?: {
rotation?: {
x?: number;
y?: number;
z?: number;
};
offset?: {
x?: number;
y?: number;
z?: number;
};
};
};
- Options for adding a point light specifically:
type PointLightOptions = GenericObject3DOptions & {
color?: ColorRepresentation,
intensity?: number,
decay?: number,
};
Here is the list of instance methods of the Layer3D class:
-
.setAmbientLight(options: {color?: ColorRepresentation, intensity?: number} = {})
To adjust the settings of the ambient light. The type ColorRepresentation means the color can be a number (such as a hex notation 0xff0000, for red), a hex string (such as "#FF0000", for red), or a ThreeJS color (read more about these here).
ℹ️ By default, the ambiant light is white (0xffffff) with an intensity of 0.5.
-
.addMeshFromURL(id: string, url: string, options: AddMeshFromURLOptions = {}) async
Adds a mesh from a URL to a glTF of glb file, given a mesh ID (will throw if not unique) and a set of options. This method returns an Item3D object that can be modified later on.
-
.addMesh(id: string, mesh: Mesh | Group | Object3D, options: MeshOptions = {})
Adds a ThreeJS mesh/Group/Object3D, given a mesh ID (will throw if not unique) and a set of options. This method returns a Promise<Item3D> object that can be modified later on.
ℹ️ By default, the mesh will have some settings (if not overwritten by the options):
- sourceOrientation:
SourceOrientation.Y_UP
- altitude:
0
- lngLat:
[0, 0]
- heading:
0
- visible:
true
-
.getItem3D(id: string): Item3D | null
Returns the Item3D instance for a given ID. This object can be used to modify the mesh's properties and control animations. See the section bellow for the methods of the Item3D object.
-
.cloneMesh(sourceId: string, id: string, options: CloneMeshOptions)
Clones a mesh that has a given ID (sourceId) and create another one with a new ID (id). The provided options will overwrite the settings of the source mesh.
-
.addPointLight(id: string, options: PointLightOptions = {})
Adds a point light with a unique ID (will throw if not unique) and some options.
ℹ️ By default, the light will have some settings (if not overwritten by the options):
- lngLat:
[0, 0] (null island)
- altitude:
2_000_000 meters
- altitudeReference:
AltitudeReference.MEAN_SEA_LEVEL
- color:
0xffffff (white)
- intensity:
75
- decay:
0.2
-
.modifyPointLight(id: string, options: PointLightOptions)
Modify a point light given its ID.
ℹ️ Only the settings provided in the option object will be updated, the others will be left as they already are.
-
.removeMesh(id: string)
Remove a mesh or point light from the scene and frees the GPU memory associated to it
-
.clear()
Removes all the meshes and point lights from the scene and frees the GPU memory associated with them
The Item3D object
The addMesh, addMeshFromURL and cloneMesh methods return an Item3D object. You can also retrieve it later using layer.getItem3D(id). This object has its own set of methods to modify its properties and control animations.
Here is a list of the most common methods for the Item3D object:
-
.modify(options: Partial<MeshOptions>)
Modify the settings of a mesh (scale, lntLat, etc.)
ℹ️ Only the settings provided in the option object will be updated, the others will be left as they already are.
-
.getAnimationNames()
Gets all the animations that were loaded with the model.
-
.getAnimation(animationName: string)
Get the AnimationAction named animationName.
-
.playAnimation(animationName: string, loop: AnimationLoopOptions)
Plays animationName. Loop defines how the animation will loop. "loop" loops the animation infinity times; "once" loops it once; "pingPong" plays the animation until the end and then plays it in reverse.
-
.pauseAnimation(animationName: string)
Pauses animationName.
-
.stopAnimation(animationName: string)
Stops animationName.
-
.updateAnimation(delta = 0.02)
Updates the mesh animations by delta seconds. This is only useful when the animationMode is set to manual.
-
.setAnimationTime(time: number)
sets the animation to time seconds.
E2E Testing
This project uses a combination of Vite for building and serving fixtures and Playwright for browser automation to perform end-to-end testing.
Currently we are unable to use Vitest as a test runner at present due to this issue with Playwright.
Test Structure
The testing setup consists of:
Adding New Tests
To add new test fixtures, add entry points to the rollup options in vite-config-e2e.ts.
Running Tests
Two npm scripts are available for testing; they must be run simultaneously:
# Start the test server that serves test fixtures
npm run e2e:serve
# Run the e2e tests against local test server
npm run e2e:local
# to update the snapshots pass the appropriate flag
npm run e2e:local -- --update-snapshots
# to run in ui mode...
npm run e2e:local -- --ui
Note: GitHub Actions integration for automated testing will be implemented in upcoming versions.
License
MapTiler JS Module
Copyright © 2024 MapTiler AG. All rights reserved.
The software and files (collectively “Software”) in this repository are licensed for use only with MapTiler service(s).
For the license terms, please reference MapTiler JavaScript Module Terms and Conditions.
This license allows users with an active MapTiler account to modify and integrate authorized portions of the Software for use with the relevant MapTiler service(s) in accordance with the MapTiler Terms. This license terminates automatically if a user no longer maintains a MapTiler account or their usage breaches MapTiler Terms.