Transform objects using position, rotation and scale.
import {
Mesh,
Vector3,
Euler,
MeshBasicMaterial,
BoxGeometry,
Object3D,
DoubleSide,
AxesHelper,
} from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import TileWMS from "ol/source/TileWMS.js";
import Extent from "@giro3d/giro3d/core/geographic/Extent.js";
import Instance from "@giro3d/giro3d/core/Instance.js";
import ColorLayer from "@giro3d/giro3d/core/layer/ColorLayer.js";
import ElevationLayer from "@giro3d/giro3d/core/layer/ElevationLayer.js";
import Interpretation from "@giro3d/giro3d/core/layer/Interpretation.js";
import Map, { isMap } from "@giro3d/giro3d/entities/Map.js";
import Inspector from "@giro3d/giro3d/gui/Inspector.js";
import TiledImageSource from "@giro3d/giro3d/sources/TiledImageSource.js";
import WmsSource from "@giro3d/giro3d/sources/WmsSource.js";
const wmsLayers = [
"metropole-de-lyon:fpc_fond_plan_communaut.fpcilot",
"metropole-de-lyon:pvo_patrimoine_voirie.pvochausseetrottoir",
"grandlyon:ortho_2009",
"metropole-de-lyon:pos_opposable.poshauvoi",
"grandlyon:MNT2015_Ombrage_2m",
"metropole-de-lyon:cad_cadastre.cadilot",
];
const cubeTransformations = [
{
position: new Vector3(0, 0, 0.5),
rotation: new Euler(),
},
{
position: new Vector3(0, 0, -0.5),
rotation: new Euler().set(Math.PI, 0, 0),
},
{
position: new Vector3(0, 0.5, 0),
rotation: new Euler().set(-Math.PI * 0.5, 0, 0),
},
{
position: new Vector3(0, -0.5, 0),
rotation: new Euler().set(Math.PI * 0.5, 0, 0),
},
{
position: new Vector3(0.5, 0, 0),
rotation: new Euler().set(0, Math.PI * 0.5, 0),
},
{
position: new Vector3(-0.5, 0, 0),
rotation: new Euler().set(0, -Math.PI * 0.5, 0),
},
];
// Define projection that we will use (taken from https://epsg.io/3946, Proj4js section)
Instance.registerCRS(
"EPSG:3946",
"+proj=lcc +lat_1=45.25 +lat_2=46.75 +lat_0=46 +lon_0=3 +x_0=1700000 +y_0=5200000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs",
);
// Define geographic extent: CRS, min/max X, min/max Y
const extent = new Extent(
"EPSG:3946",
1837900,
1837900 + 8000,
5170100,
5170100 + 8000,
);
const scale = new Vector3(1, 1, 1).divideScalar(extent.dimensions().x);
// Instantiate Giro3D
const instance = new Instance({
target: "view",
crs: extent.crs,
backgroundColor: 0x999999,
});
const cube = new Mesh(
new BoxGeometry(8000, 8000, 8000),
new MeshBasicMaterial({ color: 0xdddddd }),
);
cube.name = "root cube";
const wireframe = new Mesh(
new BoxGeometry(8000, 8000, 8000),
new MeshBasicMaterial({ color: 0x000000, wireframe: true }),
);
wireframe.name = "wireframe cube";
cube.add(wireframe);
cube.scale.copy(scale);
cube.updateMatrixWorld(true);
instance.scene.add(cube);
const axes = new AxesHelper(1);
instance.scene.add(axes);
function createColorLayer(name, url) {
const source = new WmsSource({
url,
layer: name,
imageFormat: "image/jpeg",
projection: "EPSG:3946",
});
return new ColorLayer({ name, source });
}
function createElevationLayer(name, url) {
const source = new TiledImageSource({
source: new TileWMS({
url,
projection: "EPSG:3946",
params: {
LAYERS: [name],
},
}),
});
return new ElevationLayer({
name,
source,
minmax: { min: -100, max: +250 },
interpretation: Interpretation.ScaleToMinMax(149, 621),
});
}
const allMaps = [];
for (let i = 0; i < wmsLayers.length; i++) {
const cubeSide = new Object3D();
const offset = extent
.centerAsVector3()
.negate()
.applyEuler(cubeTransformations[i].rotation);
offset.add(cubeTransformations[i].position.divide(scale));
cubeSide.position.copy(offset);
cubeSide.rotation.copy(cubeTransformations[i].rotation);
cube.add(cubeSide);
cubeSide.updateMatrixWorld(true);
const layerName = wmsLayers[i];
const map = new Map({
extent,
segments: 16,
discardNoData: true,
side: DoubleSide,
object3d: cubeSide,
});
map.name = layerName;
instance.add(map);
allMaps.push(map);
map.addLayer(
createColorLayer(
layerName,
"https://download.data.grandlyon.com/wms/grandlyon",
),
);
map.addLayer(
createElevationLayer(
"grandlyon:MNT2012_Altitude_10m_CC46",
"https://download.data.grandlyon.com/wms/grandlyon",
),
);
}
instance.view.camera.position.set(3, 3, 2);
instance.view.camera.updateMatrixWorld(true);
instance.view.camera.lookAt(new Vector3(0, 0, 0));
const controls = new OrbitControls(instance.view.camera, instance.domElement);
controls.minDistance = 1;
instance.view.setControls(controls);
instance.view.minNearPlane = 0.1;
// Request redraw
instance.notifyChange();
Inspector.attach("inspector", instance);
function highlight(event) {
for (const map of allMaps) {
map.colorimetry.brightness = 0;
map.colorimetry.saturation = 1;
map.colorimetry.contrast = 1;
}
const picked = instance.pickObjectsAt(event);
if (picked.length > 0) {
picked.sort((a, b) => a.distance - b.distance);
const first = picked[0];
if (first) {
const entity = first.entity;
if (isMap(entity)) {
entity.colorimetry.brightness = 0.3;
entity.colorimetry.saturation = 2;
entity.colorimetry.contrast = 1.5;
}
}
}
instance.notifyChange(allMaps);
}
instance.domElement.addEventListener("mousemove", highlight);
<!doctype html>
<html lang="en">
<head>
<title>3D Transformations</title>
<meta charset="UTF-8" />
<meta name="name" content="cubic_planar" />
<meta
name="description"
content="Transform objects using position, rotation and scale."
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="https://giro3d.org/images/favicon.svg" />
<link
href="https://giro3d.org/assets/bootstrap-custom.css"
rel="stylesheet"
/>
<script src="https://giro3d.org/assets/bootstrap.bundle.min.js"></script>
<link
rel="stylesheet"
type="text/css"
href="https://giro3d.org/latest/css/example.css"
/>
</head>
<body>
<div id="view" class="m-0 p-0 w-100 h-100"></div>
<div
id="inspector"
class="position-absolute top-0 start-0 mh-100 overflow-auto"
></div>
<script type="module" src="index.js"></script>
<script>
/* activate popovers */
const popoverTriggerList = [].slice.call(
document.querySelectorAll('[data-bs-toggle="popover"]'),
);
popoverTriggerList.map(
// bootstrap is used as script in the template, disable warning about undef
// eslint-disable-next-line no-undef
(popoverTriggerEl) =>
new bootstrap.Popover(popoverTriggerEl, {
trigger: "hover",
placement: "left",
content: document.getElementById(
popoverTriggerEl.getAttribute("data-bs-content"),
).innerHTML,
html: true,
}),
);
</script>
</body>
</html>
{
"name": "cubic_planar",
"dependencies": {
"@giro3d/giro3d": "0.39.0"
},
"devDependencies": {
"vite": "^3.2.3"
},
"scripts": {
"start": "vite",
"build": "vite build"
}
}