|
/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for |
|
* full list of contributors). Published under the Clear BSD license. |
|
* See http://svn.openlayers.org/trunk/openlayers/license.txt for the |
|
* full text of the license. */ |
|
|
|
/** |
|
* @requires OpenLayers/Control.js |
|
* @requires OpenLayers/Handler/Path.js |
|
* @requires OpenLayers/Layer/Vector.js |
|
*/ |
|
|
|
/** |
|
* Class: OpenLayers.Control.Split |
|
* Acts as a split feature agent while editing vector features. |
|
* |
|
* Inherits from: |
|
* - <OpenLayers.Control> |
|
*/ |
|
OpenLayers.Control.Split = OpenLayers.Class(OpenLayers.Control, { |
|
|
|
/** |
|
* Constant: EVENT_TYPES |
|
* {Array(String)} Supported application event types. Register a listener |
|
* for a particular event with the following syntax: |
|
* (code) |
|
* control.events.register(type, obj, listener); |
|
* (end) |
|
* |
|
* Listeners will be called with a reference to an event object. The |
|
* properties of this event depends on exactly what happened. |
|
* |
|
* Supported control event types (in addition to those from <OpenLayers.Control>): |
|
* beforesplit - Triggered before a split occurs. Listeners receive an |
|
* event object with *source* and *target* properties. |
|
* split - Triggered when a split occurs. Listeners receive an event with |
|
* an *original* property and a *features* property. The original |
|
* is a reference to the target feature that the sketch or modified |
|
* feature intersects. The features property is a list of all features |
|
* that result from this single split. This event is triggered before |
|
* the resulting features are added to the layer (while the layer still |
|
* has a reference to the original). |
|
* aftersplit - Triggered after all splits resulting from a single sketch |
|
* or feature modification have occurred. The original features |
|
* have been destroyed and features that result from the split |
|
* have already been added to the layer. Listeners receive an event |
|
* with a *source* and *features* property. The source references the |
|
* sketch or modified feature used as a splitter. The features |
|
* property is a list of all resulting features. |
|
*/ |
|
EVENT_TYPES: ["beforesplit", "split", "aftersplit"], |
|
|
|
/** |
|
* APIProperty: layer |
|
* {<OpenLayers.Layer.Vector>} The target layer with features to be split. |
|
* Set at construction or after construction with <setLayer>. |
|
*/ |
|
layer: null, |
|
|
|
/** |
|
* Property: source |
|
* {<OpenLayers.Layer.Vector>} Optional source layer. Any newly created |
|
* or modified features from this layer will be used to split features |
|
* on the target layer. If not provided, a temporary sketch layer will |
|
* be created. |
|
*/ |
|
source: null, |
|
|
|
/** |
|
* Property: sourceOptions |
|
* {Options} If a temporary sketch layer is created, these layer options |
|
* will be applied. |
|
*/ |
|
sourceOptions: null, |
|
|
|
/** |
|
* APIProperty: tolerance |
|
* {Number} Distance between the calculated intersection and a vertex on |
|
* the source geometry below which the existing vertex will be used |
|
* for the split. Default is null. |
|
*/ |
|
tolerance: null, |
|
|
|
/** |
|
* APIProperty: edge |
|
* {Boolean} Allow splits given intersection of edges only. Default is |
|
* true. If false, a vertex on the source must be within the |
|
* <tolerance> distance of the calculated intersection for a split |
|
* to occur. |
|
*/ |
|
edge: true, |
|
|
|
/** |
|
* APIProperty: deferDelete |
|
* {Boolean} Instead of removing features from the layer, set feature |
|
* states of split features to DELETE. This assumes a save strategy |
|
* or other component is in charge of removing features from the |
|
* layer. Default is false. If false, split features will be |
|
* immediately deleted from the layer. |
|
*/ |
|
deferDelete: false, |
|
|
|
/** |
|
* APIProperty: mutual |
|
* {Boolean} If source and target layers are the same, split source |
|
* features and target features where they intersect. Default is |
|
* true. If false, only target features will be split. |
|
*/ |
|
mutual: true, |
|
|
|
/** |
|
* APIProperty: targetFilter |
|
* {OpenLayers.Filter} Optional filter that will be evaluated |
|
* to determine if a feature from the target layer is eligible for |
|
* splitting. |
|
*/ |
|
targetFilter: null, |
|
|
|
/** |
|
* APIProperty: sourceFilter |
|
* {OpenLayers.Filter} Optional filter that will be evaluated |
|
* to determine if a feature from the target layer is eligible for |
|
* splitting. |
|
*/ |
|
sourceFilter: null, |
|
|
|
/** |
|
* Property: handler |
|
* {<OpenLayers.Handler.Path>} The temporary sketch handler created if |
|
* no source layer is provided. |
|
*/ |
|
handler: null, |
|
|
|
/** |
|
* Constructor: OpenLayers.Control.Split |
|
* Creates a new split control. A control is constructed with a target |
|
* layer and an optional source layer. While the control is active, |
|
* creating new features or modifying existing features on the source |
|
* layer will result in splitting any eligible features on the target |
|
* layer. If no source layer is provided, a temporary sketch layer will |
|
* be created to create lines for splitting features on the target. |
|
* |
|
* Parameters: |
|
* options - {Object} An object containing all configuration properties for |
|
* the control. |
|
* |
|
* Valid options: |
|
* layer - {OpenLayers.Layer.Vector} The target layer. Features from this |
|
* layer will be split by new or modified features on the source layer |
|
* or temporary sketch layer. |
|
* source - {OpenLayers.Layer.Vector} Optional source layer. If provided |
|
* newly created features or modified features will be used to split |
|
* features on the target layer. If not provided, a temporary sketch |
|
* layer will be created for drawing lines. |
|
* tolerance - {Number} Optional value for the distance between a source |
|
* vertex and the calculated intersection below which the split will |
|
* occur at the vertex. |
|
* edge - {Boolean} Allow splits given intersection of edges only. Default |
|
* is true. If false, a vertex on the source must be within the |
|
* <tolerance> distance of the calculated intersection for a split |
|
* to occur. |
|
* mutual - {Boolean} If source and target are the same, split source |
|
* features and target features where they intersect. Default is |
|
* true. If false, only target features will be split. |
|
* targetFilter - {OpenLayers.Filter} Optional filter that will be evaluated |
|
* to determine if a feature from the target layer is eligible for |
|
* splitting. |
|
* sourceFilter - {OpenLayers.Filter} Optional filter that will be evaluated |
|
* to determine if a feature from the target layer is eligible for |
|
* splitting. |
|
*/ |
|
initialize: function(options) { |
|
// concatenate events specific to measure with those from the base |
|
Array.prototype.push.apply( |
|
this.EVENT_TYPES, OpenLayers.Control.prototype.EVENT_TYPES |
|
); |
|
OpenLayers.Control.prototype.initialize.apply(this, [options]); |
|
this.options = options || {}; // TODO: this could be done by the super |
|
|
|
// set the source layer if provided |
|
if(this.options.source) { |
|
this.setSource(this.options.source); |
|
} |
|
}, |
|
|
|
/** |
|
* APIMethod: setSource |
|
* Set the source layer for edits layer. |
|
* |
|
* Parameters: |
|
* layer - {OpenLayers.Layer.Vector} The new source layer layer. If |
|
* null, a temporary sketch layer will be created. |
|
*/ |
|
setSource: function(layer) { |
|
if(this.active) { |
|
this.deactivate(); |
|
if(this.handler) { |
|
this.handler.destroy(); |
|
delete this.handler; |
|
} |
|
this.source = layer; |
|
this.activate(); |
|
} else { |
|
this.source = layer; |
|
} |
|
}, |
|
|
|
/** |
|
* APIMethod: activate |
|
* Activate the control. Activating the control registers listeners for |
|
* editing related events so that during feature creation and |
|
* modification, features in the target will be considered for |
|
* splitting. |
|
*/ |
|
activate: function() { |
|
var activated = OpenLayers.Control.prototype.activate.call(this); |
|
if(activated) { |
|
if(!this.source) { |
|
if(!this.handler) { |
|
this.handler = new OpenLayers.Handler.Path(this, |
|
{done: function(geometry) { |
|
this.onSketchComplete({ |
|
feature: new OpenLayers.Feature.Vector(geometry) |
|
}); |
|
}}, |
|
{layerOptions: this.sourceOptions} |
|
); |
|
} |
|
this.handler.activate(); |
|
} else if(this.source.events) { |
|
this.source.events.on({ |
|
sketchcomplete: this.onSketchComplete, |
|
afterfeaturemodified: this.afterFeatureModified, |
|
scope: this |
|
}); |
|
} |
|
} |
|
return activated; |
|
}, |
|
|
|
/** |
|
* APIMethod: deactivate |
|
* Deactivate the control. Deactivating the control unregisters listeners |
|
* so feature editing may proceed without engaging the split agent. |
|
*/ |
|
deactivate: function() { |
|
var deactivated = OpenLayers.Control.prototype.deactivate.call(this); |
|
if(deactivated) { |
|
if(this.source && this.source.events) { |
|
this.layer.events.un({ |
|
sketchcomplete: this.onSketchComplete, |
|
afterfeaturemodified: this.afterFeatureModified, |
|
scope: this |
|
}); |
|
} |
|
} |
|
return deactivated; |
|
}, |
|
|
|
/** |
|
* Method: onSketchComplete |
|
* Registered as a listener for the sketchcomplete event on the editable |
|
* layer. |
|
* |
|
* Parameters: |
|
* event - {Object} The sketch complete event. |
|
* |
|
* Returns: |
|
* {Boolean} Stop the sketch from being added to the layer (it has been |
|
* split). |
|
*/ |
|
onSketchComplete: function(event) { |
|
this.feature = null; |
|
return !this.considerSplit(event.feature); |
|
}, |
|
|
|
/** |
|
* Method: afterFeatureModified |
|
* Registered as a listener for the afterfeaturemodified event on the |
|
* editable layer. |
|
* |
|
* Parameters: |
|
* event - {Object} The after feature modified event. |
|
*/ |
|
afterFeatureModified: function(event) { |
|
if(event.modified) { |
|
var feature = event.feature; |
|
if(feature.geometry instanceof OpenLayers.Geometry.LineString || |
|
feature.geometry instanceof OpenLayers.Geometry.MultiLineString) { |
|
this.feature = event.feature; |
|
this.considerSplit(event.feature); |
|
} |
|
} |
|
}, |
|
|
|
/** |
|
* Method: removeByGeometry |
|
* Remove a feature from a list based on the given geometry. |
|
* |
|
* Parameters: |
|
* features - {Array(<OpenLayers.Feature.Vector>} A list of features. |
|
* geometry - {<OpenLayers.Geometry>} A geometry. |
|
*/ |
|
removeByGeometry: function(features, geometry) { |
|
for(var i=0, len=features.length; i<len; ++i) { |
|
if(features[i].geometry === geometry) { |
|
features.splice(i, 1); |
|
break; |
|
} |
|
} |
|
}, |
|
|
|
/** |
|
* Method: isEligible |
|
* Test if a target feature is eligible for splitting. |
|
* |
|
* Parameters: |
|
* target - {<OpenLayers.Feature.Vector>} The target feature. |
|
* |
|
* Returns: |
|
* {Boolean} The target is eligible for splitting. |
|
*/ |
|
isEligible: function(target) { |
|
return ( |
|
target.state !== OpenLayers.State.DELETE |
|
) && ( |
|
target.geometry instanceof OpenLayers.Geometry.LineString || |
|
target.geometry instanceof OpenLayers.Geometry.MultiLineString |
|
) && ( |
|
this.feature !== target |
|
) && ( |
|
!this.targetFilter || |
|
this.targetFilter.evaluate(target.attributes) |
|
); |
|
}, |
|
|
|
/** |
|
* Method: considerSplit |
|
* Decide whether or not to split target features with the supplied |
|
* feature. If <mutual> is true, both the source and target features |
|
* will be split if eligible. |
|
* |
|
* Parameters: |
|
* feature - {<OpenLayers.Feature.Vector}} The newly created or modified |
|
* feature. |
|
* |
|
* Returns: |
|
* {Boolean} The supplied feature was split (and destroyed). |
|
*/ |
|
considerSplit: function(feature) { |
|
var sourceSplit = false; |
|
var targetSplit = false; |
|
if(!this.sourceFilter || |
|
this.sourceFilter.evaluate(feature.attributes)) { |
|
var features = this.layer && this.layer.features || []; |
|
var target, results, proceed; |
|
var additions = [], removals = []; |
|
var mutual = (this.layer === this.source) && this.mutual; |
|
var options = { |
|
edge: this.edge, |
|
tolerance: this.tolerance, |
|
mutual: mutual |
|
}; |
|
var sourceParts = [feature.geometry]; |
|
var targetFeature, targetParts; |
|
var source, parts; |
|
for(var i=0, len=features.length; i<len; ++i) { |