Click a region on the map. The computed region will be red.
+
+ This example uses a ol.source.Raster to generate data
+ based on another source. The raster source accepts any number of
+ input sources (tile or image based) and runs a pipeline of
+ operations on the input data. The return from the final
+ operation is used as the data for the output source.
+
+
+ In this case, a single tiled source of imagery data is used as input.
+ The region is calculated in a single "image" operation using the "seed"
+ pixel provided by the user clicking on the map. The "threshold" value
+ determines whether a given contiguous pixel belongs to the "region" - the
+ difference between a candidate pixel's RGB values and the seed values must
+ be below the threshold.
+
+
+ This example also shows how an additional function can be made available
+ to the operation.
+
+tags: "raster, region growing"
+---
+
+
+
+
+
+
+
+ Threshold:
+
+
+
+
+
+
+
+
diff --git a/examples/region-growing.js b/examples/region-growing.js
new file mode 100644
index 0000000000..266c019751
--- /dev/null
+++ b/examples/region-growing.js
@@ -0,0 +1,134 @@
+// NOCOMPILE
+goog.require('ol.Map');
+goog.require('ol.View');
+goog.require('ol.layer.Image');
+goog.require('ol.layer.Tile');
+goog.require('ol.proj');
+goog.require('ol.source.BingMaps');
+goog.require('ol.source.Raster');
+
+function growRegion(inputs, data) {
+ var image = inputs[0];
+ var seed = data.pixel;
+ var delta = parseInt(data.delta);
+ if (!seed) {
+ return [image];
+ }
+
+ seed = seed.map(Math.round);
+ var width = image.width;
+ var height = image.height;
+ var inputData = image.data;
+ var outputData = new Uint8ClampedArray(inputData);
+ var seedIdx = (seed[1] * width + seed[0]) * 4;
+ var seedR = inputData[seedIdx];
+ var seedG = inputData[seedIdx + 1];
+ var seedB = inputData[seedIdx + 2];
+ var edge = [seed];
+ while (edge.length) {
+ var newedge = [];
+ for (var 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.
+ var next = nextEdges(edge[i]);
+ for (var j = 0, jj = next.length; j < jj; j++) {
+ var s = next[j][0], t = next[j][1];
+ if (s >= 0 && s < width && t >= 0 && t < height) {
+ var ci = (t * width + s) * 4;
+ var cr = inputData[ci];
+ var cg = inputData[ci + 1];
+ var cb = inputData[ci + 2];
+ var 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 [new ImageData(outputData, width, height)];
+}
+
+function next4Edges(edge) {
+ var x = edge[0], y = edge[1];
+ return [
+ [x + 1, y],
+ [x - 1, y],
+ [x, y + 1],
+ [x, y - 1]
+ ];
+}
+
+var key = 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3';
+
+var imagery = new ol.layer.Tile({
+ source: new ol.source.BingMaps({key: key, imagerySet: 'Aerial'})
+});
+
+var raster = new ol.source.Raster({
+ sources: [imagery.getSource()],
+ operationType: 'image',
+ operations: [growRegion],
+ // the contents of `lib` option will be available to the operation(s) run in
+ // the web worker.
+ lib: {
+ nextEdges: next4Edges
+ }
+});
+
+var rasterImage = new ol.layer.Image({
+ opacity: 0.8,
+ source: raster
+});
+
+var map = new ol.Map({
+ layers: [imagery, rasterImage],
+ target: 'map',
+ view: new ol.View({
+ center: ol.proj.fromLonLat([-120, 50]),
+ zoom: 6
+ })
+});
+
+var pixel;
+
+map.getView().on('change', function() {
+ pixel = null;
+});
+
+map.on('click', function(ev) {
+ pixel = map.getPixelFromCoordinate(ev.coordinate);
+ raster.changed();
+});
+
+raster.on('beforeoperations', function(event) {
+ // the event.data object will be passed to operations
+ var data = event.data;
+ data.delta = thresholdControl.value;
+ data.pixel = pixel;
+});
+
+var thresholdControl = document.getElementById('threshold');
+
+function updateControlValue() {
+ document.getElementById('threshold-value').innerText = thresholdControl.value;
+}
+updateControlValue();
+
+thresholdControl.addEventListener('input', function() {
+ updateControlValue();
+ raster.changed();
+});