planning
All checks were successful
Publish To Prod / deploy_and_publish (push) Successful in 35s

This commit is contained in:
2024-10-14 09:15:30 +02:00
parent bcba00a730
commit 6e64e138e2
21059 changed files with 2317811 additions and 1 deletions

132
node_modules/ol/src/render/Box.js generated vendored Normal file
View File

@@ -0,0 +1,132 @@
/**
* @module ol/render/Box
*/
import Disposable from '../Disposable.js';
import Polygon from '../geom/Polygon.js';
class RenderBox extends Disposable {
/**
* @param {string} className CSS class name.
*/
constructor(className) {
super();
/**
* @type {import("../geom/Polygon.js").default}
* @private
*/
this.geometry_ = null;
/**
* @type {HTMLDivElement}
* @private
*/
this.element_ = document.createElement('div');
this.element_.style.position = 'absolute';
this.element_.style.pointerEvents = 'auto';
this.element_.className = 'ol-box ' + className;
/**
* @private
* @type {import("../PluggableMap.js").default|null}
*/
this.map_ = null;
/**
* @private
* @type {import("../pixel.js").Pixel}
*/
this.startPixel_ = null;
/**
* @private
* @type {import("../pixel.js").Pixel}
*/
this.endPixel_ = null;
}
/**
* Clean up.
*/
disposeInternal() {
this.setMap(null);
}
/**
* @private
*/
render_() {
const startPixel = this.startPixel_;
const endPixel = this.endPixel_;
const px = 'px';
const style = this.element_.style;
style.left = Math.min(startPixel[0], endPixel[0]) + px;
style.top = Math.min(startPixel[1], endPixel[1]) + px;
style.width = Math.abs(endPixel[0] - startPixel[0]) + px;
style.height = Math.abs(endPixel[1] - startPixel[1]) + px;
}
/**
* @param {import("../PluggableMap.js").default|null} map Map.
*/
setMap(map) {
if (this.map_) {
this.map_.getOverlayContainer().removeChild(this.element_);
const style = this.element_.style;
style.left = 'inherit';
style.top = 'inherit';
style.width = 'inherit';
style.height = 'inherit';
}
this.map_ = map;
if (this.map_) {
this.map_.getOverlayContainer().appendChild(this.element_);
}
}
/**
* @param {import("../pixel.js").Pixel} startPixel Start pixel.
* @param {import("../pixel.js").Pixel} endPixel End pixel.
*/
setPixels(startPixel, endPixel) {
this.startPixel_ = startPixel;
this.endPixel_ = endPixel;
this.createOrUpdateGeometry();
this.render_();
}
/**
* Creates or updates the cached geometry.
*/
createOrUpdateGeometry() {
const startPixel = this.startPixel_;
const endPixel = this.endPixel_;
const pixels = [
startPixel,
[startPixel[0], endPixel[1]],
endPixel,
[endPixel[0], startPixel[1]],
];
const coordinates = pixels.map(
this.map_.getCoordinateFromPixelInternal,
this.map_
);
// close the polygon
coordinates[4] = coordinates[0].slice();
if (!this.geometry_) {
this.geometry_ = new Polygon([coordinates]);
} else {
this.geometry_.setCoordinates([coordinates]);
}
}
/**
* @return {import("../geom/Polygon.js").default} Geometry.
*/
getGeometry() {
return this.geometry_;
}
}
export default RenderBox;

44
node_modules/ol/src/render/Event.js generated vendored Normal file
View File

@@ -0,0 +1,44 @@
/**
* @module ol/render/Event
*/
import Event from '../events/Event.js';
class RenderEvent extends Event {
/**
* @param {import("./EventType.js").default} type Type.
* @param {import("../transform.js").Transform} [opt_inversePixelTransform] Transform for
* CSS pixels to rendered pixels.
* @param {import("../PluggableMap.js").FrameState} [opt_frameState] Frame state.
* @param {?(CanvasRenderingContext2D|WebGLRenderingContext)} [opt_context] Context.
*/
constructor(type, opt_inversePixelTransform, opt_frameState, opt_context) {
super(type);
/**
* Transform from CSS pixels (relative to the top-left corner of the map viewport)
* to rendered pixels on this event's `context`. Only available when a Canvas renderer is used, null otherwise.
* @type {import("../transform.js").Transform|undefined}
* @api
*/
this.inversePixelTransform = opt_inversePixelTransform;
/**
* An object representing the current render frame state.
* @type {import("../PluggableMap.js").FrameState|undefined}
* @api
*/
this.frameState = opt_frameState;
/**
* Canvas context. Not available when the event is dispatched by the map. For Canvas 2D layers,
* the context will be the 2D rendering context. For WebGL layers, the context will be the WebGL
* context.
* @type {CanvasRenderingContext2D|WebGLRenderingContext|undefined}
* @api
*/
this.context = opt_context;
}
}
export default RenderEvent;

57
node_modules/ol/src/render/EventType.js generated vendored Normal file
View File

@@ -0,0 +1,57 @@
/**
* @module ol/render/EventType
*/
/**
* @enum {string}
*/
export default {
/**
* Triggered before a layer is rendered.
* @event module:ol/render/Event~RenderEvent#prerender
* @api
*/
PRERENDER: 'prerender',
/**
* Triggered after a layer is rendered.
* @event module:ol/render/Event~RenderEvent#postrender
* @api
*/
POSTRENDER: 'postrender',
/**
* Triggered before layers are composed. When dispatched by the map, the event object will not have
* a `context` set. When dispatched by a layer, the event object will have a `context` set. Only
* WebGL layers currently dispatch this event.
* @event module:ol/render/Event~RenderEvent#precompose
* @api
*/
PRECOMPOSE: 'precompose',
/**
* Triggered after layers are composed. When dispatched by the map, the event object will not have
* a `context` set. When dispatched by a layer, the event object will have a `context` set. Only
* WebGL layers currently dispatch this event.
* @event module:ol/render/Event~RenderEvent#postcompose
* @api
*/
POSTCOMPOSE: 'postcompose',
/**
* Triggered when rendering is complete, i.e. all sources and tiles have
* finished loading for the current viewport, and all tiles are faded in.
* The event object will not have a `context` set.
* @event module:ol/render/Event~RenderEvent#rendercomplete
* @api
*/
RENDERCOMPLETE: 'rendercomplete',
};
/**
* @typedef {'postrender'|'precompose'|'postcompose'|'rendercomplete'} MapRenderEventTypes
*/
/**
* @typedef {'postrender'|'prerender'} LayerRenderEventTypes
*/

405
node_modules/ol/src/render/Feature.js generated vendored Normal file
View File

@@ -0,0 +1,405 @@
/**
* @module ol/render/Feature
*/
import Feature from '../Feature.js';
import GeometryLayout from '../geom/GeometryLayout.js';
import {
LineString,
MultiLineString,
MultiPoint,
MultiPolygon,
Point,
Polygon,
} from '../geom.js';
import {
compose as composeTransform,
create as createTransform,
} from '../transform.js';
import {
createOrUpdateFromCoordinate,
createOrUpdateFromFlatCoordinates,
getCenter,
getHeight,
} from '../extent.js';
import {extend} from '../array.js';
import {
getInteriorPointOfArray,
getInteriorPointsOfMultiArray,
} from '../geom/flat/interiorpoint.js';
import {get as getProjection} from '../proj.js';
import {inflateEnds} from '../geom/flat/orient.js';
import {interpolatePoint} from '../geom/flat/interpolate.js';
import {linearRingss as linearRingssCenter} from '../geom/flat/center.js';
import {transform2D} from '../geom/flat/transform.js';
/**
* @type {import("../transform.js").Transform}
*/
const tmpTransform = createTransform();
/**
* Lightweight, read-only, {@link module:ol/Feature~Feature} and {@link module:ol/geom/Geometry~Geometry} like
* structure, optimized for vector tile rendering and styling. Geometry access
* through the API is limited to getting the type and extent of the geometry.
*/
class RenderFeature {
/**
* @param {import("../geom/Geometry.js").Type} type Geometry type.
* @param {Array<number>} flatCoordinates Flat coordinates. These always need
* to be right-handed for polygons.
* @param {Array<number>|Array<Array<number>>} ends Ends or Endss.
* @param {Object<string, *>} properties Properties.
* @param {number|string|undefined} id Feature id.
*/
constructor(type, flatCoordinates, ends, properties, id) {
/**
* @type {import("../style/Style.js").StyleFunction|undefined}
*/
this.styleFunction;
/**
* @private
* @type {import("../extent.js").Extent|undefined}
*/
this.extent_;
/**
* @private
* @type {number|string|undefined}
*/
this.id_ = id;
/**
* @private
* @type {import("../geom/Geometry.js").Type}
*/
this.type_ = type;
/**
* @private
* @type {Array<number>}
*/
this.flatCoordinates_ = flatCoordinates;
/**
* @private
* @type {Array<number>}
*/
this.flatInteriorPoints_ = null;
/**
* @private
* @type {Array<number>}
*/
this.flatMidpoints_ = null;
/**
* @private
* @type {Array<number>|Array<Array<number>>}
*/
this.ends_ = ends;
/**
* @private
* @type {Object<string, *>}
*/
this.properties_ = properties;
}
/**
* Get a feature property by its key.
* @param {string} key Key
* @return {*} Value for the requested key.
* @api
*/
get(key) {
return this.properties_[key];
}
/**
* Get the extent of this feature's geometry.
* @return {import("../extent.js").Extent} Extent.
* @api
*/
getExtent() {
if (!this.extent_) {
this.extent_ =
this.type_ === 'Point'
? createOrUpdateFromCoordinate(this.flatCoordinates_)
: createOrUpdateFromFlatCoordinates(
this.flatCoordinates_,
0,
this.flatCoordinates_.length,
2
);
}
return this.extent_;
}
/**
* @return {Array<number>} Flat interior points.
*/
getFlatInteriorPoint() {
if (!this.flatInteriorPoints_) {
const flatCenter = getCenter(this.getExtent());
this.flatInteriorPoints_ = getInteriorPointOfArray(
this.flatCoordinates_,
0,
/** @type {Array<number>} */ (this.ends_),
2,
flatCenter,
0
);
}
return this.flatInteriorPoints_;
}
/**
* @return {Array<number>} Flat interior points.
*/
getFlatInteriorPoints() {
if (!this.flatInteriorPoints_) {
const flatCenters = linearRingssCenter(
this.flatCoordinates_,
0,
/** @type {Array<Array<number>>} */ (this.ends_),
2
);
this.flatInteriorPoints_ = getInteriorPointsOfMultiArray(
this.flatCoordinates_,
0,
/** @type {Array<Array<number>>} */ (this.ends_),
2,
flatCenters
);
}
return this.flatInteriorPoints_;
}
/**
* @return {Array<number>} Flat midpoint.
*/
getFlatMidpoint() {
if (!this.flatMidpoints_) {
this.flatMidpoints_ = interpolatePoint(
this.flatCoordinates_,
0,
this.flatCoordinates_.length,
2,
0.5
);
}
return this.flatMidpoints_;
}
/**
* @return {Array<number>} Flat midpoints.
*/
getFlatMidpoints() {
if (!this.flatMidpoints_) {
this.flatMidpoints_ = [];
const flatCoordinates = this.flatCoordinates_;
let offset = 0;
const ends = /** @type {Array<number>} */ (this.ends_);
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
const midpoint = interpolatePoint(flatCoordinates, offset, end, 2, 0.5);
extend(this.flatMidpoints_, midpoint);
offset = end;
}
}
return this.flatMidpoints_;
}
/**
* Get the feature identifier. This is a stable identifier for the feature and
* is set when reading data from a remote source.
* @return {number|string|undefined} Id.
* @api
*/
getId() {
return this.id_;
}
/**
* @return {Array<number>} Flat coordinates.
*/
getOrientedFlatCoordinates() {
return this.flatCoordinates_;
}
/**
* For API compatibility with {@link module:ol/Feature~Feature}, this method is useful when
* determining the geometry type in style function (see {@link #getType}).
* @return {RenderFeature} Feature.
* @api
*/
getGeometry() {
return this;
}
/**
* @param {number} squaredTolerance Squared tolerance.
* @return {RenderFeature} Simplified geometry.
*/
getSimplifiedGeometry(squaredTolerance) {
return this;
}
/**
* Get a transformed and simplified version of the geometry.
* @abstract
* @param {number} squaredTolerance Squared tolerance.
* @param {import("../proj.js").TransformFunction} [opt_transform] Optional transform function.
* @return {RenderFeature} Simplified geometry.
*/
simplifyTransformed(squaredTolerance, opt_transform) {
return this;
}
/**
* Get the feature properties.
* @return {Object<string, *>} Feature properties.
* @api
*/
getProperties() {
return this.properties_;
}
/**
* @return {number} Stride.
*/
getStride() {
return 2;
}
/**
* @return {import('../style/Style.js').StyleFunction|undefined} Style
*/
getStyleFunction() {
return this.styleFunction;
}
/**
* Get the type of this feature's geometry.
* @return {import("../geom/Geometry.js").Type} Geometry type.
* @api
*/
getType() {
return this.type_;
}
/**
* Transform geometry coordinates from tile pixel space to projected.
*
* @param {import("../proj.js").ProjectionLike} projection The data projection
*/
transform(projection) {
projection = getProjection(projection);
const pixelExtent = projection.getExtent();
const projectedExtent = projection.getWorldExtent();
if (pixelExtent && projectedExtent) {
const scale = getHeight(projectedExtent) / getHeight(pixelExtent);
composeTransform(
tmpTransform,
projectedExtent[0],
projectedExtent[3],
scale,
-scale,
0,
0,
0
);
transform2D(
this.flatCoordinates_,
0,
this.flatCoordinates_.length,
2,
tmpTransform,
this.flatCoordinates_
);
}
}
/**
* @return {Array<number>|Array<Array<number>>} Ends or endss.
*/
getEnds() {
return this.ends_;
}
}
RenderFeature.prototype.getEndss = RenderFeature.prototype.getEnds;
/**
* @return {Array<number>} Flat coordinates.
*/
RenderFeature.prototype.getFlatCoordinates =
RenderFeature.prototype.getOrientedFlatCoordinates;
/**
* Create a geometry from an `ol/render/Feature`
* @param {RenderFeature} renderFeature
* Render Feature
* @return {Point|MultiPoint|LineString|MultiLineString|Polygon|MultiPolygon}
* New geometry instance.
* @api
*/
export function toGeometry(renderFeature) {
const geometryType = renderFeature.getType();
switch (geometryType) {
case 'Point':
return new Point(renderFeature.getFlatCoordinates());
case 'MultiPoint':
return new MultiPoint(
renderFeature.getFlatCoordinates(),
GeometryLayout.XY
);
case 'LineString':
return new LineString(
renderFeature.getFlatCoordinates(),
GeometryLayout.XY
);
case 'MultiLineString':
return new MultiLineString(
renderFeature.getFlatCoordinates(),
GeometryLayout.XY,
/** @type {Array<number>} */ (renderFeature.getEnds())
);
case 'Polygon':
const flatCoordinates = renderFeature.getFlatCoordinates();
const ends = /** @type {Array<number>} */ (renderFeature.getEnds());
const endss = inflateEnds(flatCoordinates, ends);
return endss.length > 1
? new MultiPolygon(flatCoordinates, GeometryLayout.XY, endss)
: new Polygon(flatCoordinates, GeometryLayout.XY, ends);
default:
throw new Error('Invalid geometry type:' + geometryType);
}
}
/**
* Create an `ol/Feature` from an `ol/render/Feature`
* @param {RenderFeature} renderFeature RenderFeature
* @param {string} [opt_geometryName='geometry'] Geometry name to use
* when creating the Feature.
* @return {Feature} Newly constructed `ol/Feature` with properties,
* geometry, and id copied over.
* @api
*/
export function toFeature(renderFeature, opt_geometryName) {
const id = renderFeature.getId();
const geometry = toGeometry(renderFeature);
const properties = renderFeature.getProperties();
const feature = new Feature();
if (opt_geometryName !== undefined) {
feature.setGeometryName(opt_geometryName);
}
feature.setGeometry(geometry);
if (id !== undefined) {
feature.setId(id);
}
feature.setProperties(properties, true);
return feature;
}
export default RenderFeature;

