Display a color COG in various color spaces.
Cloud Optimized GeoTIFFs are regular GeoTIFF files whose layout is optimized for remote access. They allow streaming the image without tiling it beforehand. The GeoTIFFSource
image source supports both elevation and color data, as well as 8-bit, 16-bit and 32-bit pixels. Optionally, you can use the convertToRGB
constructor option to decode images not in the RGB color space, such as CMYK or YCbCR images.
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 Map from "@giro3d/giro3d/entities/Map.js";
import Inspector from "@giro3d/giro3d/gui/Inspector.js";
function bindDropDown(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(element.value);
};
const callback = (v) => {
element.value = v;
onChange(element.value);
};
const setOptions = (options) => {
const items = options.map(
(opt) =>
`<option value=${opt.id} ${opt.selected ? "selected" : ""}>${opt.name}</option>`,
);
element.innerHTML = items.join("\n");
};
return [callback, element.value, element, setOptions];
}
const extent = new Extent(
"EPSG:3857",
1818329.448,
1987320.77,
6062229.082,
6231700.791,
);
const center = extent.centerAsVector3();
const instance = new Instance({
target: "view",
crs: extent.crs,
backgroundColor: 0x0a3b59,
});
instance.view.camera.position.set(center.x, center.y, 250000);
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: extent.withRelativeMargin(0.1) });
instance.add(map);
// Data coming from the same source as
// https://openlayers.org/en/latest/examples/cog-math-multisource.html
const sources = {
// LZW compression, RGB colorspace
rgb: new GeoTIFFSource({
url: "https://3d.oslandia.com/giro3d/rasters/TCI.tif",
crs: extent.crs,
channels: [0, 1, 2],
}),
// LZW compression, RGB colorspace, 8-bit alpha band
rgba: new GeoTIFFSource({
url: "https://3d.oslandia.com/giro3d/rasters/TCI-alpha.tif",
crs: extent.crs,
channels: [0, 1, 2, 3],
}),
// JPEG compression, YCbCr colorspace
ycbcr: new GeoTIFFSource({
url: "https://3d.oslandia.com/giro3d/rasters/TCI-YCbCr.tif",
crs: extent.crs,
}),
// JPEG compression, YCbCr colorspace, 1-bit mask band
"ycbcr-mask": new GeoTIFFSource({
url: "https://3d.oslandia.com/giro3d/rasters/TCI-YCbCr-mask.tif",
crs: extent.crs,
}),
};
function updateSource(name) {
map.forEachLayer((layer) => map.removeLayer(layer, { disposeLayer: true }));
const layer = new ColorLayer({
name: "color-layer",
source: sources[name],
extent,
});
map.addLayer(layer);
}
Inspector.attach("inspector", instance);
bindDropDown("source-file", updateSource);
updateSource("rgb");
<!doctype html>
<html lang="en">
<head>
<title>Cloud Optimized GeoTIFF (COG)</title>
<meta charset="UTF-8" />
<meta name="name" content="cog_color" />
<meta
name="description"
content="Display a color COG in various color spaces."
/>
<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">
<!-- Source file selector -->
<div class="card">
<div class="card-body">
<div class="input-group">
<span class="input-group-text flex-grow-1">Source file</span>
<select
class="btn btn-outline-primary btn-sm"
id="source-file"
autocomplete="off"
>
<option selected value="rgb">RGB (LZW)</option>
<option value="rgba">RGBA (LZW)</option>
<option value="ycbcr">YCbCr (JPEG)</option>
<option value="ycbcr-mask">YCbCr + mask (JPEG)</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_color",
"dependencies": {
"@giro3d/giro3d": "0.41.0"
},
"devDependencies": {
"vite": "^3.2.3"
},
"scripts": {
"start": "vite",
"build": "vite build"
}
}