Files
openlayers/src/ol/renderer/canvas/Layer.js

190 lines
7.2 KiB
JavaScript

/**
* @module ol/renderer/canvas/Layer
*/
import {abstract} from '../../util.js';
import {getBottomLeft, getBottomRight, getTopLeft, getTopRight} from '../../extent.js';
import {TRUE} from '../../functions.js';
import RenderEvent from '../../render/Event.js';
import RenderEventType from '../../render/EventType.js';
import {rotateAtOffset} from '../../render/canvas.js';
import CanvasImmediateRenderer from '../../render/canvas/Immediate.js';
import LayerRenderer from '../Layer.js';
import {create as createTransform, apply as applyTransform, compose as composeTransform} from '../../transform.js';
/**
* @abstract
*/
class CanvasLayerRenderer extends LayerRenderer {
/**
* @param {import("../../layer/Layer.js").default} layer Layer.
*/
constructor(layer) {
super(layer);
/**
* @protected
* @type {number}
*/
this.renderedResolution;
/**
* @private
* @type {import("../../transform.js").Transform}
*/
this.transform_ = createTransform();
}
/**
* @param {CanvasRenderingContext2D} context Context.
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
* @param {import("../../extent.js").Extent} extent Clip extent.
* @protected
*/
clip(context, frameState, extent) {
const pixelRatio = frameState.pixelRatio;
const width = frameState.size[0] * pixelRatio;
const height = frameState.size[1] * pixelRatio;
const rotation = frameState.viewState.rotation;
const topLeft = getTopLeft(/** @type {import("../../extent.js").Extent} */ (extent));
const topRight = getTopRight(/** @type {import("../../extent.js").Extent} */ (extent));
const bottomRight = getBottomRight(/** @type {import("../../extent.js").Extent} */ (extent));
const bottomLeft = getBottomLeft(/** @type {import("../../extent.js").Extent} */ (extent));
applyTransform(frameState.coordinateToPixelTransform, topLeft);
applyTransform(frameState.coordinateToPixelTransform, topRight);
applyTransform(frameState.coordinateToPixelTransform, bottomRight);
applyTransform(frameState.coordinateToPixelTransform, bottomLeft);
context.save();
rotateAtOffset(context, -rotation, width / 2, height / 2);
context.beginPath();
context.moveTo(topLeft[0] * pixelRatio, topLeft[1] * pixelRatio);
context.lineTo(topRight[0] * pixelRatio, topRight[1] * pixelRatio);
context.lineTo(bottomRight[0] * pixelRatio, bottomRight[1] * pixelRatio);
context.lineTo(bottomLeft[0] * pixelRatio, bottomLeft[1] * pixelRatio);
context.clip();
rotateAtOffset(context, rotation, width / 2, height / 2);
}
/**
* @param {import("../../render/EventType.js").default} type Event type.
* @param {CanvasRenderingContext2D} context Context.
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
* @param {import("../../transform.js").Transform=} opt_transform Transform.
* @private
*/
dispatchComposeEvent_(type, context, frameState, opt_transform) {
const layer = this.getLayer();
if (layer.hasListener(type)) {
const width = frameState.size[0] * frameState.pixelRatio;
const height = frameState.size[1] * frameState.pixelRatio;
const rotation = frameState.viewState.rotation;
rotateAtOffset(context, -rotation, width / 2, height / 2);
const transform = opt_transform !== undefined ?
opt_transform : this.getTransform(frameState, 0);
const render = new CanvasImmediateRenderer(
context, frameState.pixelRatio, frameState.extent, transform,
frameState.viewState.rotation);
const composeEvent = new RenderEvent(type, render, frameState,
context, null);
layer.dispatchEvent(composeEvent);
rotateAtOffset(context, rotation, width / 2, height / 2);
}
}
/**
* @param {import("../../coordinate.js").Coordinate} coordinate Coordinate.
* @param {import("../../PluggableMap.js").FrameState} frameState FrameState.
* @param {number} hitTolerance Hit tolerance in pixels.
* @param {function(this: S, import("../../layer/Layer.js").default, (Uint8ClampedArray|Uint8Array)): T} callback Layer
* callback.
* @param {S} thisArg Value to use as `this` when executing `callback`.
* @return {T|undefined} Callback result.
* @template S,T,U
*/
forEachLayerAtCoordinate(coordinate, frameState, hitTolerance, callback, thisArg) {
const hasFeature = this.forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, TRUE);
if (hasFeature) {
return callback.call(thisArg, this.getLayer(), null);
} else {
return undefined;
}
}
/**
* @param {CanvasRenderingContext2D} context Context.
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
* @param {import("../../layer/Layer.js").State} layerState Layer state.
* @param {import("../../transform.js").Transform=} opt_transform Transform.
* @protected
*/
postCompose(context, frameState, layerState, opt_transform) {
this.dispatchComposeEvent_(RenderEventType.POSTCOMPOSE, context, frameState, opt_transform);
}
/**
* @param {CanvasRenderingContext2D} context Context.
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
* @param {import("../../transform.js").Transform=} opt_transform Transform.
* @protected
*/
preCompose(context, frameState, opt_transform) {
this.dispatchComposeEvent_(RenderEventType.PRECOMPOSE, context, frameState, opt_transform);
}
/**
* @param {CanvasRenderingContext2D} context Context.
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
* @param {import("../../transform.js").Transform=} opt_transform Transform.
* @protected
*/
dispatchRenderEvent(context, frameState, opt_transform) {
this.dispatchComposeEvent_(RenderEventType.RENDER, context, frameState, opt_transform);
}
/**
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
* @param {number} offsetX Offset on the x-axis in view coordinates.
* @protected
* @return {!import("../../transform.js").Transform} Transform.
*/
getTransform(frameState, offsetX) {
const viewState = frameState.viewState;
const pixelRatio = frameState.pixelRatio;
const dx1 = pixelRatio * frameState.size[0] / 2;
const dy1 = pixelRatio * frameState.size[1] / 2;
const sx = pixelRatio / viewState.resolution;
const sy = -sx;
const angle = -viewState.rotation;
const dx2 = -viewState.center[0] + offsetX;
const dy2 = -viewState.center[1];
return composeTransform(this.transform_, dx1, dy1, sx, sy, angle, dx2, dy2);
}
/**
* @abstract
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
* @param {import("../../layer/Layer.js").State} layerState Layer state.
* @param {CanvasRenderingContext2D} context Context.
*/
composeFrame(frameState, layerState, context) {
abstract();
}
/**
* @abstract
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
* @param {import("../../layer/Layer.js").State} layerState Layer state.
* @return {boolean} whether composeFrame should be called.
*/
prepareFrame(frameState, layerState) {
return abstract();
}
}
export default CanvasLayerRenderer;