115
node_modules/ol/src/render/VectorContext.js generated vendored Normal file
View File

@@ -0,0 +1,115 @@
/**
* @module ol/render/VectorContext
*/
/**
* @classdesc
* Context for drawing geometries. A vector context is available on render
* events and does not need to be constructed directly.
* @api
*/
class VectorContext {
/**
* Render a geometry with a custom renderer.
*
* @param {import("../geom/SimpleGeometry.js").default} geometry Geometry.
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @param {Function} renderer Renderer.
* @param {Function} hitDetectionRenderer Renderer.
*/
drawCustom(geometry, feature, renderer, hitDetectionRenderer) {}
/**
* Render a geometry.
*
* @param {import("../geom/Geometry.js").default} geometry The geometry to render.
*/
drawGeometry(geometry) {}
/**
* Set the rendering style.
*
* @param {import("../style/Style.js").default} style The rendering style.
*/
setStyle(style) {}
/**
* @param {import("../geom/Circle.js").default} circleGeometry Circle geometry.
* @param {import("../Feature.js").default} feature Feature.
*/
drawCircle(circleGeometry, feature) {}
/**
* @param {import("../Feature.js").default} feature Feature.
* @param {import("../style/Style.js").default} style Style.
*/
drawFeature(feature, style) {}
/**
* @param {import("../geom/GeometryCollection.js").default} geometryCollectionGeometry Geometry collection.
* @param {import("../Feature.js").default} feature Feature.
*/
drawGeometryCollection(geometryCollectionGeometry, feature) {}
/**
* @param {import("../geom/LineString.js").default|import("./Feature.js").default} lineStringGeometry Line string geometry.
* @param {import("../Feature.js").FeatureLike} feature Feature.
*/
drawLineString(lineStringGeometry, feature) {}
/**
* @param {import("../geom/MultiLineString.js").default|import("./Feature.js").default} multiLineStringGeometry MultiLineString geometry.
* @param {import("../Feature.js").FeatureLike} feature Feature.
*/
drawMultiLineString(multiLineStringGeometry, feature) {}
/**
* @param {import("../geom/MultiPoint.js").default|import("./Feature.js").default} multiPointGeometry MultiPoint geometry.
* @param {import("../Feature.js").FeatureLike} feature Feature.
*/
drawMultiPoint(multiPointGeometry, feature) {}
/**
* @param {import("../geom/MultiPolygon.js").default} multiPolygonGeometry MultiPolygon geometry.
* @param {import("../Feature.js").FeatureLike} feature Feature.
*/
drawMultiPolygon(multiPolygonGeometry, feature) {}
/**
* @param {import("../geom/Point.js").default|import("./Feature.js").default} pointGeometry Point geometry.
* @param {import("../Feature.js").FeatureLike} feature Feature.
*/
drawPoint(pointGeometry, feature) {}
/**
* @param {import("../geom/Polygon.js").default|import("./Feature.js").default} polygonGeometry Polygon geometry.
* @param {import("../Feature.js").FeatureLike} feature Feature.
*/
drawPolygon(polygonGeometry, feature) {}
/**
* @param {import("../geom/SimpleGeometry.js").default|import("./Feature.js").default} geometry Geometry.
* @param {import("../Feature.js").FeatureLike} feature Feature.
*/
drawText(geometry, feature) {}
/**
* @param {import("../style/Fill.js").default} fillStyle Fill style.
* @param {import("../style/Stroke.js").default} strokeStyle Stroke style.
*/
setFillStrokeStyle(fillStyle, strokeStyle) {}
/**
* @param {import("../style/Image.js").default} imageStyle Image style.
* @param {import("../render/canvas.js").DeclutterImageWithText} [opt_declutterImageWithText] Shared data for combined decluttering with a text style.
*/
setImageStyle(imageStyle, opt_declutterImageWithText) {}
/**
* @param {import("../style/Text.js").default} textStyle Text style.
* @param {import("../render/canvas.js").DeclutterImageWithText} [opt_declutterImageWithText] Shared data for combined decluttering with an image style.
*/
setTextStyle(textStyle, opt_declutterImageWithText) {}
}
export default VectorContext;

516
node_modules/ol/src/render/canvas.js generated vendored Normal file
View File

@@ -0,0 +1,516 @@
/**
* @module ol/render/canvas
*/
import BaseObject from '../Object.js';
import EventTarget from '../events/Target.js';
import {WORKER_OFFSCREEN_CANVAS} from '../has.js';
import {clear} from '../obj.js';
import {createCanvasContext2D} from '../dom.js';
import {getFontParameters} from '../css.js';
/**
* @typedef {'Circle' | 'Image' | 'LineString' | 'Polygon' | 'Text' | 'Default'} BuilderType
*/
/**
* @typedef {Object} FillState
* @property {import("../colorlike.js").ColorLike} fillStyle FillStyle.
*/
/**
* @typedef Label
* @property {number} width Width.
* @property {number} height Height.
* @property {Array<string|number>} contextInstructions ContextInstructions.
*/
/**
* @typedef {Object} FillStrokeState
* @property {import("../colorlike.js").ColorLike} [currentFillStyle] Current FillStyle.
* @property {import("../colorlike.js").ColorLike} [currentStrokeStyle] Current StrokeStyle.
* @property {CanvasLineCap} [currentLineCap] Current LineCap.
* @property {Array<number>} currentLineDash Current LineDash.
* @property {number} [currentLineDashOffset] Current LineDashOffset.
* @property {CanvasLineJoin} [currentLineJoin] Current LineJoin.
* @property {number} [currentLineWidth] Current LineWidth.
* @property {number} [currentMiterLimit] Current MiterLimit.
* @property {number} [lastStroke] Last stroke.
* @property {import("../colorlike.js").ColorLike} [fillStyle] FillStyle.
* @property {import("../colorlike.js").ColorLike} [strokeStyle] StrokeStyle.
* @property {CanvasLineCap} [lineCap] LineCap.
* @property {Array<number>} lineDash LineDash.
* @property {number} [lineDashOffset] LineDashOffset.
* @property {CanvasLineJoin} [lineJoin] LineJoin.
* @property {number} [lineWidth] LineWidth.
* @property {number} [miterLimit] MiterLimit.
*/
/**
* @typedef {Object} StrokeState
* @property {CanvasLineCap} lineCap LineCap.
* @property {Array<number>} lineDash LineDash.
* @property {number} lineDashOffset LineDashOffset.
* @property {CanvasLineJoin} lineJoin LineJoin.
* @property {number} lineWidth LineWidth.
* @property {number} miterLimit MiterLimit.
* @property {import("../colorlike.js").ColorLike} strokeStyle StrokeStyle.
*/
/**
* @typedef {Object} TextState
* @property {string} font Font.
* @property {string} [textAlign] TextAlign.
* @property {string} [justify] Justify.
* @property {string} textBaseline TextBaseline.
* @property {string} [placement] Placement.
* @property {number} [maxAngle] MaxAngle.
* @property {boolean} [overflow] Overflow.
* @property {import("../style/Fill.js").default} [backgroundFill] BackgroundFill.
* @property {import("../style/Stroke.js").default} [backgroundStroke] BackgroundStroke.
* @property {import("../size.js").Size} [scale] Scale.
* @property {Array<number>} [padding] Padding.
*/
/**
* @typedef {Object} SerializableInstructions
* @property {Array<*>} instructions The rendering instructions.
* @property {Array<*>} hitDetectionInstructions The rendering hit detection instructions.
* @property {Array<number>} coordinates The array of all coordinates.
* @property {!Object<string, TextState>} [textStates] The text states (decluttering).
* @property {!Object<string, FillState>} [fillStates] The fill states (decluttering).
* @property {!Object<string, StrokeState>} [strokeStates] The stroke states (decluttering).
*/
/**
* @typedef {Object<number, import("./canvas/Executor.js").ReplayImageOrLabelArgs>} DeclutterImageWithText
*/
/**
* @const
* @type {string}
*/
export const defaultFont = '10px sans-serif';
/**
* @const
* @type {import("../colorlike.js").ColorLike}
*/
export const defaultFillStyle = '#000';
/**
* @const
* @type {CanvasLineCap}
*/
export const defaultLineCap = 'round';
/**
* @const
* @type {Array<number>}
*/
export const defaultLineDash = [];
/**
* @const
* @type {number}
*/
export const defaultLineDashOffset = 0;
/**
* @const
* @type {CanvasLineJoin}
*/
export const defaultLineJoin = 'round';
/**
* @const
* @type {number}
*/
export const defaultMiterLimit = 10;
/**
* @const
* @type {import("../colorlike.js").ColorLike}
*/
export const defaultStrokeStyle = '#000';
/**
* @const
* @type {string}
*/
export const defaultTextAlign = 'center';
/**
* @const
* @type {string}
*/
export const defaultTextBaseline = 'middle';
/**
* @const
* @type {Array<number>}
*/
export const defaultPadding = [0, 0, 0, 0];
/**
* @const
* @type {number}
*/
export const defaultLineWidth = 1;
/**
* @type {BaseObject}
*/
export const checkedFonts = new BaseObject();
/**
* The label cache for text rendering. To change the default cache size of 2048
* entries, use {@link module:ol/structs/LRUCache~LRUCache#setSize cache.setSize()}.
* Deprecated - there is no label cache any more.
* @type {?}
* @api
* @deprecated
*/
export const labelCache = new EventTarget();
labelCache.setSize = function () {
console.warn('labelCache is deprecated.'); //eslint-disable-line
};
/**
* @type {CanvasRenderingContext2D}
*/
let measureContext = null;
/**
* @type {string}
*/
let measureFont;
/**
* @type {!Object<string, number>}
*/
export const textHeights = {};
/**
* Clears the label cache when a font becomes available.
* @param {string} fontSpec CSS font spec.
*/
export const registerFont = (function () {
const retries = 100;
const size = '32px ';
const referenceFonts = ['monospace', 'serif'];
const len = referenceFonts.length;
const text = 'wmytzilWMYTZIL@#/&?$%10\uF013';
let interval, referenceWidth;
/**
* @param {string} fontStyle Css font-style
* @param {string} fontWeight Css font-weight
* @param {*} fontFamily Css font-family
* @return {boolean} Font with style and weight is available
*/
function isAvailable(fontStyle, fontWeight, fontFamily) {
let available = true;
for (let i = 0; i < len; ++i) {
const referenceFont = referenceFonts[i];
referenceWidth = measureTextWidth(
fontStyle + ' ' + fontWeight + ' ' + size + referenceFont,
text
);
if (fontFamily != referenceFont) {
const width = measureTextWidth(
fontStyle +
' ' +
fontWeight +
' ' +
size +
fontFamily +
',' +
referenceFont,
text
);
// If width and referenceWidth are the same, then the fallback was used
// instead of the font we wanted, so the font is not available.
available = available && width != referenceWidth;
}
}
if (available) {
return true;
}
return false;
}
function check() {
let done = true;
const fonts = checkedFonts.getKeys();
for (let i = 0, ii = fonts.length; i < ii; ++i) {
const font = fonts[i];
if (checkedFonts.get(font) < retries) {
if (isAvailable.apply(this, font.split('\n'))) {
clear(textHeights);
// Make sure that loaded fonts are picked up by Safari
measureContext = null;
measureFont = undefined;
checkedFonts.set(font, retries);
} else {
checkedFonts.set(font, checkedFonts.get(font) + 1, true);
done = false;
}
}
}
if (done) {
clearInterval(interval);
interval = undefined;
}
}
return function (fontSpec) {
const font = getFontParameters(fontSpec);
if (!font) {
return;
}
const families = font.families;
for (let i = 0, ii = families.length; i < ii; ++i) {
const family = families[i];
const key = font.style + '\n' + font.weight + '\n' + family;
if (checkedFonts.get(key) === undefined) {
checkedFonts.set(key, retries, true);
if (!isAvailable(font.style, font.weight, family)) {
checkedFonts.set(key, 0, true);
if (interval === undefined) {
interval = setInterval(check, 32);
}
}
}
}
};
})();
/**
* @param {string} font Font to use for measuring.
* @return {import("../size.js").Size} Measurement.
*/
export const measureTextHeight = (function () {
/**
* @type {HTMLDivElement}
*/
let measureElement;
return function (fontSpec) {
let height = textHeights[fontSpec];
if (height == undefined) {
if (WORKER_OFFSCREEN_CANVAS) {
const font = getFontParameters(fontSpec);
const metrics = measureText(fontSpec, 'Žg');
const lineHeight = isNaN(Number(font.lineHeight))
? 1.2
: Number(font.lineHeight);
height =
lineHeight *
(metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent);
} else {
if (!measureElement) {
measureElement = document.createElement('div');
measureElement.innerHTML = 'M';
measureElement.style.minHeight = '0';
measureElement.style.maxHeight = 'none';
measureElement.style.height = 'auto';
measureElement.style.padding = '0';
measureElement.style.border = 'none';
measureElement.style.position = 'absolute';
measureElement.style.display = 'block';
measureElement.style.left = '-99999px';
}
measureElement.style.font = fontSpec;
document.body.appendChild(measureElement);
height = measureElement.offsetHeight;
document.body.removeChild(measureElement);
}
textHeights[fontSpec] = height;
}
return height;
};
})();
/**
* @param {string} font Font.
* @param {string} text Text.
* @return {TextMetrics} Text metrics.
*/
function measureText(font, text) {
if (!measureContext) {
measureContext = createCanvasContext2D(1, 1);
}
if (font != measureFont) {
measureContext.font = font;
measureFont = measureContext.font;
}
return measureContext.measureText(text);
}
/**
* @param {string} font Font.
* @param {string} text Text.
* @return {number} Width.
*/
export function measureTextWidth(font, text) {
return measureText(font, text).width;
}
/**
* Measure text width using a cache.
* @param {string} font The font.
* @param {string} text The text to measure.
* @param {Object<string, number>} cache A lookup of cached widths by text.
* @return {number} The text width.
*/
export function measureAndCacheTextWidth(font, text, cache) {
if (text in cache) {
return cache[text];
}
const width = text
.split('\n')
.reduce((prev, curr) => Math.max(prev, measureTextWidth(font, curr)), 0);
cache[text] = width;
return width;
}
/**
* @param {TextState} baseStyle Base style.
* @param {Array<string>} chunks Text chunks to measure.
* @return {{width: number, height: number, widths: Array<number>, heights: Array<number>, lineWidths: Array<number>}}} Text metrics.
*/
export function getTextDimensions(baseStyle, chunks) {
const widths = [];
const heights = [];
const lineWidths = [];
let width = 0;
let lineWidth = 0;
let height = 0;
let lineHeight = 0;
for (let i = 0, ii = chunks.length; i <= ii; i += 2) {
const text = chunks[i];
if (text === '\n' || i === ii) {
width = Math.max(width, lineWidth);
lineWidths.push(lineWidth);
lineWidth = 0;
height += lineHeight;
continue;
}
const font = chunks[i + 1] || baseStyle.font;
const currentWidth = measureTextWidth(font, text);
widths.push(currentWidth);
lineWidth += currentWidth;
const currentHeight = measureTextHeight(font);
heights.push(currentHeight);
lineHeight = Math.max(lineHeight, currentHeight);
}
return {width, height, widths, heights, lineWidths};
}
/**
* @param {CanvasRenderingContext2D} context Context.
* @param {number} rotation Rotation.
* @param {number} offsetX X offset.
* @param {number} offsetY Y offset.
*/
export function rotateAtOffset(context, rotation, offsetX, offsetY) {
if (rotation !== 0) {
context.translate(offsetX, offsetY);
context.rotate(rotation);
context.translate(-offsetX, -offsetY);
}
}
/**
* @param {CanvasRenderingContext2D} context Context.
* @param {import("../transform.js").Transform|null} transform Transform.
* @param {number} opacity Opacity.
* @param {Label|HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} labelOrImage Label.
* @param {number} originX Origin X.
* @param {number} originY Origin Y.
* @param {number} w Width.
* @param {number} h Height.
* @param {number} x X.
* @param {number} y Y.
* @param {import("../size.js").Size} scale Scale.
*/
export function drawImageOrLabel(
context,
transform,
opacity,
labelOrImage,
originX,
originY,
w,
h,
x,
y,
scale
) {
context.save();
if (opacity !== 1) {
context.globalAlpha *= opacity;
}
if (transform) {
context.setTransform.apply(context, transform);
}
if (/** @type {*} */ (labelOrImage).contextInstructions) {
// label
context.translate(x, y);
context.scale(scale[0], scale[1]);
executeLabelInstructions(/** @type {Label} */ (labelOrImage), context);
} else if (scale[0] < 0 || scale[1] < 0) {
// flipped image
context.translate(x, y);
context.scale(scale[0], scale[1]);
context.drawImage(
/** @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} */ (
labelOrImage
),
originX,
originY,
w,
h,
0,
0,
w,
h
);
} else {
// if image not flipped translate and scale can be avoided
context.drawImage(
/** @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} */ (
labelOrImage
),
originX,
originY,
w,
h,
x,
y,
w * scale[0],
h * scale[1]
);
}
context.restore();
}
/**
* @param {Label} label Label.
* @param {CanvasRenderingContext2D} context Context.
*/
function executeLabelInstructions(label, context) {
const contextInstructions = label.contextInstructions;
for (let i = 0, ii = contextInstructions.length; i < ii; i += 2) {
if (Array.isArray(contextInstructions[i + 1])) {
context[contextInstructions[i]].apply(
context,
contextInstructions[i + 1]
);
} else {
context[contextInstructions[i]] = contextInstructions[i + 1];
}
}
}

