@@ -685,6 +685,9 @@ class PluggableMap extends BaseObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Please the `layer.getData()` method for {@link module:ol/layer/Tile~TileLayer#getData tile layers} or
|
||||
* {@link module:ol/layer/Image~ImageLayer#getData image layers} instead of using this method.
|
||||
*
|
||||
* Detect layers that have a color value at a pixel on the viewport, and
|
||||
* execute a callback with each matching layer. Layers included in the
|
||||
* detection can be configured through `opt_layerFilter`.
|
||||
@@ -707,6 +710,7 @@ class PluggableMap extends BaseObject {
|
||||
* callback execution, or the first truthy callback return value.
|
||||
* @template S,T
|
||||
* @api
|
||||
* @deprecated
|
||||
*/
|
||||
forEachLayerAtPixel(pixel, callback, opt_options) {
|
||||
if (!this.frameState_) {
|
||||
|
||||
@@ -136,6 +136,26 @@ class BaseTileLayer extends Layer {
|
||||
setUseInterimTilesOnError(useInterimTilesOnError) {
|
||||
this.set(TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data for a pixel location. The return type depends on the source data. For image tiles,
|
||||
* a four element RGBA array will be returned. For data tiles, the array length will match the
|
||||
* number of bands in the dataset. For requests outside the layer extent, `null` will be returned.
|
||||
* Data for a image tiles can only be retrieved if the source's `crossOrigin` property is set.
|
||||
*
|
||||
* ```js
|
||||
* // display layer data on every pointer move
|
||||
* map.on('pointermove', (event) => {
|
||||
* console.log(layer.getData(event.pixel));
|
||||
* });
|
||||
* ```
|
||||
* @param {import("../pixel").Pixel} pixel Pixel.
|
||||
* @return {Uint8ClampedArray|Uint8Array|Float32Array|DataView|null} Pixel data.
|
||||
* @api
|
||||
*/
|
||||
getData(pixel) {
|
||||
return super.getData(pixel);
|
||||
}
|
||||
}
|
||||
|
||||
export default BaseTileLayer;
|
||||
|
||||
@@ -27,6 +27,25 @@ class ImageLayer extends BaseImageLayer {
|
||||
createRenderer() {
|
||||
return new CanvasImageLayerRenderer(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data for a pixel location. A four element RGBA array will be returned. For requests outside the
|
||||
* layer extent, `null` will be returned. Data for an image can only be retrieved if the
|
||||
* source's `crossOrigin` property is set.
|
||||
*
|
||||
* ```js
|
||||
* // display layer data on every pointer move
|
||||
* map.on('pointermove', (event) => {
|
||||
* console.log(layer.getData(event.pixel));
|
||||
* });
|
||||
* ```
|
||||
* @param {import("../pixel").Pixel} pixel Pixel.
|
||||
* @return {Uint8ClampedArray|Uint8Array|Float32Array|DataView|null} Pixel data.
|
||||
* @api
|
||||
*/
|
||||
getData(pixel) {
|
||||
return super.getData(pixel);
|
||||
}
|
||||
}
|
||||
|
||||
export default ImageLayer;
|
||||
|
||||
@@ -146,6 +146,12 @@ class Layer extends BaseLayer {
|
||||
*/
|
||||
this.renderer_ = null;
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.rendered = false;
|
||||
|
||||
// Overwrite default render method with a custom one
|
||||
if (options.render) {
|
||||
this.render = options.render;
|
||||
@@ -250,6 +256,17 @@ class Layer extends BaseLayer {
|
||||
return this.renderer_.getFeatures(pixel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("../pixel").Pixel} pixel Pixel.
|
||||
* @return {Uint8ClampedArray|Uint8Array|Float32Array|DataView|null} Pixel data.
|
||||
*/
|
||||
getData(pixel) {
|
||||
if (!this.renderer_ || !this.rendered) {
|
||||
return null;
|
||||
}
|
||||
return this.renderer_.getData(pixel);
|
||||
}
|
||||
|
||||
/**
|
||||
* In charge to manage the rendering of the layer. One layer type is
|
||||
* bounded with one layer renderer.
|
||||
@@ -262,15 +279,26 @@ class Layer extends BaseLayer {
|
||||
const layerRenderer = this.getRenderer();
|
||||
|
||||
if (layerRenderer.prepareFrame(frameState)) {
|
||||
this.rendered = true;
|
||||
return layerRenderer.renderFrame(frameState, target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a layer is not visible during a map render.
|
||||
*/
|
||||
unrender() {
|
||||
this.rendered = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* For use inside the library only.
|
||||
* @param {import("../PluggableMap.js").default|null} map Map.
|
||||
*/
|
||||
setMapInternal(map) {
|
||||
if (!map) {
|
||||
this.unrender();
|
||||
}
|
||||
this.set(LayerProperty.MAP, map);
|
||||
}
|
||||
|
||||
|
||||
@@ -440,6 +440,7 @@ class WebGLTileLayer extends BaseTileLayer {
|
||||
* @return {HTMLElement} The rendered element.
|
||||
*/
|
||||
render(frameState, target) {
|
||||
this.rendered = true;
|
||||
const viewState = frameState.viewState;
|
||||
const sources = this.getSources(frameState.extent, viewState.resolution);
|
||||
let ready = true;
|
||||
|
||||
@@ -118,6 +118,7 @@ class CompositeMapRenderer extends MapRenderer {
|
||||
(sourceState != SourceState.READY &&
|
||||
sourceState != SourceState.UNDEFINED)
|
||||
) {
|
||||
layer.unrender();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,14 @@ class LayerRenderer extends Observable {
|
||||
return abstract();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("../pixel.js").Pixel} pixel Pixel.
|
||||
* @return {Uint8ClampedArray|Uint8Array|Float32Array|DataView|null} Pixel data.
|
||||
*/
|
||||
getData(pixel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether render should be called.
|
||||
* @abstract
|
||||
@@ -192,6 +200,14 @@ class LayerRenderer extends Observable {
|
||||
layer.changed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up.
|
||||
*/
|
||||
disposeInternal() {
|
||||
delete this.layer_;
|
||||
super.disposeInternal();
|
||||
}
|
||||
}
|
||||
|
||||
export default LayerRenderer;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -11,9 +11,11 @@ import WebGLLayerRenderer from './Layer.js';
|
||||
import {AttributeType} from '../../webgl/Helper.js';
|
||||
import {ELEMENT_ARRAY_BUFFER, STATIC_DRAW} from '../../webgl.js';
|
||||
import {
|
||||
apply as applyTransform,
|
||||
compose as composeTransform,
|
||||
create as createTransform,
|
||||
} from '../../transform.js';
|
||||
import {containsCoordinate, getIntersection, isEmpty} from '../../extent.js';
|
||||
import {
|
||||
create as createMat4,
|
||||
fromTransform as mat4FromTransform,
|
||||
@@ -23,7 +25,6 @@ import {
|
||||
getKey as getTileCoordKey,
|
||||
} from '../../tilecoord.js';
|
||||
import {fromUserExtent} from '../../proj.js';
|
||||
import {getIntersection, isEmpty} from '../../extent.js';
|
||||
import {getUid} from '../../util.js';
|
||||
import {numberSafeCompareFunction} from '../../array.js';
|
||||
import {toSize} from '../../size.js';
|
||||
@@ -233,6 +234,12 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer {
|
||||
* @private
|
||||
*/
|
||||
this.paletteTextures_ = options.paletteTextures || [];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {import("../../PluggableMap.js").FrameState|null}
|
||||
*/
|
||||
this.frameState_ = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -355,13 +362,13 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer {
|
||||
viewState.projection
|
||||
);
|
||||
if (!tileTexture) {
|
||||
tileTexture = new TileTexture(
|
||||
tile,
|
||||
tileGrid,
|
||||
this.helper,
|
||||
tilePixelRatio,
|
||||
gutter
|
||||
);
|
||||
tileTexture = new TileTexture({
|
||||
tile: tile,
|
||||
grid: tileGrid,
|
||||
helper: this.helper,
|
||||
tilePixelRatio: tilePixelRatio,
|
||||
gutter: gutter,
|
||||
});
|
||||
tileTextureCache.set(cacheKey, tileTexture);
|
||||
} else {
|
||||
if (this.isDrawableTile_(tile)) {
|
||||
@@ -401,6 +408,7 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer {
|
||||
* @return {HTMLElement} The rendered element.
|
||||
*/
|
||||
renderFrame(frameState) {
|
||||
this.frameState_ = frameState;
|
||||
this.renderComplete = true;
|
||||
const gl = this.helper.getGL();
|
||||
this.preRender(gl, frameState);
|
||||
@@ -648,6 +656,83 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer {
|
||||
return canvas;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("../../pixel.js").Pixel} pixel Pixel.
|
||||
* @return {Uint8ClampedArray|Uint8Array|Float32Array|DataView} Data at the pixel location.
|
||||
*/
|
||||
getData(pixel) {
|
||||
const gl = this.helper.getGL();
|
||||
if (!gl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const frameState = this.frameState_;
|
||||
if (!frameState) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const layer = this.getLayer();
|
||||
const coordinate = applyTransform(
|
||||
frameState.pixelToCoordinateTransform,
|
||||
pixel.slice()
|
||||
);
|
||||
|
||||
const viewState = frameState.viewState;
|
||||
const layerExtent = layer.getExtent();
|
||||
if (layerExtent) {
|
||||
if (
|
||||
!containsCoordinate(
|
||||
fromUserExtent(layerExtent, viewState.projection),
|
||||
coordinate
|
||||
)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const source = layer.getRenderSource();
|
||||
const tileGrid = source.getTileGridForProjection(viewState.projection);
|
||||
if (!source.getWrapX()) {
|
||||
const gridExtent = tileGrid.getExtent();
|
||||
if (gridExtent) {
|
||||
if (!containsCoordinate(gridExtent, coordinate)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const tileTextureCache = this.tileTextureCache_;
|
||||
for (
|
||||
let z = tileGrid.getZForResolution(viewState.resolution);
|
||||
z >= tileGrid.getMinZoom();
|
||||
--z
|
||||
) {
|
||||
const tileCoord = tileGrid.getTileCoordForCoordAndZ(coordinate, z);
|
||||
const cacheKey = getCacheKey(source, tileCoord);
|
||||
if (!tileTextureCache.containsKey(cacheKey)) {
|
||||
continue;
|
||||
}
|
||||
const tileTexture = tileTextureCache.get(cacheKey);
|
||||
if (!tileTexture.loaded) {
|
||||
continue;
|
||||
}
|
||||
const tileOrigin = tileGrid.getOrigin(z);
|
||||
const tileSize = toSize(tileGrid.getTileSize(z));
|
||||
const tileResolution = tileGrid.getResolution(z);
|
||||
|
||||
const col =
|
||||
(coordinate[0] - tileOrigin[0]) / tileResolution -
|
||||
tileCoord[1] * tileSize[0];
|
||||
|
||||
const row =
|
||||
(tileOrigin[1] - coordinate[1]) / tileResolution -
|
||||
tileCoord[2] * tileSize[1];
|
||||
|
||||
return tileTexture.getPixelData(col, row);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for tiles covering the provided tile coordinate at an alternate
|
||||
* zoom level. Loaded tiles will be added to the provided tile texture lookup.
|
||||
@@ -719,6 +804,7 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer {
|
||||
|
||||
delete this.indices_;
|
||||
delete this.tileTextureCache_;
|
||||
delete this.frameState_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,6 @@ class DataTileSource extends TileSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
* @param {number} z Tile coordinate z.
|
||||
* @param {number} x Tile coordinate x.
|
||||
* @param {number} y Tile coordinate y.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* @module ol/webgl/TileTexture
|
||||
*/
|
||||
|
||||
import DataTile from '../DataTile.js';
|
||||
import EventTarget from '../events/Target.js';
|
||||
import EventType from '../events/EventType.js';
|
||||
import ImageTile from '../ImageTile.js';
|
||||
@@ -117,19 +118,36 @@ function uploadDataTexture(
|
||||
gl.pixelStorei(gl.UNPACK_ALIGNMENT, oldUnpackAlignment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {CanvasRenderingContext2D}
|
||||
*/
|
||||
let pixelContext = null;
|
||||
|
||||
function createPixelContext() {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 1;
|
||||
canvas.height = 1;
|
||||
pixelContext = canvas.getContext('2d');
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import("../DataTile.js").default|ImageTile|ReprojTile} TileType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {TileType} tile The tile.
|
||||
* @property {import("../tilegrid/TileGrid.js").default} grid Tile grid.
|
||||
* @property {import("../webgl/Helper.js").default} helper WebGL helper.
|
||||
* @property {number} [tilePixelRatio=1] Tile pixel ratio.
|
||||
* @property {number} [gutter=0] The size in pixels of the gutter around image tiles to ignore.
|
||||
*/
|
||||
|
||||
class TileTexture extends EventTarget {
|
||||
/**
|
||||
* @param {TileType} tile The tile.
|
||||
* @param {import("../tilegrid/TileGrid.js").default} grid Tile grid.
|
||||
* @param {import("../webgl/Helper.js").default} helper WebGL helper.
|
||||
* @param {number} [opt_tilePixelRatio=1] Tile pixel ratio.
|
||||
* @param {number} [opt_gutter=0] The size in pixels of the gutter around image tiles to ignore.
|
||||
* @param {Options} options The tile texture options.
|
||||
*/
|
||||
constructor(tile, grid, helper, opt_tilePixelRatio, opt_gutter) {
|
||||
constructor(options) {
|
||||
super();
|
||||
|
||||
/**
|
||||
@@ -143,16 +161,33 @@ class TileTexture extends EventTarget {
|
||||
this.textures = [];
|
||||
this.handleTileChange_ = this.handleTileChange_.bind(this);
|
||||
|
||||
this.size = toSize(grid.getTileSize(tile.tileCoord[0]));
|
||||
/**
|
||||
* @type {import("../size.js").Size}
|
||||
*/
|
||||
this.size = toSize(options.grid.getTileSize(options.tile.tileCoord[0]));
|
||||
|
||||
this.tilePixelRatio_ =
|
||||
opt_tilePixelRatio !== undefined ? opt_tilePixelRatio : 1;
|
||||
/**
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.tilePixelRatio_ = options.tilePixelRatio || 1;
|
||||
|
||||
this.gutter_ = opt_gutter !== undefined ? opt_gutter : 0;
|
||||
/**
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.gutter_ = options.gutter || 0;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.bandCount = NaN;
|
||||
|
||||
this.helper_ = helper;
|
||||
/**
|
||||
* @type {import("../webgl/Helper.js").default}
|
||||
* @private
|
||||
*/
|
||||
this.helper_ = options.helper;
|
||||
|
||||
const coords = new WebGLArrayBuffer(ARRAY_BUFFER, STATIC_DRAW);
|
||||
coords.fromArray([
|
||||
@@ -165,10 +200,14 @@ class TileTexture extends EventTarget {
|
||||
0, // P3
|
||||
0,
|
||||
]);
|
||||
helper.flushBufferData(coords);
|
||||
this.helper_.flushBufferData(coords);
|
||||
|
||||
/**
|
||||
* @type {WebGLArrayBuffer}
|
||||
*/
|
||||
this.coords = coords;
|
||||
this.setTile(tile);
|
||||
|
||||
this.setTile(options.tile);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -320,6 +359,50 @@ class TileTexture extends EventTarget {
|
||||
}
|
||||
this.tile.removeEventListener(EventType.CHANGE, this.handleTileChange_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data for a pixel. If the tile is not loaded, null is returned.
|
||||
* @param {number} col The column index.
|
||||
* @param {number} row The row index.
|
||||
* @return {import("../DataTile.js").Data|null} The data.
|
||||
*/
|
||||
getPixelData(col, row) {
|
||||
if (!this.loaded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
col = Math.floor(this.tilePixelRatio_ * col);
|
||||
row = Math.floor(this.tilePixelRatio_ * row);
|
||||
|
||||
if (this.tile instanceof DataTile) {
|
||||
const data = this.tile.getData();
|
||||
const pixelsPerRow = Math.floor(this.tilePixelRatio_ * this.size[0]);
|
||||
if (data instanceof DataView) {
|
||||
const bytesPerPixel = data.byteLength / (this.size[0] * this.size[1]);
|
||||
const offset = row * pixelsPerRow * bytesPerPixel + col * bytesPerPixel;
|
||||
const buffer = data.buffer.slice(offset, offset + bytesPerPixel);
|
||||
return new DataView(buffer);
|
||||
}
|
||||
|
||||
const offset = row * pixelsPerRow * this.bandCount + col * this.bandCount;
|
||||
return data.slice(offset, offset + this.bandCount);
|
||||
}
|
||||
|
||||
if (!pixelContext) {
|
||||
createPixelContext();
|
||||
}
|
||||
pixelContext.clearRect(0, 0, 1, 1);
|
||||
|
||||
let data;
|
||||
const image = this.tile.getImage();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
export default TileTexture;
|
||||
|
||||
Reference in New Issue
Block a user