Get pixel data
This commit is contained in:
@@ -5,15 +5,19 @@ import CanvasLayerRenderer from './Layer.js';
|
||||
import ViewHint from '../../ViewHint.js';
|
||||
import {ENABLE_RASTER_REPROJECTION} from '../../reproj/common.js';
|
||||
import {IMAGE_SMOOTHING_DISABLED, IMAGE_SMOOTHING_ENABLED} from './common.js';
|
||||
import {assign} from '../../obj.js';
|
||||
import {
|
||||
apply as applyTransform,
|
||||
compose as composeTransform,
|
||||
makeInverse,
|
||||
toString as toTransformString,
|
||||
} from '../../transform.js';
|
||||
import {assign} from '../../obj.js';
|
||||
import {
|
||||
containsCoordinate,
|
||||
containsExtent,
|
||||
getHeight,
|
||||
getIntersection,
|
||||
getWidth,
|
||||
intersects as intersectsExtent,
|
||||
isEmpty,
|
||||
} from '../../extent.js';
|
||||
@@ -98,6 +102,51 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
|
||||
return !!this.image_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("../../pixel.js").Pixel} pixel Pixel.
|
||||
* @return {Uint8ClampedArray} Data at the pixel location.
|
||||
*/
|
||||
getData(pixel) {
|
||||
const frameState = this.frameState;
|
||||
if (!frameState) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const layer = this.getLayer();
|
||||
const coordinate = applyTransform(
|
||||
frameState.pixelToCoordinateTransform,
|
||||
pixel.slice()
|
||||
);
|
||||
|
||||
const layerExtent = layer.getExtent();
|
||||
if (layerExtent) {
|
||||
if (!containsCoordinate(layerExtent, coordinate)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const imageExtent = this.image_.getExtent();
|
||||
const img = this.image_.getImage();
|
||||
|
||||
const imageMapWidth = getWidth(imageExtent);
|
||||
const col = Math.floor(
|
||||
img.width * ((coordinate[0] - imageExtent[0]) / imageMapWidth)
|
||||
);
|
||||
if (col < 0 || col >= img.width) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const imageMapHeight = getHeight(imageExtent);
|
||||
const row = Math.floor(
|
||||
img.height * ((imageExtent[3] - coordinate[1]) / imageMapHeight)
|
||||
);
|
||||
if (row < 0 || row >= img.height) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.getImageData(img, col, row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the layer.
|
||||
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||
|
||||
@@ -20,6 +20,18 @@ import {
|
||||
import {createCanvasContext2D} from '../../dom.js';
|
||||
import {equals} from '../../array.js';
|
||||
|
||||
/**
|
||||
* @type {CanvasRenderingContext2D}
|
||||
*/
|
||||
let pixelContext = null;
|
||||
|
||||
function createPixelContext() {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 1;
|
||||
canvas.height = 1;
|
||||
pixelContext = canvas.getContext('2d');
|
||||
}
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
* @template {import("../../layer/Layer.js").default} LayerType
|
||||
@@ -83,6 +95,34 @@ class CanvasLayerRenderer extends LayerRenderer {
|
||||
* @type {CanvasRenderingContext2D}
|
||||
*/
|
||||
this.pixelContext_ = null;
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @type {import("../../PluggableMap.js").FrameState|null}
|
||||
*/
|
||||
this.frameState = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} image Image.
|
||||
* @param {number} col The column index.
|
||||
* @param {number} row The row index.
|
||||
* @return {Uint8ClampedArray|null} The image data.
|
||||
*/
|
||||
getImageData(image, col, row) {
|
||||
if (!pixelContext) {
|
||||
createPixelContext();
|
||||
}
|
||||
pixelContext.clearRect(0, 0, 1, 1);
|
||||
|
||||
let data;
|
||||
try {
|
||||
pixelContext.drawImage(image, col, row, 1, 1, 0, 0, 1, 1);
|
||||
data = pixelContext.getImageData(0, 0, 1, 1).data;
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,6 +255,7 @@ class CanvasLayerRenderer extends LayerRenderer {
|
||||
* @protected
|
||||
*/
|
||||
preRender(context, frameState) {
|
||||
this.frameState = frameState;
|
||||
this.dispatchRenderEvent_(RenderEventType.PRERENDER, context, frameState);
|
||||
}
|
||||
|
||||
@@ -324,6 +365,14 @@ class CanvasLayerRenderer extends LayerRenderer {
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up.
|
||||
*/
|
||||
disposeInternal() {
|
||||
delete this.frameState;
|
||||
super.disposeInternal();
|
||||
}
|
||||
}
|
||||
|
||||
export default CanvasLayerRenderer;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* @module ol/renderer/canvas/TileLayer
|
||||
*/
|
||||
import CanvasLayerRenderer from './Layer.js';
|
||||
import ImageTile from '../../ImageTile.js';
|
||||
import ReprojTile from '../../reproj/Tile.js';
|
||||
import TileRange from '../../TileRange.js';
|
||||
import TileState from '../../TileState.js';
|
||||
import {IMAGE_SMOOTHING_DISABLED, IMAGE_SMOOTHING_ENABLED} from './common.js';
|
||||
@@ -13,6 +15,7 @@ import {
|
||||
} from '../../transform.js';
|
||||
import {assign} from '../../obj.js';
|
||||
import {
|
||||
containsCoordinate,
|
||||
createEmpty,
|
||||
equals,
|
||||
getIntersection,
|
||||
@@ -22,6 +25,7 @@ import {cssOpacity} from '../../css.js';
|
||||
import {fromUserExtent} from '../../proj.js';
|
||||
import {getUid} from '../../util.js';
|
||||
import {numberSafeCompareFunction} from '../../array.js';
|
||||
import {toSize} from '../../size.js';
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
@@ -136,6 +140,79 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
||||
return tile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("../../pixel.js").Pixel} pixel Pixel.
|
||||
* @return {Uint8ClampedArray} Data at the pixel location.
|
||||
*/
|
||||
getData(pixel) {
|
||||
const frameState = this.frameState;
|
||||
if (!frameState) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const layer = this.getLayer();
|
||||
const coordinate = applyTransform(
|
||||
frameState.pixelToCoordinateTransform,
|
||||
pixel.slice()
|
||||
);
|
||||
|
||||
const layerExtent = layer.getExtent();
|
||||
if (layerExtent) {
|
||||
if (!containsCoordinate(layerExtent, coordinate)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const pixelRatio = frameState.pixelRatio;
|
||||
const projection = frameState.viewState.projection;
|
||||
const viewState = frameState.viewState;
|
||||
const source = layer.getRenderSource();
|
||||
const tileGrid = source.getTileGridForProjection(viewState.projection);
|
||||
const tilePixelRatio = source.getTilePixelRatio(frameState.pixelRatio);
|
||||
|
||||
for (
|
||||
let z = tileGrid.getZForResolution(viewState.resolution);
|
||||
z >= tileGrid.getMinZoom();
|
||||
--z
|
||||
) {
|
||||
const tileCoord = tileGrid.getTileCoordForCoordAndZ(coordinate, z);
|
||||
const tile = source.getTile(
|
||||
z,
|
||||
tileCoord[1],
|
||||
tileCoord[2],
|
||||
pixelRatio,
|
||||
projection
|
||||
);
|
||||
if (!(tile instanceof ImageTile || tile instanceof ReprojTile)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (tile.getState() !== TileState.LOADED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const tileOrigin = tileGrid.getOrigin(z);
|
||||
const tileSize = toSize(tileGrid.getTileSize(z));
|
||||
const tileResolution = tileGrid.getResolution(z);
|
||||
|
||||
const col = Math.floor(
|
||||
tilePixelRatio *
|
||||
((coordinate[0] - tileOrigin[0]) / tileResolution -
|
||||
tileCoord[1] * tileSize[0])
|
||||
);
|
||||
|
||||
const row = Math.floor(
|
||||
tilePixelRatio *
|
||||
((tileOrigin[1] - coordinate[1]) / tileResolution -
|
||||
tileCoord[2] * tileSize[1])
|
||||
);
|
||||
|
||||
return this.getImageData(tile.getImage(), col, row);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object<number, Object<string, import("../../Tile.js").default>>} tiles Lookup of loaded tiles by zoom level.
|
||||
* @param {number} zoom Zoom level.
|
||||
|
||||
Reference in New Issue
Block a user