651
node_modules/ol/src/render/canvas/Builder.js generated vendored Normal file
View File

@@ -0,0 +1,651 @@
/**
* @module ol/render/canvas/Builder
*/
import CanvasInstruction from './Instruction.js';
import Relationship from '../../extent/Relationship.js';
import VectorContext from '../VectorContext.js';
import {asColorLike} from '../../colorlike.js';
import {
buffer,
clone,
containsCoordinate,
coordinateRelationship,
} from '../../extent.js';
import {
defaultFillStyle,
defaultLineCap,
defaultLineDash,
defaultLineDashOffset,
defaultLineJoin,
defaultLineWidth,
defaultMiterLimit,
defaultStrokeStyle,
} from '../canvas.js';
import {equals, reverseSubArray} from '../../array.js';
import {
inflateCoordinates,
inflateCoordinatesArray,
inflateMultiCoordinatesArray,
} from '../../geom/flat/inflate.js';
class CanvasBuilder extends VectorContext {
/**
* @param {number} tolerance Tolerance.
* @param {import("../../extent.js").Extent} maxExtent Maximum extent.
* @param {number} resolution Resolution.
* @param {number} pixelRatio Pixel ratio.
*/
constructor(tolerance, maxExtent, resolution, pixelRatio) {
super();
/**
* @protected
* @type {number}
*/
this.tolerance = tolerance;
/**
* @protected
* @const
* @type {import("../../extent.js").Extent}
*/
this.maxExtent = maxExtent;
/**
* @protected
* @type {number}
*/
this.pixelRatio = pixelRatio;
/**
* @protected
* @type {number}
*/
this.maxLineWidth = 0;
/**
* @protected
* @const
* @type {number}
*/
this.resolution = resolution;
/**
* @private
* @type {Array<*>}
*/
this.beginGeometryInstruction1_ = null;
/**
* @private
* @type {Array<*>}
*/
this.beginGeometryInstruction2_ = null;
/**
* @private
* @type {import("../../extent.js").Extent}
*/
this.bufferedMaxExtent_ = null;
/**
* @protected
* @type {Array<*>}
*/
this.instructions = [];
/**
* @protected
* @type {Array<number>}
*/
this.coordinates = [];
/**
* @private
* @type {import("../../coordinate.js").Coordinate}
*/
this.tmpCoordinate_ = [];
/**
* @protected
* @type {Array<*>}
*/
this.hitDetectionInstructions = [];
/**
* @protected
* @type {import("../canvas.js").FillStrokeState}
*/
this.state = /** @type {import("../canvas.js").FillStrokeState} */ ({});
}
/**
* @protected
* @param {Array<number>} dashArray Dash array.
* @return {Array<number>} Dash array with pixel ratio applied
*/
applyPixelRatio(dashArray) {
const pixelRatio = this.pixelRatio;
return pixelRatio == 1
? dashArray
: dashArray.map(function (dash) {
return dash * pixelRatio;
});
}
/**
* @param {Array<number>} flatCoordinates Flat coordinates.
* @param {number} stride Stride.
* @protected
* @return {number} My end
*/
appendFlatPointCoordinates(flatCoordinates, stride) {
const extent = this.getBufferedMaxExtent();
const tmpCoord = this.tmpCoordinate_;
const coordinates = this.coordinates;
let myEnd = coordinates.length;
for (let i = 0, ii = flatCoordinates.length; i < ii; i += stride) {
tmpCoord[0] = flatCoordinates[i];
tmpCoord[1] = flatCoordinates[i + 1];
if (containsCoordinate(extent, tmpCoord)) {
coordinates[myEnd++] = tmpCoord[0];
coordinates[myEnd++] = tmpCoord[1];
}
}
return myEnd;
}
/**
* @param {Array<number>} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {number} end End.
* @param {number} stride Stride.
* @param {boolean} closed Last input coordinate equals first.
* @param {boolean} skipFirst Skip first coordinate.
* @protected
* @return {number} My end.
*/
appendFlatLineCoordinates(
flatCoordinates,
offset,
end,
stride,
closed,
skipFirst
) {
const coordinates = this.coordinates;
let myEnd = coordinates.length;
const extent = this.getBufferedMaxExtent();
if (skipFirst) {
offset += stride;
}
let lastXCoord = flatCoordinates[offset];
let lastYCoord = flatCoordinates[offset + 1];
const nextCoord = this.tmpCoordinate_;
let skipped = true;
let i, lastRel, nextRel;
for (i = offset + stride; i < end; i += stride) {
nextCoord[0] = flatCoordinates[i];
nextCoord[1] = flatCoordinates[i + 1];
nextRel = coordinateRelationship(extent, nextCoord);
if (nextRel !== lastRel) {
if (skipped) {
coordinates[myEnd++] = lastXCoord;
coordinates[myEnd++] = lastYCoord;
skipped = false;
}
coordinates[myEnd++] = nextCoord[0];
coordinates[myEnd++] = nextCoord[1];
} else if (nextRel === Relationship.INTERSECTING) {
coordinates[myEnd++] = nextCoord[0];
coordinates[myEnd++] = nextCoord[1];
skipped = false;
} else {
skipped = true;
}
lastXCoord = nextCoord[0];
lastYCoord = nextCoord[1];
lastRel = nextRel;
}
// Last coordinate equals first or only one point to append:
if ((closed && skipped) || i === offset + stride) {
coordinates[myEnd++] = lastXCoord;
coordinates[myEnd++] = lastYCoord;
}
return myEnd;
}
/**
* @param {Array<number>} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {Array<number>} ends Ends.
* @param {number} stride Stride.
* @param {Array<number>} builderEnds Builder ends.
* @return {number} Offset.
*/
drawCustomCoordinates_(flatCoordinates, offset, ends, stride, builderEnds) {
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
const builderEnd = this.appendFlatLineCoordinates(
flatCoordinates,
offset,
end,
stride,
false,
false
);
builderEnds.push(builderEnd);
offset = end;
}
return offset;
}
/**
* @param {import("../../geom/SimpleGeometry.js").default} geometry Geometry.
* @param {import("../../Feature.js").FeatureLike} feature Feature.
* @param {Function} renderer Renderer.
* @param {Function} hitDetectionRenderer Renderer.
*/
drawCustom(geometry, feature, renderer, hitDetectionRenderer) {
this.beginGeometry(geometry, feature);
const type = geometry.getType();
const stride = geometry.getStride();
const builderBegin = this.coordinates.length;
let flatCoordinates, builderEnd, builderEnds, builderEndss;
let offset;
switch (type) {
case 'MultiPolygon':
flatCoordinates =
/** @type {import("../../geom/MultiPolygon.js").default} */ (
geometry
).getOrientedFlatCoordinates();
builderEndss = [];
const endss =
/** @type {import("../../geom/MultiPolygon.js").default} */ (
geometry
).getEndss();
offset = 0;
for (let i = 0, ii = endss.length; i < ii; ++i) {
const myEnds = [];
offset = this.drawCustomCoordinates_(
flatCoordinates,
offset,
endss[i],
stride,
myEnds
);
builderEndss.push(myEnds);
}
this.instructions.push([
CanvasInstruction.CUSTOM,
builderBegin,
builderEndss,
geometry,
renderer,
inflateMultiCoordinatesArray,
]);
this.hitDetectionInstructions.push([
CanvasInstruction.CUSTOM,
builderBegin,
builderEndss,
geometry,
hitDetectionRenderer || renderer,
inflateMultiCoordinatesArray,
]);
break;
case 'Polygon':
case 'MultiLineString':
builderEnds = [];
flatCoordinates =
type == 'Polygon'
? /** @type {import("../../geom/Polygon.js").default} */ (
geometry
).getOrientedFlatCoordinates()
: geometry.getFlatCoordinates();
offset = this.drawCustomCoordinates_(
flatCoordinates,
0,
/** @type {import("../../geom/Polygon.js").default|import("../../geom/MultiLineString.js").default} */ (
geometry
).getEnds(),
stride,
builderEnds
);
this.instructions.push([
CanvasInstruction.CUSTOM,
builderBegin,
builderEnds,
geometry,
renderer,
inflateCoordinatesArray,
]);
this.hitDetectionInstructions.push([
CanvasInstruction.CUSTOM,
builderBegin,
builderEnds,
geometry,
hitDetectionRenderer || renderer,
inflateCoordinatesArray,
]);
break;
case 'LineString':
case 'Circle':
flatCoordinates = geometry.getFlatCoordinates();
builderEnd = this.appendFlatLineCoordinates(
flatCoordinates,
0,
flatCoordinates.length,
stride,
false,
false
);
this.instructions.push([
CanvasInstruction.CUSTOM,
builderBegin,
builderEnd,
geometry,
renderer,
inflateCoordinates,
]);
this.hitDetectionInstructions.push([
CanvasInstruction.CUSTOM,
builderBegin,
builderEnd,
geometry,
hitDetectionRenderer || renderer,
inflateCoordinates,
]);
break;
case 'MultiPoint':
flatCoordinates = geometry.getFlatCoordinates();
builderEnd = this.appendFlatPointCoordinates(flatCoordinates, stride);
if (builderEnd > builderBegin) {
this.instructions.push([
CanvasInstruction.CUSTOM,
builderBegin,
builderEnd,
geometry,
renderer,
inflateCoordinates,
]);
this.hitDetectionInstructions.push([
CanvasInstruction.CUSTOM,
builderBegin,
builderEnd,
geometry,
hitDetectionRenderer || renderer,
inflateCoordinates,
]);
}
break;
case 'Point':
flatCoordinates = geometry.getFlatCoordinates();
this.coordinates.push(flatCoordinates[0], flatCoordinates[1]);
builderEnd = this.coordinates.length;
this.instructions.push([
CanvasInstruction.CUSTOM,
builderBegin,
builderEnd,
geometry,
renderer,
]);
this.hitDetectionInstructions.push([
CanvasInstruction.CUSTOM,
builderBegin,
builderEnd,
geometry,
hitDetectionRenderer || renderer,
]);
break;
default:
}
this.endGeometry(feature);
}
/**
* @protected
* @param {import("../../geom/Geometry").default|import("../Feature.js").default} geometry The geometry.
* @param {import("../../Feature.js").FeatureLike} feature Feature.
*/
beginGeometry(geometry, feature) {
this.beginGeometryInstruction1_ = [
CanvasInstruction.BEGIN_GEOMETRY,
feature,
0,
geometry,
];
this.instructions.push(this.beginGeometryInstruction1_);
this.beginGeometryInstruction2_ = [
CanvasInstruction.BEGIN_GEOMETRY,
feature,
0,
geometry,
];
this.hitDetectionInstructions.push(this.beginGeometryInstruction2_);
}
/**
* @return {import("../canvas.js").SerializableInstructions} the serializable instructions.
*/
finish() {
return {
instructions: this.instructions,
hitDetectionInstructions: this.hitDetectionInstructions,
coordinates: this.coordinates,
};
}
/**
* Reverse the hit detection instructions.
*/
reverseHitDetectionInstructions() {
const hitDetectionInstructions = this.hitDetectionInstructions;
// step 1 - reverse array
hitDetectionInstructions.reverse();
// step 2 - reverse instructions within geometry blocks
let i;
const n = hitDetectionInstructions.length;
let instruction;
let type;
let begin = -1;
for (i = 0; i < n; ++i) {
instruction = hitDetectionInstructions[i];
type = /** @type {import("./Instruction.js").default} */ (instruction[0]);
if (type == CanvasInstruction.END_GEOMETRY) {
begin = i;
} else if (type == CanvasInstruction.BEGIN_GEOMETRY) {
instruction[2] = i;
reverseSubArray(this.hitDetectionInstructions, begin, i);
begin = -1;
}
}
}
/**
* @param {import("../../style/Fill.js").default} fillStyle Fill style.
* @param {import("../../style/Stroke.js").default} strokeStyle Stroke style.
*/
setFillStrokeStyle(fillStyle, strokeStyle) {
const state = this.state;
if (fillStyle) {
const fillStyleColor = fillStyle.getColor();
state.fillStyle = asColorLike(
fillStyleColor ? fillStyleColor : defaultFillStyle
);
} else {
state.fillStyle = undefined;
}
if (strokeStyle) {
const strokeStyleColor = strokeStyle.getColor();
state.strokeStyle = asColorLike(
strokeStyleColor ? strokeStyleColor : defaultStrokeStyle
);
const strokeStyleLineCap = strokeStyle.getLineCap();
state.lineCap =
strokeStyleLineCap !== undefined ? strokeStyleLineCap : defaultLineCap;
const strokeStyleLineDash = strokeStyle.getLineDash();
state.lineDash = strokeStyleLineDash
? strokeStyleLineDash.slice()
: defaultLineDash;
const strokeStyleLineDashOffset = strokeStyle.getLineDashOffset();
state.lineDashOffset = strokeStyleLineDashOffset
? strokeStyleLineDashOffset
: defaultLineDashOffset;
const strokeStyleLineJoin = strokeStyle.getLineJoin();
state.lineJoin =
strokeStyleLineJoin !== undefined
? strokeStyleLineJoin
: defaultLineJoin;
const strokeStyleWidth = strokeStyle.getWidth();
state.lineWidth =
strokeStyleWidth !== undefined ? strokeStyleWidth : defaultLineWidth;
const strokeStyleMiterLimit = strokeStyle.getMiterLimit();
state.miterLimit =
strokeStyleMiterLimit !== undefined
? strokeStyleMiterLimit
: defaultMiterLimit;
if (state.lineWidth > this.maxLineWidth) {
this.maxLineWidth = state.lineWidth;
// invalidate the buffered max extent cache
this.bufferedMaxExtent_ = null;
}
} else {
state.strokeStyle = undefined;
state.lineCap = undefined;
state.lineDash = null;
state.lineDashOffset = undefined;
state.lineJoin = undefined;
state.lineWidth = undefined;
state.miterLimit = undefined;
}
}
/**
* @param {import("../canvas.js").FillStrokeState} state State.
* @return {Array<*>} Fill instruction.
*/
createFill(state) {
const fillStyle = state.fillStyle;
/** @type {Array<*>} */
const fillInstruction = [CanvasInstruction.SET_FILL_STYLE, fillStyle];
if (typeof fillStyle !== 'string') {
// Fill is a pattern or gradient - align it!
fillInstruction.push(true);
}
return fillInstruction;
}
/**
* @param {import("../canvas.js").FillStrokeState} state State.
*/
applyStroke(state) {
this.instructions.push(this.createStroke(state));
}
/**
* @param {import("../canvas.js").FillStrokeState} state State.
* @return {Array<*>} Stroke instruction.
*/
createStroke(state) {
return [
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle,
state.lineWidth * this.pixelRatio,
state.lineCap,
state.lineJoin,
state.miterLimit,
this.applyPixelRatio(state.lineDash),
state.lineDashOffset * this.pixelRatio,
];
}
/**
* @param {import("../canvas.js").FillStrokeState} state State.
* @param {function(this:CanvasBuilder, import("../canvas.js").FillStrokeState):Array<*>} createFill Create fill.
*/
updateFillStyle(state, createFill) {
const fillStyle = state.fillStyle;
if (typeof fillStyle !== 'string' || state.currentFillStyle != fillStyle) {
if (fillStyle !== undefined) {
this.instructions.push(createFill.call(this, state));
}
state.currentFillStyle = fillStyle;
}
}
/**
* @param {import("../canvas.js").FillStrokeState} state State.
* @param {function(this:CanvasBuilder, import("../canvas.js").FillStrokeState): void} applyStroke Apply stroke.
*/
updateStrokeStyle(state, applyStroke) {
const strokeStyle = state.strokeStyle;
const lineCap = state.lineCap;
const lineDash = state.lineDash;
const lineDashOffset = state.lineDashOffset;
const lineJoin = state.lineJoin;
const lineWidth = state.lineWidth;
const miterLimit = state.miterLimit;
if (
state.currentStrokeStyle != strokeStyle ||
state.currentLineCap != lineCap ||
(lineDash != state.currentLineDash &&
!equals(state.currentLineDash, lineDash)) ||
state.currentLineDashOffset != lineDashOffset ||
state.currentLineJoin != lineJoin ||
state.currentLineWidth != lineWidth ||
state.currentMiterLimit != miterLimit
) {
if (strokeStyle !== undefined) {
applyStroke.call(this, state);
}
state.currentStrokeStyle = strokeStyle;
state.currentLineCap = lineCap;
state.currentLineDash = lineDash;
state.currentLineDashOffset = lineDashOffset;
state.currentLineJoin = lineJoin;
state.currentLineWidth = lineWidth;
state.currentMiterLimit = miterLimit;
}
}
/**
* @param {import("../../Feature.js").FeatureLike} feature Feature.
*/
endGeometry(feature) {
this.beginGeometryInstruction1_[2] = this.instructions.length;
this.beginGeometryInstruction1_ = null;
this.beginGeometryInstruction2_[2] = this.hitDetectionInstructions.length;
this.beginGeometryInstruction2_ = null;
const endGeometryInstruction = [CanvasInstruction.END_GEOMETRY, feature];
this.instructions.push(endGeometryInstruction);
this.hitDetectionInstructions.push(endGeometryInstruction);
}
/**
* Get the buffered rendering extent. Rendering will be clipped to the extent
* provided to the constructor. To account for symbolizers that may intersect
* this extent, we calculate a buffered extent (e.g. based on stroke width).
* @return {import("../../extent.js").Extent} The buffered rendering extent.
* @protected
*/
getBufferedMaxExtent() {
if (!this.bufferedMaxExtent_) {
this.bufferedMaxExtent_ = clone(this.maxExtent);
if (this.maxLineWidth > 0) {
const width = (this.resolution * (this.maxLineWidth + 1)) / 2;
buffer(this.bufferedMaxExtent_, width, this.bufferedMaxExtent_);
}
}
return this.bufferedMaxExtent_;
}
}
export default CanvasBuilder;

