This commit is contained in:
132
node_modules/ol/src/render/Box.js
generated
vendored
Normal file
132
node_modules/ol/src/render/Box.js
generated
vendored
Normal 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
44
node_modules/ol/src/render/Event.js
generated
vendored
Normal 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
57
node_modules/ol/src/render/EventType.js
generated
vendored
Normal 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
405
node_modules/ol/src/render/Feature.js
generated
vendored
Normal 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
115
node_modules/ol/src/render/VectorContext.js
generated
vendored
Normal 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
516
node_modules/ol/src/render/canvas.js
generated
vendored
Normal 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
651
node_modules/ol/src/render/canvas/Builder.js
generated
vendored
Normal 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
105
node_modules/ol/src/render/canvas/BuilderGroup.js
generated
vendored
Normal 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
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
422
node_modules/ol/src/render/canvas/ExecutorGroup.js
generated
vendored
Normal 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
271
node_modules/ol/src/render/canvas/ImageBuilder.js
generated
vendored
Normal 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
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
44
node_modules/ol/src/render/canvas/Instruction.js
generated
vendored
Normal 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
163
node_modules/ol/src/render/canvas/LineStringBuilder.js
generated
vendored
Normal 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
257
node_modules/ol/src/render/canvas/PolygonBuilder.js
generated
vendored
Normal 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
636
node_modules/ol/src/render/canvas/TextBuilder.js
generated
vendored
Normal 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
185
node_modules/ol/src/render/canvas/hitdetect.js
generated
vendored
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user