Add events on 3D models

Add event listeners to your 3D models to improve interaction with objects added to your maps.

This demonstration showcases how to add user interactivity to 3D models, click, doubleclick, mouseenter, mouseleave.

Click or move the mouse over the 3D models to get to event information

NPM module setup


npm install --save @maptiler/sdk @maptiler/3d three
import { Map, MapStyle, config } from '@maptiler/sdk';
import '@maptiler/sdk/dist/maptiler-sdk.css';
import { Layer3D } from '@maptiler/3d';

import * as THREE from 'three';

config.apiKey = 'YOUR_MAPTILER_API_KEY_HERE';

const map = new Map({
  container: 'map',
  style: MapStyle.SATELLITE,
  center: [-119.63553428649902, 37.728162793005595], // starting position [lng, lat]
  zoom: 12.5, // starting zoom
  hash: true,
  bearing: 0,
  pitch: 45,
  terrainControl: true,
  terrain: true,
});

const shapes = [
  {
    lngLat: [-119.65308666229248, 37.738277034695685],
      mesh: (
        new THREE.Mesh(
          new THREE.BoxGeometry(10, 10, 10),
          new THREE.MeshStandardMaterial({ color: "red", roughness: 0.5, metalness: 0.5 })
        )
    ),
    name: "box",
    altitude: 120,
  },
  {
    lngLat: [-119.61553573, 37.723071209025974],
    mesh: (
      new THREE.Mesh(
        new THREE.SphereGeometry(10),
        new THREE.MeshStandardMaterial({ color: "blue", roughness: 0.5, metalness: 0.5 })
      )
    ),
    name: "sphere",
    altitude: 120,
  },
  {
    lngLat: [-119.63008403778076, 37.735154664553534],
    altitude: 260,
    mesh: (
        new THREE.Mesh(
          new THREE.TorusGeometry(10, 5, 32, 32),
          new THREE.MeshStandardMaterial({ color: "green", roughness: 0.5, metalness: 0.5 })
        )
    ),
    name: "torus"
  },
  {
    lngLat: [-119.65128421783447, 37.72208679574618],
    altitude: 300,
    mesh: (
        new THREE.Mesh(
          new THREE.TorusKnotGeometry(10, 2, 32, 32),
          new THREE.MeshStandardMaterial({ color: "rebeccapurple", roughness: 0.5, metalness: 0.5 })
        )
    ),
    name: "torusKnot"
  },
];

const info = document.getElementById("event-info");

function setInfo(text) {
  info.innerHTML = text;
}

// Waiting for the map to be ready
map.on('ready', async () => {
  // Create a Layer3D and add it
  const layer3D = new Layer3D("custom-3D-layer");
  map.addLayer(layer3D);

   // Increasing the intensity of the ambient light
  layer3D.setAmbientLight({ intensity: 2 });

  // Adding a point light
  layer3D.addPointLight("point-light", { intensity: 30 });

  shapes.forEach((shape) => {
    const initialScale = 20;

    layer3D.addMesh(shape.name, shape.mesh, {
      lngLat: new maptilersdk.LngLat(shape.lngLat[0], shape.lngLat[1]),
      heading: 0,
      scale: initialScale,
      altitude: shape.altitude,
    });
    const mesh = layer3D.getItem3D(shape.name);

    const threeMesh = mesh?.mesh;

    const originalMaterial = threeMesh?.material;
    const swapMaterial = new THREE.MeshBasicMaterial({ color: "dodgerblue" });

    mesh?.on("mouseenter", (event) => {
      const mesh = layer3D.getItem3D(shape.name);
      const threeMesh = mesh?.mesh;
      if (threeMesh && "material" in threeMesh) {
        threeMesh.material = swapMaterial;
      }
      setInfo(`mouseenter! \n
        name: ${shape.name}\n
        lng: ${shape.lngLat[0]} \n
        lat: ${shape.lngLat[1]} \n
        x, y: ${event.point.x}, ${event.point.y} \n
      `);
    });
    mesh?.on("mouseleave", (event) => {
      const mesh = layer3D.getItem3D(shape.name);
      const internalMesh = mesh?.mesh;
      if (internalMesh && "material" in internalMesh) {
        internalMesh.material = originalMaterial;
      }
      setInfo(`mouseleave! \n
        name: ${shape.name}\n
        lng: ${shape.lngLat[0]} \n
        lat: ${shape.lngLat[1]} \n
        x, y: ${event.point.x}, ${event.point.y} \n
      `);
    });
    
    mesh?.on("click", (event) => {
      if (mesh?.scale[0] === initialScale) {
        mesh?.setScale(initialScale * 1.5);
      } else {
        mesh?.setScale(initialScale);
      }
      setInfo(`click! \n
        name: ${shape.name}\n
        lng: ${shape.lngLat[0]} \n
        lat: ${shape.lngLat[1]} \n
        x, y: ${event.point.x}, ${event.point.y} \n
      `);
    });

    mesh?.on("dblclick", (event) => {
      setInfo(`dblclick! \n
        name: ${shape.name}\n
        lng: ${shape.lngLat[0]} \n
        lat: ${shape.lngLat[1]} \n
        x, y: ${event.point.x}, ${event.point.y} \n
      `);
    });
  });

  let heading = 0;

  function loop() {
    requestAnimationFrame(loop);
    for (const { name: meshName } of shapes) {
      layer3D.getItem3D(meshName)?.modify({
        heading: heading,
      });
    }
    heading += 0.5;
  }

  loop();
});
Display an animated 3D model

Animate a 3D model

Examples

Animate 3D models and display them on a map.

Add a airplane 3D model

Add a plane 3D model

Examples

Add and position flying 3D airplane models on maps.

Add multiple 3D models to the map

Add multiple 3D models

Examples

Incorporate multiple 3D models with adjustable parameters on maps.

Display a 3D terrain map

3D terrain map

Tutorials

Create and display 3D terrain maps on web pages.

Was this helpful?