User coordinates during snapping

This commit is contained in:
Tim Schaub
2019-09-27 11:55:48 +02:00
parent b40709ea5b
commit 07678d960a
3 changed files with 109 additions and 97 deletions

View File

@@ -920,12 +920,12 @@ class Modify extends PointerInteraction {
*/
handlePointerAtPixel_(pixel, map) {
const pixelCoordinate = map.getCoordinateFromPixel(pixel);
const projection = map.getView().getProjection();
const sortByDistance = function(a, b) {
return pointDistanceToSegmentDataSquared(pixelCoordinate, a) -
pointDistanceToSegmentDataSquared(pixelCoordinate, b);
return projectedDistanceToSegmentDataSquared(pixelCoordinate, a, projection) -
projectedDistanceToSegmentDataSquared(pixelCoordinate, b, projection);
};
const projection = map.getView().getProjection();
const viewExtent = fromUserExtent(createExtent(pixelCoordinate, tempExtent), projection);
const buffer = map.getView().getResolution() * this.pixelTolerance_;
const box = toUserExtent(bufferExtent(viewExtent, buffer, tempExtent), projection);
@@ -1235,9 +1235,10 @@ function compareIndexes(a, b) {
* which to calculate the distance.
* @param {SegmentData} segmentData The object describing the line
* segment we are calculating the distance to.
* @param {import("../proj/Projection.js").default} projection The view projection.
* @return {number} The square of the distance between a point and a line segment.
*/
function pointDistanceToSegmentDataSquared(pointCoordinates, segmentData) {
function projectedDistanceToSegmentDataSquared(pointCoordinates, segmentData, projection) {
const geometry = segmentData.geometry;
if (geometry.getType() === GeometryType.CIRCLE) {
@@ -1251,7 +1252,11 @@ function pointDistanceToSegmentDataSquared(pointCoordinates, segmentData) {
return distanceToCircumference * distanceToCircumference;
}
}
return squaredDistanceToSegment(pointCoordinates, segmentData.segment);
const coordinate = fromUserCoordinate(pointCoordinates, projection);
tempSegment[0] = fromUserCoordinate(segmentData.segment[0], projection);
tempSegment[1] = fromUserCoordinate(segmentData.segment[1], projection);
return squaredDistanceToSegment(coordinate, tempSegment);
}
/**

View File

@@ -14,6 +14,7 @@ import PointerInteraction from './Pointer.js';
import {getValues} from '../obj.js';
import VectorEventType from '../source/VectorEventType.js';
import RBush from '../structs/RBush.js';
import {fromUserCoordinate, toUserCoordinate} from '../proj.js';
/**
@@ -52,9 +53,10 @@ function getFeatureFromEvent(evt) {
} else if (/** @type {import("../Collection.js").CollectionEvent} */ (evt).element) {
return /** @type {import("../Feature.js").default} */ (/** @type {import("../Collection.js").CollectionEvent} */ (evt).element);
}
}
const tempSegment = [];
/**
* @classdesc
* Handles snapping of vector features while modifying or drawing them. The
@@ -70,10 +72,12 @@ function getFeatureFromEvent(evt) {
*
* import Snap from 'ol/interaction/Snap';
*
* var snap = new Snap({
* const snap = new Snap({
* source: source
* });
*
* map.addInteraction(snap);
*
* @api
*/
class Snap extends PointerInteraction {
@@ -149,13 +153,6 @@ class Snap extends PointerInteraction {
*/
this.pendingFeatures_ = {};
/**
* Used for distance sorting in sortByDistance_
* @type {import("../coordinate.js").Coordinate}
* @private
*/
this.pixelCoordinate_ = null;
/**
* @type {number}
* @private
@@ -163,13 +160,6 @@ class Snap extends PointerInteraction {
this.pixelTolerance_ = options.pixelTolerance !== undefined ?
options.pixelTolerance : 10;
/**
* @type {function(SegmentData, SegmentData): number}
* @private
*/
this.sortByDistance_ = sortByDistance.bind(this);
/**
* Segment RTree for each layer
* @type {import("../structs/RBush.js").default<SegmentData>}
@@ -177,22 +167,21 @@ class Snap extends PointerInteraction {
*/
this.rBush_ = new RBush();
/**
* @const
* @private
* @type {Object<string, function(import("../Feature.js").default, import("../geom/Geometry.js").default): void>}
*/
this.SEGMENT_WRITERS_ = {
'Point': this.writePointGeometry_,
'LineString': this.writeLineStringGeometry_,
'LinearRing': this.writeLineStringGeometry_,
'Polygon': this.writePolygonGeometry_,
'MultiPoint': this.writeMultiPointGeometry_,
'MultiLineString': this.writeMultiLineStringGeometry_,
'MultiPolygon': this.writeMultiPolygonGeometry_,
'GeometryCollection': this.writeGeometryCollectionGeometry_,
'Circle': this.writeCircleGeometry_
'Point': this.writePointGeometry_.bind(this),
'LineString': this.writeLineStringGeometry_.bind(this),
'LinearRing': this.writeLineStringGeometry_.bind(this),
'Polygon': this.writePolygonGeometry_.bind(this),
'MultiPoint': this.writeMultiPointGeometry_.bind(this),
'MultiLineString': this.writeMultiLineStringGeometry_.bind(this),
'MultiPolygon': this.writeMultiPolygonGeometry_.bind(this),
'GeometryCollection': this.writeGeometryCollectionGeometry_.bind(this),
'Circle': this.writeCircleGeometry_.bind(this)
};
}
@@ -211,7 +200,7 @@ class Snap extends PointerInteraction {
const segmentWriter = this.SEGMENT_WRITERS_[geometry.getType()];
if (segmentWriter) {
this.indexedFeaturesExtents_[feature_uid] = geometry.getExtent(createEmpty());
segmentWriter.call(this, feature, geometry);
segmentWriter(feature, geometry);
}
}
@@ -383,10 +372,9 @@ class Snap extends PointerInteraction {
* @return {Result} Snap result
*/
snapTo(pixel, pixelCoordinate, map) {
const lowerLeft = map.getCoordinateFromPixelInternal(
const lowerLeft = map.getCoordinateFromPixel(
[pixel[0] - this.pixelTolerance_, pixel[1] + this.pixelTolerance_]);
const upperRight = map.getCoordinateFromPixelInternal(
const upperRight = map.getCoordinateFromPixel(
[pixel[0] + this.pixelTolerance_, pixel[1] - this.pixelTolerance_]);
const box = boundingExtent([lowerLeft, upperRight]);
@@ -400,57 +388,78 @@ class Snap extends PointerInteraction {
});
}
let snappedToVertex = false;
let snapped = false;
let vertex = null;
let vertexPixel = null;
let dist, pixel1, pixel2, squaredDist1, squaredDist2;
if (segments.length > 0) {
this.pixelCoordinate_ = pixelCoordinate;
segments.sort(this.sortByDistance_);
const closestSegment = segments[0].segment;
const isCircle = segments[0].feature.getGeometry().getType() ===
GeometryType.CIRCLE;
if (this.vertex_ && !this.edge_) {
pixel1 = map.getPixelFromCoordinateInternal(closestSegment[0]);
pixel2 = map.getPixelFromCoordinateInternal(closestSegment[1]);
squaredDist1 = squaredCoordinateDistance(pixel, pixel1);
squaredDist2 = squaredCoordinateDistance(pixel, pixel2);
dist = Math.sqrt(Math.min(squaredDist1, squaredDist2));
snappedToVertex = dist <= this.pixelTolerance_;
if (snappedToVertex) {
snapped = true;
vertex = squaredDist1 > squaredDist2 ? closestSegment[1] : closestSegment[0];
vertexPixel = map.getPixelFromCoordinateInternal(vertex);
}
} else if (this.edge_) {
if (isCircle) {
vertex = closestOnCircle(pixelCoordinate,
/** @type {import("../geom/Circle.js").default} */ (segments[0].feature.getGeometry()));
} else {
vertex = closestOnSegment(pixelCoordinate, closestSegment);
}
vertexPixel = map.getPixelFromCoordinateInternal(vertex);
if (coordinateDistance(pixel, vertexPixel) <= this.pixelTolerance_) {
snapped = true;
if (this.vertex_ && !isCircle) {
pixel1 = map.getPixelFromCoordinateInternal(closestSegment[0]);
pixel2 = map.getPixelFromCoordinateInternal(closestSegment[1]);
squaredDist1 = squaredCoordinateDistance(vertexPixel, pixel1);
squaredDist2 = squaredCoordinateDistance(vertexPixel, pixel2);
dist = Math.sqrt(Math.min(squaredDist1, squaredDist2));
snappedToVertex = dist <= this.pixelTolerance_;
if (snappedToVertex) {
vertex = squaredDist1 > squaredDist2 ? closestSegment[1] : closestSegment[0];
vertexPixel = map.getPixelFromCoordinateInternal(vertex);
}
if (segments.length === 0) {
return {
snapped: snapped,
vertex: vertex,
vertexPixel: vertexPixel
};
}
const projection = map.getView().getProjection();
const projectedCoordinate = fromUserCoordinate(pixelCoordinate, projection);
let closestSegmentData;
let minSquaredDistance = Infinity;
for (let i = 0; i < segments.length; ++i) {
const segmentData = segments[i];
tempSegment[0] = fromUserCoordinate(segmentData.segment[0], projection);
tempSegment[1] = fromUserCoordinate(segmentData.segment[1], projection);
const delta = squaredDistanceToSegment(projectedCoordinate, tempSegment);
if (delta < minSquaredDistance) {
closestSegmentData = segmentData;
minSquaredDistance = delta;
}
}
const closestSegment = closestSegmentData.segment;
if (this.vertex_ && !this.edge_) {
const pixel1 = map.getPixelFromCoordinate(closestSegment[0]);
const pixel2 = map.getPixelFromCoordinate(closestSegment[1]);
const squaredDist1 = squaredCoordinateDistance(pixel, pixel1);
const squaredDist2 = squaredCoordinateDistance(pixel, pixel2);
const dist = Math.sqrt(Math.min(squaredDist1, squaredDist2));
if (dist <= this.pixelTolerance_) {
snapped = true;
vertex = squaredDist1 > squaredDist2 ? closestSegment[1] : closestSegment[0];
vertexPixel = map.getPixelFromCoordinate(vertex);
}
} else if (this.edge_) {
const isCircle = closestSegmentData.feature.getGeometry().getType() === GeometryType.CIRCLE;
if (isCircle) {
vertex = closestOnCircle(pixelCoordinate,
/** @type {import("../geom/Circle.js").default} */ (closestSegmentData.feature.getGeometry()));
} else {
tempSegment[0] = fromUserCoordinate(closestSegment[0], projection);
tempSegment[1] = fromUserCoordinate(closestSegment[1], projection);
vertex = toUserCoordinate(closestOnSegment(projectedCoordinate, tempSegment), projection);
}
vertexPixel = map.getPixelFromCoordinate(vertex);
if (coordinateDistance(pixel, vertexPixel) <= this.pixelTolerance_) {
snapped = true;
if (this.vertex_ && !isCircle) {
const pixel1 = map.getPixelFromCoordinate(closestSegment[0]);
const pixel2 = map.getPixelFromCoordinate(closestSegment[1]);
const squaredDist1 = squaredCoordinateDistance(vertexPixel, pixel1);
const squaredDist2 = squaredCoordinateDistance(vertexPixel, pixel2);
const dist = Math.sqrt(Math.min(squaredDist1, squaredDist2));
if (dist <= this.pixelTolerance_) {
vertex = squaredDist1 > squaredDist2 ? closestSegment[1] : closestSegment[0];
vertexPixel = map.getPixelFromCoordinate(vertex);
}
}
}
if (snapped) {
vertexPixel = [Math.round(vertexPixel[0]), Math.round(vertexPixel[1])];
}
}
if (snapped) {
vertexPixel = [Math.round(vertexPixel[0]), Math.round(vertexPixel[1])];
}
return {
snapped: snapped,
vertex: vertex,
@@ -495,7 +504,7 @@ class Snap extends PointerInteraction {
for (let i = 0; i < geometries.length; ++i) {
const segmentWriter = this.SEGMENT_WRITERS_[geometries[i].getType()];
if (segmentWriter) {
segmentWriter.call(this, feature, geometries[i]);
segmentWriter(feature, geometries[i]);
}
}
}
@@ -613,17 +622,4 @@ class Snap extends PointerInteraction {
}
/**
* Sort segments by distance, helper function
* @param {SegmentData} a The first segment data.
* @param {SegmentData} b The second segment data.
* @return {number} The difference in distance.
* @this {Snap}
*/
function sortByDistance(a, b) {
const deltaA = squaredDistanceToSegment(this.pixelCoordinate_, a.segment);
const deltaB = squaredDistanceToSegment(this.pixelCoordinate_, b.segment);
return deltaA - deltaB;
}
export default Snap;