3D model set the position relative to another 3D model

Use the MapTiler 3D JS module to set the position of una 3D model relative to another 3D model or a 3D position.

Apply a offset: offset.x = longitude direction, offset.y = altitude, offset.z = latitude. Define the units: "meters" (default), "feet", "km", "miles".

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, AltitudeReference } from '@maptiler/3d';

import * as THREE from 'three';
config.apiKey = 'YOUR_MAPTILER_API_KEY_HERE';

const center = [2.35357, 48.85845];
const modelAltitude = 200;

const map = new Map({
  container: document.getElementById('map'),
  hash: true,
  style: MapStyle.SATELLITE,
  center: [2.35872, 48.86306],
  zoom: 14.48,
  bearing: 31.9,
  pitch: 63,
  maxPitch: 85,
  projectionControl: true,
  centerClampedToGround: false,
});

(async () => {
  await map.onReadyAsync();

  const layer3D = new Layer3D("layer3d");
  map.addLayer(layer3D);


  layer3D.setAmbientLight({ intensity: 1 });
  layer3D.addPointLight("point-light", {
    intensity: 30,
    lngLat: center,
    altitude: 1000,
    altitudeReference: AltitudeReference.MEAN_SEA_LEVEL,
  });

  // First Item3D at center (anchor)
  const anchor = layer3D.addMesh("anchor", createAxesMeshes(), {
    lngLat: center,
    altitude: modelAltitude,
    altitudeReference: AltitudeReference.MEAN_SEA_LEVEL,
    scale: 5,
    sourceOrientation: maptiler3d.SourceOrientation.Y_UP,
  });

  let mousedown = false;
  let startLngLat = new maptilersdk.LngLat(0, 0);

  anchor.on("mousedown", (e) => {
    map.dragPan.disable();
    map.dragRotate.disable();
    mousedown = true;
    startLngLat = e.lngLat;
  });

  // we need to disable the drag pan and rotate when the mouse is up, otherwise the map will continue to pan and rotate when the mouse is released.
  map.on("mouseup", (e) => {
    if (mousedown) {
      mousedown = false;
      map.dragPan.enable();
      map.dragRotate.enable();
    }
  });

  // re-enable the drag pan and rotate when the mouse is up
  anchor.on("mouseup", () => {
    mousedown = false;
    map.dragPan.enable();
    map.dragRotate.enable();
  });

  map.on("mousemove", (e) => {
    if (mousedown) {
      // this is not an exact calculation but will suffice for the purposes of the demo.
      const eventLngLat = e.lngLat;

      const diff = new maptilersdk.LngLat(eventLngLat.lng - startLngLat.lng, eventLngLat.lat - startLngLat.lat);
      const newLngLat = new maptilersdk.LngLat(anchor.lngLat.lng + diff.lng, anchor.lngLat.lat + diff.lat);

      anchor.setLngLat(newLngLat);
      shadowingAnchor.setPositionRelativeTo(anchor, { x: 100, y: 100, z: 100 }, "meters");

      startLngLat = eventLngLat;
    }
  });

  const shadowingAnchor = anchor.clone();
  shadowingAnchor.setPositionRelativeTo(anchor, { x: 100, y: 100, z: 100 }, "meters");
  shadowingAnchor.setRelativeScale(0.4);

  addForm({ anchor, shadowingAnchor });
})()

function createAxesMeshes() {
  const directions = ["x", "y", "z"];
  const group = new THREE.Group();

  for (const direction of directions) {
    const arrow = new THREE.Group();
    const color = direction === "x" ? 0xff0000 : direction === "y" ? 0x00ff00 : 0x0000ff;
    const stem = new THREE.Mesh(new THREE.CylinderGeometry(0.1, 0.1, 1), new THREE.MeshStandardMaterial({ color: color }));

    stem.position.set(0, 0.5, 0);

    const head = new THREE.Mesh(new THREE.ConeGeometry(0.2, 1), new THREE.MeshStandardMaterial({ color: color }));

    head.position.set(0, 1.5, 0);

    arrow.add(stem);
    arrow.add(head);
    arrow.name = direction;
    arrow.rotation.set(direction === "x" ? Math.PI / 2 : 0, direction === "y" ? Math.PI / 2 : 0, direction === "z" ? Math.PI / 2 : 0);

    group.add(arrow);
  }

  const centralSphere = new THREE.Mesh(new THREE.SphereGeometry(0.25), new THREE.MeshStandardMaterial({ color: 0xffffff }));
  group.add(centralSphere);

  group.scale.set(10, 10, 10);

  return group;
}

function addForm({ anchor, shadowingAnchor }) {
  const form = document.getElementById("form");

  form.addEventListener("submit", function (e) {
    e.preventDefault();
    const formData = new FormData(form);
    const x = Number(formData.get("x"));
    const y = Number(formData.get("y"));
    const z = Number(formData.get("z"));
    const units = formData.get("units");
    anchor.moveBy({ x, y, z }, units);
    shadowingAnchor.setPositionRelativeTo(anchor, { x: 100, y: 100, z: 100 }, units);
  });
}

Low-Poly Biplane by ElectrikGoat0395 is licensed under Creative Commons Attribution ↩

3D model set the pitch or tilt

3D model pitch

Examples

Rotate 3D models by setting the pitch tilt property.

Add events on 3D models

3D model events

Examples

Listen for mouse events on 3D models in maps.

3D models intersections

3D models intersections

Examples

Detect intersections between 3D models using MapTiler JS module.

Display an animated 3D model

Animate a 3D model

Examples

Animate 3D models and display them on a map.

Was this helpful?