105
node_modules/ol/src/render/canvas/BuilderGroup.js generated vendored Normal file
View File

@@ -0,0 +1,105 @@
/**
* @module ol/render/canvas/BuilderGroup
*/
import Builder from './Builder.js';
import ImageBuilder from './ImageBuilder.js';
import LineStringBuilder from './LineStringBuilder.js';
import PolygonBuilder from './PolygonBuilder.js';
import TextBuilder from './TextBuilder.js';
/**
* @type {Object<import("../canvas.js").BuilderType, typeof Builder>}
*/
const BATCH_CONSTRUCTORS = {
'Circle': PolygonBuilder,
'Default': Builder,
'Image': ImageBuilder,
'LineString': LineStringBuilder,
'Polygon': PolygonBuilder,
'Text': TextBuilder,
};
class BuilderGroup {
/**
* @param {number} tolerance Tolerance.
* @param {import("../../extent.js").Extent} maxExtent Max extent.
* @param {number} resolution Resolution.
* @param {number} pixelRatio Pixel ratio.
*/
constructor(tolerance, maxExtent, resolution, pixelRatio) {
/**
* @private
* @type {number}
*/
this.tolerance_ = tolerance;
/**
* @private
* @type {import("../../extent.js").Extent}
*/
this.maxExtent_ = maxExtent;
/**
* @private
* @type {number}
*/
this.pixelRatio_ = pixelRatio;
/**
* @private
* @type {number}
*/
this.resolution_ = resolution;
/**
* @private
* @type {!Object<string, !Object<import("../canvas.js").BuilderType, Builder>>}
*/
this.buildersByZIndex_ = {};
}
/**
* @return {!Object<string, !Object<import("../canvas.js").BuilderType, import("./Builder.js").SerializableInstructions>>} The serializable instructions
*/
finish() {
const builderInstructions = {};
for (const zKey in this.buildersByZIndex_) {
builderInstructions[zKey] = builderInstructions[zKey] || {};
const builders = this.buildersByZIndex_[zKey];
for (const builderKey in builders) {
const builderInstruction = builders[builderKey].finish();
builderInstructions[zKey][builderKey] = builderInstruction;
}
}
return builderInstructions;
}
/**
* @param {number|undefined} zIndex Z index.
* @param {import("../canvas.js").BuilderType} builderType Replay type.
* @return {import("../VectorContext.js").default} Replay.
*/
getBuilder(zIndex, builderType) {
const zIndexKey = zIndex !== undefined ? zIndex.toString() : '0';
let replays = this.buildersByZIndex_[zIndexKey];
if (replays === undefined) {
replays = {};
this.buildersByZIndex_[zIndexKey] = replays;
}
let replay = replays[builderType];
if (replay === undefined) {
const Constructor = BATCH_CONSTRUCTORS[builderType];
replay = new Constructor(
this.tolerance_,
this.maxExtent_,
this.resolution_,
this.pixelRatio_
);
replays[builderType] = replay;
}
return replay;
}
}
export default BuilderGroup;

1265
node_modules/ol/src/render/canvas/Executor.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

