MapConductor React SDK Samples

Three.js (web only) | MapLibre | MapConductor React SDK

MapConductor React SDKでMapLibreを使用するThree.js (web only)のサンプルです。

このインタラクティブサンプルでは、MapConductorの抽象APIを通じてMapLibre上でThree.js (web only)を実装します。

コード例

import { useEffect, useMemo } from 'react';
import * as THREE from 'three';
import { createGeoPoint, createMapCameraPosition } from '@mapconductor/js-sdk-core';
import { useMapLoaded } from '@mapconductor/js-sdk-react';
import { MapLibreDesign, MapLibreMapView2D, useMapLibreViewState } from '@mapconductor/react-for-maplibre';


// (1) 地図の作成


const initialCamera = createMapCameraPosition({
  position: createGeoPoint({ latitude: 35.6812, longitude: 139.7671 }),
  zoom: 12,
});


const mapViewState = useMapLibreViewState({
  mapDesignType: MapLibreDesign.OsmBrightJa,
  cameraPosition: initialCamera,
});


// (2) データと操作用Stateの準備
const position = useMemo(() => createGeoPoint({
  latitude: 35.6812,
  longitude: 139.7671,
}), []);


// (3) 地図とオーバーレイの描画


function ThreeMapObject({ mapViewState, position }) {
  const isMapLoaded = useMapLoaded();


  useEffect(() => {
    if (!isMapLoaded) return;
    const holder = mapViewState.getMapViewHolder();
    if (!holder) return;


    const scene = new THREE.Scene();
    const camera = new THREE.OrthographicCamera(0, 1, 1, 0, 0.1, 1000);
    camera.position.z = 200;
    const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
    const width = holder.mapView.clientWidth;
    const height = holder.mapView.clientHeight;
    renderer.setSize(width, height);
    camera.right = width;
    camera.top = height;
    camera.updateProjectionMatrix();
    renderer.domElement.style.position = 'absolute';
    renderer.domElement.style.inset = '0';
    holder.mapView.appendChild(renderer.domElement);


    const object = new THREE.Mesh(
      new THREE.TorusKnotGeometry(13, 4),
      new THREE.MeshNormalMaterial(),
    );
    scene.add(object);


    let frame = 0;
    const draw = () => {
      const offset = holder.toScreenOffset(position);
      if (offset && !(offset instanceof Promise)) {
        object.position.set(offset.x, height - offset.y, 0);
      }
      object.rotation.y += 0.02;
      renderer.render(scene, camera);
      frame = requestAnimationFrame(draw);
    };
    draw();


    return () => {
      cancelAnimationFrame(frame);
      renderer.domElement.remove();
      renderer.dispose();
    };
  }, [isMapLoaded, mapViewState, position]);


  return null;
}


<MapLibreMapView2D state={mapViewState}>
  <ThreeMapObject mapViewState={mapViewState} position={position} />
</MapLibreMapView2D>

コードの読み方

透明な Three.js の canvas を地図へ重ね、Provider 非依存の MapViewHolder.toScreenOffset() で立体オブジェクトを地理座標へ固定します。

useMapLoaded が地図の準備が整うまで useEffect を待たせ、その後 getMapViewHolder() が返すホルダーの WebGL canvas を地図の上へ追加します。

毎フレーム holder.toScreenOffset(position) が地理座標をピクセル座標へ変換するため、地図を動かしても立体オブジェクトが固定され、どのプロバイダーでも同じ呼び出しで動きます。