Fix Snap -> Interaction
This commit changes ol.Snap -> ol.interaction.Snap, which extends ol.interaction.Pointer. The 'pointerdown', 'pointermove' and 'pointerup' map browser events are hanlded to edit the pixel and coordinate properties to make them 'snap' to the closest feature.
This commit is contained in:
77
examples/snap.html
Normal file
77
examples/snap.html
Normal file
@@ -0,0 +1,77 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
|
||||
<link rel="stylesheet" href="../css/ol.css" type="text/css">
|
||||
<link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
|
||||
<link rel="stylesheet" href="../resources/layout.css" type="text/css">
|
||||
<link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
|
||||
<title> Snap example </title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid">
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<div id="map" class="map"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
|
||||
<div class="span12">
|
||||
<h4 id="title">Snap interaction example</h4>
|
||||
<p id="shortdesc">Example of using the snap interaction together with
|
||||
draw and modify interactions. The snap interaction must be added
|
||||
last, as it needs to be the first to handle the
|
||||
<code>pointermove</code> event.</p>
|
||||
<form id="options-form" automplete="off">
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="interaction" value="draw" id="draw" checked>
|
||||
Draw
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="interaction" value="modify">
|
||||
Modify
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Draw type </label>
|
||||
<select name="draw-type" id="draw-type">
|
||||
<option value="Point">Point</option>
|
||||
<option value="LineString">LineString</option>
|
||||
<option value="Polygon">Polygon</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="docs">
|
||||
<p>See the <a href="snap.js" target="_blank">snap.js source</a> to see how this is done.</p>
|
||||
</div>
|
||||
<div id="tags">draw, edit, modify, vector, featureoverlay, snap</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="../resources/jquery.min.js" type="text/javascript"></script>
|
||||
<script src="../resources/example-behaviour.js" type="text/javascript"></script>
|
||||
<script src="loader.js?id=snap" type="text/javascript"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
162
examples/snap.js
Normal file
162
examples/snap.js
Normal file
@@ -0,0 +1,162 @@
|
||||
goog.require('ol.FeatureOverlay');
|
||||
goog.require('ol.Map');
|
||||
goog.require('ol.View');
|
||||
goog.require('ol.interaction');
|
||||
goog.require('ol.interaction.Draw');
|
||||
goog.require('ol.interaction.Modify');
|
||||
goog.require('ol.interaction.Select');
|
||||
goog.require('ol.interaction.Snap');
|
||||
goog.require('ol.layer.Tile');
|
||||
goog.require('ol.source.MapQuest');
|
||||
goog.require('ol.style.Circle');
|
||||
goog.require('ol.style.Fill');
|
||||
goog.require('ol.style.Stroke');
|
||||
goog.require('ol.style.Style');
|
||||
|
||||
var raster = new ol.layer.Tile({
|
||||
source: new ol.source.MapQuest({layer: 'sat'})
|
||||
});
|
||||
|
||||
var map = new ol.Map({
|
||||
layers: [raster],
|
||||
target: 'map',
|
||||
view: new ol.View({
|
||||
center: [-11000000, 4600000],
|
||||
zoom: 4
|
||||
})
|
||||
});
|
||||
|
||||
// The features are not added to a regular vector layer/source,
|
||||
// but to a feature overlay which holds a collection of features.
|
||||
// This collection is passed to the modify and also the draw
|
||||
// interaction, so that both can add or modify features.
|
||||
var featureOverlay = new ol.FeatureOverlay({
|
||||
style: new ol.style.Style({
|
||||
fill: new ol.style.Fill({
|
||||
color: 'rgba(255, 255, 255, 0.2)'
|
||||
}),
|
||||
stroke: new ol.style.Stroke({
|
||||
color: '#ffcc33',
|
||||
width: 2
|
||||
}),
|
||||
image: new ol.style.Circle({
|
||||
radius: 7,
|
||||
fill: new ol.style.Fill({
|
||||
color: '#ffcc33'
|
||||
})
|
||||
})
|
||||
}),
|
||||
map: map
|
||||
});
|
||||
|
||||
var Modify = {
|
||||
init: function() {
|
||||
this.select = new ol.interaction.Select();
|
||||
map.addInteraction(this.select);
|
||||
|
||||
this.modify = new ol.interaction.Modify({
|
||||
features: this.select.getFeatures()
|
||||
});
|
||||
map.addInteraction(this.modify);
|
||||
|
||||
this.setEvents();
|
||||
},
|
||||
setEvents: function() {
|
||||
var selectedFeatures = this.select.getFeatures();
|
||||
|
||||
this.select.on('change:active', function() {
|
||||
selectedFeatures.forEach(selectedFeatures.remove, selectedFeatures);
|
||||
});
|
||||
|
||||
// since snap control duplicates the geometry of features
|
||||
// when creating the index and it doesn't rebuild the index
|
||||
// on 'change:geometry', modified geometries should be handled manualy
|
||||
selectedFeatures.on('add', function(evt) {
|
||||
// removes the feature geometry from the snap index
|
||||
featureOverlay.getFeatures().remove(evt.element);
|
||||
});
|
||||
selectedFeatures.on('remove', function(evt) {
|
||||
// adds the feature geometry to the snap index
|
||||
featureOverlay.getFeatures().push(evt.element);
|
||||
});
|
||||
},
|
||||
setActive: function(active) {
|
||||
this.select.setActive(active);
|
||||
this.modify.setActive(active);
|
||||
}
|
||||
};
|
||||
Modify.init();
|
||||
|
||||
|
||||
var Draw = {
|
||||
init: function() {
|
||||
map.addInteraction(this.Point);
|
||||
this.Point.setActive(false);
|
||||
map.addInteraction(this.LineString);
|
||||
this.LineString.setActive(false);
|
||||
map.addInteraction(this.Polygon);
|
||||
this.Polygon.setActive(false);
|
||||
},
|
||||
Point: new ol.interaction.Draw({
|
||||
features: featureOverlay.getFeatures(),
|
||||
type: /** @type {ol.geom.GeometryType} */ ('Point')
|
||||
}),
|
||||
LineString: new ol.interaction.Draw({
|
||||
features: featureOverlay.getFeatures(),
|
||||
type: /** @type {ol.geom.GeometryType} */ ('LineString')
|
||||
}),
|
||||
Polygon: new ol.interaction.Draw({
|
||||
features: featureOverlay.getFeatures(),
|
||||
type: /** @type {ol.geom.GeometryType} */ ('Polygon')
|
||||
}),
|
||||
getActive: function() {
|
||||
return this.activeType ? this[this.activeType].getActive() : false;
|
||||
},
|
||||
setActive: function(active) {
|
||||
var type = optionsForm.elements['draw-type'].value;
|
||||
if (active) {
|
||||
this.activeType && this[this.activeType].setActive(false);
|
||||
this[type].setActive(true);
|
||||
this.activeType = type;
|
||||
} else {
|
||||
this.activeType && this[this.activeType].setActive(false);
|
||||
this.activeType = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
Draw.init();
|
||||
|
||||
var optionsForm = document.getElementById('options-form');
|
||||
|
||||
|
||||
/**
|
||||
* Let user change the geometry type.
|
||||
* @param {Event} e Change event.
|
||||
*/
|
||||
optionsForm.onchange = function(e) {
|
||||
var type = e.target.getAttribute('name');
|
||||
var value = e.target.value;
|
||||
if (type == 'draw-type') {
|
||||
Draw.getActive() && Draw.setActive(true);
|
||||
} else if (type == 'interaction') {
|
||||
if (value == 'modify') {
|
||||
Draw.setActive(false);
|
||||
Modify.setActive(true);
|
||||
} else if (value == 'draw') {
|
||||
Draw.setActive(true);
|
||||
Modify.setActive(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Draw.setActive(true);
|
||||
Modify.setActive(false);
|
||||
|
||||
|
||||
// snap duplicates the geometries so it can create a index.
|
||||
// when a gemetry is modified, index needs to be rebuild for that
|
||||
// geometry by removeing the old and adding the new
|
||||
var snap = new ol.interaction.Snap({
|
||||
features: featureOverlay.getFeatures()
|
||||
});
|
||||
map.addInteraction(snap);
|
||||
@@ -2450,7 +2450,7 @@ olx.interaction.ModifyOptions.prototype.deleteCondition;
|
||||
|
||||
/**
|
||||
* Pixel tolerance for considering the pointer close enough to a segment or
|
||||
* vertex for editing. Default is `10` pixels.
|
||||
* vertex for editing.
|
||||
* @type {number|undefined}
|
||||
* @api
|
||||
*/
|
||||
@@ -2671,6 +2671,42 @@ olx.interaction.SelectOptions.prototype.toggleCondition;
|
||||
olx.interaction.SelectOptions.prototype.multi;
|
||||
|
||||
|
||||
/**
|
||||
* Options for snap
|
||||
* @typedef {{
|
||||
* features: (Array.<ol.Feature>|ol.Collection.<ol.Feature>|undefined),
|
||||
* pixelTolerance: (number|undefined),
|
||||
* source: (ol.source.Vector|undefined)
|
||||
* }}
|
||||
* @api
|
||||
*/
|
||||
olx.interaction.SnapOptions;
|
||||
|
||||
|
||||
/**
|
||||
* Snap to this features
|
||||
* @type {Array.<ol.Feature>|ol.Collection.<ol.Feature>|undefined}
|
||||
* @api
|
||||
*/
|
||||
olx.interaction.SnapOptions.prototype.features;
|
||||
|
||||
|
||||
/**
|
||||
* Pixel tolerance for considering the pointer close enough to a segment or
|
||||
* vertex for editing. Default is `10` pixels.
|
||||
* @type {number|undefined}
|
||||
* @api
|
||||
*/
|
||||
olx.interaction.SnapOptions.prototype.pixelTolerance;
|
||||
|
||||
|
||||
/**
|
||||
* Snap to features from this source
|
||||
* @type {ol.source.Vector|undefined}
|
||||
*/
|
||||
olx.interaction.SnapOptions.prototype.source;
|
||||
|
||||
|
||||
/**
|
||||
* Namespace.
|
||||
* @type {Object}
|
||||
|
||||
490
src/ol/interaction/snapinteraction.js
Normal file
490
src/ol/interaction/snapinteraction.js
Normal file
@@ -0,0 +1,490 @@
|
||||
goog.provide('ol.interaction.Snap');
|
||||
goog.provide('ol.interaction.SnapProperty');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.events');
|
||||
goog.require('ol.Collection');
|
||||
goog.require('ol.CollectionEvent');
|
||||
goog.require('ol.CollectionEventType');
|
||||
goog.require('ol.Feature');
|
||||
goog.require('ol.MapBrowserEvent.EventType');
|
||||
goog.require('ol.Observable');
|
||||
goog.require('ol.coordinate');
|
||||
goog.require('ol.extent');
|
||||
goog.require('ol.interaction.Pointer');
|
||||
goog.require('ol.source.Vector');
|
||||
goog.require('ol.source.VectorEvent');
|
||||
goog.require('ol.source.VectorEventType');
|
||||
goog.require('ol.structs.RBush');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
* Helper class for providing snap in ol.interaction.Pointer.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* var snap = new ol.interaction.Snap({
|
||||
* source: source
|
||||
* });
|
||||
*
|
||||
* @constructor
|
||||
* @extends {ol.interaction.Pointer}
|
||||
* @param {olx.interaction.SnapOptions=} opt_options Options.
|
||||
* @api
|
||||
*/
|
||||
ol.interaction.Snap = function(opt_options) {
|
||||
|
||||
goog.base(this, {
|
||||
handleDownEvent: ol.interaction.Snap.handleDownAndUpEvent,
|
||||
handleEvent: ol.interaction.Snap.handleEvent,
|
||||
handleUpEvent: ol.interaction.Snap.handleDownAndUpEvent
|
||||
});
|
||||
|
||||
var options = goog.isDef(opt_options) ? opt_options : {};
|
||||
|
||||
/**
|
||||
* @type {?ol.source.Vector}
|
||||
* @private
|
||||
*/
|
||||
this.source_ = goog.isDef(options.source) ? options.source : null;
|
||||
|
||||
/**
|
||||
* @type {?ol.Collection.<ol.Feature>}
|
||||
* @private
|
||||
*/
|
||||
this.features_ = goog.isDef(options.features) ?
|
||||
goog.isArray(options.features) ?
|
||||
new ol.Collection(options.features) :
|
||||
options.features :
|
||||
null;
|
||||
|
||||
var features;
|
||||
if (!goog.isNull(this.features_)) {
|
||||
features = this.features_;
|
||||
} else if (!goog.isNull(this.source_)) {
|
||||
features = this.source_.getFeatures();
|
||||
}
|
||||
goog.asserts.assert(goog.isDef(features));
|
||||
|
||||
/**
|
||||
* @type {ol.Collection.<goog.events.Key>}
|
||||
* @private
|
||||
*/
|
||||
this.featuresListenerKeys_ = new ol.Collection();
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.pixelTolerance_ = goog.isDef(options.pixelTolerance) ?
|
||||
options.pixelTolerance : 10;
|
||||
|
||||
|
||||
/**
|
||||
* Segment RTree for each layer
|
||||
* @type {Object.<*, ol.structs.RBush>}
|
||||
* @private
|
||||
*/
|
||||
this.rBush_ = new ol.structs.RBush();
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @private
|
||||
* @type {Object.<string, function(ol.Feature, ol.geom.Geometry)> }
|
||||
*/
|
||||
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_
|
||||
};
|
||||
|
||||
|
||||
features.forEach(this.addFeature, this);
|
||||
};
|
||||
goog.inherits(ol.interaction.Snap, ol.interaction.Pointer);
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.Feature} feature Feature.
|
||||
* @api
|
||||
*/
|
||||
ol.interaction.Snap.prototype.addFeature = function(feature) {
|
||||
var geometry = feature.getGeometry();
|
||||
if (goog.isDef(this.SEGMENT_WRITERS_[geometry.getType()])) {
|
||||
this.SEGMENT_WRITERS_[geometry.getType()].call(this, feature, geometry);
|
||||
}
|
||||
};
|
||||
goog.exportProperty(
|
||||
ol.interaction.Snap.prototype,
|
||||
'addFeature',
|
||||
ol.interaction.Snap.prototype.addFeature);
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.interaction.Snap.prototype.setMap = function(map) {
|
||||
var currentMap = this.getMap();
|
||||
var keys = this.featuresListenerKeys_;
|
||||
var features = this.features_;
|
||||
var source = this.source_;
|
||||
|
||||
if (currentMap) {
|
||||
keys.forEach(ol.Observable.unByKey, this);
|
||||
keys.clear();
|
||||
}
|
||||
|
||||
goog.base(this, 'setMap', map);
|
||||
|
||||
if (map) {
|
||||
if (!goog.isNull(features)) {
|
||||
keys.push(features.on(ol.CollectionEventType.ADD,
|
||||
this.handleFeatureAdd_, this));
|
||||
keys.push(features.on(ol.CollectionEventType.REMOVE,
|
||||
this.handleFeatureRemove_, this));
|
||||
} else if (!goog.isNull(source)) {
|
||||
keys.push(source.on(ol.source.VectorEventType.ADDFEATURE,
|
||||
this.handleFeatureAdd_, this));
|
||||
keys.push(source.on(ol.source.VectorEventType.REMOVEFEATURE,
|
||||
this.handleFeatureRemove_, this));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.MapBrowserPointerEvent} evt Event.
|
||||
* @return {boolean} Stop drag sequence?
|
||||
* @this {ol.interaction.Snap}
|
||||
* @api
|
||||
*/
|
||||
ol.interaction.Snap.handleDownAndUpEvent = function(evt) {
|
||||
return this.handleEvent_(evt);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
|
||||
* @return {boolean} `false` to stop event propagation.
|
||||
* @this {ol.interaction.Snap}
|
||||
* @api
|
||||
*/
|
||||
ol.interaction.Snap.handleEvent = function(mapBrowserEvent) {
|
||||
var pass = true;
|
||||
if (mapBrowserEvent.type === ol.MapBrowserEvent.EventType.POINTERMOVE) {
|
||||
pass = this.handleEvent_(mapBrowserEvent);
|
||||
}
|
||||
return ol.interaction.Pointer.handleEvent.call(this, mapBrowserEvent) && pass;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handle 'pointerdown', 'pointermove' and 'pointerup' events.
|
||||
* @param {ol.MapBrowserEvent} evt A move event.
|
||||
* @return {boolean} Pass the event to other interactions.
|
||||
* @private
|
||||
*/
|
||||
ol.interaction.Snap.prototype.handleEvent_ = function(evt) {
|
||||
var result = this.snapTo(evt.pixel, evt.coordinate, evt.map);
|
||||
if (result.snapped) {
|
||||
evt.coordinate = result.vertex;
|
||||
evt.pixel = result.vertexPixel;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.source.VectorEvent|ol.CollectionEvent} evt Event.
|
||||
* @private
|
||||
*/
|
||||
ol.interaction.Snap.prototype.handleFeatureAdd_ = function(evt) {
|
||||
var feature;
|
||||
if (evt instanceof ol.source.VectorEvent) {
|
||||
feature = evt.feature;
|
||||
} else if (evt instanceof ol.CollectionEvent) {
|
||||
feature = evt.element;
|
||||
}
|
||||
goog.asserts.assertInstanceof(feature, ol.Feature);
|
||||
this.addFeature(feature);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.source.VectorEvent|ol.CollectionEvent} evt Event.
|
||||
* @private
|
||||
*/
|
||||
ol.interaction.Snap.prototype.handleFeatureRemove_ = function(evt) {
|
||||
var feature;
|
||||
if (evt instanceof ol.source.VectorEvent) {
|
||||
feature = evt.feature;
|
||||
} else if (evt instanceof ol.CollectionEvent) {
|
||||
feature = evt.element;
|
||||
}
|
||||
goog.asserts.assertInstanceof(feature, ol.Feature);
|
||||
this.removeFeature(feature,
|
||||
feature.getGeometry().getExtent());
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.Feature} feature Feature
|
||||
* @param {ol.geom.Point} geometry Geometry.
|
||||
* @private
|
||||
*/
|
||||
ol.interaction.Snap.prototype.writePointGeometry_ =
|
||||
function(feature, geometry) {
|
||||
var coordinates = geometry.getCoordinates();
|
||||
var segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({
|
||||
feature: feature,
|
||||
segment: [coordinates, coordinates]
|
||||
});
|
||||
this.rBush_.insert(geometry.getExtent(), segmentData);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.Feature} feature Feature
|
||||
* @param {ol.geom.MultiPoint} geometry Geometry.
|
||||
* @private
|
||||
*/
|
||||
ol.interaction.Snap.prototype.writeMultiPointGeometry_ =
|
||||
function(feature, geometry) {
|
||||
var points = geometry.getCoordinates();
|
||||
var coordinates, i, ii, segmentData;
|
||||
for (i = 0, ii = points.length; i < ii; ++i) {
|
||||
coordinates = points[i];
|
||||
segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({
|
||||
feature: feature,
|
||||
segment: [coordinates, coordinates]
|
||||
});
|
||||
this.rBush_.insert(geometry.getExtent(), segmentData);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.Feature} feature Feature
|
||||
* @param {ol.geom.LineString} geometry Geometry.
|
||||
* @private
|
||||
*/
|
||||
ol.interaction.Snap.prototype.writeLineStringGeometry_ =
|
||||
function(feature, geometry) {
|
||||
var coordinates = geometry.getCoordinates();
|
||||
var i, ii, segment, segmentData;
|
||||
for (i = 0, ii = coordinates.length - 1; i < ii; ++i) {
|
||||
segment = coordinates.slice(i, i + 2);
|
||||
segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({
|
||||
feature: feature,
|
||||
segment: segment
|
||||
});
|
||||
this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.Feature} feature Feature
|
||||
* @param {ol.geom.MultiLineString} geometry Geometry.
|
||||
* @private
|
||||
*/
|
||||
ol.interaction.Snap.prototype.writeMultiLineStringGeometry_ =
|
||||
function(feature, geometry) {
|
||||
var lines = geometry.getCoordinates();
|
||||
var coordinates, i, ii, j, jj, segment, segmentData;
|
||||
for (j = 0, jj = lines.length; j < jj; ++j) {
|
||||
coordinates = lines[j];
|
||||
for (i = 0, ii = coordinates.length - 1; i < ii; ++i) {
|
||||
segment = coordinates.slice(i, i + 2);
|
||||
segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({
|
||||
feature: feature,
|
||||
segment: segment
|
||||
});
|
||||
this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.Feature} feature Feature
|
||||
* @param {ol.geom.Polygon} geometry Geometry.
|
||||
* @private
|
||||
*/
|
||||
ol.interaction.Snap.prototype.writePolygonGeometry_ =
|
||||
function(feature, geometry) {
|
||||
var rings = geometry.getCoordinates();
|
||||
var coordinates, i, ii, j, jj, segment, segmentData;
|
||||
for (j = 0, jj = rings.length; j < jj; ++j) {
|
||||
coordinates = rings[j];
|
||||
for (i = 0, ii = coordinates.length - 1; i < ii; ++i) {
|
||||
segment = coordinates.slice(i, i + 2);
|
||||
segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({
|
||||
feature: feature,
|
||||
segment: segment
|
||||
});
|
||||
this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.Feature} feature Feature
|
||||
* @param {ol.geom.MultiPolygon} geometry Geometry.
|
||||
* @private
|
||||
*/
|
||||
ol.interaction.Snap.prototype.writeMultiPolygonGeometry_ =
|
||||
function(feature, geometry) {
|
||||
var polygons = geometry.getCoordinates();
|
||||
var coordinates, i, ii, j, jj, k, kk, rings, segment, segmentData;
|
||||
for (k = 0, kk = polygons.length; k < kk; ++k) {
|
||||
rings = polygons[k];
|
||||
for (j = 0, jj = rings.length; j < jj; ++j) {
|
||||
coordinates = rings[j];
|
||||
for (i = 0, ii = coordinates.length - 1; i < ii; ++i) {
|
||||
segment = coordinates.slice(i, i + 2);
|
||||
segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({
|
||||
feature: feature,
|
||||
segment: segment
|
||||
});
|
||||
this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.Feature} feature Feature
|
||||
* @param {ol.geom.GeometryCollection} geometry Geometry.
|
||||
* @private
|
||||
*/
|
||||
ol.interaction.Snap.prototype.writeGeometryCollectionGeometry_ =
|
||||
function(feature, geometry) {
|
||||
var i, geometries = geometry.getGeometriesArray();
|
||||
for (i = 0; i < geometries.length; ++i) {
|
||||
this.SEGMENT_WRITERS_[geometries[i].getType()].call(
|
||||
this, feature, geometries[i]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.Feature} feature Feature
|
||||
* @param {ol.Extent} extent Extent.
|
||||
* @api
|
||||
*/
|
||||
ol.interaction.Snap.prototype.removeFeature = function(feature, extent) {
|
||||
var rBush = this.rBush_;
|
||||
var i, nodesToRemove = [];
|
||||
rBush.forEachInExtent(extent, function(node) {
|
||||
if (feature === node.feature) {
|
||||
nodesToRemove.push(node);
|
||||
}
|
||||
});
|
||||
for (i = nodesToRemove.length - 1; i >= 0; --i) {
|
||||
rBush.remove(nodesToRemove[i]);
|
||||
}
|
||||
};
|
||||
goog.exportProperty(
|
||||
ol.interaction.Snap.prototype,
|
||||
'removeFeature',
|
||||
ol.interaction.Snap.prototype.removeFeature);
|
||||
|
||||
|
||||
/**
|
||||
* @param {ol.Pixel} pixel Pixel
|
||||
* @param {ol.Coordinate} pixelCoordinate Coordinate
|
||||
* @param {ol.Map} map Map.
|
||||
* @return {ol.interaction.Snap.ResultType} Snap result
|
||||
*/
|
||||
ol.interaction.Snap.prototype.snapTo = function(pixel, pixelCoordinate, map) {
|
||||
|
||||
var lowerLeft = map.getCoordinateFromPixel(
|
||||
[pixel[0] - this.pixelTolerance_, pixel[1] + this.pixelTolerance_]);
|
||||
var upperRight = map.getCoordinateFromPixel(
|
||||
[pixel[0] + this.pixelTolerance_, pixel[1] - this.pixelTolerance_]);
|
||||
var box = ol.extent.boundingExtent([lowerLeft, upperRight]);
|
||||
|
||||
var segments = this.rBush_.getInExtent(box);
|
||||
var snappedToVertex = false;
|
||||
var snapped = false;
|
||||
var vertex = null;
|
||||
var vertexPixel = null;
|
||||
if (segments.length > 0) {
|
||||
segments.sort(goog.partial(
|
||||
ol.interaction.Snap.sortByDistance, pixelCoordinate));
|
||||
var closestSegment = segments[0].segment;
|
||||
vertex = (ol.coordinate.closestOnSegment(pixelCoordinate,
|
||||
closestSegment));
|
||||
vertexPixel = map.getPixelFromCoordinate(vertex);
|
||||
if (Math.sqrt(ol.coordinate.squaredDistance(pixel, vertexPixel)) <=
|
||||
this.pixelTolerance_) {
|
||||
snapped = true;
|
||||
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));
|
||||
snappedToVertex = dist <= this.pixelTolerance_;
|
||||
if (snappedToVertex) {
|
||||
vertex = squaredDist1 > squaredDist2 ?
|
||||
closestSegment[1] : closestSegment[0];
|
||||
vertexPixel = map.getPixelFromCoordinate(vertex);
|
||||
vertexPixel = [Math.round(vertexPixel[0]), Math.round(vertexPixel[1])];
|
||||
}
|
||||
}
|
||||
}
|
||||
return /** @type {ol.interaction.Snap.ResultType} */ ({
|
||||
snapped: snapped,
|
||||
vertex: vertex,
|
||||
vertexPixel: vertexPixel
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sort segments by distance, helper function
|
||||
* @param {ol.Coordinate} pixelCoordinate Coordinate to determine distance
|
||||
* @param {ol.interaction.Snap.SegmentDataType} a
|
||||
* @param {ol.interaction.Snap.SegmentDataType} b
|
||||
* @return {number}
|
||||
*/
|
||||
ol.interaction.Snap.sortByDistance = function(pixelCoordinate, a, b) {
|
||||
return ol.coordinate.squaredDistanceToSegment(pixelCoordinate, a.segment) -
|
||||
ol.coordinate.squaredDistanceToSegment(pixelCoordinate, b.segment);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
ol.interaction.Snap.prototype.shouldStopEvent = goog.functions.FALSE;
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* snapped: {boolean},
|
||||
* vertex: (ol.Coordinate|null),
|
||||
* vertexPixel: (ol.Pixel|null)
|
||||
* }}
|
||||
*/
|
||||
ol.interaction.Snap.ResultType;
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* feature: ol.Feature,
|
||||
* segment: Array.<ol.Coordinate>
|
||||
* }}
|
||||
*/
|
||||
ol.interaction.Snap.SegmentDataType;
|
||||
Reference in New Issue
Block a user