This adds the pixelTolerance option parameter to the constructor of ol.interaction.Extent. In this way the user can override the current default value of 10.
468 lines
13 KiB
JavaScript
468 lines
13 KiB
JavaScript
goog.provide('ol.interaction.Extent');
|
|
|
|
goog.require('ol');
|
|
goog.require('ol.Feature');
|
|
goog.require('ol.MapBrowserEventType');
|
|
goog.require('ol.MapBrowserPointerEvent');
|
|
goog.require('ol.coordinate');
|
|
goog.require('ol.events.Event');
|
|
goog.require('ol.extent');
|
|
goog.require('ol.geom.GeometryType');
|
|
goog.require('ol.geom.Point');
|
|
goog.require('ol.geom.Polygon');
|
|
goog.require('ol.interaction.ExtentEventType');
|
|
goog.require('ol.interaction.Pointer');
|
|
goog.require('ol.layer.Vector');
|
|
goog.require('ol.source.Vector');
|
|
goog.require('ol.style.Style');
|
|
|
|
|
|
/**
|
|
* @classdesc
|
|
* Allows the user to draw a vector box by clicking and dragging on the map.
|
|
* Once drawn, the vector box can be modified by dragging its vertices or edges.
|
|
* This interaction is only supported for mouse devices.
|
|
*
|
|
* @constructor
|
|
* @extends {ol.interaction.Pointer}
|
|
* @fires ol.interaction.Extent.Event
|
|
* @param {olx.interaction.ExtentOptions=} opt_options Options.
|
|
* @api
|
|
*/
|
|
ol.interaction.Extent = function(opt_options) {
|
|
|
|
var options = opt_options ? opt_options : {};
|
|
|
|
/**
|
|
* Extent of the drawn box
|
|
* @type {ol.Extent}
|
|
* @private
|
|
*/
|
|
this.extent_ = null;
|
|
|
|
/**
|
|
* Handler for pointer move events
|
|
* @type {function (ol.Coordinate): ol.Extent|null}
|
|
* @private
|
|
*/
|
|
this.pointerHandler_ = null;
|
|
|
|
/**
|
|
* Pixel threshold to snap to extent
|
|
* @type {number}
|
|
* @private
|
|
*/
|
|
this.pixelTolerance_ = options.pixelTolerance !== undefined ?
|
|
options.pixelTolerance : 10;
|
|
|
|
/**
|
|
* Is the pointer snapped to an extent vertex
|
|
* @type {boolean}
|
|
* @private
|
|
*/
|
|
this.snappedToVertex_ = false;
|
|
|
|
/**
|
|
* Feature for displaying the visible extent
|
|
* @type {ol.Feature}
|
|
* @private
|
|
*/
|
|
this.extentFeature_ = null;
|
|
|
|
/**
|
|
* Feature for displaying the visible pointer
|
|
* @type {ol.Feature}
|
|
* @private
|
|
*/
|
|
this.vertexFeature_ = null;
|
|
|
|
if (!opt_options) {
|
|
opt_options = {};
|
|
}
|
|
|
|
/* Inherit ol.interaction.Pointer */
|
|
ol.interaction.Pointer.call(this, {
|
|
handleDownEvent: ol.interaction.Extent.handleDownEvent_,
|
|
handleDragEvent: ol.interaction.Extent.handleDragEvent_,
|
|
handleEvent: ol.interaction.Extent.handleEvent_,
|
|
handleUpEvent: ol.interaction.Extent.handleUpEvent_
|
|
});
|
|
|
|
/**
|
|
* Layer for the extentFeature
|
|
* @type {ol.layer.Vector}
|
|
* @private
|
|
*/
|
|
this.extentOverlay_ = new ol.layer.Vector({
|
|
source: new ol.source.Vector({
|
|
useSpatialIndex: false,
|
|
wrapX: !!opt_options.wrapX
|
|
}),
|
|
style: opt_options.boxStyle ? opt_options.boxStyle : ol.interaction.Extent.getDefaultExtentStyleFunction_(),
|
|
updateWhileAnimating: true,
|
|
updateWhileInteracting: true
|
|
});
|
|
|
|
/**
|
|
* Layer for the vertexFeature
|
|
* @type {ol.layer.Vector}
|
|
* @private
|
|
*/
|
|
this.vertexOverlay_ = new ol.layer.Vector({
|
|
source: new ol.source.Vector({
|
|
useSpatialIndex: false,
|
|
wrapX: !!opt_options.wrapX
|
|
}),
|
|
style: opt_options.pointerStyle ? opt_options.pointerStyle : ol.interaction.Extent.getDefaultPointerStyleFunction_(),
|
|
updateWhileAnimating: true,
|
|
updateWhileInteracting: true
|
|
});
|
|
|
|
if (opt_options.extent) {
|
|
this.setExtent(opt_options.extent);
|
|
}
|
|
};
|
|
|
|
ol.inherits(ol.interaction.Extent, ol.interaction.Pointer);
|
|
|
|
/**
|
|
* @param {ol.MapBrowserEvent} mapBrowserEvent Event.
|
|
* @return {boolean} Propagate event?
|
|
* @this {ol.interaction.Extent}
|
|
* @private
|
|
*/
|
|
ol.interaction.Extent.handleEvent_ = function(mapBrowserEvent) {
|
|
if (!(mapBrowserEvent instanceof ol.MapBrowserPointerEvent)) {
|
|
return true;
|
|
}
|
|
//display pointer (if not dragging)
|
|
if (mapBrowserEvent.type == ol.MapBrowserEventType.POINTERMOVE && !this.handlingDownUpSequence) {
|
|
this.handlePointerMove_(mapBrowserEvent);
|
|
}
|
|
//call pointer to determine up/down/drag
|
|
ol.interaction.Pointer.handleEvent.call(this, mapBrowserEvent);
|
|
//return false to stop propagation
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
|
|
* @return {boolean} Event handled?
|
|
* @this {ol.interaction.Extent}
|
|
* @private
|
|
*/
|
|
ol.interaction.Extent.handleDownEvent_ = function(mapBrowserEvent) {
|
|
var pixel = mapBrowserEvent.pixel;
|
|
var map = mapBrowserEvent.map;
|
|
|
|
var extent = this.getExtent();
|
|
var vertex = this.snapToVertex_(pixel, map);
|
|
|
|
//find the extent corner opposite the passed corner
|
|
var getOpposingPoint = function(point) {
|
|
var x_ = null;
|
|
var y_ = null;
|
|
if (point[0] == extent[0]) {
|
|
x_ = extent[2];
|
|
} else if (point[0] == extent[2]) {
|
|
x_ = extent[0];
|
|
}
|
|
if (point[1] == extent[1]) {
|
|
y_ = extent[3];
|
|
} else if (point[1] == extent[3]) {
|
|
y_ = extent[1];
|
|
}
|
|
if (x_ !== null && y_ !== null) {
|
|
return [x_, y_];
|
|
}
|
|
return null;
|
|
};
|
|
if (vertex && extent) {
|
|
var x = (vertex[0] == extent[0] || vertex[0] == extent[2]) ? vertex[0] : null;
|
|
var y = (vertex[1] == extent[1] || vertex[1] == extent[3]) ? vertex[1] : null;
|
|
|
|
//snap to point
|
|
if (x !== null && y !== null) {
|
|
this.pointerHandler_ = ol.interaction.Extent.getPointHandler_(getOpposingPoint(vertex));
|
|
//snap to edge
|
|
} else if (x !== null) {
|
|
this.pointerHandler_ = ol.interaction.Extent.getEdgeHandler_(
|
|
getOpposingPoint([x, extent[1]]),
|
|
getOpposingPoint([x, extent[3]])
|
|
);
|
|
} else if (y !== null) {
|
|
this.pointerHandler_ = ol.interaction.Extent.getEdgeHandler_(
|
|
getOpposingPoint([extent[0], y]),
|
|
getOpposingPoint([extent[2], y])
|
|
);
|
|
}
|
|
//no snap - new bbox
|
|
} else {
|
|
vertex = map.getCoordinateFromPixel(pixel);
|
|
this.setExtent([vertex[0], vertex[1], vertex[0], vertex[1]]);
|
|
this.pointerHandler_ = ol.interaction.Extent.getPointHandler_(vertex);
|
|
}
|
|
return true; //event handled; start downup sequence
|
|
};
|
|
|
|
/**
|
|
* @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
|
|
* @return {boolean} Event handled?
|
|
* @this {ol.interaction.Extent}
|
|
* @private
|
|
*/
|
|
ol.interaction.Extent.handleDragEvent_ = function(mapBrowserEvent) {
|
|
if (this.pointerHandler_) {
|
|
var pixelCoordinate = mapBrowserEvent.coordinate;
|
|
this.setExtent(this.pointerHandler_(pixelCoordinate));
|
|
this.createOrUpdatePointerFeature_(pixelCoordinate);
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
|
|
* @return {boolean} Stop drag sequence?
|
|
* @this {ol.interaction.Extent}
|
|
* @private
|
|
*/
|
|
ol.interaction.Extent.handleUpEvent_ = function(mapBrowserEvent) {
|
|
this.pointerHandler_ = null;
|
|
//If bbox is zero area, set to null;
|
|
var extent = this.getExtent();
|
|
if (!extent || ol.extent.getArea(extent) === 0) {
|
|
this.setExtent(null);
|
|
}
|
|
return false; //Stop handling downup sequence
|
|
};
|
|
|
|
/**
|
|
* Returns the default style for the drawn bbox
|
|
*
|
|
* @return {ol.StyleFunction} Default Extent style
|
|
* @private
|
|
*/
|
|
ol.interaction.Extent.getDefaultExtentStyleFunction_ = function() {
|
|
var style = ol.style.Style.createDefaultEditing();
|
|
return function(feature, resolution) {
|
|
return style[ol.geom.GeometryType.POLYGON];
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Returns the default style for the pointer
|
|
*
|
|
* @return {ol.StyleFunction} Default pointer style
|
|
* @private
|
|
*/
|
|
ol.interaction.Extent.getDefaultPointerStyleFunction_ = function() {
|
|
var style = ol.style.Style.createDefaultEditing();
|
|
return function(feature, resolution) {
|
|
return style[ol.geom.GeometryType.POINT];
|
|
};
|
|
};
|
|
|
|
/**
|
|
* @param {ol.Coordinate} fixedPoint corner that will be unchanged in the new extent
|
|
* @returns {function (ol.Coordinate): ol.Extent} event handler
|
|
* @private
|
|
*/
|
|
ol.interaction.Extent.getPointHandler_ = function(fixedPoint) {
|
|
return function(point) {
|
|
return ol.extent.boundingExtent([fixedPoint, point]);
|
|
};
|
|
};
|
|
|
|
/**
|
|
* @param {ol.Coordinate} fixedP1 first corner that will be unchanged in the new extent
|
|
* @param {ol.Coordinate} fixedP2 second corner that will be unchanged in the new extent
|
|
* @returns {function (ol.Coordinate): ol.Extent|null} event handler
|
|
* @private
|
|
*/
|
|
ol.interaction.Extent.getEdgeHandler_ = function(fixedP1, fixedP2) {
|
|
if (fixedP1[0] == fixedP2[0]) {
|
|
return function(point) {
|
|
return ol.extent.boundingExtent([fixedP1, [point[0], fixedP2[1]]]);
|
|
};
|
|
} else if (fixedP1[1] == fixedP2[1]) {
|
|
return function(point) {
|
|
return ol.extent.boundingExtent([fixedP1, [fixedP2[0], point[1]]]);
|
|
};
|
|
} else {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {ol.Extent} extent extent
|
|
* @returns {Array<Array<ol.Coordinate>>} extent line segments
|
|
* @private
|
|
*/
|
|
ol.interaction.Extent.getSegments_ = function(extent) {
|
|
return [
|
|
[[extent[0], extent[1]], [extent[0], extent[3]]],
|
|
[[extent[0], extent[3]], [extent[2], extent[3]]],
|
|
[[extent[2], extent[3]], [extent[2], extent[1]]],
|
|
[[extent[2], extent[1]], [extent[0], extent[1]]]
|
|
];
|
|
};
|
|
|
|
/**
|
|
* @param {ol.Pixel} pixel cursor location
|
|
* @param {ol.PluggableMap} map map
|
|
* @returns {ol.Coordinate|null} snapped vertex on extent
|
|
* @private
|
|
*/
|
|
ol.interaction.Extent.prototype.snapToVertex_ = function(pixel, map) {
|
|
var pixelCoordinate = map.getCoordinateFromPixel(pixel);
|
|
var sortByDistance = function(a, b) {
|
|
return ol.coordinate.squaredDistanceToSegment(pixelCoordinate, a) -
|
|
ol.coordinate.squaredDistanceToSegment(pixelCoordinate, b);
|
|
};
|
|
var extent = this.getExtent();
|
|
if (extent) {
|
|
//convert extents to line segments and find the segment closest to pixelCoordinate
|
|
var segments = ol.interaction.Extent.getSegments_(extent);
|
|
segments.sort(sortByDistance);
|
|
var closestSegment = segments[0];
|
|
|
|
var vertex = (ol.coordinate.closestOnSegment(pixelCoordinate,
|
|
closestSegment));
|
|
var vertexPixel = map.getPixelFromCoordinate(vertex);
|
|
|
|
//if the distance is within tolerance, snap to the segment
|
|
if (ol.coordinate.distance(pixel, vertexPixel) <= this.pixelTolerance_) {
|
|
//test if we should further snap to a vertex
|
|
var pixel1 = map.getPixelFromCoordinate(closestSegment[0]);
|
|
var pixel2 = map.getPixelFromCoordinate(closestSegment[1]);
|
|
var squaredDist1 = ol.coordinate.squaredDistance(vertexPixel, pixel1);
|
|
var squaredDist2 = ol.coordinate.squaredDistance(vertexPixel, pixel2);
|
|
var dist = Math.sqrt(Math.min(squaredDist1, squaredDist2));
|
|
this.snappedToVertex_ = dist <= this.pixelTolerance_;
|
|
if (this.snappedToVertex_) {
|
|
vertex = squaredDist1 > squaredDist2 ?
|
|
closestSegment[1] : closestSegment[0];
|
|
}
|
|
return vertex;
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* @param {ol.MapBrowserEvent} mapBrowserEvent pointer move event
|
|
* @private
|
|
*/
|
|
ol.interaction.Extent.prototype.handlePointerMove_ = function(mapBrowserEvent) {
|
|
var pixel = mapBrowserEvent.pixel;
|
|
var map = mapBrowserEvent.map;
|
|
|
|
var vertex = this.snapToVertex_(pixel, map);
|
|
if (!vertex) {
|
|
vertex = map.getCoordinateFromPixel(pixel);
|
|
}
|
|
this.createOrUpdatePointerFeature_(vertex);
|
|
};
|
|
|
|
/**
|
|
* @param {ol.Extent} extent extent
|
|
* @returns {ol.Feature} extent as featrue
|
|
* @private
|
|
*/
|
|
ol.interaction.Extent.prototype.createOrUpdateExtentFeature_ = function(extent) {
|
|
var extentFeature = this.extentFeature_;
|
|
|
|
if (!extentFeature) {
|
|
if (!extent) {
|
|
extentFeature = new ol.Feature({});
|
|
} else {
|
|
extentFeature = new ol.Feature(ol.geom.Polygon.fromExtent(extent));
|
|
}
|
|
this.extentFeature_ = extentFeature;
|
|
this.extentOverlay_.getSource().addFeature(extentFeature);
|
|
} else {
|
|
if (!extent) {
|
|
extentFeature.setGeometry(undefined);
|
|
} else {
|
|
extentFeature.setGeometry(ol.geom.Polygon.fromExtent(extent));
|
|
}
|
|
}
|
|
return extentFeature;
|
|
};
|
|
|
|
|
|
/**
|
|
* @param {ol.Coordinate} vertex location of feature
|
|
* @returns {ol.Feature} vertex as feature
|
|
* @private
|
|
*/
|
|
ol.interaction.Extent.prototype.createOrUpdatePointerFeature_ = function(vertex) {
|
|
var vertexFeature = this.vertexFeature_;
|
|
if (!vertexFeature) {
|
|
vertexFeature = new ol.Feature(new ol.geom.Point(vertex));
|
|
this.vertexFeature_ = vertexFeature;
|
|
this.vertexOverlay_.getSource().addFeature(vertexFeature);
|
|
} else {
|
|
var geometry = /** @type {ol.geom.Point} */ (vertexFeature.getGeometry());
|
|
geometry.setCoordinates(vertex);
|
|
}
|
|
return vertexFeature;
|
|
};
|
|
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
ol.interaction.Extent.prototype.setMap = function(map) {
|
|
this.extentOverlay_.setMap(map);
|
|
this.vertexOverlay_.setMap(map);
|
|
ol.interaction.Pointer.prototype.setMap.call(this, map);
|
|
};
|
|
|
|
/**
|
|
* Returns the current drawn extent in the view projection
|
|
*
|
|
* @return {ol.Extent} Drawn extent in the view projection.
|
|
* @api
|
|
*/
|
|
ol.interaction.Extent.prototype.getExtent = function() {
|
|
return this.extent_;
|
|
};
|
|
|
|
/**
|
|
* Manually sets the drawn extent, using the view projection.
|
|
*
|
|
* @param {ol.Extent} extent Extent
|
|
* @api
|
|
*/
|
|
ol.interaction.Extent.prototype.setExtent = function(extent) {
|
|
//Null extent means no bbox
|
|
this.extent_ = extent ? extent : null;
|
|
this.createOrUpdateExtentFeature_(extent);
|
|
this.dispatchEvent(new ol.interaction.Extent.Event(this.extent_));
|
|
};
|
|
|
|
|
|
/**
|
|
* @classdesc
|
|
* Events emitted by {@link ol.interaction.Extent} instances are instances of
|
|
* this type.
|
|
*
|
|
* @constructor
|
|
* @implements {oli.ExtentEvent}
|
|
* @param {ol.Extent} extent the new extent
|
|
* @extends {ol.events.Event}
|
|
*/
|
|
ol.interaction.Extent.Event = function(extent) {
|
|
ol.events.Event.call(this, ol.interaction.ExtentEventType.EXTENTCHANGED);
|
|
|
|
/**
|
|
* The current extent.
|
|
* @type {ol.Extent}
|
|
* @api
|
|
*/
|
|
this.extent = extent;
|
|
|
|
};
|
|
ol.inherits(ol.interaction.Extent.Event, ol.events.Event);
|