原文链接及内容

运行界面

本例中的 GeoTIFF 图层来自两个 Sentinel 2 数据:一个红色波段和一个近红外波段。图层样式包括一个颜色表达式,该表达式根据两个波段的值计算归一化植被指数 (NDVI),插值表达式用于将NDVI值映射为颜色值。颜色渐变的stop值来源于应用程序提供的样式变量。如上图,使用上面的输入,可以调整最小和最大颜色及NDVI值,调用layer.updateStyleVariables()方法来更新插值颜色表达式中使用的变量。

main.js代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import GeoTIFF from 'ol/source/GeoTIFF.js';
import Map from 'ol/Map.js';
import TileLayer from 'ol/layer/WebGLTile.js';

const segments = 10;

const defaultMinColor = '#0300AD';
const defaultMaxColor = '#00ff00';

const defaultMinValue = -0.5;
const defaultMaxValue = 0.7;

const minColorInput = document.getElementById('min-color');
minColorInput.value = defaultMinColor;

const maxColorInput = document.getElementById('max-color');
maxColorInput.value = defaultMaxColor;

const minValueOutput = document.getElementById('min-value-output');
const minValueInput = document.getElementById('min-value-input');
minValueInput.value = defaultMinValue.toString();

const maxValueOutput = document.getElementById('max-value-output');
const maxValueInput = document.getElementById('max-value-input');
maxValueInput.value = defaultMaxValue.toString();

function getVariables() {
const variables = {};

const minColor = minColorInput.value;
const maxColor = maxColorInput.value;
const scale = chroma.scale([minColor, maxColor]).mode('lab');

const minValue = parseFloat(minValueInput.value);
const maxValue = parseFloat(maxValueInput.value);
const delta = (maxValue - minValue) / segments;

for (let i = 0; i <= segments; ++i) {
const color = scale(i / segments).rgb();
const value = minValue + i * delta;
variables[`value${i}`] = value;
variables[`red${i}`] = color[0];
variables[`green${i}`] = color[1];
variables[`blue${i}`] = color[2];
}
return variables;
}

function colors() {
const stops = [];
for (let i = 0; i <= segments; ++i) {
stops[i * 2] = ['var', `value${i}`];
const red = ['var', `red${i}`];
const green = ['var', `green${i}`];
const blue = ['var', `blue${i}`];
stops[i * 2 + 1] = ['color', red, green, blue];
}
return stops;
}

const ndvi = [
'/',
['-', ['band', 2], ['band', 1]],
['+', ['band', 2], ['band', 1]],
];

const source = new GeoTIFF({
sources: [
{
// visible red, band 1 in the style expression above
url: 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/36/Q/WD/2020/7/S2A_36QWD_20200701_0_L2A/B04.tif',
max: 10000,
},
{
// near infrared, band 2 in the style expression above
url: 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/36/Q/WD/2020/7/S2A_36QWD_20200701_0_L2A/B08.tif',
max: 10000,
},
],
});

const layer = new TileLayer({
style: {
variables: getVariables(),
color: ['interpolate', ['linear'], ndvi, ...colors()],
},
source: source,
});

function update() {
layer.updateStyleVariables(getVariables());
minValueOutput.innerText = parseFloat(minValueInput.value).toFixed(1);
maxValueOutput.innerText = parseFloat(maxValueInput.value).toFixed(1);
}

minColorInput.addEventListener('input', update);
maxColorInput.addEventListener('input', update);
minValueInput.addEventListener('input', update);
maxValueInput.addEventListener('input', update);
update();

const map = new Map({
target: 'map',
layers: [layer],
view: source.getView(),
});

界面布局文件index.html代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>NDVI with a Dynamic Color Ramp</title>
<link rel="stylesheet" href="node_modules/ol/ol.css">
<style>
.map {
width: 100%;
height: 400px;
}
.data {
text-align: right;
font-family: monospace;
}
td {
padding-right: 10px;
}
input[type="range"] {
vertical-align: text-bottom;
}
</style>
</head>
<body>
<div id="map" class="map"></div>
<table>
<tbody>
<tr>
<td>Min NDVI</td>
<td><input type="range" id="min-value-input" min="-1.0" max="-0.1" step="0.01"></td>
<td class="data" id="min-value-output"></td>
<td><input type="color" id="min-color"></td>
</tr>
<tr>
<td>Max NDVI</td>
<td><input type="range" id="max-value-input" min="0.1" max="1.0" step="0.01"></td>
<td class="data" id="max-value-output"></td>
<td><input type="color" id="max-color"></td>
</tr>
</tbody>
</table>
<!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer -->
<script src="https://cdn.jsdelivr.net/npm/elm-pep@1.0.6/dist/elm-pep.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chroma-js/2.1.2/chroma.min.js"></script>
<script type="module" src="main.js"></script>
</body>
</html>