Merge pull request #12646 from ashchurova/fix-8136-hit-detection-custom-renderer
Support for hit detection in styles with custom rendering
This commit is contained in:
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
layout: example.html
|
||||||
|
title: Custom Hit Detection Render
|
||||||
|
shortdesc: Example of a custom hit detection renderer.
|
||||||
|
docs: >
|
||||||
|
This example demonstrates the use of 'ol/style/Style' hitDetectionRender option function in
|
||||||
|
detecting if pointer is over a particular feature.
|
||||||
|
Move pointer over the label for Columbus Circle feature and see that only label is used in
|
||||||
|
hit detection.
|
||||||
|
tags: "circle, feature, vector, render, custom, hitDetectionRenderer"
|
||||||
|
---
|
||||||
|
<div id="map" class="map"></div>
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import Feature from '../src/ol/Feature.js';
|
||||||
|
import Map from '../src/ol/Map.js';
|
||||||
|
import View from '../src/ol/View.js';
|
||||||
|
import {Circle} from '../src/ol/geom.js';
|
||||||
|
import {OSM, Vector as VectorSource} from '../src/ol/source.js';
|
||||||
|
import {Style} from '../src/ol/style.js';
|
||||||
|
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
|
||||||
|
import {fromLonLat} from '../src/ol/proj.js';
|
||||||
|
|
||||||
|
const columbusCircleCoords = fromLonLat([-73.98189, 40.76805]);
|
||||||
|
const labelTextStroke = 'rgba(120, 120, 120, 1)';
|
||||||
|
const labelText = 'Columbus Circle';
|
||||||
|
|
||||||
|
let pointerOverFeature = null;
|
||||||
|
|
||||||
|
const renderLabelText = (ctx, x, y, stroke) => {
|
||||||
|
ctx.fillStyle = 'rgba(255,0,0,1)';
|
||||||
|
ctx.strokeStyle = stroke;
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.font = `bold 30px verdana`;
|
||||||
|
ctx.filter = 'drop-shadow(7px 7px 2px #e81)';
|
||||||
|
ctx.fillText(labelText, x, y);
|
||||||
|
ctx.strokeText(labelText, x, y);
|
||||||
|
};
|
||||||
|
|
||||||
|
const circleFeature = new Feature({
|
||||||
|
geometry: new Circle(columbusCircleCoords, 50),
|
||||||
|
});
|
||||||
|
|
||||||
|
circleFeature.set('label-color', labelTextStroke);
|
||||||
|
|
||||||
|
circleFeature.setStyle(
|
||||||
|
new Style({
|
||||||
|
renderer(coordinates, state) {
|
||||||
|
const [[x, y], [x1, y1]] = coordinates;
|
||||||
|
const ctx = state.context;
|
||||||
|
const dx = x1 - x;
|
||||||
|
const dy = y1 - y;
|
||||||
|
const radius = Math.sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
|
const innerRadius = 0;
|
||||||
|
const outerRadius = radius * 1.4;
|
||||||
|
|
||||||
|
const gradient = ctx.createRadialGradient(
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
innerRadius,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
outerRadius
|
||||||
|
);
|
||||||
|
gradient.addColorStop(0, 'rgba(255,0,0,0)');
|
||||||
|
gradient.addColorStop(0.6, 'rgba(255,0,0,0.2)');
|
||||||
|
gradient.addColorStop(1, 'rgba(255,0,0,0.8)');
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, radius, 0, 2 * Math.PI, true);
|
||||||
|
ctx.fillStyle = gradient;
|
||||||
|
ctx.fill();
|
||||||
|
ctx.strokeStyle = 'rgba(255,0,0,1)';
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
renderLabelText(ctx, x, y, circleFeature.get('label-color'));
|
||||||
|
},
|
||||||
|
hitDetectionRenderer(coordinates, state) {
|
||||||
|
const [x, y] = coordinates[0];
|
||||||
|
const ctx = state.context;
|
||||||
|
renderLabelText(ctx, x, y, circleFeature.get('label-color'));
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const map = new Map({
|
||||||
|
layers: [
|
||||||
|
new TileLayer({
|
||||||
|
source: new OSM(),
|
||||||
|
visible: true,
|
||||||
|
}),
|
||||||
|
new VectorLayer({
|
||||||
|
source: new VectorSource({
|
||||||
|
features: [circleFeature],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
target: 'map',
|
||||||
|
view: new View({
|
||||||
|
center: columbusCircleCoords,
|
||||||
|
zoom: 19,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
map.on('pointermove', (evt) => {
|
||||||
|
const featureOver = map.forEachFeatureAtPixel(evt.pixel, (feature) => {
|
||||||
|
feature.set('label-color', 'rgba(255,255,255,1)');
|
||||||
|
return feature;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (pointerOverFeature && pointerOverFeature != featureOver) {
|
||||||
|
pointerOverFeature.set('label-color', labelTextStroke);
|
||||||
|
}
|
||||||
|
pointerOverFeature = featureOver;
|
||||||
|
});
|
||||||
@@ -15,8 +15,9 @@ class VectorContext {
|
|||||||
* @param {import("../geom/SimpleGeometry.js").default} geometry Geometry.
|
* @param {import("../geom/SimpleGeometry.js").default} geometry Geometry.
|
||||||
* @param {import("../Feature.js").FeatureLike} feature Feature.
|
* @param {import("../Feature.js").FeatureLike} feature Feature.
|
||||||
* @param {Function} renderer Renderer.
|
* @param {Function} renderer Renderer.
|
||||||
|
* @param {Function} hitDetectionRenderer Renderer.
|
||||||
*/
|
*/
|
||||||
drawCustom(geometry, feature, renderer) {}
|
drawCustom(geometry, feature, renderer, hitDetectionRenderer) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render a geometry.
|
* Render a geometry.
|
||||||
|
|||||||
+140
-92
@@ -247,97 +247,104 @@ class CanvasBuilder extends VectorContext {
|
|||||||
* @param {import("../../geom/SimpleGeometry.js").default} geometry Geometry.
|
* @param {import("../../geom/SimpleGeometry.js").default} geometry Geometry.
|
||||||
* @param {import("../../Feature.js").FeatureLike} feature Feature.
|
* @param {import("../../Feature.js").FeatureLike} feature Feature.
|
||||||
* @param {Function} renderer Renderer.
|
* @param {Function} renderer Renderer.
|
||||||
|
* @param {Function} hitDetectionRenderer Renderer.
|
||||||
*/
|
*/
|
||||||
drawCustom(geometry, feature, renderer) {
|
drawCustom(geometry, feature, renderer, hitDetectionRenderer) {
|
||||||
this.beginGeometry(geometry, feature);
|
this.beginGeometry(geometry, feature);
|
||||||
|
|
||||||
const type = geometry.getType();
|
const type = geometry.getType();
|
||||||
const stride = geometry.getStride();
|
const stride = geometry.getStride();
|
||||||
const builderBegin = this.coordinates.length;
|
const builderBegin = this.coordinates.length;
|
||||||
|
|
||||||
let flatCoordinates, builderEnd, builderEnds, builderEndss;
|
let flatCoordinates, builderEnd, builderEnds, builderEndss;
|
||||||
let offset;
|
let offset;
|
||||||
if (type == GeometryType.MULTI_POLYGON) {
|
|
||||||
flatCoordinates =
|
switch (type) {
|
||||||
/** @type {import("../../geom/MultiPolygon.js").default} */ (
|
case GeometryType.MULTI_POLYGON:
|
||||||
geometry
|
flatCoordinates =
|
||||||
).getOrientedFlatCoordinates();
|
/** @type {import("../../geom/MultiPolygon.js").default} */ (
|
||||||
builderEndss = [];
|
geometry
|
||||||
const endss =
|
).getOrientedFlatCoordinates();
|
||||||
/** @type {import("../../geom/MultiPolygon.js").default} */ (
|
builderEndss = [];
|
||||||
geometry
|
const endss =
|
||||||
).getEndss();
|
/** @type {import("../../geom/MultiPolygon.js").default} */ (
|
||||||
offset = 0;
|
geometry
|
||||||
for (let i = 0, ii = endss.length; i < ii; ++i) {
|
).getEndss();
|
||||||
const myEnds = [];
|
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 GeometryType.POLYGON:
|
||||||
|
case GeometryType.MULTI_LINE_STRING:
|
||||||
|
builderEnds = [];
|
||||||
|
flatCoordinates =
|
||||||
|
type == GeometryType.POLYGON
|
||||||
|
? /** @type {import("../../geom/Polygon.js").default} */ (
|
||||||
|
geometry
|
||||||
|
).getOrientedFlatCoordinates()
|
||||||
|
: geometry.getFlatCoordinates();
|
||||||
offset = this.drawCustomCoordinates_(
|
offset = this.drawCustomCoordinates_(
|
||||||
flatCoordinates,
|
flatCoordinates,
|
||||||
offset,
|
0,
|
||||||
endss[i],
|
/** @type {import("../../geom/Polygon.js").default|import("../../geom/MultiLineString.js").default} */ (
|
||||||
|
geometry
|
||||||
|
).getEnds(),
|
||||||
stride,
|
stride,
|
||||||
myEnds
|
builderEnds
|
||||||
|
);
|
||||||
|
this.instructions.push([
|
||||||
|
CanvasInstruction.CUSTOM,
|
||||||
|
builderBegin,
|
||||||
|
builderEnds,
|
||||||
|
geometry,
|
||||||
|
renderer,
|
||||||
|
inflateCoordinatesArray,
|
||||||
|
]);
|
||||||
|
this.hitDetectionInstructions.push([
|
||||||
|
CanvasInstruction.CUSTOM,
|
||||||
|
builderBegin,
|
||||||
|
builderEnds,
|
||||||
|
geometry,
|
||||||
|
hitDetectionRenderer || renderer,
|
||||||
|
inflateCoordinatesArray,
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
case GeometryType.LINE_STRING:
|
||||||
|
case GeometryType.CIRCLE:
|
||||||
|
flatCoordinates = geometry.getFlatCoordinates();
|
||||||
|
builderEnd = this.appendFlatLineCoordinates(
|
||||||
|
flatCoordinates,
|
||||||
|
0,
|
||||||
|
flatCoordinates.length,
|
||||||
|
stride,
|
||||||
|
false,
|
||||||
|
false
|
||||||
);
|
);
|
||||||
builderEndss.push(myEnds);
|
|
||||||
}
|
|
||||||
this.instructions.push([
|
|
||||||
CanvasInstruction.CUSTOM,
|
|
||||||
builderBegin,
|
|
||||||
builderEndss,
|
|
||||||
geometry,
|
|
||||||
renderer,
|
|
||||||
inflateMultiCoordinatesArray,
|
|
||||||
]);
|
|
||||||
} else if (
|
|
||||||
type == GeometryType.POLYGON ||
|
|
||||||
type == GeometryType.MULTI_LINE_STRING
|
|
||||||
) {
|
|
||||||
builderEnds = [];
|
|
||||||
flatCoordinates =
|
|
||||||
type == GeometryType.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,
|
|
||||||
]);
|
|
||||||
} else if (
|
|
||||||
type == GeometryType.LINE_STRING ||
|
|
||||||
type == GeometryType.CIRCLE
|
|
||||||
) {
|
|
||||||
flatCoordinates = geometry.getFlatCoordinates();
|
|
||||||
builderEnd = this.appendFlatLineCoordinates(
|
|
||||||
flatCoordinates,
|
|
||||||
0,
|
|
||||||
flatCoordinates.length,
|
|
||||||
stride,
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
this.instructions.push([
|
|
||||||
CanvasInstruction.CUSTOM,
|
|
||||||
builderBegin,
|
|
||||||
builderEnd,
|
|
||||||
geometry,
|
|
||||||
renderer,
|
|
||||||
inflateCoordinates,
|
|
||||||
]);
|
|
||||||
} else if (type == GeometryType.MULTI_POINT) {
|
|
||||||
flatCoordinates = geometry.getFlatCoordinates();
|
|
||||||
builderEnd = this.appendFlatPointCoordinates(flatCoordinates, stride);
|
|
||||||
if (builderEnd > builderBegin) {
|
|
||||||
this.instructions.push([
|
this.instructions.push([
|
||||||
CanvasInstruction.CUSTOM,
|
CanvasInstruction.CUSTOM,
|
||||||
builderBegin,
|
builderBegin,
|
||||||
@@ -346,18 +353,59 @@ class CanvasBuilder extends VectorContext {
|
|||||||
renderer,
|
renderer,
|
||||||
inflateCoordinates,
|
inflateCoordinates,
|
||||||
]);
|
]);
|
||||||
}
|
this.hitDetectionInstructions.push([
|
||||||
} else if (type == GeometryType.POINT) {
|
CanvasInstruction.CUSTOM,
|
||||||
flatCoordinates = geometry.getFlatCoordinates();
|
builderBegin,
|
||||||
this.coordinates.push(flatCoordinates[0], flatCoordinates[1]);
|
builderEnd,
|
||||||
builderEnd = this.coordinates.length;
|
geometry,
|
||||||
this.instructions.push([
|
hitDetectionRenderer || renderer,
|
||||||
CanvasInstruction.CUSTOM,
|
inflateCoordinates,
|
||||||
builderBegin,
|
]);
|
||||||
builderEnd,
|
break;
|
||||||
geometry,
|
case GeometryType.MULTI_POINT:
|
||||||
renderer,
|
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 GeometryType.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);
|
this.endGeometry(feature);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,7 +208,8 @@ function renderGeometry(replayGroup, geometry, style, feature) {
|
|||||||
replay.drawCustom(
|
replay.drawCustom(
|
||||||
/** @type {import("../geom/SimpleGeometry.js").default} */ (geometry),
|
/** @type {import("../geom/SimpleGeometry.js").default} */ (geometry),
|
||||||
feature,
|
feature,
|
||||||
style.getRenderer()
|
style.getRenderer(),
|
||||||
|
style.getHitDetectionRenderer()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ import {assert} from '../asserts.js';
|
|||||||
* @property {import("./Image.js").default} [image] Image style.
|
* @property {import("./Image.js").default} [image] Image style.
|
||||||
* @property {RenderFunction} [renderer] Custom renderer. When configured, `fill`, `stroke` and `image` will be
|
* @property {RenderFunction} [renderer] Custom renderer. When configured, `fill`, `stroke` and `image` will be
|
||||||
* ignored, and the provided function will be called with each render frame for each geometry.
|
* ignored, and the provided function will be called with each render frame for each geometry.
|
||||||
|
* @property {RenderFunction} [hitDetectionRenderer] Custom renderer for hit detection. If provided will be used
|
||||||
|
* in hit detection rendering.
|
||||||
* @property {import("./Stroke.js").default} [stroke] Stroke style.
|
* @property {import("./Stroke.js").default} [stroke] Stroke style.
|
||||||
* @property {import("./Text.js").default} [text] Text style.
|
* @property {import("./Text.js").default} [text] Text style.
|
||||||
* @property {number} [zIndex] Z index.
|
* @property {number} [zIndex] Z index.
|
||||||
@@ -186,6 +188,15 @@ class Style {
|
|||||||
*/
|
*/
|
||||||
this.renderer_ = options.renderer !== undefined ? options.renderer : null;
|
this.renderer_ = options.renderer !== undefined ? options.renderer : null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {RenderFunction|null}
|
||||||
|
*/
|
||||||
|
this.hitDetectionRenderer_ =
|
||||||
|
options.hitDetectionRenderer !== undefined
|
||||||
|
? options.hitDetectionRenderer
|
||||||
|
: null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {import("./Stroke.js").default}
|
* @type {import("./Stroke.js").default}
|
||||||
@@ -248,6 +259,26 @@ class Style {
|
|||||||
this.renderer_ = renderer;
|
this.renderer_ = renderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a custom renderer function for this style used
|
||||||
|
* in hit detection.
|
||||||
|
* @param {RenderFunction|null} renderer Custom renderer function.
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
setHitDetectionRenderer(renderer) {
|
||||||
|
this.hitDetectionRenderer_ = renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the custom renderer function that was configured with
|
||||||
|
* {@link #setHitDetectionRenderer} or the `hitDetectionRenderer` constructor option.
|
||||||
|
* @return {RenderFunction|null} Custom renderer function.
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
getHitDetectionRenderer() {
|
||||||
|
return this.hitDetectionRenderer_;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the geometry to be rendered.
|
* Get the geometry to be rendered.
|
||||||
* @return {string|import("../geom/Geometry.js").default|GeometryFunction}
|
* @return {string|import("../geom/Geometry.js").default|GeometryFunction}
|
||||||
|
|||||||
@@ -236,87 +236,193 @@ describe('ol.render.canvas.BuilderGroup', function () {
|
|||||||
expect(lineDashOffset).to.be(4);
|
expect(lineDashOffset).to.be(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls the renderer function configured for the style', function () {
|
describe('use renderer and hitDetectionRenderer defined in style', function () {
|
||||||
const calls = [];
|
let point, multipoint, linestring, multilinestring;
|
||||||
const style = new Style({
|
let polygon, multipolygon, geometrycollection;
|
||||||
renderer: function (coords, state) {
|
|
||||||
calls.push({
|
/**
|
||||||
coords: coords,
|
* @param {BuilderGroup} builder The builder to get instructions from.
|
||||||
geometry: state.geometry,
|
* @param {number} [pixelRatio] The pixel ratio.
|
||||||
feature: state.feature,
|
* @param {boolean} [overlaps] Whether there is overlaps.
|
||||||
context: state.context,
|
* @param {Array<number>} [coordinate] Used for hit detection.
|
||||||
pixelRatio: state.pixelRatio,
|
*/
|
||||||
rotation: state.rotation,
|
function executeHitDetectionForCoordinate(
|
||||||
resolution: state.resolution,
|
builder,
|
||||||
});
|
pixelRatio,
|
||||||
},
|
overlaps,
|
||||||
|
coordinate
|
||||||
|
) {
|
||||||
|
const executor = new ExecutorGroup(
|
||||||
|
[-180, -90, 180, 90],
|
||||||
|
1,
|
||||||
|
pixelRatio || 1,
|
||||||
|
!!overlaps,
|
||||||
|
builder.finish()
|
||||||
|
);
|
||||||
|
|
||||||
|
executor.execute(context, 1, transform, 0, false);
|
||||||
|
executor.forEachFeatureAtCoordinate(coordinate, 1, 0, 1, () => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
point = new Feature(new Point([45, 90]));
|
||||||
|
multipoint = new Feature(
|
||||||
|
new MultiPoint([
|
||||||
|
[45, 90],
|
||||||
|
[90, 45],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
linestring = new Feature(
|
||||||
|
new LineString([
|
||||||
|
[45, 90],
|
||||||
|
[45, 45],
|
||||||
|
[90, 45],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
multilinestring = new Feature(
|
||||||
|
new MultiLineString([
|
||||||
|
linestring.getGeometry().getCoordinates(),
|
||||||
|
linestring.getGeometry().getCoordinates(),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
polygon = feature1;
|
||||||
|
multipolygon = new Feature(
|
||||||
|
new MultiPolygon([
|
||||||
|
polygon.getGeometry().getCoordinates(),
|
||||||
|
polygon.getGeometry().getCoordinates(),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
geometrycollection = new Feature(
|
||||||
|
new GeometryCollection([
|
||||||
|
point.getGeometry(),
|
||||||
|
linestring.getGeometry(),
|
||||||
|
polygon.getGeometry(),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('calls the renderer function in hit detection', function () {
|
||||||
|
const calls = [];
|
||||||
|
const style = new Style({
|
||||||
|
renderer: function (coords, state) {
|
||||||
|
calls.push({
|
||||||
|
coords: coords,
|
||||||
|
geometry: state.geometry,
|
||||||
|
feature: state.feature,
|
||||||
|
context: state.context,
|
||||||
|
pixelRatio: state.pixelRatio,
|
||||||
|
rotation: state.rotation,
|
||||||
|
resolution: state.resolution,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
builder = new BuilderGroup(1, [-180, -90, 180, 90], 1, 1, true);
|
||||||
|
renderFeature(builder, point, style, 1);
|
||||||
|
renderFeature(builder, multipoint, style, 1);
|
||||||
|
renderFeature(builder, linestring, style, 1);
|
||||||
|
renderFeature(builder, multilinestring, style, 1);
|
||||||
|
renderFeature(builder, polygon, style, 1);
|
||||||
|
renderFeature(builder, multipolygon, style, 1);
|
||||||
|
renderFeature(builder, geometrycollection, style, 1);
|
||||||
|
scaleTransform(transform, 0.1, 0.1);
|
||||||
|
executeHitDetectionForCoordinate(builder, 1, true, [45, 90]);
|
||||||
|
|
||||||
|
// since renderer will be used for rendering and hit detection
|
||||||
|
// expect calls.length to be ass twice was in rendering
|
||||||
|
expect(calls.length).to.be(18);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls the hit detection renderer in hit detection', function () {
|
||||||
|
const calls = [];
|
||||||
|
const hitDetectionCalls = [];
|
||||||
|
const style = new Style({
|
||||||
|
renderer: function (coords, state) {
|
||||||
|
calls.push({
|
||||||
|
coords: coords,
|
||||||
|
geometry: state.geometry,
|
||||||
|
feature: state.feature,
|
||||||
|
context: state.context,
|
||||||
|
pixelRatio: state.pixelRatio,
|
||||||
|
rotation: state.rotation,
|
||||||
|
resolution: state.resolution,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
hitDetectionRenderer: function (coords, state) {
|
||||||
|
hitDetectionCalls.push({
|
||||||
|
coords: coords,
|
||||||
|
geometry: state.geometry,
|
||||||
|
feature: state.feature,
|
||||||
|
context: state.context,
|
||||||
|
pixelRatio: state.pixelRatio,
|
||||||
|
rotation: state.rotation,
|
||||||
|
resolution: state.resolution,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
builder = new BuilderGroup(1, [-180, -90, 180, 90], 1, 1, true);
|
||||||
|
renderFeature(builder, point, style, 1);
|
||||||
|
renderFeature(builder, multipoint, style, 1);
|
||||||
|
renderFeature(builder, linestring, style, 1);
|
||||||
|
renderFeature(builder, multilinestring, style, 1);
|
||||||
|
renderFeature(builder, polygon, style, 1);
|
||||||
|
renderFeature(builder, multipolygon, style, 1);
|
||||||
|
renderFeature(builder, geometrycollection, style, 1);
|
||||||
|
scaleTransform(transform, 0.1, 0.1);
|
||||||
|
executeHitDetectionForCoordinate(builder, 1, true, [45, 90]);
|
||||||
|
expect(calls.length).to.be(9);
|
||||||
|
expect(hitDetectionCalls.length).to.be(9);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls the renderer function configured for the style', function () {
|
||||||
|
const calls = [];
|
||||||
|
const style = new Style({
|
||||||
|
renderer: function (coords, state) {
|
||||||
|
calls.push({
|
||||||
|
coords: coords,
|
||||||
|
geometry: state.geometry,
|
||||||
|
feature: state.feature,
|
||||||
|
context: state.context,
|
||||||
|
pixelRatio: state.pixelRatio,
|
||||||
|
rotation: state.rotation,
|
||||||
|
resolution: state.resolution,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
builder = new BuilderGroup(1, [-180, -90, 180, 90], 1, 1, true);
|
||||||
|
renderFeature(builder, point, style, 1);
|
||||||
|
renderFeature(builder, multipoint, style, 1);
|
||||||
|
renderFeature(builder, linestring, style, 1);
|
||||||
|
renderFeature(builder, multilinestring, style, 1);
|
||||||
|
renderFeature(builder, polygon, style, 1);
|
||||||
|
renderFeature(builder, multipolygon, style, 1);
|
||||||
|
renderFeature(builder, geometrycollection, style, 1);
|
||||||
|
scaleTransform(transform, 0.1, 0.1);
|
||||||
|
execute(builder, 1, true);
|
||||||
|
expect(calls.length).to.be(9);
|
||||||
|
expect(calls[0].geometry).to.be(point.getGeometry());
|
||||||
|
expect(calls[0].feature).to.be(point);
|
||||||
|
expect(calls[0].context).to.be(context);
|
||||||
|
expect(calls[0].pixelRatio).to.be(1);
|
||||||
|
expect(calls[0].rotation).to.be(0);
|
||||||
|
expect(calls[0].resolution).to.be(1);
|
||||||
|
expect(calls[0].coords).to.eql([4.5, 9]);
|
||||||
|
expect(calls[1].feature).to.be(multipoint);
|
||||||
|
expect(calls[1].coords[0]).to.eql([4.5, 9]);
|
||||||
|
expect(calls[2].feature).to.be(linestring);
|
||||||
|
expect(calls[2].coords[0]).to.eql([4.5, 9]);
|
||||||
|
expect(calls[3].feature).to.be(multilinestring);
|
||||||
|
expect(calls[3].coords[0][0]).to.eql([4.5, 9]);
|
||||||
|
expect(calls[4].feature).to.be(polygon);
|
||||||
|
expect(calls[4].coords[0][0]).to.eql([-9, -4.5]);
|
||||||
|
expect(calls[5].feature).to.be(multipolygon);
|
||||||
|
expect(calls[5].coords[0][0][0]).to.eql([-9, -4.5]);
|
||||||
|
expect(calls[6].feature).to.be(geometrycollection);
|
||||||
|
expect(calls[6].geometry.getCoordinates()).to.eql([45, 90]);
|
||||||
|
expect(calls[7].geometry.getCoordinates()[0]).to.eql([45, 90]);
|
||||||
|
expect(calls[8].geometry.getCoordinates()[0][0]).to.eql([-90, -45]);
|
||||||
});
|
});
|
||||||
const point = new Feature(new Point([45, 90]));
|
|
||||||
const multipoint = new Feature(
|
|
||||||
new MultiPoint([
|
|
||||||
[45, 90],
|
|
||||||
[90, 45],
|
|
||||||
])
|
|
||||||
);
|
|
||||||
const linestring = new Feature(
|
|
||||||
new LineString([
|
|
||||||
[45, 90],
|
|
||||||
[45, 45],
|
|
||||||
[90, 45],
|
|
||||||
])
|
|
||||||
);
|
|
||||||
const multilinestring = new Feature(
|
|
||||||
new MultiLineString([
|
|
||||||
linestring.getGeometry().getCoordinates(),
|
|
||||||
linestring.getGeometry().getCoordinates(),
|
|
||||||
])
|
|
||||||
);
|
|
||||||
const polygon = feature1;
|
|
||||||
const multipolygon = new Feature(
|
|
||||||
new MultiPolygon([
|
|
||||||
polygon.getGeometry().getCoordinates(),
|
|
||||||
polygon.getGeometry().getCoordinates(),
|
|
||||||
])
|
|
||||||
);
|
|
||||||
const geometrycollection = new Feature(
|
|
||||||
new GeometryCollection([
|
|
||||||
point.getGeometry(),
|
|
||||||
linestring.getGeometry(),
|
|
||||||
polygon.getGeometry(),
|
|
||||||
])
|
|
||||||
);
|
|
||||||
builder = new BuilderGroup(1, [-180, -90, 180, 90], 1, 1, true);
|
|
||||||
renderFeature(builder, point, style, 1);
|
|
||||||
renderFeature(builder, multipoint, style, 1);
|
|
||||||
renderFeature(builder, linestring, style, 1);
|
|
||||||
renderFeature(builder, multilinestring, style, 1);
|
|
||||||
renderFeature(builder, polygon, style, 1);
|
|
||||||
renderFeature(builder, multipolygon, style, 1);
|
|
||||||
renderFeature(builder, geometrycollection, style, 1);
|
|
||||||
scaleTransform(transform, 0.1, 0.1);
|
|
||||||
execute(builder, 1, true);
|
|
||||||
expect(calls.length).to.be(9);
|
|
||||||
expect(calls[0].geometry).to.be(point.getGeometry());
|
|
||||||
expect(calls[0].feature).to.be(point);
|
|
||||||
expect(calls[0].context).to.be(context);
|
|
||||||
expect(calls[0].pixelRatio).to.be(1);
|
|
||||||
expect(calls[0].rotation).to.be(0);
|
|
||||||
expect(calls[0].resolution).to.be(1);
|
|
||||||
expect(calls[0].coords).to.eql([4.5, 9]);
|
|
||||||
expect(calls[1].feature).to.be(multipoint);
|
|
||||||
expect(calls[1].coords[0]).to.eql([4.5, 9]);
|
|
||||||
expect(calls[2].feature).to.be(linestring);
|
|
||||||
expect(calls[2].coords[0]).to.eql([4.5, 9]);
|
|
||||||
expect(calls[3].feature).to.be(multilinestring);
|
|
||||||
expect(calls[3].coords[0][0]).to.eql([4.5, 9]);
|
|
||||||
expect(calls[4].feature).to.be(polygon);
|
|
||||||
expect(calls[4].coords[0][0]).to.eql([-9, -4.5]);
|
|
||||||
expect(calls[5].feature).to.be(multipolygon);
|
|
||||||
expect(calls[5].coords[0][0][0]).to.eql([-9, -4.5]);
|
|
||||||
expect(calls[6].feature).to.be(geometrycollection);
|
|
||||||
expect(calls[6].geometry.getCoordinates()).to.eql([45, 90]);
|
|
||||||
expect(calls[7].geometry.getCoordinates()[0]).to.eql([45, 90]);
|
|
||||||
expect(calls[8].geometry.getCoordinates()[0][0]).to.eql([-90, -45]);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user