Parameters
Assign various blending modes to a color layer.
Color layers can have different blending modes depending on the desired effect. The default blending mode (BlendingMode.Normal) is alpha blending, where the transparency of the pixels is used to blend the layer with the previous layer (or the background).
import { MapControls } from "three/examples/jsm/controls/MapControls.js";
import { Stroke, Style } from "ol/style.js";
import XYZ from "ol/source/XYZ.js";
import GeoJSON from "ol/format/GeoJSON.js";
import Extent from "@giro3d/giro3d/core/geographic/Extent.js";
import Instance from "@giro3d/giro3d/core/Instance.js";
import Map from "@giro3d/giro3d/entities/Map.js";
import ColorLayer from "@giro3d/giro3d/core/layer/ColorLayer.js";
import BlendingMode from "@giro3d/giro3d/core/layer/BlendingMode.js";
import TiledImageSource from "@giro3d/giro3d/sources/TiledImageSource.js";
import Inspector from "@giro3d/giro3d/gui/Inspector.js";
import StaticImageSource from "@giro3d/giro3d/sources/StaticImageSource.js";
import VectorSource from "@giro3d/giro3d/sources/VectorSource.js";
function bindNumericalDropDown(id, onChange) {
const element = document.getElementById(id);
if (!(element instanceof HTMLSelectElement)) {
throw new Error(
"invalid binding element: expected HTMLSelectElement, got: " +
element.constructor.name,
);
}
element.onchange = () => {
onChange(parseInt(element.value));
};
const callback = (v) => {
element.value = v.toString();
onChange(parseInt(element.value));
};
return [callback, parseInt(element.value), element];
}
function bindButton(id, onClick) {
const element = document.getElementById(id);
if (!(element instanceof HTMLButtonElement)) {
throw new Error(
"invalid binding element: expected HTMLButtonElement, got: " +
element.constructor.name,
);
}
element.onclick = () => {
onClick(element);
};
return element;
}
function bindColorPicker(id, onChange) {
const element = document.getElementById(id);
if (!(element instanceof HTMLInputElement)) {
throw new Error(
"invalid binding element: expected HTMLInputElement, got: " +
element.constructor.name,
);
}
element.oninput = function oninput() {
// Let's change the classification color with the color picker value
const hexColor = element.value;
onChange(new Color(hexColor));
};
const externalFunction = (v) => {
element.value = `#${new Color(v).getHexString()}`;
onChange(element.value);
};
return [externalFunction, new Color(element.value), element];
}
function bindToggle(id, onChange) {
const element = document.getElementById(id);
if (!(element instanceof HTMLInputElement)) {
throw new Error(
"invalid binding element: expected HTMLButtonElement, got: " +
element.constructor.name,
);
}
element.oninput = function oninput() {
onChange(element.checked);
};
const callback = (v) => {
element.checked = v;
onChange(element.checked);
};
return [callback, element.checked, element];
}
const instance = new Instance({
target: "view",
crs: "EPSG:4326",
});
const extent = new Extent("EPSG:4326", -180, 180, -90, 90);
const map = new Map({ extent, backgroundColor: "blue" });
instance.add(map);
const key =
"pk.eyJ1IjoidG11Z3VldCIsImEiOiJjbGJ4dTNkOW0wYWx4M25ybWZ5YnpicHV6In0.KhDJ7W5N3d1z3ArrsDjX_A";
// Create a satellite layer with no blending at all (layer is completely opaque)
const satellite = new ColorLayer({
name: "satellite",
blendingMode: BlendingMode.None,
source: new TiledImageSource({
source: new XYZ({
url: `https://api.mapbox.com/v4/mapbox.satellite/{z}/{x}/{y}.webp?access_token=${key}`,
crossOrigin: "anonymous",
}),
}),
});
map.addLayer(satellite).catch((e) => console.error(e));
// Create a vector layer with normal blending mode.
const vector = new ColorLayer({
name: "boundaries",
blendingMode: BlendingMode.Normal,
source: new VectorSource({
data: {
url: "https://3d.oslandia.com/giro3d/vectors/countries.geojson",
format: new GeoJSON(),
},
style: new Style({
stroke: new Stroke({ color: "red", width: 2 }),
}),
dataProjection: "EPSG:4326",
}),
});
map.addLayer(vector).catch((e) => console.error(e));
// Create a cloud coverage layer with an additive blending mode
const cloud = new ColorLayer({
name: "clouds",
blendingMode: BlendingMode.Add,
source: new StaticImageSource({
source: "https://3d.oslandia.com/giro3d/images/cloud_cover.webp",
extent,
}),
});
map.addLayer(cloud).catch((e) => console.error(e));
instance.view.camera.position.set(0, 0, 230);
const controls = new MapControls(instance.view.camera, instance.domElement);
instance.view.setControls(controls);
// Example GUI
const [setBackground] = bindColorPicker("color", (v) => {
map.backgroundColor = v;
instance.notifyChange(map);
});
const setMode = (layer, mode) => {
layer.blendingMode = mode;
instance.notifyChange(layer);
};
const [setCloudMode] = bindNumericalDropDown("cloud", (v) => setMode(cloud, v));
const [setVectorMode] = bindNumericalDropDown("vector", (v) =>
setMode(vector, v),
);
const [setSatelliteMode] = bindNumericalDropDown("satellite", (v) =>
setMode(satellite, v),
);
const show = (layer, v) => {
layer.visible = v;
instance.notifyChange(layer);
};
const [showClouds] = bindToggle("show-cloud", (v) => show(cloud, v));
const [showSatellite] = bindToggle("show-satellite", (v) => show(satellite, v));
const [showVector] = bindToggle("show-vector", (v) => show(vector, v));
const [showBackground] = bindToggle("show-background", (v) => {
map.backgroundOpacity = v ? 1 : 0;
instance.notifyChange(map);
});
const reset = () => {
setCloudMode(BlendingMode.Add);
setVectorMode(BlendingMode.Normal);
setSatelliteMode(BlendingMode.None);
showClouds(true);
showVector(true);
showSatellite(true);
setBackground("blue");
showBackground(true);
};
bindButton("reset", reset);
reset();
Inspector.attach("inspector", instance);
<!doctype html>
<html lang="en">
<head>
<title>Blending modes</title>
<meta charset="UTF-8" />
<meta name="name" content="layer_blending_modes" />
<meta
name="description"
content="Assign various blending modes to a color layer."
/>
<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/examples/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>
<div class="side-pane-with-status-bar">
<!--Parameters -->
<div class="card">
<div class="card-header">
Parameters
<button
type="button"
id="reset"
class="btn btn-sm btn-primary rounded float-end"
>
reset
</button>
</div>
<div class="card-body">
<!-- Cloud layer -->
<div class="input-group mb-3">
<div class="input-group-text">
<input
class="form-check-input"
type="checkbox"
checked="true"
role="switch"
id="show-cloud"
autocomplete="off"
/>
</div>
<label class="input-group-text" style="width: 8rem" for="cloud"
>Cloud layer</label
>
<select class="form-select" id="cloud" autocomplete="off">
<option value="0">None</option>
<option value="1">Normal</option>
<option value="2" selected>Add</option>
<option value="3">Multiply</option>
</select>
</div>
<!-- Vector layer -->
<div class="input-group mb-3">
<div class="input-group-text">
<input
class="form-check-input"
type="checkbox"
checked="true"
role="switch"
id="show-vector"
autocomplete="off"
/>
</div>
<label class="input-group-text" style="width: 8rem" for="vector"
>Vector layer</label
>
<select class="form-select" id="vector" autocomplete="off">
<option value="0">None</option>
<option value="1" selected>Normal</option>
<option value="2">Add</option>
<option value="3">Multiply</option>
</select>
</div>
<!-- Satellite layer -->
<div class="input-group mb-3">
<div class="input-group-text">
<input
class="form-check-input"
type="checkbox"
checked="true"
role="switch"
id="show-satellite"
autocomplete="off"
/>
</div>
<label class="input-group-text" style="width: 8rem" for="satellite"
>Satellite layer</label
>
<select class="form-select" id="satellite" autocomplete="off">
<option value="0" selected>None</option>
<option value="1">Normal</option>
<option value="2">Add</option>
<option value="3">Multiply</option>
</select>
</div>
<!-- Background color -->
<div class="input-group">
<div class="input-group-text">
<input
class="form-check-input"
type="checkbox"
checked="true"
role="switch"
id="show-background"
autocomplete="off"
/>
</div>
<label class="input-group-text" style="width: 8rem" for="color"
>Background</label
>
<input
type="color"
class="form-control form-control-color"
id="color"
value="#2978b4"
title="color"
/>
</div>
</div>
</div>
</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": "layer_blending_modes",
"dependencies": {
"@giro3d/giro3d": "0.43.6"
},
"devDependencies": {
"vite": "^3.2.3"
},
"scripts": {
"start": "vite",
"build": "vite build"
}
}