package.renderer.vector.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ol Show documentation
Show all versions of ol Show documentation
OpenLayers mapping library
The newest version!
/**
* @module ol/renderer/vector
*/
import ImageState from '../ImageState.js';
import {getUid} from '../util.js';
/**
* Feature callback. The callback will be called with three arguments. The first
* argument is one {@link module:ol/Feature~Feature feature} or {@link module:ol/render/Feature~RenderFeature render feature}
* at the pixel, the second is the {@link module:ol/layer/Layer~Layer layer} of the feature and will be null for
* unmanaged layers. The third is the {@link module:ol/geom/SimpleGeometry~SimpleGeometry} of the feature. For features
* with a GeometryCollection geometry, it will be the first detected geometry from the collection.
* @template T
* @typedef {function(import("../Feature.js").FeatureLike, import("../layer/Layer.js").default, import("../geom/SimpleGeometry.js").default): T} FeatureCallback
*/
/**
* Tolerance for geometry simplification in device pixels.
* @type {number}
*/
const SIMPLIFY_TOLERANCE = 0.5;
/**
* @const
* @type {Object}
*/
const GEOMETRY_RENDERERS = {
'Point': renderPointGeometry,
'LineString': renderLineStringGeometry,
'Polygon': renderPolygonGeometry,
'MultiPoint': renderMultiPointGeometry,
'MultiLineString': renderMultiLineStringGeometry,
'MultiPolygon': renderMultiPolygonGeometry,
'GeometryCollection': renderGeometryCollectionGeometry,
'Circle': renderCircleGeometry,
};
/**
* @param {import("../Feature.js").FeatureLike} feature1 Feature 1.
* @param {import("../Feature.js").FeatureLike} feature2 Feature 2.
* @return {number} Order.
*/
export function defaultOrder(feature1, feature2) {
return parseInt(getUid(feature1), 10) - parseInt(getUid(feature2), 10);
}
/**
* @param {number} resolution Resolution.
* @param {number} pixelRatio Pixel ratio.
* @return {number} Squared pixel tolerance.
*/
export function getSquaredTolerance(resolution, pixelRatio) {
const tolerance = getTolerance(resolution, pixelRatio);
return tolerance * tolerance;
}
/**
* @param {number} resolution Resolution.
* @param {number} pixelRatio Pixel ratio.
* @return {number} Pixel tolerance.
*/
export function getTolerance(resolution, pixelRatio) {
return (SIMPLIFY_TOLERANCE * resolution) / pixelRatio;
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} builderGroup Builder group.
* @param {import("../geom/Circle.js").default} geometry Geometry.
* @param {import("../style/Style.js").default} style Style.
* @param {import("../Feature.js").default} feature Feature.
* @param {number} [index] Render order index.
*/
function renderCircleGeometry(builderGroup, geometry, style, feature, index) {
const fillStyle = style.getFill();
const strokeStyle = style.getStroke();
if (fillStyle || strokeStyle) {
const circleReplay = builderGroup.getBuilder(style.getZIndex(), 'Circle');
circleReplay.setFillStrokeStyle(fillStyle, strokeStyle);
circleReplay.drawCircle(geometry, feature, index);
}
const textStyle = style.getText();
if (textStyle && textStyle.getText()) {
const textReplay = builderGroup.getBuilder(style.getZIndex(), 'Text');
textReplay.setTextStyle(textStyle);
textReplay.drawText(geometry, feature);
}
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} replayGroup Replay group.
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @param {import("../style/Style.js").default} style Style.
* @param {number} squaredTolerance Squared tolerance.
* @param {function(import("../events/Event.js").default): void} listener Listener function.
* @param {import("../proj.js").TransformFunction} [transform] Transform from user to view projection.
* @param {boolean} [declutter] Enable decluttering.
* @param {number} [index] Render order index..
* @return {boolean} `true` if style is loading.
*/
export function renderFeature(
replayGroup,
feature,
style,
squaredTolerance,
listener,
transform,
declutter,
index,
) {
const loadingPromises = [];
const imageStyle = style.getImage();
if (imageStyle) {
let loading = true;
const imageState = imageStyle.getImageState();
if (imageState == ImageState.LOADED || imageState == ImageState.ERROR) {
loading = false;
} else {
if (imageState == ImageState.IDLE) {
imageStyle.load();
}
}
if (loading) {
loadingPromises.push(imageStyle.ready());
}
}
const fillStyle = style.getFill();
if (fillStyle && fillStyle.loading()) {
loadingPromises.push(fillStyle.ready());
}
const loading = loadingPromises.length > 0;
if (loading) {
Promise.all(loadingPromises).then(() => listener(null));
}
renderFeatureInternal(
replayGroup,
feature,
style,
squaredTolerance,
transform,
declutter,
index,
);
return loading;
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} replayGroup Replay group.
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @param {import("../style/Style.js").default} style Style.
* @param {number} squaredTolerance Squared tolerance.
* @param {import("../proj.js").TransformFunction} [transform] Optional transform function.
* @param {boolean} [declutter] Enable decluttering.
* @param {number} [index] Render order index..
*/
function renderFeatureInternal(
replayGroup,
feature,
style,
squaredTolerance,
transform,
declutter,
index,
) {
const geometry = style.getGeometryFunction()(feature);
if (!geometry) {
return;
}
const simplifiedGeometry = geometry.simplifyTransformed(
squaredTolerance,
transform,
);
const renderer = style.getRenderer();
if (renderer) {
renderGeometry(replayGroup, simplifiedGeometry, style, feature, index);
} else {
const geometryRenderer = GEOMETRY_RENDERERS[simplifiedGeometry.getType()];
geometryRenderer(
replayGroup,
simplifiedGeometry,
style,
feature,
index,
declutter,
);
}
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} replayGroup Replay group.
* @param {import("../geom/Geometry.js").default|import("../render/Feature.js").default} geometry Geometry.
* @param {import("../style/Style.js").default} style Style.
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @param {number} [index] Render order index.
*/
function renderGeometry(replayGroup, geometry, style, feature, index) {
if (geometry.getType() == 'GeometryCollection') {
const geometries =
/** @type {import("../geom/GeometryCollection.js").default} */ (
geometry
).getGeometries();
for (let i = 0, ii = geometries.length; i < ii; ++i) {
renderGeometry(replayGroup, geometries[i], style, feature, index);
}
return;
}
const replay = replayGroup.getBuilder(style.getZIndex(), 'Default');
replay.drawCustom(
/** @type {import("../geom/SimpleGeometry.js").default} */ (geometry),
feature,
style.getRenderer(),
style.getHitDetectionRenderer(),
index,
);
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} replayGroup Replay group.
* @param {import("../geom/GeometryCollection.js").default} geometry Geometry.
* @param {import("../style/Style.js").default} style Style.
* @param {import("../Feature.js").default} feature Feature.
* @param {import("../render/canvas/BuilderGroup.js").default} [declutterBuilderGroup] Builder for decluttering.
* @param {number} [index] Render order index.
*/
function renderGeometryCollectionGeometry(
replayGroup,
geometry,
style,
feature,
declutterBuilderGroup,
index,
) {
const geometries = geometry.getGeometriesArray();
let i, ii;
for (i = 0, ii = geometries.length; i < ii; ++i) {
const geometryRenderer = GEOMETRY_RENDERERS[geometries[i].getType()];
geometryRenderer(
replayGroup,
geometries[i],
style,
feature,
declutterBuilderGroup,
index,
);
}
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} builderGroup Replay group.
* @param {import("../geom/LineString.js").default|import("../render/Feature.js").default} geometry Geometry.
* @param {import("../style/Style.js").default} style Style.
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @param {number} [index] Render order index.
*/
function renderLineStringGeometry(
builderGroup,
geometry,
style,
feature,
index,
) {
const strokeStyle = style.getStroke();
if (strokeStyle) {
const lineStringReplay = builderGroup.getBuilder(
style.getZIndex(),
'LineString',
);
lineStringReplay.setFillStrokeStyle(null, strokeStyle);
lineStringReplay.drawLineString(geometry, feature, index);
}
const textStyle = style.getText();
if (textStyle && textStyle.getText()) {
const textReplay = builderGroup.getBuilder(style.getZIndex(), 'Text');
textReplay.setTextStyle(textStyle);
textReplay.drawText(geometry, feature, index);
}
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} builderGroup Replay group.
* @param {import("../geom/MultiLineString.js").default|import("../render/Feature.js").default} geometry Geometry.
* @param {import("../style/Style.js").default} style Style.
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @param {number} [index] Render order index.
*/
function renderMultiLineStringGeometry(
builderGroup,
geometry,
style,
feature,
index,
) {
const strokeStyle = style.getStroke();
if (strokeStyle) {
const lineStringReplay = builderGroup.getBuilder(
style.getZIndex(),
'LineString',
);
lineStringReplay.setFillStrokeStyle(null, strokeStyle);
lineStringReplay.drawMultiLineString(geometry, feature, index);
}
const textStyle = style.getText();
if (textStyle && textStyle.getText()) {
const textReplay = builderGroup.getBuilder(style.getZIndex(), 'Text');
textReplay.setTextStyle(textStyle);
textReplay.drawText(geometry, feature, index);
}
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} builderGroup Replay group.
* @param {import("../geom/MultiPolygon.js").default} geometry Geometry.
* @param {import("../style/Style.js").default} style Style.
* @param {import("../Feature.js").default} feature Feature.
* @param {number} [index] Render order index.
*/
function renderMultiPolygonGeometry(
builderGroup,
geometry,
style,
feature,
index,
) {
const fillStyle = style.getFill();
const strokeStyle = style.getStroke();
if (strokeStyle || fillStyle) {
const polygonReplay = builderGroup.getBuilder(style.getZIndex(), 'Polygon');
polygonReplay.setFillStrokeStyle(fillStyle, strokeStyle);
polygonReplay.drawMultiPolygon(geometry, feature, index);
}
const textStyle = style.getText();
if (textStyle && textStyle.getText()) {
const textReplay = builderGroup.getBuilder(style.getZIndex(), 'Text');
textReplay.setTextStyle(textStyle);
textReplay.drawText(geometry, feature, index);
}
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} builderGroup Replay group.
* @param {import("../geom/Point.js").default|import("../render/Feature.js").default} geometry Geometry.
* @param {import("../style/Style.js").default} style Style.
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @param {number} [index] Render order index.
* @param {boolean} [declutter] Enable decluttering.
*/
function renderPointGeometry(
builderGroup,
geometry,
style,
feature,
index,
declutter,
) {
const imageStyle = style.getImage();
const textStyle = style.getText();
const hasText = textStyle && textStyle.getText();
/** @type {import("../render/canvas.js").DeclutterImageWithText} */
const declutterImageWithText =
declutter && imageStyle && hasText ? {} : undefined;
if (imageStyle) {
if (imageStyle.getImageState() != ImageState.LOADED) {
return;
}
const imageReplay = builderGroup.getBuilder(style.getZIndex(), 'Image');
imageReplay.setImageStyle(imageStyle, declutterImageWithText);
imageReplay.drawPoint(geometry, feature, index);
}
if (hasText) {
const textReplay = builderGroup.getBuilder(style.getZIndex(), 'Text');
textReplay.setTextStyle(textStyle, declutterImageWithText);
textReplay.drawText(geometry, feature, index);
}
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} builderGroup Replay group.
* @param {import("../geom/MultiPoint.js").default|import("../render/Feature.js").default} geometry Geometry.
* @param {import("../style/Style.js").default} style Style.
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @param {number} [index] Render order index.
* @param {boolean} [declutter] Enable decluttering.
*/
function renderMultiPointGeometry(
builderGroup,
geometry,
style,
feature,
index,
declutter,
) {
const imageStyle = style.getImage();
const hasImage = imageStyle && imageStyle.getOpacity() !== 0;
const textStyle = style.getText();
const hasText = textStyle && textStyle.getText();
/** @type {import("../render/canvas.js").DeclutterImageWithText} */
const declutterImageWithText =
declutter && hasImage && hasText ? {} : undefined;
if (hasImage) {
if (imageStyle.getImageState() != ImageState.LOADED) {
return;
}
const imageReplay = builderGroup.getBuilder(style.getZIndex(), 'Image');
imageReplay.setImageStyle(imageStyle, declutterImageWithText);
imageReplay.drawMultiPoint(geometry, feature, index);
}
if (hasText) {
const textReplay = builderGroup.getBuilder(style.getZIndex(), 'Text');
textReplay.setTextStyle(textStyle, declutterImageWithText);
textReplay.drawText(geometry, feature, index);
}
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} builderGroup Replay group.
* @param {import("../geom/Polygon.js").default|import("../render/Feature.js").default} geometry Geometry.
* @param {import("../style/Style.js").default} style Style.
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @param {number} [index] Render order index.
*/
function renderPolygonGeometry(builderGroup, geometry, style, feature, index) {
const fillStyle = style.getFill();
const strokeStyle = style.getStroke();
if (fillStyle || strokeStyle) {
const polygonReplay = builderGroup.getBuilder(style.getZIndex(), 'Polygon');
polygonReplay.setFillStrokeStyle(fillStyle, strokeStyle);
polygonReplay.drawPolygon(geometry, feature, index);
}
const textStyle = style.getText();
if (textStyle && textStyle.getText()) {
const textReplay = builderGroup.getBuilder(style.getZIndex(), 'Text');
textReplay.setTextStyle(textStyle);
textReplay.drawText(geometry, feature, index);
}
}