422
node_modules/ol/src/render/canvas/ExecutorGroup.js generated vendored Normal file
View File

@@ -0,0 +1,422 @@
/**
* @module ol/render/canvas/ExecutorGroup
*/
import Executor from './Executor.js';
import {buffer, createEmpty, extendCoordinate} from '../../extent.js';
import {
compose as composeTransform,
create as createTransform,
} from '../../transform.js';
import {createCanvasContext2D} from '../../dom.js';
import {isEmpty} from '../../obj.js';
import {numberSafeCompareFunction} from '../../array.js';
import {transform2D} from '../../geom/flat/transform.js';
/**
* @const
* @type {Array<import("../canvas.js").BuilderType>}
*/
const ORDER = ['Polygon', 'Circle', 'LineString', 'Image', 'Text', 'Default'];
class ExecutorGroup {
/**
* @param {import("../../extent.js").Extent} maxExtent Max extent for clipping. When a
* `maxExtent` was set on the Builder for this executor group, the same `maxExtent`
* should be set here, unless the target context does not exceed that extent (which
* can be the case when rendering to tiles).
* @param {number} resolution Resolution.
* @param {number} pixelRatio Pixel ratio.
* @param {boolean} overlaps The executor group can have overlapping geometries.
* @param {!Object<string, !Object<import("../canvas.js").BuilderType, import("../canvas.js").SerializableInstructions>>} allInstructions
* The serializable instructions.
* @param {number} [opt_renderBuffer] Optional rendering buffer.
*/
constructor(
maxExtent,
resolution,
pixelRatio,
overlaps,
allInstructions,
opt_renderBuffer
) {
/**
* @private
* @type {import("../../extent.js").Extent}
*/
this.maxExtent_ = maxExtent;
/**
* @private
* @type {boolean}
*/
this.overlaps_ = overlaps;
/**
* @private
* @type {number}
*/
this.pixelRatio_ = pixelRatio;
/**
* @private
* @type {number}
*/
this.resolution_ = resolution;
/**
* @private
* @type {number|undefined}
*/
this.renderBuffer_ = opt_renderBuffer;
/**
* @private
* @type {!Object<string, !Object<import("../canvas.js").BuilderType, import("./Executor").default>>}
*/
this.executorsByZIndex_ = {};
/**
* @private
* @type {CanvasRenderingContext2D}
*/
this.hitDetectionContext_ = null;
/**
* @private
* @type {import("../../transform.js").Transform}
*/
this.hitDetectionTransform_ = createTransform();
this.createExecutors_(allInstructions);
}
/**
* @param {CanvasRenderingContext2D} context Context.
* @param {import("../../transform.js").Transform} transform Transform.
*/
clip(context, transform) {
const flatClipCoords = this.getClipCoords(transform);
context.beginPath();
context.moveTo(flatClipCoords[0], flatClipCoords[1]);
context.lineTo(flatClipCoords[2], flatClipCoords[3]);
context.lineTo(flatClipCoords[4], flatClipCoords[5]);
context.lineTo(flatClipCoords[6], flatClipCoords[7]);
context.clip();
}
/**
* Create executors and populate them using the provided instructions.
* @private
* @param {!Object<string, !Object<import("../canvas.js").BuilderType, import("../canvas.js").SerializableInstructions>>} allInstructions The serializable instructions
*/
createExecutors_(allInstructions) {
for (const zIndex in allInstructions) {
let executors = this.executorsByZIndex_[zIndex];
if (executors === undefined) {
executors = {};
this.executorsByZIndex_[zIndex] = executors;
}
const instructionByZindex = allInstructions[zIndex];
for (const builderType in instructionByZindex) {
const instructions = instructionByZindex[builderType];
executors[builderType] = new Executor(
this.resolution_,
this.pixelRatio_,
this.overlaps_,
instructions
);
}
}
}
/**
* @param {Array<import("../canvas.js").BuilderType>} executors Executors.
* @return {boolean} Has executors of the provided types.
*/
hasExecutors(executors) {
for (const zIndex in this.executorsByZIndex_) {
const candidates = this.executorsByZIndex_[zIndex];
for (let i = 0, ii = executors.length; i < ii; ++i) {
if (executors[i] in candidates) {
return true;
}
}
}
return false;
}
/**
* @param {import("../../coordinate.js").Coordinate} coordinate Coordinate.
* @param {number} resolution Resolution.
* @param {number} rotation Rotation.
* @param {number} hitTolerance Hit tolerance in pixels.
* @param {function(import("../../Feature.js").FeatureLike, import("../../geom/SimpleGeometry.js").default, number): T} callback Feature callback.
* @param {Array<import("../../Feature.js").FeatureLike>} declutteredFeatures Decluttered features.
* @return {T|undefined} Callback result.
* @template T
*/
forEachFeatureAtCoordinate(
coordinate,
resolution,
rotation,
hitTolerance,
callback,
declutteredFeatures
) {
hitTolerance = Math.round(hitTolerance);
const contextSize = hitTolerance * 2 + 1;
const transform = composeTransform(
this.hitDetectionTransform_,
hitTolerance + 0.5,
hitTolerance + 0.5,
1 / resolution,
-1 / resolution,
-rotation,
-coordinate[0],
-coordinate[1]
);
const newContext = !this.hitDetectionContext_;
if (newContext) {
this.hitDetectionContext_ = createCanvasContext2D(
contextSize,
contextSize
);
}
const context = this.hitDetectionContext_;
if (
context.canvas.width !== contextSize ||
context.canvas.height !== contextSize
) {
context.canvas.width = contextSize;
context.canvas.height = contextSize;
} else if (!newContext) {
context.clearRect(0, 0, contextSize, contextSize);
}
/**
* @type {import("../../extent.js").Extent}
*/
let hitExtent;
if (this.renderBuffer_ !== undefined) {
hitExtent = createEmpty();
extendCoordinate(hitExtent, coordinate);
buffer(
hitExtent,
resolution * (this.renderBuffer_ + hitTolerance),
hitExtent
);
}
const indexes = getPixelIndexArray(hitTolerance);
let builderType;
/**
* @param {import("../../Feature.js").FeatureLike} feature Feature.
* @param {import("../../geom/SimpleGeometry.js").default} geometry Geometry.
* @return {T|undefined} Callback result.
*/
function featureCallback(feature, geometry) {
const imageData = context.getImageData(
0,
0,
contextSize,
contextSize
).data;
for (let i = 0, ii = indexes.length; i < ii; i++) {
if (imageData[indexes[i]] > 0) {
if (
!declutteredFeatures ||
(builderType !== 'Image' && builderType !== 'Text') ||
declutteredFeatures.indexOf(feature) !== -1
) {
const idx = (indexes[i] - 3) / 4;
const x = hitTolerance - (idx % contextSize);
const y = hitTolerance - ((idx / contextSize) | 0);
const result = callback(feature, geometry, x * x + y * y);
if (result) {
return result;
}
}
context.clearRect(0, 0, contextSize, contextSize);
break;
}
}
return undefined;
}
/** @type {Array<number>} */
const zs = Object.keys(this.executorsByZIndex_).map(Number);
zs.sort(numberSafeCompareFunction);
let i, j, executors, executor, result;
for (i = zs.length - 1; i >= 0; --i) {
const zIndexKey = zs[i].toString();
executors = this.executorsByZIndex_[zIndexKey];
for (j = ORDER.length - 1; j >= 0; --j) {
builderType = ORDER[j];
executor = executors[builderType];
if (executor !== undefined) {
result = executor.executeHitDetection(
context,
transform,
rotation,
featureCallback,
hitExtent
);
if (result) {
return result;
}
}
}
}
return undefined;
}
/**
* @param {import("../../transform.js").Transform} transform Transform.
* @return {Array<number>|null} Clip coordinates.
*/
getClipCoords(transform) {
const maxExtent = this.maxExtent_;
if (!maxExtent) {
return null;
}
const minX = maxExtent[0];
const minY = maxExtent[1];
const maxX = maxExtent[2];
const maxY = maxExtent[3];
const flatClipCoords = [minX, minY, minX, maxY, maxX, maxY, maxX, minY];
transform2D(flatClipCoords, 0, 8, 2, transform, flatClipCoords);
return flatClipCoords;
}
/**
* @return {boolean} Is empty.
*/
isEmpty() {
return isEmpty(this.executorsByZIndex_);
}
/**
* @param {CanvasRenderingContext2D} context Context.
* @param {number} contextScale Scale of the context.
* @param {import("../../transform.js").Transform} transform Transform.
* @param {number} viewRotation View rotation.
* @param {boolean} snapToPixel Snap point symbols and test to integer pixel.
* @param {Array<import("../canvas.js").BuilderType>} [opt_builderTypes] Ordered replay types to replay.
* Default is {@link module:ol/render/replay~ORDER}
* @param {import("rbush").default} [opt_declutterTree] Declutter tree.
*/
execute(
context,
contextScale,
transform,
viewRotation,
snapToPixel,
opt_builderTypes,
opt_declutterTree
) {
/** @type {Array<number>} */
const zs = Object.keys(this.executorsByZIndex_).map(Number);
zs.sort(numberSafeCompareFunction);
// setup clipping so that the parts of over-simplified geometries are not
// visible outside the current extent when panning
if (this.maxExtent_) {
context.save();
this.clip(context, transform);
}
const builderTypes = opt_builderTypes ? opt_builderTypes : ORDER;
let i, ii, j, jj, replays, replay;
if (opt_declutterTree) {
zs.reverse();
}
for (i = 0, ii = zs.length; i < ii; ++i) {
const zIndexKey = zs[i].toString();
replays = this.executorsByZIndex_[zIndexKey];
for (j = 0, jj = builderTypes.length; j < jj; ++j) {
const builderType = builderTypes[j];
replay = replays[builderType];
if (replay !== undefined) {
replay.execute(
context,
contextScale,
transform,
viewRotation,
snapToPixel,
opt_declutterTree
);
}
}
}
if (this.maxExtent_) {
context.restore();
}
}
}
/**
* This cache is used to store arrays of indexes for calculated pixel circles
* to increase performance.
* It is a static property to allow each Replaygroup to access it.
* @type {Object<number, Array<number>>}
*/
const circlePixelIndexArrayCache = {};
/**
* This methods creates an array with indexes of all pixels within a circle,
* ordered by how close they are to the center.
* A cache is used to increase performance.
* @param {number} radius Radius.
* @return {Array<number>} An array with indexes within a circle.
*/
export function getPixelIndexArray(radius) {
if (circlePixelIndexArrayCache[radius] !== undefined) {
return circlePixelIndexArrayCache[radius];
}
const size = radius * 2 + 1;
const maxDistanceSq = radius * radius;
const distances = new Array(maxDistanceSq + 1);
for (let i = 0; i <= radius; ++i) {
for (let j = 0; j <= radius; ++j) {
const distanceSq = i * i + j * j;
if (distanceSq > maxDistanceSq) {
break;
}
let distance = distances[distanceSq];
if (!distance) {
distance = [];
distances[distanceSq] = distance;
}
distance.push(((radius + i) * size + (radius + j)) * 4 + 3);
if (i > 0) {
distance.push(((radius - i) * size + (radius + j)) * 4 + 3);
}
if (j > 0) {
distance.push(((radius + i) * size + (radius - j)) * 4 + 3);
if (i > 0) {
distance.push(((radius - i) * size + (radius - j)) * 4 + 3);
}
}
}
}
const pixelIndex = [];
for (let i = 0, ii = distances.length; i < ii; ++i) {
if (distances[i]) {
pixelIndex.push(...distances[i]);
}
}
circlePixelIndexArrayCache[radius] = pixelIndex;
return pixelIndex;
}
export default ExecutorGroup;

271
node_modules/ol/src/render/canvas/ImageBuilder.js generated vendored Normal file
View File

