Raster source for composing pixels from other sources
This commit is contained in:
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
template: example.html
|
||||||
|
title: Raster Source
|
||||||
|
shortdesc: Demonstrates pixelwise operations with a raster source.
|
||||||
|
docs: >
|
||||||
|
A dynamically generated raster source.
|
||||||
|
tags: "raster, pixel"
|
||||||
|
---
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span12">
|
||||||
|
<div id="map" class="map"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
goog.require('ol.Map');
|
||||||
|
goog.require('ol.View');
|
||||||
|
goog.require('ol.layer.Image');
|
||||||
|
goog.require('ol.source.OSM');
|
||||||
|
goog.require('ol.source.Raster');
|
||||||
|
|
||||||
|
|
||||||
|
var map = new ol.Map({
|
||||||
|
layers: [
|
||||||
|
new ol.layer.Image({
|
||||||
|
source: new ol.source.Raster({
|
||||||
|
sources: [new ol.source.OSM()],
|
||||||
|
operations: [function(pixels) {
|
||||||
|
var pixel = pixels[0];
|
||||||
|
var b = pixel[2];
|
||||||
|
pixel[2] = pixel[0];
|
||||||
|
pixel[0] = b;
|
||||||
|
return pixels;
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
],
|
||||||
|
target: 'map',
|
||||||
|
view: new ol.View({
|
||||||
|
center: [0, 0],
|
||||||
|
zoom: 2
|
||||||
|
})
|
||||||
|
});
|
||||||
@@ -4498,6 +4498,31 @@ olx.source.ImageVectorOptions.prototype.source;
|
|||||||
olx.source.ImageVectorOptions.prototype.style;
|
olx.source.ImageVectorOptions.prototype.style;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{sources: Array.<ol.source.Source>,
|
||||||
|
* operations: (Array.<ol.raster.Operation>|undefined)}}
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
olx.source.RasterOptions;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Input sources.
|
||||||
|
* @type {Array.<ol.source.Source>}
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
olx.source.RasterOptions.prototype.sources;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pixel operations. Operations will be called with pixels from input sources
|
||||||
|
* and the final output will be assigned to the raster source.
|
||||||
|
* @type {Array.<ol.raster.Operation>|undefined}
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
olx.source.RasterOptions.prototype.operations;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {{attributions: (Array.<ol.Attribution>|undefined),
|
* @typedef {{attributions: (Array.<ol.Attribution>|undefined),
|
||||||
* crossOrigin: (null|string|undefined),
|
* crossOrigin: (null|string|undefined),
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
goog.provide('ol.raster.IdentityOp');
|
||||||
|
goog.provide('ol.raster.Operation');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function that takes an array of {@link ol.raster.Pixel} as inputs, performs
|
||||||
|
* some operation on them, and returns an array of {@link ol.raster.Pixel} as
|
||||||
|
* outputs.
|
||||||
|
*
|
||||||
|
* @typedef {function(Array.<ol.raster.Pixel>): Array.<ol.raster.Pixel>}
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
ol.raster.Operation;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The identity operation for pixels. Returns the supplied input pixels as
|
||||||
|
* outputs.
|
||||||
|
* @param {Array.<ol.raster.Pixel>} inputs Input pixels.
|
||||||
|
* @return {Array.<ol.raster.Pixel>} The input pixels as output.
|
||||||
|
*/
|
||||||
|
ol.raster.IdentityOp = function(inputs) {
|
||||||
|
return inputs;
|
||||||
|
};
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
goog.provide('ol.raster.Pixel');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array of numbers representing pixel values.
|
||||||
|
* @typedef {Array.<number>} ol.raster.Pixel
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
ol.raster.Pixel;
|
||||||
@@ -113,7 +113,6 @@ ol.renderer.Layer.prototype.createLoadedTileFinder = function(source, tiles) {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @protected
|
|
||||||
* @return {ol.layer.Layer} Layer.
|
* @return {ol.layer.Layer} Layer.
|
||||||
*/
|
*/
|
||||||
ol.renderer.Layer.prototype.getLayer = function() {
|
ol.renderer.Layer.prototype.getLayer = function() {
|
||||||
|
|||||||
@@ -0,0 +1,316 @@
|
|||||||
|
goog.provide('ol.source.Raster');
|
||||||
|
|
||||||
|
goog.require('goog.asserts');
|
||||||
|
goog.require('goog.functions');
|
||||||
|
goog.require('goog.vec.Mat4');
|
||||||
|
goog.require('ol.ImageCanvas');
|
||||||
|
goog.require('ol.TileQueue');
|
||||||
|
goog.require('ol.dom');
|
||||||
|
goog.require('ol.extent');
|
||||||
|
goog.require('ol.layer.Image');
|
||||||
|
goog.require('ol.layer.Tile');
|
||||||
|
goog.require('ol.raster.IdentityOp');
|
||||||
|
goog.require('ol.renderer.canvas.ImageLayer');
|
||||||
|
goog.require('ol.renderer.canvas.TileLayer');
|
||||||
|
goog.require('ol.source.Image');
|
||||||
|
goog.require('ol.source.Tile');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @classdesc
|
||||||
|
* An source that transforms data from any number of input sources source using
|
||||||
|
* an array of {@link ol.raster.Operation} functions to transform input pixel
|
||||||
|
* values into output pixel values.
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @extends {ol.source.Image}
|
||||||
|
* @param {olx.source.RasterOptions} options Options.
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
ol.source.Raster = function(options) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {Array.<ol.raster.Operation>}
|
||||||
|
*/
|
||||||
|
this.operations_ = goog.isDef(options.operations) ?
|
||||||
|
options.operations : [ol.raster.IdentityOp];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {Array.<ol.renderer.canvas.Layer>}
|
||||||
|
*/
|
||||||
|
this.renderers_ = ol.source.Raster.createRenderers_(options.sources);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {CanvasRenderingContext2D}
|
||||||
|
*/
|
||||||
|
this.canvasContext_ = ol.dom.createCanvasContext2D();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {ol.TileQueue}
|
||||||
|
*/
|
||||||
|
this.tileQueue_ = new ol.TileQueue(
|
||||||
|
goog.functions.constant(1),
|
||||||
|
goog.bind(this.changed, this));
|
||||||
|
|
||||||
|
var layerStatesArray = ol.source.Raster.getLayerStatesArray_(this.renderers_);
|
||||||
|
var layerStates = {};
|
||||||
|
for (var i = 0, ii = layerStatesArray.length; i < ii; ++i) {
|
||||||
|
layerStates[goog.getUid(layerStatesArray[i].layer)] = layerStatesArray[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {olx.FrameState}
|
||||||
|
*/
|
||||||
|
this.frameState_ = {
|
||||||
|
animate: false,
|
||||||
|
attributions: {},
|
||||||
|
coordinateToPixelMatrix: goog.vec.Mat4.createNumber(),
|
||||||
|
extent: null,
|
||||||
|
focus: null,
|
||||||
|
index: 0,
|
||||||
|
layerStates: layerStates,
|
||||||
|
layerStatesArray: layerStatesArray,
|
||||||
|
logos: {},
|
||||||
|
pixelRatio: 1,
|
||||||
|
pixelToCoordinateMatrix: goog.vec.Mat4.createNumber(),
|
||||||
|
postRenderFunctions: [],
|
||||||
|
size: [0, 0],
|
||||||
|
skippedFeatureUids: {},
|
||||||
|
tileQueue: this.tileQueue_,
|
||||||
|
time: Date.now(),
|
||||||
|
usedTiles: {},
|
||||||
|
viewState: /** @type {olx.ViewState} */ ({
|
||||||
|
rotation: 0
|
||||||
|
}),
|
||||||
|
viewHints: [],
|
||||||
|
wantedTiles: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
goog.base(this, {
|
||||||
|
// TODO: pass along any relevant options
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
goog.inherits(ol.source.Raster, ol.source.Image);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the stored frame state.
|
||||||
|
* @param {ol.Extent} extent The view extent (in map units).
|
||||||
|
* @param {number} resolution The view resolution.
|
||||||
|
* @param {ol.proj.Projection} projection The view projection.
|
||||||
|
* @return {olx.FrameState} The updated frame state.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.source.Raster.prototype.updateFrameState_ =
|
||||||
|
function(extent, resolution, projection) {
|
||||||
|
var frameState = this.frameState_;
|
||||||
|
|
||||||
|
var center = ol.extent.getCenter(extent);
|
||||||
|
var width = ol.extent.getWidth(extent) / resolution;
|
||||||
|
var height = ol.extent.getHeight(extent) / resolution;
|
||||||
|
|
||||||
|
frameState.extent = extent;
|
||||||
|
frameState.focus = ol.extent.getCenter(extent);
|
||||||
|
frameState.size[0] = width;
|
||||||
|
frameState.size[1] = height;
|
||||||
|
|
||||||
|
var viewState = frameState.viewState;
|
||||||
|
viewState.center = center;
|
||||||
|
viewState.projection = projection;
|
||||||
|
viewState.resolution = resolution;
|
||||||
|
return frameState;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
ol.source.Raster.prototype.getImage =
|
||||||
|
function(extent, resolution, pixelRatio, projection) {
|
||||||
|
|
||||||
|
var context = this.canvasContext_;
|
||||||
|
var canvas = context.canvas;
|
||||||
|
|
||||||
|
var width = ol.extent.getWidth(extent) / resolution;
|
||||||
|
var height = ol.extent.getHeight(extent) / resolution;
|
||||||
|
|
||||||
|
if (width !== canvas.width ||
|
||||||
|
height !== canvas.height) {
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
var frameState = this.updateFrameState_(extent, resolution, projection);
|
||||||
|
this.composeFrame_(frameState);
|
||||||
|
|
||||||
|
var imageCanvas = new ol.ImageCanvas(extent, resolution, 1,
|
||||||
|
this.getAttributions(), canvas);
|
||||||
|
|
||||||
|
return imageCanvas;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compose the frame. This renders data from all sources, runs pixel-wise
|
||||||
|
* operations, and renders the result to the stored canvas context.
|
||||||
|
* @param {olx.FrameState} frameState The frame state.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.source.Raster.prototype.composeFrame_ = function(frameState) {
|
||||||
|
var len = this.renderers_.length;
|
||||||
|
var imageDatas = new Array(len);
|
||||||
|
var pixels = new Array(len);
|
||||||
|
|
||||||
|
var context = this.canvasContext_;
|
||||||
|
var canvas = context.canvas;
|
||||||
|
|
||||||
|
for (var i = 0; i < len; ++i) {
|
||||||
|
pixels[i] = [0, 0, 0, 0];
|
||||||
|
imageDatas[i] = ol.source.Raster.getImageData_(
|
||||||
|
this.renderers_[i], canvas.width, canvas.height,
|
||||||
|
frameState, frameState.layerStatesArray[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetImageData = context.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
|
var target = targetImageData.data;
|
||||||
|
|
||||||
|
var source, pixel;
|
||||||
|
for (var j = 0, jj = target.length; j < jj; j += 4) {
|
||||||
|
for (var k = 0; k < len; ++k) {
|
||||||
|
source = imageDatas[k].data;
|
||||||
|
pixel = pixels[k];
|
||||||
|
pixel[0] = source[j];
|
||||||
|
pixel[1] = source[j + 1];
|
||||||
|
pixel[2] = source[j + 2];
|
||||||
|
pixel[3] = source[j + 3];
|
||||||
|
}
|
||||||
|
this.transformPixels_(pixels);
|
||||||
|
pixel = pixels[0];
|
||||||
|
target[j] = pixel[0];
|
||||||
|
target[j + 1] = pixel[1];
|
||||||
|
target[j + 2] = pixel[2];
|
||||||
|
target[j + 3] = pixel[3];
|
||||||
|
}
|
||||||
|
context.putImageData(targetImageData, 0, 0);
|
||||||
|
|
||||||
|
frameState.tileQueue.loadMoreTiles(16, 16);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run pixel-wise operations to transform pixels.
|
||||||
|
* @param {Array.<ol.raster.Pixel>} pixels The input pixels.
|
||||||
|
* @return {Array.<ol.raster.Pixel>} The modified pixels.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.source.Raster.prototype.transformPixels_ = function(pixels) {
|
||||||
|
for (var i = 0, ii = this.operations_.length; i < ii; ++i) {
|
||||||
|
pixels = this.operations_[i](pixels);
|
||||||
|
}
|
||||||
|
return pixels;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get image data from a renderer.
|
||||||
|
* @param {ol.renderer.canvas.Layer} renderer Layer renderer.
|
||||||
|
* @param {number} width Data width.
|
||||||
|
* @param {number} height Data height.
|
||||||
|
* @param {olx.FrameState} frameState The frame state.
|
||||||
|
* @param {ol.layer.LayerState} layerState The layer state.
|
||||||
|
* @return {ImageData} The image data.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.source.Raster.getImageData_ =
|
||||||
|
function(renderer, width, height, frameState, layerState) {
|
||||||
|
renderer.prepareFrame(frameState, layerState);
|
||||||
|
var canvas = renderer.getImage();
|
||||||
|
var imageTransform = renderer.getImageTransform();
|
||||||
|
var dx = goog.vec.Mat4.getElement(imageTransform, 0, 3);
|
||||||
|
var dy = goog.vec.Mat4.getElement(imageTransform, 1, 3);
|
||||||
|
return canvas.getContext('2d').getImageData(
|
||||||
|
Math.round(-dx), Math.round(-dy),
|
||||||
|
width, height);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of layer states from a list of renderers.
|
||||||
|
* @param {Array.<ol.renderer.canvas.Layer>} renderers Layer renderers.
|
||||||
|
* @return {Array.<ol.layer.LayerState>} The layer states.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.source.Raster.getLayerStatesArray_ = function(renderers) {
|
||||||
|
return renderers.map(function(renderer) {
|
||||||
|
return renderer.getLayer().getLayerState();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create renderers for all sources.
|
||||||
|
* @param {Array.<ol.source.Source>} sources The sources.
|
||||||
|
* @return {Array.<ol.renderer.canvas.Layer>} Array of layer renderers.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.source.Raster.createRenderers_ = function(sources) {
|
||||||
|
var len = sources.length;
|
||||||
|
var renderers = new Array(len);
|
||||||
|
for (var i = 0; i < len; ++i) {
|
||||||
|
renderers[i] = ol.source.Raster.createRenderer_(sources[i]);
|
||||||
|
}
|
||||||
|
return renderers;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a renderer for the provided source.
|
||||||
|
* @param {ol.source.Source} source The source.
|
||||||
|
* @return {ol.renderer.canvas.Layer} The renderer.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.source.Raster.createRenderer_ = function(source) {
|
||||||
|
var renderer = null;
|
||||||
|
if (source instanceof ol.source.Tile) {
|
||||||
|
renderer = ol.source.Raster.createTileRenderer_(
|
||||||
|
/** @type {ol.source.Tile} */ (source));
|
||||||
|
} else if (source instanceof ol.source.Image) {
|
||||||
|
renderer = ol.source.Raster.createImageRenderer_(
|
||||||
|
/** @type {ol.source.Image} */ (source));
|
||||||
|
} else {
|
||||||
|
goog.asserts.fail('Unsupported source type: ' + source);
|
||||||
|
}
|
||||||
|
return renderer;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an image renderer for the provided source.
|
||||||
|
* @param {ol.source.Image} source The source.
|
||||||
|
* @return {ol.renderer.canvas.Layer} The renderer.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.source.Raster.createImageRenderer_ = function(source) {
|
||||||
|
var layer = new ol.layer.Image({source: source});
|
||||||
|
return new ol.renderer.canvas.ImageLayer(layer);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a tile renderer for the provided source.
|
||||||
|
* @param {ol.source.Tile} source The source.
|
||||||
|
* @return {ol.renderer.canvas.Layer} The renderer.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.source.Raster.createTileRenderer_ = function(source) {
|
||||||
|
var layer = new ol.layer.Tile({source: source});
|
||||||
|
return new ol.renderer.canvas.TileLayer(layer);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user