Skip to content

Commit

Permalink
Merge pull request #15312 from ahocevar/segment-snap
Browse files Browse the repository at this point in the history
Segment snap
  • Loading branch information
ahocevar committed Nov 9, 2023
2 parents 00a4c16 + fef663f commit 4c6eb5a
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 5 deletions.
7 changes: 7 additions & 0 deletions src/ol/events/SnapEvent.js
Expand Up @@ -26,6 +26,7 @@ export class SnapEvent extends Event {
* @param {import("../coordinate.js").Coordinate} options.vertex The snapped vertex.
* @param {import("../coordinate.js").Coordinate} options.vertexPixel The pixel of the snapped vertex.
* @param {import("../Feature.js").default} options.feature The feature being snapped.
* @param {Array<import("../coordinate.js").Coordinate>|null} options.segment Segment, or `null` if snapped to a vertex.
*/
constructor(type, options) {
super(type);
Expand All @@ -47,5 +48,11 @@ export class SnapEvent extends Event {
* @api
*/
this.feature = options.feature;
/**
* The segment closest to the snapped point, if snapped to a segment.
* @type {Array<import("../coordinate.js").Coordinate>|null}
* @api
*/
this.segment = options.segment;
}
}
8 changes: 8 additions & 0 deletions src/ol/interaction/Snap.js
Expand Up @@ -29,6 +29,7 @@ import {listen, unlistenByKey} from '../events.js';
* @property {import("../coordinate.js").Coordinate|null} vertex Vertex.
* @property {import("../pixel.js").Pixel|null} vertexPixel VertexPixel.
* @property {import("../Feature.js").default|null} feature Feature.
* @property {Array<import("../coordinate.js").Coordinate>|null} segment Segment, or `null` if snapped to a vertex.
*/

/**
Expand Down Expand Up @@ -303,6 +304,7 @@ class Snap extends PointerInteraction {
vertex: evt.coordinate,
vertexPixel: evt.pixel,
feature: result.feature,
segment: result.segment,
})
);
}
Expand Down Expand Up @@ -476,6 +478,7 @@ class Snap extends PointerInteraction {
let closestVertex;
let minSquaredDistance = Infinity;
let closestFeature;
let closestSegment = null;

const squaredPixelTolerance = this.pixelTolerance_ * this.pixelTolerance_;
const getResult = () => {
Expand All @@ -490,6 +493,7 @@ class Snap extends PointerInteraction {
Math.round(vertexPixel[1]),
],
feature: closestFeature,
segment: closestSegment,
};
}
}
Expand Down Expand Up @@ -546,6 +550,10 @@ class Snap extends PointerInteraction {
const delta = squaredDistance(projectedCoordinate, vertex);
if (delta < minSquaredDistance) {
closestVertex = toUserCoordinate(vertex, projection);
closestSegment =
segmentData.feature.getGeometry().getType() === 'Circle'
? null
: segmentData.segment;
minSquaredDistance = delta;
}
}
Expand Down
59 changes: 54 additions & 5 deletions test/browser/spec/ol/interaction/snap.test.js
Expand Up @@ -76,6 +76,7 @@ describe('ol.interaction.Snap', function () {
expect(snapEvent.vertex).to.be(event.coordinate);
expect(snapEvent.vertexPixel).to.be(event.pixel);
expect(snapEvent.feature).to.eql(point);
expect(snapEvent.segment).to.be(null);

// check that the coordinate is in XY and not XYZ
expect(event.coordinate).to.eql([0, 0]);
Expand Down Expand Up @@ -105,6 +106,7 @@ describe('ol.interaction.Snap', function () {
expect(snapEvent.vertex).to.be(event.coordinate);
expect(snapEvent.vertexPixel).to.be(event.pixel);
expect(snapEvent.feature).to.be(point);
expect(snapEvent.segment).to.be(null);

expect(event.coordinate).to.eql([0, 0]);
expect(event.pixel).to.eql([width / 2, height / 2]);
Expand All @@ -115,14 +117,14 @@ describe('ol.interaction.Snap', function () {
});

it('snaps to edges only', function (done) {
const point = new Feature(
const line = new Feature(
new LineString([
[-10, 0],
[10, 0],
])
);
const snapInteraction = new Snap({
features: new Collection([point]),
features: new Collection([line]),
pixelTolerance: 5,
vertex: false,
});
Expand All @@ -135,6 +137,10 @@ describe('ol.interaction.Snap', function () {

snapInteraction.on('snap', function (snapEvent) {
expect(snapEvent.feature).to.be(undefined);
expect(snapEvent.segment).to.eql([
[-10, 0],
[10, 0],
]);

expect(event.coordinate).to.eql([7, 0]);

Expand All @@ -148,14 +154,14 @@ describe('ol.interaction.Snap', function () {
const userProjection = 'EPSG:3857';
setUserProjection(userProjection);
const viewProjection = map.getView().getProjection();
const point = new Feature(
const line = new Feature(
new LineString([
[-10, 0],
[10, 0],
]).transform(viewProjection, userProjection)
);
const snapInteraction = new Snap({
features: new Collection([point]),
features: new Collection([line]),
pixelTolerance: 5,
vertex: false,
});
Expand All @@ -171,6 +177,10 @@ describe('ol.interaction.Snap', function () {

snapInteraction.on('snap', function (snapEvent) {
expect(snapEvent.feature).to.be(undefined);
expect(snapEvent.segment).to.eql([
transform([-10, 0], viewProjection, userProjection),
transform([10, 0], viewProjection, userProjection),
]);

expect(event.coordinate[0]).to.roughlyEqual(coordinate[0], 1e-10);
expect(event.coordinate[1]).to.roughlyEqual(coordinate[1], 1e-10);
Expand Down Expand Up @@ -201,6 +211,7 @@ describe('ol.interaction.Snap', function () {
};
snapInteraction.on('snap', function (snapEvent) {
expect(snapEvent.feature).to.be(point);
expect(snapEvent.segment).to.be(null);

expect(event.coordinate).to.eql([10, 0]);

Expand All @@ -209,7 +220,7 @@ describe('ol.interaction.Snap', function () {
snapInteraction.handleEvent(event);
});

it('snaps to vertex on line', function (done) {
it('snaps to point', function (done) {
const line = new Feature(
new LineString([
[0, 0],
Expand All @@ -228,6 +239,7 @@ describe('ol.interaction.Snap', function () {
};
snapInteraction.on('snap', function (snapEvent) {
expect(snapEvent.feature).to.be(point);
expect(snapEvent.segment).to.be(null);

expect(event.coordinate).to.eql([5, 0]);

Expand All @@ -236,6 +248,37 @@ describe('ol.interaction.Snap', function () {
snapInteraction.handleEvent(event);
});

it('snaps to point along line', function (done) {
const line = new Feature(
new LineString([
[0, 0],
[50, 0],
])
);
const point = new Feature(new Point([5, 0]));
const snapInteraction = new Snap({
features: new Collection([line, point]),
});
snapInteraction.setMap(map);
const event = {
pixel: [16 + width / 2, 5 + height / 2],
coordinate: [16, 5],
map: map,
};
snapInteraction.on('snap', function (snapEvent) {
expect(snapEvent.feature).to.be(line);
expect(snapEvent.segment).to.eql([
[0, 0],
[50, 0],
]);

expect(event.coordinate).to.eql([16, 0]);

done();
});
snapInteraction.handleEvent(event);
});

it('snaps to circle', function (done) {
const circle = new Feature(new Circle([0, 0], 10));
const snapInteraction = new Snap({
Expand All @@ -251,6 +294,7 @@ describe('ol.interaction.Snap', function () {
};
snapInteraction.on('snap', function (snapEvent) {
expect(snapEvent.feature).to.be(undefined);
expect(snapEvent.segment).to.be(null);

expect(event.coordinate[0]).to.roughlyEqual(
Math.sin(Math.PI / 4) * 10,
Expand Down Expand Up @@ -294,6 +338,7 @@ describe('ol.interaction.Snap', function () {

snapInteraction.on('snap', function (snapEvent) {
expect(snapEvent.feature).to.be(undefined);
expect(snapEvent.segment).to.be(null);

expect(event.coordinate[0]).to.roughlyEqual(coordinate[0], 1e-10);
expect(event.coordinate[1]).to.roughlyEqual(coordinate[1], 1e-10);
Expand Down Expand Up @@ -326,6 +371,7 @@ describe('ol.interaction.Snap', function () {
};
snapInteraction.on('snap', function (snapEvent) {
expect(snapEvent.feature).to.be(feature);
expect(snapEvent.segment).to.be(null);

expect(event.coordinate).to.eql([10, 0]);

Expand Down Expand Up @@ -360,6 +406,7 @@ describe('ol.interaction.Snap', function () {
};
snapInteraction.on('snap', function (snapEvent) {
expect(snapEvent.feature).to.be(line);
expect(snapEvent.segment).to.be(null);

expect(event.coordinate).to.eql([10, 0]);

Expand Down Expand Up @@ -396,6 +443,7 @@ describe('ol.interaction.Snap', function () {

snapInteraction.on('snap', function (snapEvent) {
expect(snapEvent.feature).to.be(line);
expect(snapEvent.segment).to.be(null);

expect(event.coordinate).to.eql([10, 0]);

Expand Down Expand Up @@ -475,6 +523,7 @@ describe('ol.interaction.Snap', function () {
};
snapInteraction.on('snap', function (snapEvent) {
expect(snapEvent.feature).to.be(point);
expect(snapEvent.segment).to.be(null);

expect(event.coordinate).to.eql([lon, lat]);
expect(event.pixel).to.eql(expectedPixel);
Expand Down

0 comments on commit 4c6eb5a

Please sign in to comment.