heli-agri
Version:
HeliAgri is a high-performance, feature-packed library for creating interactive maps on the web. It can display map tiles, vector data and markers loaded from any source on any web page. OpenLayers has been developed to further the use of geographic infor
371 lines (329 loc) • 10.4 kB
JavaScript
/**
* @module ol/renderer/webgl/TileLayer
*/
import ReprojDataTile from '../../reproj/DataTile.js';
import ReprojTile from '../../reproj/Tile.js';
import TileState from '../../TileState.js';
import TileTexture from '../../webgl/TileTexture.js';
import WebGLArrayBuffer from '../../webgl/Buffer.js';
import WebGLBaseTileLayerRenderer, {
Uniforms as BaseUniforms,
getCacheKey,
} from './TileLayerBase.js';
import {AttributeType} from '../../webgl/Helper.js';
import {ELEMENT_ARRAY_BUFFER, STATIC_DRAW} from '../../webgl.js';
import {apply as applyTransform} from '../../transform.js';
import {
boundingExtent,
containsCoordinate,
getIntersection,
} from '../../extent.js';
import {fromUserExtent} from '../../proj.js';
import {fromTransform as mat4FromTransform} from '../../vec/mat4.js';
import {toSize} from '../../size.js';
export const Uniforms = {
...BaseUniforms,
TILE_TEXTURE_ARRAY: 'u_tileTextures',
TEXTURE_PIXEL_WIDTH: 'u_texturePixelWidth',
TEXTURE_PIXEL_HEIGHT: 'u_texturePixelHeight',
TEXTURE_RESOLUTION: 'u_textureResolution', // map units per texture pixel
TEXTURE_ORIGIN_X: 'u_textureOriginX', // map x coordinate of left edge of texture
TEXTURE_ORIGIN_Y: 'u_textureOriginY', // map y coordinate of top edge of texture
};
export const Attributes = {
TEXTURE_COORD: 'a_textureCoord',
};
/**
* @type {Array<import('../../webgl/Helper.js').AttributeDescription>}
*/
const attributeDescriptions = [
{
name: Attributes.TEXTURE_COORD,
size: 2,
type: AttributeType.FLOAT,
},
];
/**
* @typedef {Object} Options
* @property {string} vertexShader Vertex shader source.
* @property {string} fragmentShader Fragment shader source.
* @property {Object<string, import("../../webgl/Helper").UniformValue>} [uniforms] Additional uniforms
* made available to shaders.
* @property {Array<import("../../webgl/PaletteTexture.js").default>} [paletteTextures] Palette textures.
* @property {number} [cacheSize=512] The texture cache size.
*/
/**
* @typedef {import("../../layer/WebGLTile.js").default} LayerType
*/
/**
* @typedef {import("../../webgl/TileTexture.js").TileType} TileTextureType
*/
/**
* @typedef {import("../../webgl/TileTexture.js").default} TileTextureRepresentation
*/
/**
* @classdesc
* WebGL renderer for tile layers.
* @extends {WebGLBaseTileLayerRenderer<LayerType, TileTextureType, TileTextureRepresentation>}
* @api
*/
class WebGLTileLayerRenderer extends WebGLBaseTileLayerRenderer {
/**
* @param {LayerType} tileLayer Tile layer.
* @param {Options} options Options.
*/
constructor(tileLayer, options) {
super(tileLayer, options);
/**
* @type {WebGLProgram}
* @private
*/
this.program_;
/**
* @private
*/
this.vertexShader_ = options.vertexShader;
/**
* @private
*/
this.fragmentShader_ = options.fragmentShader;
/**
* Tiles are rendered as a quad with the following structure:
*
* [P3]---------[P2]
* |` |
* | ` B |
* | ` |
* | ` |
* | A ` |
* | ` |
* [P0]---------[P1]
*
* Triangle A: P0, P1, P3
* Triangle B: P1, P2, P3
*
* @private
*/
this.indices_ = new WebGLArrayBuffer(ELEMENT_ARRAY_BUFFER, STATIC_DRAW);
this.indices_.fromArray([0, 1, 3, 1, 2, 3]);
/**
* @type {Array<import("../../webgl/PaletteTexture.js").default>}
* @private
*/
this.paletteTextures_ = options.paletteTextures || [];
}
/**
* @param {Options} options Options.
*/
reset(options) {
super.reset(options);
this.vertexShader_ = options.vertexShader;
this.fragmentShader_ = options.fragmentShader;
if (this.helper) {
this.program_ = this.helper.getProgram(
this.fragmentShader_,
this.vertexShader_
);
}
}
afterHelperCreated() {
this.program_ = this.helper.getProgram(
this.fragmentShader_,
this.vertexShader_
);
this.helper.flushBufferData(this.indices_);
}
createTileRepresentation(options) {
return new TileTexture(options);
}
beforeTilesRender(frameState, tilesWithAlpha) {
super.beforeTilesRender(frameState, tilesWithAlpha);
this.helper.useProgram(this.program_, frameState);
}
renderTile(
tileTexture,
tileTransform,
frameState,
renderExtent,
tileResolution,
tileSize,
tileOrigin,
tileExtent,
depth,
gutter,
alpha
) {
const gl = this.helper.getGL();
this.helper.bindBuffer(tileTexture.coords);
this.helper.bindBuffer(this.indices_);
this.helper.enableAttributes(attributeDescriptions);
let textureSlot = 0;
while (textureSlot < tileTexture.textures.length) {
const uniformName = `${Uniforms.TILE_TEXTURE_ARRAY}[${textureSlot}]`;
this.helper.bindTexture(
tileTexture.textures[textureSlot],
textureSlot,
uniformName
);
++textureSlot;
}
for (
let paletteIndex = 0;
paletteIndex < this.paletteTextures_.length;
++paletteIndex
) {
const paletteTexture = this.paletteTextures_[paletteIndex];
const texture = paletteTexture.getTexture(gl);
this.helper.bindTexture(texture, textureSlot, paletteTexture.name);
++textureSlot;
}
const viewState = frameState.viewState;
const tileWidthWithGutter = tileSize[0] + 2 * gutter;
const tileHeightWithGutter = tileSize[1] + 2 * gutter;
const tile = tileTexture.tile;
const tileCoord = tile.tileCoord;
const tileCenterI = tileCoord[1];
const tileCenterJ = tileCoord[2];
this.helper.setUniformMatrixValue(
Uniforms.TILE_TRANSFORM,
mat4FromTransform(this.tempMat4, tileTransform)
);
this.helper.setUniformFloatValue(Uniforms.TRANSITION_ALPHA, alpha);
this.helper.setUniformFloatValue(Uniforms.DEPTH, depth);
let gutterExtent = renderExtent;
if (gutter > 0) {
gutterExtent = tileExtent;
getIntersection(gutterExtent, renderExtent, gutterExtent);
}
this.helper.setUniformFloatVec4(Uniforms.RENDER_EXTENT, gutterExtent);
this.helper.setUniformFloatValue(Uniforms.RESOLUTION, viewState.resolution);
this.helper.setUniformFloatValue(Uniforms.ZOOM, viewState.zoom);
this.helper.setUniformFloatValue(
Uniforms.TEXTURE_PIXEL_WIDTH,
tileWidthWithGutter
);
this.helper.setUniformFloatValue(
Uniforms.TEXTURE_PIXEL_HEIGHT,
tileHeightWithGutter
);
this.helper.setUniformFloatValue(
Uniforms.TEXTURE_RESOLUTION,
tileResolution
);
this.helper.setUniformFloatValue(
Uniforms.TEXTURE_ORIGIN_X,
tileOrigin[0] +
tileCenterI * tileSize[0] * tileResolution -
gutter * tileResolution
);
this.helper.setUniformFloatValue(
Uniforms.TEXTURE_ORIGIN_Y,
tileOrigin[1] -
tileCenterJ * tileSize[1] * tileResolution +
gutter * tileResolution
);
this.helper.drawElements(0, this.indices_.getSize());
}
/**
* @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;
}
}
// determine last source suitable for rendering at coordinate
const sources = layer.getSources(
boundingExtent([coordinate]),
viewState.resolution
);
let i, source, tileGrid;
for (i = sources.length - 1; i >= 0; --i) {
source = sources[i];
if (source.getState() === 'ready') {
tileGrid = source.getTileGridForProjection(viewState.projection);
if (source.getWrapX()) {
break;
}
const gridExtent = tileGrid.getExtent();
if (!gridExtent || containsCoordinate(gridExtent, coordinate)) {
break;
}
}
}
if (i < 0) {
return null;
}
const tileTextureCache = this.tileRepresentationCache;
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);
const tile = tileTexture.tile;
if (
(tile instanceof ReprojTile || tile instanceof ReprojDataTile) &&
tile.getState() === TileState.EMPTY
) {
return null;
}
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;
}
/**
* Clean up.
*/
disposeInternal() {
const helper = this.helper;
if (helper) {
const gl = helper.getGL();
gl.deleteProgram(this.program_);
delete this.program_;
helper.deleteBuffer(this.indices_);
}
super.disposeInternal();
delete this.indices_;
}
}
export default WebGLTileLayerRenderer;