Channel mapping
Select which bands to view on GeoTIFFs with multiple bands.
GeoTIFFs may have multiple bands. You can select which bands to view, and specify how to map them to regular R, G, B colors.
import { MapControls } from "three/examples/jsm/controls/MapControls.js";
import Extent from "@giro3d/giro3d/core/geographic/Extent.js";
import GeoTIFFSource from "@giro3d/giro3d/sources/GeoTIFFSource.js";
import Instance from "@giro3d/giro3d/core/Instance.js";
import ColorLayer from "@giro3d/giro3d/core/layer/ColorLayer.js";
import Interpretation from "@giro3d/giro3d/core/layer/Interpretation.js";
import Map from "@giro3d/giro3d/entities/Map.js";
import Inspector from "@giro3d/giro3d/gui/Inspector.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];
}
Instance.registerCRS(
"EPSG:32611",
"+proj=utm +zone=11 +datum=WGS84 +units=m +no_defs +type=crs",
);
const extent = new Extent("EPSG:32611", 666285, 668533.5, 3997174, 3998444);
const center = extent.centerAsVector3();
const instance = new Instance({
target: "view",
crs: extent.crs,
backgroundColor: 0x0a3b59,
});
instance.view.camera.position.set(center.x, center.y, 2500);
const controls = new MapControls(instance.view.camera, instance.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.2;
controls.target.set(center.x, center.y + 1, center.z);
instance.view.setControls(controls);
const map = new Map({ extent });
instance.add(map);
// Data coming from the same source as
// https://openlayers.org/en/latest/examples/cog-math-multisource.html
const source = new GeoTIFFSource({
url: "https://3d.oslandia.com/cog_data/20200428_211318_ssc8d1_0017_pansharpened.cog.tif",
crs: extent.crs,
channels: [0, 1, 2],
});
const layer = new ColorLayer({
name: "color-layer",
source,
extent,
interpretation: Interpretation.CompressTo8Bit(0, 900),
});
map.addLayer(layer);
bindNumericalDropDown("r-channel", (v) => {
source.channels[0] = v;
source.update();
});
bindNumericalDropDown("g-channel", (v) => {
source.channels[1] = v;
source.update();
});
bindNumericalDropDown("b-channel", (v) => {
source.channels[2] = v;
source.update();
});
Inspector.attach("inspector", instance);
<!doctype html>
<html lang="en">
<head>
<title>GeoTIFF image manipulation</title>
<meta charset="UTF-8" />
<meta name="name" content="cog_channel_mapping" />
<meta
name="description"
content="Select which bands to view on GeoTIFFs with multiple bands."
/>
<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/next/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">
<div class="card">
<div class="card-header">Channel mapping</div>
<div class="card-body">
<div class="input-group mb-3">
<label class="input-group-text" for="r-channel">R</label>
<select class="form-select" id="r-channel" autocomplete="off">
<option value="0" selected>Band 1</option>
<option value="1">Band 2</option>
<option value="2">Band 3</option>
<option value="3">Band 4</option>
</select>
</div>
<div class="input-group mb-3">
<label class="input-group-text" for="g-channel">G</label>
<select class="form-select" id="g-channel" autocomplete="off">
<option value="0">Band 1</option>
<option value="1" selected>Band 2</option>
<option value="2">Band 3</option>
<option value="3">Band 4</option>
</select>
</div>
<div class="input-group">
<label class="input-group-text" for="b-channel" autocomplete="off"
>B</label
>
<select class="form-select" id="b-channel">
<option value="0" selected>Band 1</option>
<option value="1">Band 2</option>
<option value="2" selected>Band 3</option>
<option value="3">Band 4</option>
</select>
</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": "cog_channel_mapping",
"dependencies": {
"@giro3d/giro3d": "git+https://gitlab.com/giro3d/giro3d.git"
},
"devDependencies": {
"vite": "^3.2.3"
},
"scripts": {
"start": "vite",
"build": "vite build"
}
}