@@ -0,0 +1,271 @@
/**
* @module ol/render/canvas/ImageBuilder
*/
import CanvasBuilder from './Builder.js';
import CanvasInstruction from './Instruction.js';
class CanvasImageBuilder extends CanvasBuilder {
/**
* @param {number} tolerance Tolerance.
* @param {import("../../extent.js").Extent} maxExtent Maximum extent.
* @param {number} resolution Resolution.
* @param {number} pixelRatio Pixel ratio.
*/
constructor(tolerance, maxExtent, resolution, pixelRatio) {
super(tolerance, maxExtent, resolution, pixelRatio);
/**
* @private
* @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement}
*/
this.hitDetectionImage_ = null;
/**
* @private
* @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement}
*/
this.image_ = null;
/**
* @private
* @type {number|undefined}
*/
this.imagePixelRatio_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.anchorX_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.anchorY_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.height_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.opacity_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.originX_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.originY_ = undefined;
/**
* @private
* @type {boolean|undefined}
*/
this.rotateWithView_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.rotation_ = undefined;
/**
* @private
* @type {import("../../size.js").Size|undefined}
*/
this.scale_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.width_ = undefined;
/**
* @private
* @type {"declutter"|"obstacle"|"none"|undefined}
*/
this.declutterMode_ = undefined;
/**
* Data shared with a text builder for combined decluttering.
* @private
* @type {import("../canvas.js").DeclutterImageWithText}
*/
this.declutterImageWithText_ = undefined;
}
/**
* @param {import("../../geom/Point.js").default|import("../Feature.js").default} pointGeometry Point geometry.
* @param {import("../../Feature.js").FeatureLike} feature Feature.
*/
drawPoint(pointGeometry, feature) {
if (!this.image_) {
return;
}
this.beginGeometry(pointGeometry, feature);
const flatCoordinates = pointGeometry.getFlatCoordinates();
const stride = pointGeometry.getStride();
const myBegin = this.coordinates.length;
const myEnd = this.appendFlatPointCoordinates(flatCoordinates, stride);
this.instructions.push([
CanvasInstruction.DRAW_IMAGE,
myBegin,
myEnd,
this.image_,
// Remaining arguments to DRAW_IMAGE are in alphabetical order
this.anchorX_ * this.imagePixelRatio_,
this.anchorY_ * this.imagePixelRatio_,
Math.ceil(this.height_ * this.imagePixelRatio_),
this.opacity_,
this.originX_ * this.imagePixelRatio_,
this.originY_ * this.imagePixelRatio_,
this.rotateWithView_,
this.rotation_,
[
(this.scale_[0] * this.pixelRatio) / this.imagePixelRatio_,
(this.scale_[1] * this.pixelRatio) / this.imagePixelRatio_,
],
Math.ceil(this.width_ * this.imagePixelRatio_),
this.declutterMode_,
this.declutterImageWithText_,
]);
this.hitDetectionInstructions.push([
CanvasInstruction.DRAW_IMAGE,
myBegin,
myEnd,
this.hitDetectionImage_,
// Remaining arguments to DRAW_IMAGE are in alphabetical order
this.anchorX_,
this.anchorY_,
this.height_,
this.opacity_,
this.originX_,
this.originY_,
this.rotateWithView_,
this.rotation_,
this.scale_,
this.width_,
this.declutterMode_,
this.declutterImageWithText_,
]);
this.endGeometry(feature);
}
/**
* @param {import("../../geom/MultiPoint.js").default|import("../Feature.js").default} multiPointGeometry MultiPoint geometry.
* @param {import("../../Feature.js").FeatureLike} feature Feature.
*/
drawMultiPoint(multiPointGeometry, feature) {
if (!this.image_) {
return;
}
this.beginGeometry(multiPointGeometry, feature);
const flatCoordinates = multiPointGeometry.getFlatCoordinates();
const stride = multiPointGeometry.getStride();
const myBegin = this.coordinates.length;
const myEnd = this.appendFlatPointCoordinates(flatCoordinates, stride);
this.instructions.push([
CanvasInstruction.DRAW_IMAGE,
myBegin,
myEnd,
this.image_,
// Remaining arguments to DRAW_IMAGE are in alphabetical order
this.anchorX_ * this.imagePixelRatio_,
this.anchorY_ * this.imagePixelRatio_,
Math.ceil(this.height_ * this.imagePixelRatio_),
this.opacity_,
this.originX_ * this.imagePixelRatio_,
this.originY_ * this.imagePixelRatio_,
this.rotateWithView_,
this.rotation_,
[
(this.scale_[0] * this.pixelRatio) / this.imagePixelRatio_,
(this.scale_[1] * this.pixelRatio) / this.imagePixelRatio_,
],
Math.ceil(this.width_ * this.imagePixelRatio_),
this.declutterMode_,
this.declutterImageWithText_,
]);
this.hitDetectionInstructions.push([
CanvasInstruction.DRAW_IMAGE,
myBegin,
myEnd,
this.hitDetectionImage_,
// Remaining arguments to DRAW_IMAGE are in alphabetical order
this.anchorX_,
this.anchorY_,
this.height_,
this.opacity_,
this.originX_,
this.originY_,
this.rotateWithView_,
this.rotation_,
this.scale_,
this.width_,
this.declutterMode_,
this.declutterImageWithText_,
]);
this.endGeometry(feature);
}
/**
* @return {import("../canvas.js").SerializableInstructions} the serializable instructions.
*/
finish() {
this.reverseHitDetectionInstructions();
// FIXME this doesn't really protect us against further calls to draw*Geometry
this.anchorX_ = undefined;
this.anchorY_ = undefined;
this.hitDetectionImage_ = null;
this.image_ = null;
this.imagePixelRatio_ = undefined;
this.height_ = undefined;
this.scale_ = undefined;
this.opacity_ = undefined;
this.originX_ = undefined;
this.originY_ = undefined;
this.rotateWithView_ = undefined;
this.rotation_ = undefined;
this.width_ = undefined;
return super.finish();
}
/**
* @param {import("../../style/Image.js").default} imageStyle Image style.
* @param {Object} [opt_sharedData] Shared data.
*/
setImageStyle(imageStyle, opt_sharedData) {
const anchor = imageStyle.getAnchor();
const size = imageStyle.getSize();
const origin = imageStyle.getOrigin();
this.imagePixelRatio_ = imageStyle.getPixelRatio(this.pixelRatio);
this.anchorX_ = anchor[0];
this.anchorY_ = anchor[1];
this.hitDetectionImage_ = imageStyle.getHitDetectionImage();
this.image_ = imageStyle.getImage(this.pixelRatio);
this.height_ = size[1];
this.opacity_ = imageStyle.getOpacity();
this.originX_ = origin[0];
this.originY_ = origin[1];
this.rotateWithView_ = imageStyle.getRotateWithView();
this.rotation_ = imageStyle.getRotation();
this.scale_ = imageStyle.getScaleArray();
this.width_ = size[0];
this.declutterMode_ = imageStyle.getDeclutterMode();
this.declutterImageWithText_ = opt_sharedData;
}
}
export default CanvasImageBuilder;

1169
node_modules/ol/src/render/canvas/Immediate.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

44
node_modules/ol/src/render/canvas/Instruction.js generated vendored Normal file
View File

@@ -0,0 +1,44 @@
/**
* @module ol/render/canvas/Instruction
*/
/**
* @enum {number}
*/
const Instruction = {
BEGIN_GEOMETRY: 0,
BEGIN_PATH: 1,
CIRCLE: 2,
CLOSE_PATH: 3,
CUSTOM: 4,
DRAW_CHARS: 5,
DRAW_IMAGE: 6,
END_GEOMETRY: 7,
FILL: 8,
MOVE_TO_LINE_TO: 9,
SET_FILL_STYLE: 10,
SET_STROKE_STYLE: 11,
STROKE: 12,
};
/**
* @type {Array<Instruction>}
*/
export const fillInstruction = [Instruction.FILL];
/**
* @type {Array<Instruction>}
*/
export const strokeInstruction = [Instruction.STROKE];
/**
* @type {Array<Instruction>}
*/
export const beginPathInstruction = [Instruction.BEGIN_PATH];
/**
* @type {Array<Instruction>}
*/
export const closePathInstruction = [Instruction.CLOSE_PATH];
export default Instruction;

163
node_modules/ol/src/render/canvas/LineStringBuilder.js generated vendored Normal file
View File

@@ -0,0 +1,163 @@
/**
* @module ol/render/canvas/LineStringBuilder
*/
import CanvasBuilder from './Builder.js';
import CanvasInstruction, {
beginPathInstruction,
strokeInstruction,
} from './Instruction.js';
import {defaultLineDash, defaultLineDashOffset} from '../canvas.js';
class CanvasLineStringBuilder extends CanvasBuilder {
/**
* @param {number} tolerance Tolerance.
* @param {import("../../extent.js").Extent} maxExtent Maximum extent.
* @param {number} resolution Resolution.
* @param {number} pixelRatio Pixel ratio.
*/
constructor(tolerance, maxExtent, resolution, pixelRatio) {
super(tolerance, maxExtent, resolution, pixelRatio);
}
/**
* @param {Array<number>} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {number} end End.
* @param {number} stride Stride.
* @private
* @return {number} end.
*/
drawFlatCoordinates_(flatCoordinates, offset, end, stride) {
const myBegin = this.coordinates.length;
const myEnd = this.appendFlatLineCoordinates(
flatCoordinates,
offset,
end,
stride,
false,
false
);
const moveToLineToInstruction = [
CanvasInstruction.MOVE_TO_LINE_TO,
myBegin,
myEnd,
];
this.instructions.push(moveToLineToInstruction);
this.hitDetectionInstructions.push(moveToLineToInstruction);
return end;
}
/**
* @param {import("../../geom/LineString.js").default|import("../Feature.js").default} lineStringGeometry Line string geometry.
* @param {import("../../Feature.js").FeatureLike} feature Feature.
*/
drawLineString(lineStringGeometry, feature) {
const state = this.state;
const strokeStyle = state.strokeStyle;
const lineWidth = state.lineWidth;
if (strokeStyle === undefined || lineWidth === undefined) {
return;
}
this.updateStrokeStyle(state, this.applyStroke);
this.beginGeometry(lineStringGeometry, feature);
this.hitDetectionInstructions.push(
[
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle,
state.lineWidth,
state.lineCap,
state.lineJoin,
state.miterLimit,
defaultLineDash,
defaultLineDashOffset,
],
beginPathInstruction
);
const flatCoordinates = lineStringGeometry.getFlatCoordinates();
const stride = lineStringGeometry.getStride();
this.drawFlatCoordinates_(
flatCoordinates,
0,
flatCoordinates.length,
stride
);
this.hitDetectionInstructions.push(strokeInstruction);
this.endGeometry(feature);
}
/**
* @param {import("../../geom/MultiLineString.js").default|import("../Feature.js").default} multiLineStringGeometry MultiLineString geometry.
* @param {import("../../Feature.js").FeatureLike} feature Feature.
*/
drawMultiLineString(multiLineStringGeometry, feature) {
const state = this.state;
const strokeStyle = state.strokeStyle;
const lineWidth = state.lineWidth;
if (strokeStyle === undefined || lineWidth === undefined) {
return;
}
this.updateStrokeStyle(state, this.applyStroke);
this.beginGeometry(multiLineStringGeometry, feature);
this.hitDetectionInstructions.push(
[
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle,
state.lineWidth,
state.lineCap,
state.lineJoin,
state.miterLimit,
state.lineDash,
state.lineDashOffset,
],
beginPathInstruction
);
const ends = multiLineStringGeometry.getEnds();
const flatCoordinates = multiLineStringGeometry.getFlatCoordinates();
const stride = multiLineStringGeometry.getStride();
let offset = 0;
for (let i = 0, ii = ends.length; i < ii; ++i) {
offset = this.drawFlatCoordinates_(
flatCoordinates,
offset,
/** @type {number} */ (ends[i]),
stride
);
}
this.hitDetectionInstructions.push(strokeInstruction);
this.endGeometry(feature);
}
/**
* @return {import("../canvas.js").SerializableInstructions} the serializable instructions.
*/
finish() {
const state = this.state;
if (
state.lastStroke != undefined &&
state.lastStroke != this.coordinates.length
) {
this.instructions.push(strokeInstruction);
}
this.reverseHitDetectionInstructions();
this.state = null;
return super.finish();
}
/**
* @param {import("../canvas.js").FillStrokeState} state State.
*/
applyStroke(state) {
if (
state.lastStroke != undefined &&
state.lastStroke != this.coordinates.length
) {
this.instructions.push(strokeInstruction);
state.lastStroke = this.coordinates.length;
}
state.lastStroke = 0;
super.applyStroke(state);
this.instructions.push(beginPathInstruction);
}
}
export default CanvasLineStringBuilder;

257
node_modules/ol/src/render/canvas/PolygonBuilder.js generated vendored Normal file
View File

