原文链接及内容

运行界面

单击地图上的一个区域。计算区域将显示为红色。
本例使用ol/source/Raster类基于另一个数据源对象来生成数据。栅格数据源接受任意数量的输入数据源(基于切片或图像),并在输入像素上运行一个操作管道,最终操作的返回值用作输出数据源的数据。
在这种情况下,使用单个切片图像数据源作为输入,该区域是使用用户点击地图时提供的“种子”像素在单幅“图像”操作中计算出来的。使用“阈值”决定给定的连续像素是否属于同一个“区域”-候选像素的RGB值与种子像素值之间的差异必须低于阈值。
这个示例还展示了如何为上述操作提供一个附加函数。

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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import Map from 'ol/Map.js';
import RasterSource from 'ol/source/Raster.js';
import View from 'ol/View.js';
import XYZ from 'ol/source/XYZ.js';
import {Image as ImageLayer, Tile as TileLayer} from 'ol/layer.js';
import {fromLonLat} from 'ol/proj.js';

function growRegion(inputs, data) {
const image = inputs[0];
let seed = data.pixel;
const delta = parseInt(data.delta);
if (!seed) {
return image;
}

seed = seed.map(Math.round);
const width = image.width;
const height = image.height;
const inputData = image.data;
const outputData = new Uint8ClampedArray(inputData);
const seedIdx = (seed[1] * width + seed[0]) * 4;
const seedR = inputData[seedIdx];
const seedG = inputData[seedIdx + 1];
const seedB = inputData[seedIdx + 2];
let edge = [seed];
while (edge.length) {
const newedge = [];
for (let i = 0, ii = edge.length; i < ii; i++) {
// As noted in the Raster source constructor, this function is provided
// using the `lib` option. Other functions will NOT be visible unless
// provided using the `lib` option.
const next = next4Edges(edge[i]);
for (let j = 0, jj = next.length; j < jj; j++) {
const s = next[j][0];
const t = next[j][1];
if (s >= 0 && s < width && t >= 0 && t < height) {
const ci = (t * width + s) * 4;
const cr = inputData[ci];
const cg = inputData[ci + 1];
const cb = inputData[ci + 2];
const ca = inputData[ci + 3];
// if alpha is zero, carry on
if (ca === 0) {
continue;
}
if (
Math.abs(seedR - cr) < delta &&
Math.abs(seedG - cg) < delta &&
Math.abs(seedB - cb) < delta
) {
outputData[ci] = 255;
outputData[ci + 1] = 0;
outputData[ci + 2] = 0;
outputData[ci + 3] = 255;
newedge.push([s, t]);
}
// mark as visited
inputData[ci + 3] = 0;
}
}
}
edge = newedge;
}
return {data: outputData, width: width, height: height};
}

function next4Edges(edge) {
const x = edge[0];
const y = edge[1];
return [
[x + 1, y],
[x - 1, y],
[x, y + 1],
[x, y - 1],
];
}

const key = 'Get your own API key at https://www.maptiler.com/cloud/';
const attributions =
'<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> ' +
'<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>';

const imagery = new TileLayer({
source: new XYZ({
attributions: attributions,
url: 'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
maxZoom: 20,
crossOrigin: '',
}),
});

const raster = new RasterSource({
sources: [imagery.getSource()],
operationType: 'image',
operation: growRegion,
// Functions in the `lib` object will be available to the operation run in
// the web worker.
lib: {
next4Edges: next4Edges,
},
});

const rasterImage = new ImageLayer({
opacity: 0.7,
source: raster,
});

const map = new Map({
layers: [imagery, rasterImage],
target: 'map',
view: new View({
center: fromLonLat([-119.07, 47.65]),
zoom: 11,
}),
});

let coordinate;

map.on('click', function (event) {
coordinate = event.coordinate;
raster.changed();
});

const thresholdControl = document.getElementById('threshold');

raster.on('beforeoperations', function (event) {
// the event.data object will be passed to operations
const data = event.data;
data.delta = thresholdControl.value;
if (coordinate) {
data.pixel = map.getPixelFromCoordinate(coordinate);
}
});

function updateControlValue() {
document.getElementById('threshold-value').innerText = thresholdControl.value;
}
updateControlValue();

thresholdControl.addEventListener('input', function () {
updateControlValue();
raster.changed();
});

界面布局文件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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Region Growing</title>
<link rel="stylesheet" href="node_modules/ol/ol.css">
<style>
.map {
width: 100%;
height: 400px;
}
.map {
cursor: pointer;
}
#threshold {
margin: 0 0.6em;
}
</style>
</head>
<body>
<div id="map" class="map"></div>
<div>
<label class="input-group">
Threshold:
<input id="threshold" type="range" min="1" max="50" value="20">
<span id="threshold-value"></span>
</label>
</div>
<!-- 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 type="module" src="main.js"></script>
</body>
</html>