Select which bands to view on COGs with multiple bands.
COGs 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 CogSource from '@giro3d/giro3d/sources/CogSource.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';
Instance.registerCRS('EPSG:32611', '+proj=utm +zone=11 +datum=WGS84 +units=m +no_defs +type=crs');
// Define geographic extent: CRS, min/max X, min/max Y
const extent = new Extent('EPSG:32611', 666285, 668533.5, 3997174, 3998444);
const center = extent.centerAsVector3();
// `viewerDiv` will contain Giro3D' rendering area (the canvas element)
const viewerDiv = document.getElementById('viewerDiv');
// Instantiate Giro3D
const instance = new Instance(viewerDiv, {
crs: extent.crs(),
renderer: {
clearColor: 0x0a3b59,
},
});
// Instantiate the camera
instance.camera.camera3D.position.set(center.x, center.y, 2500);
// Instantiate the controls
const controls = new MapControls(instance.camera.camera3D, instance.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.2;
controls.target.set(center.x, center.y + 1, center.z);
instance.useTHREEControls(controls);
// Construct a map and add it to the instance
const map = new Map('planar', { extent });
instance.add(map);
const channels = [0, 1, 2];
function createLayer() {
// Data coming from the same source as
// https://openlayers.org/en/latest/examples/cog-math-multisource.html
const source = new CogSource({
url: 'https://3d.oslandia.com/cog_data/20200428_211318_ssc8d1_0017_pansharpened.cog.tif',
crs: extent.crs(),
channels,
});
return new ColorLayer({
name: 'color-layer',
source,
extent,
interpretation: Interpretation.CompressTo8Bit(0, 900),
});
}
let layer = createLayer();
map.addLayer(layer);
function bindDropdown(id, action) {
document.getElementById(id).addEventListener('change', e => {
const value = parseInt(e.target.value, 10);
action(value);
map.removeLayer(layer, { disposeLayer: true });
layer = createLayer();
map.addLayer(layer);
});
}
bindDropdown('r-channel', v => {
channels[0] = v;
});
bindDropdown('g-channel', v => {
channels[1] = v;
});
bindDropdown('b-channel', v => {
channels[2] = v;
});
Inspector.attach(document.getElementById('panelDiv'), instance);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Giro3D - COG image manipulation</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
<style>
body {
padding: 0;
margin: 0;
width: 100vw;
height: 100vh;
}
#viewerDiv {
width: 100%;
height: 100%;
}
#panelDiv {
position: absolute;
top: 0;
left: 0;
}
</style>
</head>
<body>
<div id="viewerDiv"></div>
<div id="panelDiv"></div>
<div class="m-2 position-absolute top-0 end-0">
<div class="card">
<div class="card-body">
<!-- Mapping -->
<form>
<fieldset>
<legend>Channel mapping</legend>
<div class="input-group mb-3">
<label class="input-group-text" for="r-channel">R</label>
<select class="form-select" id="r-channel">
<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">
<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 mb-3">
<label class="input-group-text" for="b-channel">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>
</fieldset>
</form>
</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": "0.35.0"
},
"devDependencies": {
"vite": "^3.2.3"
},
"scripts": {
"start": "vite",
"build": "vite build"
}
}