@@ -0,0 +1,257 @@
/**
* @module ol/render/canvas/PolygonBuilder
*/
import CanvasBuilder from './Builder.js';
import CanvasInstruction, {
beginPathInstruction,
closePathInstruction,
fillInstruction,
strokeInstruction,
} from './Instruction.js';
import {defaultFillStyle} from '../canvas.js';
import {snap} from '../../geom/flat/simplify.js';
class CanvasPolygonBuilder extends CanvasBuilder {
/**
* @param {number} tolerance Tolerance.
* @param {import("../../extent.js").Extent} maxExtent Maximum extent.
* @param {number} resolution Resolution.
* @param {number} pixelRatio Pixel ratio.
*/
constructor(tolerance, maxExtent, resolution, pixelRatio) {
super(tolerance, maxExtent, resolution, pixelRatio);
}
/**
* @param {Array<number>} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {Array<number>} ends Ends.
* @param {number} stride Stride.
* @private
* @return {number} End.
*/
drawFlatCoordinatess_(flatCoordinates, offset, ends, stride) {
const state = this.state;
const fill = state.fillStyle !== undefined;
const stroke = state.strokeStyle !== undefined;
const numEnds = ends.length;
this.instructions.push(beginPathInstruction);
this.hitDetectionInstructions.push(beginPathInstruction);
for (let i = 0; i < numEnds; ++i) {
const end = ends[i];
const myBegin = this.coordinates.length;
const myEnd = this.appendFlatLineCoordinates(
flatCoordinates,
offset,
end,
stride,
true,
!stroke
);
const moveToLineToInstruction = [
CanvasInstruction.MOVE_TO_LINE_TO,
myBegin,
myEnd,
];
this.instructions.push(moveToLineToInstruction);
this.hitDetectionInstructions.push(moveToLineToInstruction);
if (stroke) {
// Performance optimization: only call closePath() when we have a stroke.
// Otherwise the ring is closed already (see appendFlatLineCoordinates above).
this.instructions.push(closePathInstruction);
this.hitDetectionInstructions.push(closePathInstruction);
}
offset = end;
}
if (fill) {
this.instructions.push(fillInstruction);
this.hitDetectionInstructions.push(fillInstruction);
}
if (stroke) {
this.instructions.push(strokeInstruction);
this.hitDetectionInstructions.push(strokeInstruction);
}
return offset;
}
/**
* @param {import("../../geom/Circle.js").default} circleGeometry Circle geometry.
* @param {import("../../Feature.js").default} feature Feature.
*/
drawCircle(circleGeometry, feature) {
const state = this.state;
const fillStyle = state.fillStyle;
const strokeStyle = state.strokeStyle;
if (fillStyle === undefined && strokeStyle === undefined) {
return;
}
this.setFillStrokeStyles_();
this.beginGeometry(circleGeometry, feature);
if (state.fillStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_FILL_STYLE,
defaultFillStyle,
]);
}
if (state.strokeStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle,
state.lineWidth,
state.lineCap,
state.lineJoin,
state.miterLimit,
state.lineDash,
state.lineDashOffset,
]);
}
const flatCoordinates = circleGeometry.getFlatCoordinates();
const stride = circleGeometry.getStride();
const myBegin = this.coordinates.length;
this.appendFlatLineCoordinates(
flatCoordinates,
0,
flatCoordinates.length,
stride,
false,
false
);
const circleInstruction = [CanvasInstruction.CIRCLE, myBegin];
this.instructions.push(beginPathInstruction, circleInstruction);
this.hitDetectionInstructions.push(beginPathInstruction, circleInstruction);
if (state.fillStyle !== undefined) {
this.instructions.push(fillInstruction);
this.hitDetectionInstructions.push(fillInstruction);
}
if (state.strokeStyle !== undefined) {
this.instructions.push(strokeInstruction);
this.hitDetectionInstructions.push(strokeInstruction);
}
this.endGeometry(feature);
}
/**
* @param {import("../../geom/Polygon.js").default|import("../Feature.js").default} polygonGeometry Polygon geometry.
* @param {import("../../Feature.js").FeatureLike} feature Feature.
*/
drawPolygon(polygonGeometry, feature) {
const state = this.state;
const fillStyle = state.fillStyle;
const strokeStyle = state.strokeStyle;
if (fillStyle === undefined && strokeStyle === undefined) {
return;
}
this.setFillStrokeStyles_();
this.beginGeometry(polygonGeometry, feature);
if (state.fillStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_FILL_STYLE,
defaultFillStyle,
]);
}
if (state.strokeStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle,
state.lineWidth,
state.lineCap,
state.lineJoin,
state.miterLimit,
state.lineDash,
state.lineDashOffset,
]);
}
const ends = polygonGeometry.getEnds();
const flatCoordinates = polygonGeometry.getOrientedFlatCoordinates();
const stride = polygonGeometry.getStride();
this.drawFlatCoordinatess_(
flatCoordinates,
0,
/** @type {Array<number>} */ (ends),
stride
);
this.endGeometry(feature);
}
/**
* @param {import("../../geom/MultiPolygon.js").default} multiPolygonGeometry MultiPolygon geometry.
* @param {import("../../Feature.js").FeatureLike} feature Feature.
*/
drawMultiPolygon(multiPolygonGeometry, feature) {
const state = this.state;
const fillStyle = state.fillStyle;
const strokeStyle = state.strokeStyle;
if (fillStyle === undefined && strokeStyle === undefined) {
return;
}
this.setFillStrokeStyles_();
this.beginGeometry(multiPolygonGeometry, feature);
if (state.fillStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_FILL_STYLE,
defaultFillStyle,
]);
}
if (state.strokeStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle,
state.lineWidth,
state.lineCap,
state.lineJoin,
state.miterLimit,
state.lineDash,
state.lineDashOffset,
]);
}
const endss = multiPolygonGeometry.getEndss();
const flatCoordinates = multiPolygonGeometry.getOrientedFlatCoordinates();
const stride = multiPolygonGeometry.getStride();
let offset = 0;
for (let i = 0, ii = endss.length; i < ii; ++i) {
offset = this.drawFlatCoordinatess_(
flatCoordinates,
offset,
endss[i],
stride
);
}
this.endGeometry(feature);
}
/**
* @return {import("../canvas.js").SerializableInstructions} the serializable instructions.
*/
finish() {
this.reverseHitDetectionInstructions();
this.state = null;
// We want to preserve topology when drawing polygons. Polygons are
// simplified using quantization and point elimination. However, we might
// have received a mix of quantized and non-quantized geometries, so ensure
// that all are quantized by quantizing all coordinates in the batch.
const tolerance = this.tolerance;
if (tolerance !== 0) {
const coordinates = this.coordinates;
for (let i = 0, ii = coordinates.length; i < ii; ++i) {
coordinates[i] = snap(coordinates[i], tolerance);
}
}
return super.finish();
}
/**
* @private
*/
setFillStrokeStyles_() {
const state = this.state;
const fillStyle = state.fillStyle;
if (fillStyle !== undefined) {
this.updateFillStyle(state, this.createFill);
}
if (state.strokeStyle !== undefined) {
this.updateStrokeStyle(state, this.applyStroke);
}
}
}
export default CanvasPolygonBuilder;

636
node_modules/ol/src/render/canvas/TextBuilder.js generated vendored Normal file
View File

@@ -0,0 +1,636 @@
/**
* @module ol/render/canvas/TextBuilder
*/
import CanvasBuilder from './Builder.js';
import CanvasInstruction from './Instruction.js';
import TextPlacement from '../../style/TextPlacement.js';
import {asColorLike} from '../../colorlike.js';
import {
defaultFillStyle,
defaultFont,
defaultLineCap,
defaultLineDash,
defaultLineDashOffset,
defaultLineJoin,
defaultLineWidth,
defaultMiterLimit,
defaultPadding,
defaultStrokeStyle,
defaultTextAlign,
defaultTextBaseline,
registerFont,
} from '../canvas.js';
import {getUid} from '../../util.js';
import {intersects} from '../../extent.js';
import {matchingChunk} from '../../geom/flat/straightchunk.js';
/**
* @const
* @enum {number}
*/
export const TEXT_ALIGN = {
'left': 0,
'end': 0,
'center': 0.5,
'right': 1,
'start': 1,
'top': 0,
'middle': 0.5,
'hanging': 0.2,
'alphabetic': 0.8,
'ideographic': 0.8,
'bottom': 1,
};
class CanvasTextBuilder extends CanvasBuilder {
/**
* @param {number} tolerance Tolerance.
* @param {import("../../extent.js").Extent} maxExtent Maximum extent.
* @param {number} resolution Resolution.
* @param {number} pixelRatio Pixel ratio.
*/
constructor(tolerance, maxExtent, resolution, pixelRatio) {
super(tolerance, maxExtent, resolution, pixelRatio);
/**
* @private
* @type {Array<HTMLCanvasElement>}
*/
this.labels_ = null;
/**
* @private
* @type {string|Array<string>}
*/
this.text_ = '';
/**
* @private
* @type {number}
*/
this.textOffsetX_ = 0;
/**
* @private
* @type {number}
*/
this.textOffsetY_ = 0;
/**
* @private
* @type {boolean|undefined}
*/
this.textRotateWithView_ = undefined;
/**
* @private
* @type {number}
*/
this.textRotation_ = 0;
/**
* @private
* @type {?import("../canvas.js").FillState}
*/
this.textFillState_ = null;
/**
* @type {!Object<string, import("../canvas.js").FillState>}
*/
this.fillStates = {};
/**
* @private
* @type {?import("../canvas.js").StrokeState}
*/
this.textStrokeState_ = null;
/**
* @type {!Object<string, import("../canvas.js").StrokeState>}
*/
this.strokeStates = {};
/**
* @private
* @type {import("../canvas.js").TextState}
*/
this.textState_ = /** @type {import("../canvas.js").TextState} */ ({});
/**
* @type {!Object<string, import("../canvas.js").TextState>}
*/
this.textStates = {};
/**
* @private
* @type {string}
*/
this.textKey_ = '';
/**
* @private
* @type {string}
*/
this.fillKey_ = '';
/**
* @private
* @type {string}
*/
this.strokeKey_ = '';
/**
* Data shared with an image builder for combined decluttering.
* @private
* @type {import("../canvas.js").DeclutterImageWithText}
*/
this.declutterImageWithText_ = undefined;
}
/**
* @return {import("../canvas.js").SerializableInstructions} the serializable instructions.
*/
finish() {
const instructions = super.finish();
instructions.textStates = this.textStates;
instructions.fillStates = this.fillStates;
instructions.strokeStates = this.strokeStates;
return instructions;
}
/**
* @param {import("../../geom/SimpleGeometry.js").default|import("../Feature.js").default} geometry Geometry.
* @param {import("../../Feature.js").FeatureLike} feature Feature.
*/
drawText(geometry, feature) {
const fillState = this.textFillState_;
const strokeState = this.textStrokeState_;
const textState = this.textState_;
if (this.text_ === '' || !textState || (!fillState && !strokeState)) {
return;
}
const coordinates = this.coordinates;
let begin = coordinates.length;
const geometryType = geometry.getType();
let flatCoordinates = null;
let stride = geometry.getStride();
if (
textState.placement === TextPlacement.LINE &&
(geometryType == 'LineString' ||
geometryType == 'MultiLineString' ||
geometryType == 'Polygon' ||
geometryType == 'MultiPolygon')
) {
if (!intersects(this.getBufferedMaxExtent(), geometry.getExtent())) {
return;
}
let ends;
flatCoordinates = geometry.getFlatCoordinates();
if (geometryType == 'LineString') {
ends = [flatCoordinates.length];
} else if (geometryType == 'MultiLineString') {
ends = /** @type {import("../../geom/MultiLineString.js").default} */ (
geometry
).getEnds();
} else if (geometryType == 'Polygon') {
ends = /** @type {import("../../geom/Polygon.js").default} */ (geometry)
.getEnds()
.slice(0, 1);
} else if (geometryType == 'MultiPolygon') {
const endss =
/** @type {import("../../geom/MultiPolygon.js").default} */ (
geometry
).getEndss();
ends = [];
for (let i = 0, ii = endss.length; i < ii; ++i) {
ends.push(endss[i][0]);
}
}
this.beginGeometry(geometry, feature);
const textAlign = textState.textAlign;
// No `justify` support for line placement.
let flatOffset = 0;
let flatEnd;
for (let o = 0, oo = ends.length; o < oo; ++o) {
if (textAlign == undefined) {
const range = matchingChunk(
textState.maxAngle,
flatCoordinates,
flatOffset,
ends[o],
stride
);
flatOffset = range[0];
flatEnd = range[1];
} else {
flatEnd = ends[o];
}
for (let i = flatOffset; i < flatEnd; i += stride) {
coordinates.push(flatCoordinates[i], flatCoordinates[i + 1]);
}
const end = coordinates.length;
flatOffset = ends[o];
this.drawChars_(begin, end);
begin = end;
}
this.endGeometry(feature);
} else {
let geometryWidths = textState.overflow ? null : [];
switch (geometryType) {
case 'Point':
case 'MultiPoint':
flatCoordinates =
/** @type {import("../../geom/MultiPoint.js").default} */ (
geometry
).getFlatCoordinates();
break;
case 'LineString':
flatCoordinates =
/** @type {import("../../geom/LineString.js").default} */ (
geometry
).getFlatMidpoint();
break;
case 'Circle':
flatCoordinates =
/** @type {import("../../geom/Circle.js").default} */ (
geometry
).getCenter();
break;
case 'MultiLineString':
flatCoordinates =
/** @type {import("../../geom/MultiLineString.js").default} */ (
geometry
).getFlatMidpoints();
stride = 2;
break;
case 'Polygon':
flatCoordinates =
/** @type {import("../../geom/Polygon.js").default} */ (
geometry
).getFlatInteriorPoint();
if (!textState.overflow) {
geometryWidths.push(flatCoordinates[2] / this.resolution);
}
stride = 3;
break;
case 'MultiPolygon':
const interiorPoints =
/** @type {import("../../geom/MultiPolygon.js").default} */ (
geometry
).getFlatInteriorPoints();
flatCoordinates = [];
for (let i = 0, ii = interiorPoints.length; i < ii; i += 3) {
if (!textState.overflow) {
geometryWidths.push(interiorPoints[i + 2] / this.resolution);
}
flatCoordinates.push(interiorPoints[i], interiorPoints[i + 1]);
}
if (flatCoordinates.length === 0) {
return;
}
stride = 2;
break;
default:
}
const end = this.appendFlatPointCoordinates(flatCoordinates, stride);
if (end === begin) {
return;
}
if (
geometryWidths &&
(end - begin) / 2 !== flatCoordinates.length / stride
) {
let beg = begin / 2;
geometryWidths = geometryWidths.filter((w, i) => {
const keep =
coordinates[(beg + i) * 2] === flatCoordinates[i * stride] &&
coordinates[(beg + i) * 2 + 1] === flatCoordinates[i * stride + 1];
if (!keep) {
--beg;
}
return keep;
});
}
this.saveTextStates_();
if (textState.backgroundFill || textState.backgroundStroke) {
this.setFillStrokeStyle(
textState.backgroundFill,
textState.backgroundStroke
);
if (textState.backgroundFill) {
this.updateFillStyle(this.state, this.createFill);
this.hitDetectionInstructions.push(this.createFill(this.state));
}
if (textState.backgroundStroke) {
this.updateStrokeStyle(this.state, this.applyStroke);
this.hitDetectionInstructions.push(this.createStroke(this.state));
}
}
this.beginGeometry(geometry, feature);
// adjust padding for negative scale
let padding = textState.padding;
if (
padding != defaultPadding &&
(textState.scale[0] < 0 || textState.scale[1] < 0)
) {
let p0 = textState.padding[0];
let p1 = textState.padding[1];
let p2 = textState.padding[2];
let p3 = textState.padding[3];
if (textState.scale[0] < 0) {
p1 = -p1;
p3 = -p3;
}
if (textState.scale[1] < 0) {
p0 = -p0;
p2 = -p2;
}
padding = [p0, p1, p2, p3];
}
// The image is unknown at this stage so we pass null; it will be computed at render time.
// For clarity, we pass NaN for offsetX, offsetY, width and height, which will be computed at
// render time.
const pixelRatio = this.pixelRatio;
this.instructions.push([
CanvasInstruction.DRAW_IMAGE,
begin,
end,
null,
NaN,
NaN,
NaN,
1,
0,
0,
this.textRotateWithView_,
this.textRotation_,
[1, 1],
NaN,
undefined,
this.declutterImageWithText_,
padding == defaultPadding
? defaultPadding
: padding.map(function (p) {
return p * pixelRatio;
}),
!!textState.backgroundFill,
!!textState.backgroundStroke,
this.text_,
this.textKey_,
this.strokeKey_,
this.fillKey_,
this.textOffsetX_,
this.textOffsetY_,
geometryWidths,
]);
const scale = 1 / pixelRatio;
this.hitDetectionInstructions.push([
CanvasInstruction.DRAW_IMAGE,
begin,
end,
null,
NaN,
NaN,
NaN,
1,
0,
0,
this.textRotateWithView_,
this.textRotation_,
[scale, scale],
NaN,
undefined,
this.declutterImageWithText_,
padding,
!!textState.backgroundFill,
!!textState.backgroundStroke,
this.text_,
this.textKey_,
this.strokeKey_,
this.fillKey_,
this.textOffsetX_,
this.textOffsetY_,
geometryWidths,
]);
this.endGeometry(feature);
}
}
/**
* @private
*/
saveTextStates_() {
const strokeState = this.textStrokeState_;
const textState = this.textState_;
const fillState = this.textFillState_;
const strokeKey = this.strokeKey_;
if (strokeState) {
if (!(strokeKey in this.strokeStates)) {
this.strokeStates[strokeKey] = {
strokeStyle: strokeState.strokeStyle,
lineCap: strokeState.lineCap,
lineDashOffset: strokeState.lineDashOffset,
lineWidth: strokeState.lineWidth,
lineJoin: strokeState.lineJoin,
miterLimit: strokeState.miterLimit,
lineDash: strokeState.lineDash,
};
}
}
const textKey = this.textKey_;
if (!(textKey in this.textStates)) {
this.textStates[textKey] = {
font: textState.font,
textAlign: textState.textAlign || defaultTextAlign,
justify: textState.justify,
textBaseline: textState.textBaseline || defaultTextBaseline,
scale: textState.scale,
};
}
const fillKey = this.fillKey_;
if (fillState) {
if (!(fillKey in this.fillStates)) {
this.fillStates[fillKey] = {
fillStyle: fillState.fillStyle,
};
}
}
}
/**
* @private
* @param {number} begin Begin.
* @param {number} end End.
*/
drawChars_(begin, end) {
const strokeState = this.textStrokeState_;
const textState = this.textState_;
const strokeKey = this.strokeKey_;
const textKey = this.textKey_;
const fillKey = this.fillKey_;
this.saveTextStates_();
const pixelRatio = this.pixelRatio;
const baseline = TEXT_ALIGN[textState.textBaseline];
const offsetY = this.textOffsetY_ * pixelRatio;
const text = this.text_;
const strokeWidth = strokeState
? (strokeState.lineWidth * Math.abs(textState.scale[0])) / 2
: 0;
this.instructions.push([
CanvasInstruction.DRAW_CHARS,
begin,
end,
baseline,
textState.overflow,
fillKey,
textState.maxAngle,
pixelRatio,
offsetY,
strokeKey,
strokeWidth * pixelRatio,
text,
textKey,
1,
]);
this.hitDetectionInstructions.push([
CanvasInstruction.DRAW_CHARS,
begin,
end,
baseline,
textState.overflow,
fillKey,
textState.maxAngle,
1,
offsetY,
strokeKey,
strokeWidth,
text,
textKey,
1 / pixelRatio,
]);
}
/**
* @param {import("../../style/Text.js").default} textStyle Text style.
* @param {Object} [opt_sharedData] Shared data.
*/
setTextStyle(textStyle, opt_sharedData) {
let textState, fillState, strokeState;
if (!textStyle) {
this.text_ = '';
} else {
const textFillStyle = textStyle.getFill();
if (!textFillStyle) {
fillState = null;
this.textFillState_ = fillState;
} else {
fillState = this.textFillState_;
if (!fillState) {
fillState = /** @type {import("../canvas.js").FillState} */ ({});
this.textFillState_ = fillState;
}
fillState.fillStyle = asColorLike(
textFillStyle.getColor() || defaultFillStyle
);
}
const textStrokeStyle = textStyle.getStroke();
if (!textStrokeStyle) {
strokeState = null;
this.textStrokeState_ = strokeState;
} else {
strokeState = this.textStrokeState_;
if (!strokeState) {
strokeState = /** @type {import("../canvas.js").StrokeState} */ ({});
this.textStrokeState_ = strokeState;
}
const lineDash = textStrokeStyle.getLineDash();
const lineDashOffset = textStrokeStyle.getLineDashOffset();
const lineWidth = textStrokeStyle.getWidth();
const miterLimit = textStrokeStyle.getMiterLimit();
strokeState.lineCap = textStrokeStyle.getLineCap() || defaultLineCap;
strokeState.lineDash = lineDash ? lineDash.slice() : defaultLineDash;
strokeState.lineDashOffset =
lineDashOffset === undefined ? defaultLineDashOffset : lineDashOffset;
strokeState.lineJoin = textStrokeStyle.getLineJoin() || defaultLineJoin;
strokeState.lineWidth =
lineWidth === undefined ? defaultLineWidth : lineWidth;
strokeState.miterLimit =
miterLimit === undefined ? defaultMiterLimit : miterLimit;
strokeState.strokeStyle = asColorLike(
textStrokeStyle.getColor() || defaultStrokeStyle
);
}
textState = this.textState_;
const font = textStyle.getFont() || defaultFont;
registerFont(font);
const textScale = textStyle.getScaleArray();
textState.overflow = textStyle.getOverflow();
textState.font = font;
textState.maxAngle = textStyle.getMaxAngle();
textState.placement = textStyle.getPlacement();
textState.textAlign = textStyle.getTextAlign();
textState.justify = textStyle.getJustify();
textState.textBaseline =
textStyle.getTextBaseline() || defaultTextBaseline;
textState.backgroundFill = textStyle.getBackgroundFill();
textState.backgroundStroke = textStyle.getBackgroundStroke();
textState.padding = textStyle.getPadding() || defaultPadding;
textState.scale = textScale === undefined ? [1, 1] : textScale;
const textOffsetX = textStyle.getOffsetX();
const textOffsetY = textStyle.getOffsetY();
const textRotateWithView = textStyle.getRotateWithView();
const textRotation = textStyle.getRotation();
this.text_ = textStyle.getText() || '';
this.textOffsetX_ = textOffsetX === undefined ? 0 : textOffsetX;
this.textOffsetY_ = textOffsetY === undefined ? 0 : textOffsetY;
this.textRotateWithView_ =
textRotateWithView === undefined ? false : textRotateWithView;
this.textRotation_ = textRotation === undefined ? 0 : textRotation;
this.strokeKey_ = strokeState
? (typeof strokeState.strokeStyle == 'string'
? strokeState.strokeStyle
: getUid(strokeState.strokeStyle)) +
strokeState.lineCap +
strokeState.lineDashOffset +
'|' +
strokeState.lineWidth +
strokeState.lineJoin +
strokeState.miterLimit +
'[' +
strokeState.lineDash.join() +
']'
: '';
this.textKey_ =
textState.font +
textState.scale +
(textState.textAlign || '?') +
(textState.justify || '?') +
(textState.textBaseline || '?');
this.fillKey_ = fillState
? typeof fillState.fillStyle == 'string'
? fillState.fillStyle
: '|' + getUid(fillState.fillStyle)
: '';
}
this.declutterImageWithText_ = opt_sharedData;
}
}
export default CanvasTextBuilder;

