Merge pull request #1269 from ahocevar/modify-interaction
Modify interaction for feature modification
This commit is contained in:
50
examples/modify-features.html
Normal file
50
examples/modify-features.html
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<!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>Modify features 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">Modify features example</h4>
|
||||||
|
<p id="shortdesc">Example of using the Modify interaction. Select a feature and drag the circle that appears when the cursor gets close to the selected geometry.</p>
|
||||||
|
<div id="docs">
|
||||||
|
<p>See the <a href="modify-features.js" target="_blank">modify-features.js source</a> to see how this is done.</p>
|
||||||
|
</div>
|
||||||
|
<div id="tags">modify, edit, vector</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="loader.js?id=modify-features" type="text/javascript"></script>
|
||||||
|
<script src="../resources/example-behaviour.js" type="text/javascript"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
109
examples/modify-features.js
Normal file
109
examples/modify-features.js
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
goog.require('ol.Map');
|
||||||
|
goog.require('ol.RendererHint');
|
||||||
|
goog.require('ol.View2D');
|
||||||
|
goog.require('ol.interaction');
|
||||||
|
goog.require('ol.interaction.Modify');
|
||||||
|
goog.require('ol.interaction.Select');
|
||||||
|
goog.require('ol.layer.Tile');
|
||||||
|
goog.require('ol.layer.Vector');
|
||||||
|
goog.require('ol.parser.ogc.GML_v3');
|
||||||
|
goog.require('ol.source.MapQuestOpenAerial');
|
||||||
|
goog.require('ol.source.Vector');
|
||||||
|
goog.require('ol.style.Fill');
|
||||||
|
goog.require('ol.style.Rule');
|
||||||
|
goog.require('ol.style.Shape');
|
||||||
|
goog.require('ol.style.Stroke');
|
||||||
|
goog.require('ol.style.Style');
|
||||||
|
|
||||||
|
var raster = new ol.layer.Tile({
|
||||||
|
source: new ol.source.MapQuestOpenAerial()
|
||||||
|
});
|
||||||
|
|
||||||
|
var vector = new ol.layer.Vector({
|
||||||
|
id: 'vector',
|
||||||
|
source: new ol.source.Vector({
|
||||||
|
parser: new ol.parser.ogc.GML_v3(),
|
||||||
|
url: 'data/gml/topp-states-wfs.xml'
|
||||||
|
}),
|
||||||
|
style: new ol.style.Style({
|
||||||
|
rules: [
|
||||||
|
new ol.style.Rule({
|
||||||
|
filter: 'renderIntent("selected")',
|
||||||
|
symbolizers: [
|
||||||
|
new ol.style.Fill({
|
||||||
|
color: '#ffffff',
|
||||||
|
opacity: 0.2
|
||||||
|
}),
|
||||||
|
new ol.style.Stroke({
|
||||||
|
color: 'white',
|
||||||
|
width: 5
|
||||||
|
}),
|
||||||
|
new ol.style.Stroke({
|
||||||
|
color: '#0099ff',
|
||||||
|
width: 3
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
new ol.style.Rule({
|
||||||
|
filter: 'renderIntent("temporary")',
|
||||||
|
symbolizers: [
|
||||||
|
new ol.style.Shape({
|
||||||
|
fill: new ol.style.Fill({
|
||||||
|
color: '#0099ff',
|
||||||
|
opacity: 1
|
||||||
|
}),
|
||||||
|
stroke: new ol.style.Stroke({
|
||||||
|
color: 'white',
|
||||||
|
opacity: 0.75
|
||||||
|
}),
|
||||||
|
size: 14,
|
||||||
|
zIndex: 1
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
new ol.style.Rule({
|
||||||
|
filter: 'renderIntent("future")',
|
||||||
|
symbolizers: [
|
||||||
|
new ol.style.Shape({
|
||||||
|
fill: new ol.style.Fill({
|
||||||
|
color: '#00ff33',
|
||||||
|
opacity: 1
|
||||||
|
}),
|
||||||
|
stroke: new ol.style.Stroke({
|
||||||
|
color: 'white',
|
||||||
|
opacity: 0.75
|
||||||
|
}),
|
||||||
|
size: 14,
|
||||||
|
zIndex: 1
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
],
|
||||||
|
symbolizers: [
|
||||||
|
new ol.style.Fill({
|
||||||
|
color: '#ffffff',
|
||||||
|
opacity: 0.1
|
||||||
|
}),
|
||||||
|
new ol.style.Stroke({
|
||||||
|
color: '#ffcc33',
|
||||||
|
width: 2
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
var selectInteraction = new ol.interaction.Select({
|
||||||
|
layerFilter: function(layer) { return layer.get('id') == 'vector'; }
|
||||||
|
});
|
||||||
|
|
||||||
|
var map = new ol.Map({
|
||||||
|
interactions: ol.interaction.defaults().extend(
|
||||||
|
[selectInteraction, new ol.interaction.Modify()]),
|
||||||
|
layers: [raster, vector],
|
||||||
|
renderer: ol.RendererHint.CANVAS,
|
||||||
|
target: 'map',
|
||||||
|
view: new ol.View2D({
|
||||||
|
center: [-11000000, 4600000],
|
||||||
|
zoom: 4
|
||||||
|
})
|
||||||
|
});
|
||||||
@@ -377,6 +377,14 @@
|
|||||||
* @todo stability experimental
|
* @todo stability experimental
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} ol.interaction.ModifyOptions
|
||||||
|
* @property {undefined|Array.<ol.layer.Layer>|function(ol.layer.Layer):boolean} layers
|
||||||
|
* Layers or filter function to restrict modification to a subset of layers.
|
||||||
|
* @property {number|undefined} pixelTolerance Pixel tolerance for considering
|
||||||
|
* the pointer close enough to a vertex for editing. Default is 20 pixels.
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} ol.interaction.SelectOptions
|
* @typedef {Object} ol.interaction.SelectOptions
|
||||||
* @property {ol.events.ConditionType|undefined} addCondition A conditional
|
* @property {ol.events.ConditionType|undefined} addCondition A conditional
|
||||||
|
|||||||
@@ -135,6 +135,23 @@ ol.coordinate.format = function(coordinate, template, opt_fractionDigits) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ol.Coordinate} coordinate1 First coordinate.
|
||||||
|
* @param {ol.Coordinate} coordinate2 Second coordinate.
|
||||||
|
* @return {boolean} Whether the passed coordinates are equal.
|
||||||
|
*/
|
||||||
|
ol.coordinate.equals = function(coordinate1, coordinate2) {
|
||||||
|
var equals = true;
|
||||||
|
for (var i = coordinate1.length - 1; i >= 0; --i) {
|
||||||
|
if (coordinate1[i] != coordinate2[i]) {
|
||||||
|
equals = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return equals;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {ol.Coordinate} coordinate Coordinate.
|
* @param {ol.Coordinate} coordinate Coordinate.
|
||||||
* @param {number} angle Angle.
|
* @param {number} angle Angle.
|
||||||
|
|||||||
@@ -42,6 +42,13 @@ ol.Feature = function(opt_values) {
|
|||||||
*/
|
*/
|
||||||
this.geometryName_;
|
this.geometryName_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Original of this feature when it was modified.
|
||||||
|
* @type {ol.Feature}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.original_ = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The render intent for this feature.
|
* The render intent for this feature.
|
||||||
* @type {ol.layer.VectorLayerRenderIntent|string}
|
* @type {ol.layer.VectorLayerRenderIntent|string}
|
||||||
@@ -101,6 +108,15 @@ ol.Feature.prototype.getGeometry = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the original of this feature when it was modified.
|
||||||
|
* @return {ol.Feature} Original.
|
||||||
|
*/
|
||||||
|
ol.Feature.prototype.getOriginal = function() {
|
||||||
|
return this.original_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get any symbolizers set directly on the feature.
|
* Get any symbolizers set directly on the feature.
|
||||||
* @return {Array.<ol.style.Symbolizer>} Symbolizers (or null if none).
|
* @return {Array.<ol.style.Symbolizer>} Symbolizers (or null if none).
|
||||||
@@ -176,6 +192,15 @@ ol.Feature.prototype.setGeometry = function(geometry) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the original of this feature when it was modified.
|
||||||
|
* @param {ol.Feature} original Original.
|
||||||
|
*/
|
||||||
|
ol.Feature.prototype.setOriginal = function(original) {
|
||||||
|
this.original_ = original;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the renderIntent for this feature.
|
* Gets the renderIntent for this feature.
|
||||||
* @return {string} Render intent.
|
* @return {string} Render intent.
|
||||||
|
|||||||
@@ -26,6 +26,13 @@ ol.interaction.Drag = function() {
|
|||||||
*/
|
*/
|
||||||
this.dragging_ = false;
|
this.dragging_ = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delta for INTERACTING view hint. Subclasses that do not want the
|
||||||
|
* INTERACTING hint to be set should override this to 0.
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.interactingHint = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {number}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
@@ -60,6 +67,14 @@ ol.interaction.Drag = function() {
|
|||||||
goog.inherits(ol.interaction.Drag, ol.interaction.Interaction);
|
goog.inherits(ol.interaction.Drag, ol.interaction.Interaction);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {boolean} Whether we're dragging.
|
||||||
|
*/
|
||||||
|
ol.interaction.Drag.prototype.getDragging = function() {
|
||||||
|
return this.dragging_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {ol.MapBrowserEvent} mapBrowserEvent Event.
|
* @param {ol.MapBrowserEvent} mapBrowserEvent Event.
|
||||||
* @protected
|
* @protected
|
||||||
@@ -115,7 +130,7 @@ ol.interaction.Drag.prototype.handleMapBrowserEvent =
|
|||||||
goog.asserts.assertInstanceof(browserEvent, goog.events.BrowserEvent);
|
goog.asserts.assertInstanceof(browserEvent, goog.events.BrowserEvent);
|
||||||
this.deltaX = browserEvent.clientX - this.startX;
|
this.deltaX = browserEvent.clientX - this.startX;
|
||||||
this.deltaY = browserEvent.clientY - this.startY;
|
this.deltaY = browserEvent.clientY - this.startY;
|
||||||
view.setHint(ol.ViewHint.INTERACTING, -1);
|
view.setHint(ol.ViewHint.INTERACTING, -this.interactingHint);
|
||||||
this.dragging_ = false;
|
this.dragging_ = false;
|
||||||
this.handleDragEnd(mapBrowserEvent);
|
this.handleDragEnd(mapBrowserEvent);
|
||||||
}
|
}
|
||||||
@@ -131,7 +146,7 @@ ol.interaction.Drag.prototype.handleMapBrowserEvent =
|
|||||||
(mapBrowserEvent.getCoordinate());
|
(mapBrowserEvent.getCoordinate());
|
||||||
var handled = this.handleDragStart(mapBrowserEvent);
|
var handled = this.handleDragStart(mapBrowserEvent);
|
||||||
if (handled) {
|
if (handled) {
|
||||||
view.setHint(ol.ViewHint.INTERACTING, 1);
|
view.setHint(ol.ViewHint.INTERACTING, this.interactingHint);
|
||||||
this.dragging_ = true;
|
this.dragging_ = true;
|
||||||
mapBrowserEvent.preventDefault();
|
mapBrowserEvent.preventDefault();
|
||||||
stopEvent = true;
|
stopEvent = true;
|
||||||
|
|||||||
1
src/ol/interaction/modifyinteraction.exports
Normal file
1
src/ol/interaction/modifyinteraction.exports
Normal file
@@ -0,0 +1 @@
|
|||||||
|
@exportClass ol.interaction.Modify ol.interaction.ModifyOptions
|
||||||
524
src/ol/interaction/modifyinteraction.js
Normal file
524
src/ol/interaction/modifyinteraction.js
Normal file
@@ -0,0 +1,524 @@
|
|||||||
|
goog.provide('ol.interaction.Modify');
|
||||||
|
|
||||||
|
goog.require('goog.array');
|
||||||
|
goog.require('goog.asserts');
|
||||||
|
goog.require('goog.events');
|
||||||
|
goog.require('goog.functions');
|
||||||
|
goog.require('goog.object');
|
||||||
|
goog.require('ol.CollectionEventType');
|
||||||
|
goog.require('ol.Feature');
|
||||||
|
goog.require('ol.MapBrowserEvent.EventType');
|
||||||
|
goog.require('ol.ViewHint');
|
||||||
|
goog.require('ol.coordinate');
|
||||||
|
goog.require('ol.extent');
|
||||||
|
goog.require('ol.geom.AbstractCollection');
|
||||||
|
goog.require('ol.geom.LineString');
|
||||||
|
goog.require('ol.geom.LinearRing');
|
||||||
|
goog.require('ol.geom.Point');
|
||||||
|
goog.require('ol.geom.Polygon');
|
||||||
|
goog.require('ol.interaction.Drag');
|
||||||
|
goog.require('ol.layer.Layer');
|
||||||
|
goog.require('ol.layer.Vector');
|
||||||
|
goog.require('ol.layer.VectorEventType');
|
||||||
|
goog.require('ol.layer.VectorLayerRenderIntent');
|
||||||
|
goog.require('ol.source.Vector');
|
||||||
|
goog.require('ol.structs.RTree');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{feature: ol.Feature,
|
||||||
|
* geometry: ol.geom.Geometry,
|
||||||
|
* index: (number|undefined),
|
||||||
|
* style: ol.style.Style,
|
||||||
|
* segment: (Array.<ol.Extent>|undefined)}}
|
||||||
|
*/
|
||||||
|
ol.interaction.SegmentDataType;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @constructor
|
||||||
|
* @extends {ol.interaction.Drag}
|
||||||
|
* @param {ol.interaction.ModifyOptions=} opt_options Options.
|
||||||
|
*/
|
||||||
|
ol.interaction.Modify = function(opt_options) {
|
||||||
|
goog.base(this);
|
||||||
|
|
||||||
|
var options = goog.isDef(opt_options) ? opt_options : {};
|
||||||
|
|
||||||
|
var layerFilter = options.layers;
|
||||||
|
if (!goog.isDef(layerFilter)) {
|
||||||
|
layerFilter = goog.functions.TRUE;
|
||||||
|
} else if (goog.isArray(layerFilter)) {
|
||||||
|
layerFilter = function(layer) {return options.layers.indexOf(layer) > -1;};
|
||||||
|
}
|
||||||
|
goog.asserts.assertFunction(layerFilter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {function(ol.layer.Layer):boolean}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.layerFilter_ = layerFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Temporary sketch layer.
|
||||||
|
* @type {ol.layer.Vector}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.sketchLayer_ = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Editing vertex.
|
||||||
|
* @type {ol.Feature}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.vertexFeature_ = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {boolean}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.modifiable_ = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Segment RTree for each layer
|
||||||
|
* @type {Object.<*, ol.structs.RTree>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.rTree_ = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {number}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.pixelTolerance_ = goog.isDef(options.pixelTolerance) ?
|
||||||
|
options.pixelTolerance : 20;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Array}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.dragSegments_ = null;
|
||||||
|
|
||||||
|
this.interactingHint = 0;
|
||||||
|
};
|
||||||
|
goog.inherits(ol.interaction.Modify, ol.interaction.Drag);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
ol.interaction.Modify.prototype.setMap = function(map) {
|
||||||
|
var oldMap = this.getMap();
|
||||||
|
var layers;
|
||||||
|
if (!goog.isNull(oldMap)) {
|
||||||
|
oldMap.removeLayer(this.sketchLayer_);
|
||||||
|
layers = oldMap.getLayerGroup().getLayers();
|
||||||
|
goog.asserts.assert(goog.isDef(layers));
|
||||||
|
layers.forEach(goog.bind(this.removeLayer_, this));
|
||||||
|
layers.unlisten(ol.CollectionEventType.ADD, this.handleLayerAdded_, false,
|
||||||
|
this);
|
||||||
|
layers.unlisten(ol.CollectionEventType.REMOVE, this.handleLayerRemoved_,
|
||||||
|
false, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!goog.isNull(map)) {
|
||||||
|
if (goog.isNull(this.rTree_)) {
|
||||||
|
this.rTree_ = new ol.structs.RTree();
|
||||||
|
}
|
||||||
|
if (goog.isNull(this.sketchLayer_)) {
|
||||||
|
var sketchLayer = new ol.layer.Vector({
|
||||||
|
source: new ol.source.Vector({parser: null})
|
||||||
|
});
|
||||||
|
this.sketchLayer_ = sketchLayer;
|
||||||
|
sketchLayer.setTemporary(true);
|
||||||
|
map.addLayer(sketchLayer);
|
||||||
|
}
|
||||||
|
layers = map.getLayerGroup().getLayers();
|
||||||
|
goog.asserts.assert(goog.isDef(layers));
|
||||||
|
layers.forEach(goog.bind(this.addLayer_, this));
|
||||||
|
layers.listen(ol.CollectionEventType.ADD, this.handleLayerAdded_, false,
|
||||||
|
this);
|
||||||
|
layers.listen(ol.CollectionEventType.REMOVE, this.handleLayerRemoved_,
|
||||||
|
false, this);
|
||||||
|
} else {
|
||||||
|
// removing from a map, clean up
|
||||||
|
this.rTree_ = null;
|
||||||
|
this.sketchLayer_ = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
goog.base(this, 'setMap', map);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ol.CollectionEvent} evt Event.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.interaction.Modify.prototype.handleLayerAdded_ = function(evt) {
|
||||||
|
goog.asserts.assertInstanceof(evt.getElement, ol.layer.Layer);
|
||||||
|
this.addLayer_(evt.getElement);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a layer for modification.
|
||||||
|
* @param {ol.layer.Layer} layer Layer.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.interaction.Modify.prototype.addLayer_ = function(layer) {
|
||||||
|
if (this.layerFilter_(layer) && layer instanceof ol.layer.Vector &&
|
||||||
|
!layer.getTemporary()) {
|
||||||
|
this.addIndex_(layer.getFeatures(ol.layer.Vector.selectedFeaturesFilter),
|
||||||
|
layer);
|
||||||
|
goog.events.listen(layer, ol.layer.VectorEventType.INTENTCHANGE,
|
||||||
|
this.handleIntentChange_, false, this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ol.CollectionEvent} evt Event.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.interaction.Modify.prototype.handleLayerRemoved_ = function(evt) {
|
||||||
|
goog.asserts.assertInstanceof(evt.getElement, ol.layer.Layer);
|
||||||
|
this.removeLayer_(evt.getElement());
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a layer for modification.
|
||||||
|
* @param {ol.layer.Layer} layer Layer.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.interaction.Modify.prototype.removeLayer_ = function(layer) {
|
||||||
|
if (this.layerFilter_(layer) && layer instanceof ol.layer.Vector &&
|
||||||
|
!layer.getTemporary()) {
|
||||||
|
this.removeIndex_(
|
||||||
|
layer.getFeatures(ol.layer.Vector.selectedFeaturesFilter));
|
||||||
|
goog.events.unlisten(layer, ol.layer.VectorEventType.INTENTCHANGE,
|
||||||
|
this.handleIntentChange_, false, this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array.<ol.Feature>} features Array of features.
|
||||||
|
* @param {ol.layer.Vector} layer Layer the features belong to.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.interaction.Modify.prototype.addIndex_ = function(features, layer) {
|
||||||
|
for (var i = 0, ii = features.length; i < ii; ++i) {
|
||||||
|
var feature = features[i];
|
||||||
|
var geometry = feature.getGeometry();
|
||||||
|
if (geometry instanceof ol.geom.AbstractCollection) {
|
||||||
|
var components = geometry.getComponents();
|
||||||
|
for (var j = 0, jj = components.length; j < jj; ++j) {
|
||||||
|
this.addSegments_(feature, components[j], layer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.addSegments_(feature, geometry, layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array.<ol.Feature>} features Array of features.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.interaction.Modify.prototype.removeIndex_ = function(features) {
|
||||||
|
var rTree = this.rTree_;
|
||||||
|
for (var i = 0, ii = features.length; i < ii; ++i) {
|
||||||
|
var feature = features[i];
|
||||||
|
var segmentDataMatches = rTree.search(feature.getGeometry().getBounds(),
|
||||||
|
goog.getUid(feature));
|
||||||
|
for (var j = segmentDataMatches.length - 1; j >= 0; --j) {
|
||||||
|
var segmentDataMatch = segmentDataMatches[j];
|
||||||
|
rTree.remove(ol.extent.boundingExtent(segmentDataMatch.segment),
|
||||||
|
segmentDataMatch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen for feature additions.
|
||||||
|
* @param {ol.layer.VectorEvent} evt Event object.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.interaction.Modify.prototype.handleIntentChange_ = function(evt) {
|
||||||
|
var layer = evt.target;
|
||||||
|
goog.asserts.assertInstanceof(layer, ol.layer.Vector);
|
||||||
|
var features = evt.features;
|
||||||
|
for (var i = 0, ii = features.length; i < ii; ++i) {
|
||||||
|
var feature = features[i];
|
||||||
|
var renderIntent = feature.getRenderIntent();
|
||||||
|
if (renderIntent == ol.layer.VectorLayerRenderIntent.SELECTED) {
|
||||||
|
this.addIndex_([feature], layer);
|
||||||
|
} else {
|
||||||
|
this.removeIndex_([feature]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ol.Feature} feature Feature to add segments for.
|
||||||
|
* @param {ol.geom.Geometry} geometry Geometry to add segments for.
|
||||||
|
* @param {ol.layer.Vector} layer Vector layer to add segments for.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.interaction.Modify.prototype.addSegments_ =
|
||||||
|
function(feature, geometry, layer) {
|
||||||
|
var uid = goog.getUid(feature);
|
||||||
|
var rTree = this.rTree_;
|
||||||
|
var segment, segmentData, coordinates;
|
||||||
|
if (geometry instanceof ol.geom.Point) {
|
||||||
|
segmentData = /** @type {ol.interaction.SegmentDataType} */ ({
|
||||||
|
feature: feature,
|
||||||
|
geometry: geometry,
|
||||||
|
style: layer.getStyle()
|
||||||
|
});
|
||||||
|
rTree.insert(geometry.getBounds(), segmentData, uid);
|
||||||
|
} else if (geometry instanceof ol.geom.LineString ||
|
||||||
|
geometry instanceof ol.geom.LinearRing) {
|
||||||
|
coordinates = geometry.getCoordinates();
|
||||||
|
for (var i = 0, ii = coordinates.length - 1; i < ii; ++i) {
|
||||||
|
segment = coordinates.slice(i, i + 2);
|
||||||
|
segmentData = /** @type {ol.interaction.SegmentDataType} */ ({
|
||||||
|
feature: feature,
|
||||||
|
geometry: geometry,
|
||||||
|
index: i,
|
||||||
|
style: layer.getStyle(),
|
||||||
|
segment: segment
|
||||||
|
});
|
||||||
|
rTree.insert(ol.extent.boundingExtent(segment), segmentData, uid);
|
||||||
|
}
|
||||||
|
} else if (geometry instanceof ol.geom.Polygon) {
|
||||||
|
var rings = geometry.getRings();
|
||||||
|
for (var j = 0, jj = rings.length; j < jj; ++j) {
|
||||||
|
this.addSegments_(feature, rings[j], layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ol.style.Style} style Style of the layer that the feature being
|
||||||
|
* modified belongs to.
|
||||||
|
* @param {ol.Coordinate} coordinates Coordinates.
|
||||||
|
* @return {ol.Feature} Vertex feature.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.interaction.Modify.prototype.createOrUpdateVertexFeature_ =
|
||||||
|
function(style, coordinates) {
|
||||||
|
var vertexFeature = this.vertexFeature_;
|
||||||
|
if (goog.isNull(vertexFeature)) {
|
||||||
|
vertexFeature = new ol.Feature({g: new ol.geom.Point(coordinates)});
|
||||||
|
this.vertexFeature_ = vertexFeature;
|
||||||
|
this.sketchLayer_.addFeatures([vertexFeature]);
|
||||||
|
} else {
|
||||||
|
var geometry = vertexFeature.getGeometry();
|
||||||
|
geometry.setCoordinates(coordinates);
|
||||||
|
}
|
||||||
|
if (this.sketchLayer_.getStyle() !== style) {
|
||||||
|
this.sketchLayer_.setStyle(style);
|
||||||
|
}
|
||||||
|
return vertexFeature;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
ol.interaction.Modify.prototype.handleDragStart = function(evt) {
|
||||||
|
this.dragSegments_ = [];
|
||||||
|
var vertexFeature = this.vertexFeature_;
|
||||||
|
var renderIntent = vertexFeature.getRenderIntent();
|
||||||
|
if (goog.isDef(vertexFeature) &&
|
||||||
|
renderIntent != ol.layer.VectorLayerRenderIntent.HIDDEN) {
|
||||||
|
var insertVertices = [];
|
||||||
|
var vertex = vertexFeature.getGeometry().getCoordinates();
|
||||||
|
var vertexExtent = ol.extent.boundingExtent([vertex]);
|
||||||
|
var segmentDataMatches = this.rTree_.search(vertexExtent);
|
||||||
|
var distinctFeatures = {};
|
||||||
|
for (var i = 0, ii = segmentDataMatches.length; i < ii; ++i) {
|
||||||
|
var segmentDataMatch = segmentDataMatches[i];
|
||||||
|
var segment = segmentDataMatch.segment;
|
||||||
|
if (!(goog.getUid(segmentDataMatch.feature) in distinctFeatures)) {
|
||||||
|
var feature = segmentDataMatch.feature;
|
||||||
|
distinctFeatures[goog.getUid(feature)] = true;
|
||||||
|
var original = new ol.Feature(feature.getAttributes());
|
||||||
|
original.setGeometry(feature.getGeometry().clone());
|
||||||
|
original.setId(feature.getId());
|
||||||
|
original.setOriginal(feature.getOriginal());
|
||||||
|
original.setSymbolizers(feature.getSymbolizers());
|
||||||
|
feature.setOriginal(original);
|
||||||
|
}
|
||||||
|
if (renderIntent == ol.layer.VectorLayerRenderIntent.TEMPORARY) {
|
||||||
|
if (ol.coordinate.equals(segment[0], vertex)) {
|
||||||
|
this.dragSegments_.push([segmentDataMatch, 0]);
|
||||||
|
} else if (ol.coordinate.equals(segment[1], vertex)) {
|
||||||
|
this.dragSegments_.push([segmentDataMatch, 1]);
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
ol.coordinate.squaredDistanceToSegment(vertex, segment) === 0) {
|
||||||
|
insertVertices.push([segmentDataMatch, vertex]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = insertVertices.length - 1; i >= 0; --i) {
|
||||||
|
this.insertVertex_.apply(this, insertVertices[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.modifiable_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
ol.interaction.Modify.prototype.handleDrag = function(evt) {
|
||||||
|
var vertex = evt.getCoordinate();
|
||||||
|
for (var i = 0, ii = this.dragSegments_.length; i < ii; ++i) {
|
||||||
|
var dragSegment = this.dragSegments_[i];
|
||||||
|
var segmentData = dragSegment[0];
|
||||||
|
var feature = segmentData.feature;
|
||||||
|
var geometry = segmentData.geometry;
|
||||||
|
var coordinates = geometry.getCoordinates();
|
||||||
|
|
||||||
|
var oldBounds, newBounds;
|
||||||
|
if (geometry instanceof ol.geom.Point) {
|
||||||
|
oldBounds = geometry.getBounds();
|
||||||
|
geometry.setCoordinates(vertex);
|
||||||
|
newBounds = geometry.getBounds();
|
||||||
|
} else {
|
||||||
|
var index = dragSegment[1];
|
||||||
|
coordinates[segmentData.index + index] = vertex;
|
||||||
|
geometry.setCoordinates(coordinates);
|
||||||
|
var segment = segmentData.segment;
|
||||||
|
oldBounds = ol.extent.boundingExtent(segment);
|
||||||
|
segment[index] = vertex;
|
||||||
|
newBounds = ol.extent.boundingExtent(segment);
|
||||||
|
}
|
||||||
|
this.createOrUpdateVertexFeature_(segmentData.style, vertex);
|
||||||
|
this.rTree_.remove(oldBounds, segmentData);
|
||||||
|
this.rTree_.insert(newBounds, segmentData, goog.getUid(feature));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
ol.interaction.Modify.prototype.handleMapBrowserEvent =
|
||||||
|
function(mapBrowserEvent) {
|
||||||
|
if (!mapBrowserEvent.map.getView().getHints()[ol.ViewHint.INTERACTING] &&
|
||||||
|
!this.getDragging() &&
|
||||||
|
mapBrowserEvent.type == ol.MapBrowserEvent.EventType.MOUSEMOVE) {
|
||||||
|
this.handleMouseMove_(mapBrowserEvent);
|
||||||
|
}
|
||||||
|
goog.base(this, 'handleMapBrowserEvent', mapBrowserEvent);
|
||||||
|
return !this.modifiable_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ol.MapBrowserEvent} evt Event.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.interaction.Modify.prototype.handleMouseMove_ = function(evt) {
|
||||||
|
var map = evt.map;
|
||||||
|
var pixel = evt.getPixel();
|
||||||
|
var pixelCoordinate = map.getCoordinateFromPixel(pixel);
|
||||||
|
var sortByDistance = function(a, b) {
|
||||||
|
return ol.coordinate.squaredDistanceToSegment(pixelCoordinate, a.segment) -
|
||||||
|
ol.coordinate.squaredDistanceToSegment(pixelCoordinate, b.segment);
|
||||||
|
};
|
||||||
|
|
||||||
|
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]);
|
||||||
|
|
||||||
|
this.modifiable_ = false;
|
||||||
|
var vertexFeature = this.vertexFeature_;
|
||||||
|
var rTree = this.rTree_;
|
||||||
|
var segmentDataMatches = rTree.search(box);
|
||||||
|
var renderIntent = ol.layer.VectorLayerRenderIntent.HIDDEN;
|
||||||
|
if (segmentDataMatches.length > 0) {
|
||||||
|
segmentDataMatches.sort(sortByDistance);
|
||||||
|
var segmentDataMatch = segmentDataMatches[0];
|
||||||
|
var segment = segmentDataMatch.segment; // the closest segment
|
||||||
|
var vertex = (ol.coordinate.closestOnSegment(pixelCoordinate, segment));
|
||||||
|
var vertexPixel = map.getPixelFromCoordinate(vertex);
|
||||||
|
if (Math.sqrt(ol.coordinate.squaredDistance(pixel, vertexPixel)) <=
|
||||||
|
this.pixelTolerance_) {
|
||||||
|
var pixel1 = map.getPixelFromCoordinate(segment[0]);
|
||||||
|
var pixel2 = map.getPixelFromCoordinate(segment[1]);
|
||||||
|
var squaredDist1 = ol.coordinate.squaredDistance(vertexPixel, pixel1);
|
||||||
|
var squaredDist2 = ol.coordinate.squaredDistance(vertexPixel, pixel2);
|
||||||
|
var dist = Math.sqrt(Math.min(squaredDist1, squaredDist2));
|
||||||
|
renderIntent = ol.layer.VectorLayerRenderIntent.FUTURE;
|
||||||
|
if (dist <= 10) {
|
||||||
|
vertex = squaredDist1 > squaredDist2 ? segment[1] : segment[0];
|
||||||
|
renderIntent = ol.layer.VectorLayerRenderIntent.TEMPORARY;
|
||||||
|
}
|
||||||
|
vertexFeature = this.createOrUpdateVertexFeature_(segmentDataMatch.style,
|
||||||
|
vertex);
|
||||||
|
this.modifiable_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!goog.isNull(vertexFeature) &&
|
||||||
|
renderIntent != vertexFeature.getRenderIntent()) {
|
||||||
|
vertexFeature.setRenderIntent(renderIntent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ol.interaction.SegmentDataType} segmentData Segment data.
|
||||||
|
* @param {ol.Coordinate} vertex Vertex.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.interaction.Modify.prototype.insertVertex_ =
|
||||||
|
function(segmentData, vertex) {
|
||||||
|
var segment = segmentData.segment;
|
||||||
|
var feature = segmentData.feature;
|
||||||
|
var geometry = segmentData.geometry;
|
||||||
|
var index = segmentData.index;
|
||||||
|
var coordinates = geometry.getCoordinates();
|
||||||
|
coordinates.splice(index + 1, 0, vertex);
|
||||||
|
geometry.setCoordinates(coordinates);
|
||||||
|
var rTree = this.rTree_;
|
||||||
|
goog.asserts.assert(goog.isDef(segment));
|
||||||
|
rTree.remove(ol.extent.boundingExtent(segment), segmentData);
|
||||||
|
var uid = goog.getUid(feature);
|
||||||
|
var segmentDataMatches = this.rTree_.search(geometry.getBounds(), uid);
|
||||||
|
for (var i = 0, ii = segmentDataMatches.length; i < ii; ++i) {
|
||||||
|
var segmentDataMatch = segmentDataMatches[i];
|
||||||
|
if (segmentDataMatch.geometry === geometry &&
|
||||||
|
segmentDataMatch.index > index) {
|
||||||
|
++segmentDataMatch.index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var newSegmentData = /** @type {ol.interaction.SegmentDataType} */ ({
|
||||||
|
style: segmentData.style,
|
||||||
|
segment: [segment[0], vertex],
|
||||||
|
feature: feature,
|
||||||
|
geometry: geometry,
|
||||||
|
index: index
|
||||||
|
});
|
||||||
|
rTree.insert(ol.extent.boundingExtent(newSegmentData.segment), newSegmentData,
|
||||||
|
uid);
|
||||||
|
this.dragSegments_.push([newSegmentData, 1]);
|
||||||
|
newSegmentData = goog.object.clone(newSegmentData);
|
||||||
|
newSegmentData.segment = [vertex, segment[1]];
|
||||||
|
newSegmentData.index += 1;
|
||||||
|
rTree.insert(ol.extent.boundingExtent(newSegmentData.segment), newSegmentData,
|
||||||
|
uid);
|
||||||
|
this.dragSegments_.push([newSegmentData, 0]);
|
||||||
|
};
|
||||||
@@ -158,7 +158,7 @@ ol.layer.Vector = function(options) {
|
|||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this.temp_ = false;
|
this.temporary_ = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
goog.inherits(ol.layer.Vector, ol.layer.Layer);
|
goog.inherits(ol.layer.Vector, ol.layer.Layer);
|
||||||
@@ -241,7 +241,7 @@ ol.layer.Vector.prototype.clear = function() {
|
|||||||
* @return {boolean} Whether this layer is temporary.
|
* @return {boolean} Whether this layer is temporary.
|
||||||
*/
|
*/
|
||||||
ol.layer.Vector.prototype.getTemporary = function() {
|
ol.layer.Vector.prototype.getTemporary = function() {
|
||||||
return this.temp_;
|
return this.temporary_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -463,10 +463,10 @@ ol.layer.Vector.prototype.removeFeatures = function(features) {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {boolean} temp Whether this layer is temporary.
|
* @param {boolean} temporary Whether this layer is temporary.
|
||||||
*/
|
*/
|
||||||
ol.layer.Vector.prototype.setTemporary = function(temp) {
|
ol.layer.Vector.prototype.setTemporary = function(temporary) {
|
||||||
this.temp_ = temp;
|
this.temporary_ = temporary;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ goog.provide('ol.layer.VectorLayerRenderIntent');
|
|||||||
*/
|
*/
|
||||||
ol.layer.VectorLayerRenderIntent = {
|
ol.layer.VectorLayerRenderIntent = {
|
||||||
DEFAULT: 'default',
|
DEFAULT: 'default',
|
||||||
|
FUTURE: 'future',
|
||||||
HIDDEN: 'hidden',
|
HIDDEN: 'hidden',
|
||||||
SELECTED: 'selected',
|
SELECTED: 'selected',
|
||||||
TEMPORARY: 'temporary'
|
TEMPORARY: 'temporary'
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ goog.require('ol.extent');
|
|||||||
* leaf: (Object|undefined),
|
* leaf: (Object|undefined),
|
||||||
* nodes: (Array.<ol.structs.RTreeNode>|undefined),
|
* nodes: (Array.<ol.structs.RTreeNode>|undefined),
|
||||||
* target: (Object|undefined),
|
* target: (Object|undefined),
|
||||||
* type: (string|undefined)}}
|
* type: (string|number|undefined)}}
|
||||||
*/
|
*/
|
||||||
ol.structs.RTreeNode;
|
ol.structs.RTreeNode;
|
||||||
|
|
||||||
@@ -186,7 +186,8 @@ ol.structs.RTree.prototype.chooseLeafSubtree_ = function(rect, root) {
|
|||||||
*
|
*
|
||||||
* @param {ol.Extent} extent Extent.
|
* @param {ol.Extent} extent Extent.
|
||||||
* @param {Object} obj Object to insert.
|
* @param {Object} obj Object to insert.
|
||||||
* @param {string=} opt_type Optional type to store along with the object.
|
* @param {string|number=} opt_type Optional type to store along with the
|
||||||
|
* object.
|
||||||
*/
|
*/
|
||||||
ol.structs.RTree.prototype.insert = function(extent, obj, opt_type) {
|
ol.structs.RTree.prototype.insert = function(extent, obj, opt_type) {
|
||||||
var node = /** @type {ol.structs.RTreeNode} */
|
var node = /** @type {ol.structs.RTreeNode} */
|
||||||
@@ -554,7 +555,8 @@ ol.structs.RTree.prototype.removeSubtree_ = function(rect, obj, root) {
|
|||||||
* Non-recursive search function
|
* Non-recursive search function
|
||||||
*
|
*
|
||||||
* @param {ol.Extent} extent Extent.
|
* @param {ol.Extent} extent Extent.
|
||||||
* @param {string=} opt_type Optional type of the objects we want to find.
|
* @param {string|number=} opt_type Optional type of the objects we want to
|
||||||
|
* find.
|
||||||
* @return {Array} Result.
|
* @return {Array} Result.
|
||||||
* @this {ol.structs.RTree}
|
* @this {ol.structs.RTree}
|
||||||
*/
|
*/
|
||||||
@@ -569,7 +571,8 @@ ol.structs.RTree.prototype.search = function(extent, opt_type) {
|
|||||||
* Non-recursive search function
|
* Non-recursive search function
|
||||||
*
|
*
|
||||||
* @param {ol.Extent} extent Extent.
|
* @param {ol.Extent} extent Extent.
|
||||||
* @param {string=} opt_type Optional type of the objects we want to find.
|
* @param {string|number=} opt_type Optional type of the objects we want to
|
||||||
|
* find.
|
||||||
* @return {Object} Result. Keys are UIDs of the values.
|
* @return {Object} Result. Keys are UIDs of the values.
|
||||||
* @this {ol.structs.RTree}
|
* @this {ol.structs.RTree}
|
||||||
*/
|
*/
|
||||||
@@ -587,7 +590,7 @@ ol.structs.RTree.prototype.searchReturningObject = function(extent, opt_type) {
|
|||||||
* @param {boolean} returnNode Do we return nodes?
|
* @param {boolean} returnNode Do we return nodes?
|
||||||
* @param {Array|Object} result Result.
|
* @param {Array|Object} result Result.
|
||||||
* @param {ol.structs.RTreeNode} root Root.
|
* @param {ol.structs.RTreeNode} root Root.
|
||||||
* @param {string=} opt_type Optional type to search for.
|
* @param {string|number=} opt_type Optional type to search for.
|
||||||
* @param {boolean=} opt_resultAsObject If set, result will be an object keyed
|
* @param {boolean=} opt_resultAsObject If set, result will be an object keyed
|
||||||
* by UID.
|
* by UID.
|
||||||
* @private
|
* @private
|
||||||
|
|||||||
Reference in New Issue
Block a user