185
node_modules/ol/src/render/canvas/hitdetect.js generated vendored Normal file
View File

@@ -0,0 +1,185 @@
/**
* @module ol/render/canvas/hitdetect
*/
import CanvasImmediateRenderer from './Immediate.js';
import IconAnchorUnits from '../../style/IconAnchorUnits.js';
import {Icon} from '../../style.js';
import {clamp} from '../../math.js';
import {createCanvasContext2D} from '../../dom.js';
import {intersects} from '../../extent.js';
import {numberSafeCompareFunction} from '../../array.js';
export const HIT_DETECT_RESOLUTION = 0.5;
/**
* @param {import("../../size.js").Size} size Canvas size in css pixels.
* @param {Array<import("../../transform.js").Transform>} transforms Transforms
* for rendering features to all worlds of the viewport, from coordinates to css
* pixels.
* @param {Array<import("../../Feature.js").FeatureLike>} features
* Features to consider for hit detection.
* @param {import("../../style/Style.js").StyleFunction|undefined} styleFunction
* Layer style function.
* @param {import("../../extent.js").Extent} extent Extent.
* @param {number} resolution Resolution.
* @param {number} rotation Rotation.
* @return {ImageData} Hit detection image data.
*/
export function createHitDetectionImageData(
size,
transforms,
features,
styleFunction,
extent,
resolution,
rotation
) {
const width = size[0] * HIT_DETECT_RESOLUTION;
const height = size[1] * HIT_DETECT_RESOLUTION;
const context = createCanvasContext2D(width, height);
context.imageSmoothingEnabled = false;
const canvas = context.canvas;
const renderer = new CanvasImmediateRenderer(
context,
HIT_DETECT_RESOLUTION,
extent,
null,
rotation
);
const featureCount = features.length;
// Stretch hit detection index to use the whole available color range
const indexFactor = Math.floor((256 * 256 * 256 - 1) / featureCount);
const featuresByZIndex = {};
for (let i = 1; i <= featureCount; ++i) {
const feature = features[i - 1];
const featureStyleFunction = feature.getStyleFunction() || styleFunction;
if (!styleFunction) {
continue;
}
let styles = featureStyleFunction(feature, resolution);
if (!styles) {
continue;
}
if (!Array.isArray(styles)) {
styles = [styles];
}
const index = i * indexFactor;
const color = '#' + ('000000' + index.toString(16)).slice(-6);
for (let j = 0, jj = styles.length; j < jj; ++j) {
const originalStyle = styles[j];
const geometry = originalStyle.getGeometryFunction()(feature);
if (!geometry || !intersects(extent, geometry.getExtent())) {
continue;
}
const style = originalStyle.clone();
const fill = style.getFill();
if (fill) {
fill.setColor(color);
}
const stroke = style.getStroke();
if (stroke) {
stroke.setColor(color);
stroke.setLineDash(null);
}
style.setText(undefined);
const image = originalStyle.getImage();
if (image && image.getOpacity() !== 0) {
const imgSize = image.getImageSize();
if (!imgSize) {
continue;
}
const imgContext = createCanvasContext2D(
imgSize[0],
imgSize[1],
undefined,
{alpha: false}
);
const img = imgContext.canvas;
imgContext.fillStyle = color;
imgContext.fillRect(0, 0, img.width, img.height);
style.setImage(
new Icon({
img: img,
imgSize: imgSize,
anchor: image.getAnchor(),
anchorXUnits: IconAnchorUnits.PIXELS,
anchorYUnits: IconAnchorUnits.PIXELS,
offset: image.getOrigin(),
opacity: 1,
size: image.getSize(),
scale: image.getScale(),
rotation: image.getRotation(),
rotateWithView: image.getRotateWithView(),
})
);
}
const zIndex = style.getZIndex() || 0;
let byGeometryType = featuresByZIndex[zIndex];
if (!byGeometryType) {
byGeometryType = {};
featuresByZIndex[zIndex] = byGeometryType;
byGeometryType['Polygon'] = [];
byGeometryType['Circle'] = [];
byGeometryType['LineString'] = [];
byGeometryType['Point'] = [];
}
byGeometryType[geometry.getType().replace('Multi', '')].push(
geometry,
style
);
}
}
const zIndexKeys = Object.keys(featuresByZIndex)
.map(Number)
.sort(numberSafeCompareFunction);
for (let i = 0, ii = zIndexKeys.length; i < ii; ++i) {
const byGeometryType = featuresByZIndex[zIndexKeys[i]];
for (const type in byGeometryType) {
const geomAndStyle = byGeometryType[type];
for (let j = 0, jj = geomAndStyle.length; j < jj; j += 2) {
renderer.setStyle(geomAndStyle[j + 1]);
for (let k = 0, kk = transforms.length; k < kk; ++k) {
renderer.setTransform(transforms[k]);
renderer.drawGeometry(geomAndStyle[j]);
}
}
}
}
return context.getImageData(0, 0, canvas.width, canvas.height);
}
/**
* @param {import("../../pixel").Pixel} pixel Pixel coordinate on the hit
* detection canvas in css pixels.
* @param {Array<import("../../Feature").FeatureLike>} features Features. Has to
* match the `features` array that was passed to `createHitDetectionImageData()`.
* @param {ImageData} imageData Hit detection image data generated by
* `createHitDetectionImageData()`.
* @return {Array<import("../../Feature").FeatureLike>} features Features.
*/
export function hitDetect(pixel, features, imageData) {
const resultFeatures = [];
if (imageData) {
const x = Math.floor(Math.round(pixel[0]) * HIT_DETECT_RESOLUTION);
const y = Math.floor(Math.round(pixel[1]) * HIT_DETECT_RESOLUTION);
// The pixel coordinate is clamped down to the hit-detect canvas' size to account
// for browsers returning coordinates slightly larger than the actual canvas size
// due to a non-integer pixel ratio.
const index =
(clamp(x, 0, imageData.width - 1) +
clamp(y, 0, imageData.height - 1) * imageData.width) *
4;
const r = imageData.data[index];
const g = imageData.data[index + 1];
const b = imageData.data[index + 2];
const i = b + 256 * (g + 256 * r);
const indexFactor = Math.floor((256 * 256 * 256 - 1) / features.length);
if (i && i % indexFactor === 0) {
resultFeatures.push(features[i / indexFactor - 1]);
}
}
return resultFeatures;
}