Split.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. /* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for
  2. * full list of contributors). Published under the Clear BSD license.
  3. * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  4. * full text of the license. */
  5. /**
  6. * @requires OpenLayers/Control.js
  7. * @requires OpenLayers/Handler/Path.js
  8. * @requires OpenLayers/Layer/Vector.js
  9. */
  10. /**
  11. * Class: OpenLayers.Control.Split
  12. * Acts as a split feature agent while editing vector features.
  13. *
  14. * Inherits from:
  15. * - <OpenLayers.Control>
  16. */
  17. OpenLayers.Control.Split = OpenLayers.Class(OpenLayers.Control, {
  18. /**
  19. * Constant: EVENT_TYPES
  20. * {Array(String)} Supported application event types. Register a listener
  21. * for a particular event with the following syntax:
  22. * (code)
  23. * control.events.register(type, obj, listener);
  24. * (end)
  25. *
  26. * Listeners will be called with a reference to an event object. The
  27. * properties of this event depends on exactly what happened.
  28. *
  29. * Supported control event types (in addition to those from <OpenLayers.Control>):
  30. * beforesplit - Triggered before a split occurs. Listeners receive an
  31. * event object with *source* and *target* properties.
  32. * split - Triggered when a split occurs. Listeners receive an event with
  33. * an *original* property and a *features* property. The original
  34. * is a reference to the target feature that the sketch or modified
  35. * feature intersects. The features property is a list of all features
  36. * that result from this single split. This event is triggered before
  37. * the resulting features are added to the layer (while the layer still
  38. * has a reference to the original).
  39. * aftersplit - Triggered after all splits resulting from a single sketch
  40. * or feature modification have occurred. The original features
  41. * have been destroyed and features that result from the split
  42. * have already been added to the layer. Listeners receive an event
  43. * with a *source* and *features* property. The source references the
  44. * sketch or modified feature used as a splitter. The features
  45. * property is a list of all resulting features.
  46. */
  47. EVENT_TYPES: ["beforesplit", "split", "aftersplit"],
  48. /**
  49. * APIProperty: layer
  50. * {<OpenLayers.Layer.Vector>} The target layer with features to be split.
  51. * Set at construction or after construction with <setLayer>.
  52. */
  53. layer: null,
  54. /**
  55. * Property: source
  56. * {<OpenLayers.Layer.Vector>} Optional source layer. Any newly created
  57. * or modified features from this layer will be used to split features
  58. * on the target layer. If not provided, a temporary sketch layer will
  59. * be created.
  60. */
  61. source: null,
  62. /**
  63. * Property: sourceOptions
  64. * {Options} If a temporary sketch layer is created, these layer options
  65. * will be applied.
  66. */
  67. sourceOptions: null,
  68. /**
  69. * APIProperty: tolerance
  70. * {Number} Distance between the calculated intersection and a vertex on
  71. * the source geometry below which the existing vertex will be used
  72. * for the split. Default is null.
  73. */
  74. tolerance: null,
  75. /**
  76. * APIProperty: edge
  77. * {Boolean} Allow splits given intersection of edges only. Default is
  78. * true. If false, a vertex on the source must be within the
  79. * <tolerance> distance of the calculated intersection for a split
  80. * to occur.
  81. */
  82. edge: true,
  83. /**
  84. * APIProperty: deferDelete
  85. * {Boolean} Instead of removing features from the layer, set feature
  86. * states of split features to DELETE. This assumes a save strategy
  87. * or other component is in charge of removing features from the
  88. * layer. Default is false. If false, split features will be
  89. * immediately deleted from the layer.
  90. */
  91. deferDelete: false,
  92. /**
  93. * APIProperty: mutual
  94. * {Boolean} If source and target layers are the same, split source
  95. * features and target features where they intersect. Default is
  96. * true. If false, only target features will be split.
  97. */
  98. mutual: true,
  99. /**
  100. * APIProperty: targetFilter
  101. * {OpenLayers.Filter} Optional filter that will be evaluated
  102. * to determine if a feature from the target layer is eligible for
  103. * splitting.
  104. */
  105. targetFilter: null,
  106. /**
  107. * APIProperty: sourceFilter
  108. * {OpenLayers.Filter} Optional filter that will be evaluated
  109. * to determine if a feature from the target layer is eligible for
  110. * splitting.
  111. */
  112. sourceFilter: null,
  113. /**
  114. * Property: handler
  115. * {<OpenLayers.Handler.Path>} The temporary sketch handler created if
  116. * no source layer is provided.
  117. */
  118. handler: null,
  119. /**
  120. * Constructor: OpenLayers.Control.Split
  121. * Creates a new split control. A control is constructed with a target
  122. * layer and an optional source layer. While the control is active,
  123. * creating new features or modifying existing features on the source
  124. * layer will result in splitting any eligible features on the target
  125. * layer. If no source layer is provided, a temporary sketch layer will
  126. * be created to create lines for splitting features on the target.
  127. *
  128. * Parameters:
  129. * options - {Object} An object containing all configuration properties for
  130. * the control.
  131. *
  132. * Valid options:
  133. * layer - {OpenLayers.Layer.Vector} The target layer. Features from this
  134. * layer will be split by new or modified features on the source layer
  135. * or temporary sketch layer.
  136. * source - {OpenLayers.Layer.Vector} Optional source layer. If provided
  137. * newly created features or modified features will be used to split
  138. * features on the target layer. If not provided, a temporary sketch
  139. * layer will be created for drawing lines.
  140. * tolerance - {Number} Optional value for the distance between a source
  141. * vertex and the calculated intersection below which the split will
  142. * occur at the vertex.
  143. * edge - {Boolean} Allow splits given intersection of edges only. Default
  144. * is true. If false, a vertex on the source must be within the
  145. * <tolerance> distance of the calculated intersection for a split
  146. * to occur.
  147. * mutual - {Boolean} If source and target are the same, split source
  148. * features and target features where they intersect. Default is
  149. * true. If false, only target features will be split.
  150. * targetFilter - {OpenLayers.Filter} Optional filter that will be evaluated
  151. * to determine if a feature from the target layer is eligible for
  152. * splitting.
  153. * sourceFilter - {OpenLayers.Filter} Optional filter that will be evaluated
  154. * to determine if a feature from the target layer is eligible for
  155. * splitting.
  156. */
  157. initialize: function(options) {
  158. // concatenate events specific to measure with those from the base
  159. Array.prototype.push.apply(
  160. this.EVENT_TYPES, OpenLayers.Control.prototype.EVENT_TYPES
  161. );
  162. OpenLayers.Control.prototype.initialize.apply(this, [options]);
  163. this.options = options || {}; // TODO: this could be done by the super
  164. // set the source layer if provided
  165. if(this.options.source) {
  166. this.setSource(this.options.source);
  167. }
  168. },
  169. /**
  170. * APIMethod: setSource
  171. * Set the source layer for edits layer.
  172. *
  173. * Parameters:
  174. * layer - {OpenLayers.Layer.Vector} The new source layer layer. If
  175. * null, a temporary sketch layer will be created.
  176. */
  177. setSource: function(layer) {
  178. if(this.active) {
  179. this.deactivate();
  180. if(this.handler) {
  181. this.handler.destroy();
  182. delete this.handler;
  183. }
  184. this.source = layer;
  185. this.activate();
  186. } else {
  187. this.source = layer;
  188. }
  189. },
  190. /**
  191. * APIMethod: activate
  192. * Activate the control. Activating the control registers listeners for
  193. * editing related events so that during feature creation and
  194. * modification, features in the target will be considered for
  195. * splitting.
  196. */
  197. activate: function() {
  198. var activated = OpenLayers.Control.prototype.activate.call(this);
  199. if(activated) {
  200. if(!this.source) {
  201. if(!this.handler) {
  202. this.handler = new OpenLayers.Handler.Path(this,
  203. {done: function(geometry) {
  204. this.onSketchComplete({
  205. feature: new OpenLayers.Feature.Vector(geometry)
  206. });
  207. }},
  208. {layerOptions: this.sourceOptions}
  209. );
  210. }
  211. this.handler.activate();
  212. } else if(this.source.events) {
  213. this.source.events.on({
  214. sketchcomplete: this.onSketchComplete,
  215. afterfeaturemodified: this.afterFeatureModified,
  216. scope: this
  217. });
  218. }
  219. }
  220. return activated;
  221. },
  222. /**
  223. * APIMethod: deactivate
  224. * Deactivate the control. Deactivating the control unregisters listeners
  225. * so feature editing may proceed without engaging the split agent.
  226. */
  227. deactivate: function() {
  228. var deactivated = OpenLayers.Control.prototype.deactivate.call(this);
  229. if(deactivated) {
  230. if(this.source && this.source.events) {
  231. this.layer.events.un({
  232. sketchcomplete: this.onSketchComplete,
  233. afterfeaturemodified: this.afterFeatureModified,
  234. scope: this
  235. });
  236. }
  237. }
  238. return deactivated;
  239. },
  240. /**
  241. * Method: onSketchComplete
  242. * Registered as a listener for the sketchcomplete event on the editable
  243. * layer.
  244. *
  245. * Parameters:
  246. * event - {Object} The sketch complete event.
  247. *
  248. * Returns:
  249. * {Boolean} Stop the sketch from being added to the layer (it has been
  250. * split).
  251. */
  252. onSketchComplete: function(event) {
  253. this.feature = null;
  254. return !this.considerSplit(event.feature);
  255. },
  256. /**
  257. * Method: afterFeatureModified
  258. * Registered as a listener for the afterfeaturemodified event on the
  259. * editable layer.
  260. *
  261. * Parameters:
  262. * event - {Object} The after feature modified event.
  263. */
  264. afterFeatureModified: function(event) {
  265. if(event.modified) {
  266. var feature = event.feature;
  267. if(feature.geometry instanceof OpenLayers.Geometry.LineString ||
  268. feature.geometry instanceof OpenLayers.Geometry.MultiLineString) {
  269. this.feature = event.feature;
  270. this.considerSplit(event.feature);
  271. }
  272. }
  273. },
  274. /**
  275. * Method: removeByGeometry
  276. * Remove a feature from a list based on the given geometry.
  277. *
  278. * Parameters:
  279. * features - {Array(<OpenLayers.Feature.Vector>)} A list of features.
  280. * geometry - {<OpenLayers.Geometry>} A geometry.
  281. */
  282. removeByGeometry: function(features, geometry) {
  283. for(var i=0, len=features.length; i<len; ++i) {
  284. if(features[i].geometry === geometry) {
  285. features.splice(i, 1);
  286. break;
  287. }
  288. }
  289. },
  290. /**
  291. * Method: isEligible
  292. * Test if a target feature is eligible for splitting.
  293. *
  294. * Parameters:
  295. * target - {<OpenLayers.Feature.Vector>} The target feature.
  296. *
  297. * Returns:
  298. * {Boolean} The target is eligible for splitting.
  299. */
  300. isEligible: function(target) {
  301. return (
  302. target.state !== OpenLayers.State.DELETE
  303. ) && (
  304. target.geometry instanceof OpenLayers.Geometry.LineString ||
  305. target.geometry instanceof OpenLayers.Geometry.MultiLineString
  306. ) && (
  307. this.feature !== target
  308. ) && (
  309. !this.targetFilter ||
  310. this.targetFilter.evaluate(target.attributes)
  311. );
  312. },
  313. /**
  314. * Method: considerSplit
  315. * Decide whether or not to split target features with the supplied
  316. * feature. If <mutual> is true, both the source and target features
  317. * will be split if eligible.
  318. *
  319. * Parameters:
  320. * feature - {<OpenLayers.Feature.Vector>} The newly created or modified
  321. * feature.
  322. *
  323. * Returns:
  324. * {Boolean} The supplied feature was split (and destroyed).
  325. */
  326. considerSplit: function(feature) {
  327. var sourceSplit = false;
  328. var targetSplit = false;
  329. if(!this.sourceFilter ||
  330. this.sourceFilter.evaluate(feature.attributes)) {
  331. var features = this.layer && this.layer.features || [];
  332. var target, results, proceed;
  333. var additions = [], removals = [];
  334. var mutual = (this.layer === this.source) && this.mutual;
  335. var options = {
  336. edge: this.edge,
  337. tolerance: this.tolerance,
  338. mutual: mutual
  339. };
  340. var sourceParts = [feature.geometry];
  341. var targetFeature, targetParts;
  342. var source, parts;
  343. for(var i=0, len=features.length; i<len; ++i) {
  344. targetFeature = features[i];
  345. if(this.isEligible(targetFeature)) {
  346. targetParts = [targetFeature.geometry];
  347. // work through source geoms - this array may change
  348. for(var j=0; j<sourceParts.length; ++j) {
  349. source = sourceParts[j];
  350. // work through target parts - this array may change
  351. for(var k=0; k<targetParts.length; ++k) {
  352. target = targetParts[k];
  353. if(source.getBounds().intersectsBounds(target.getBounds())) {
  354. results = source.split(target, options);
  355. if(results) {
  356. proceed = this.events.triggerEvent(
  357. "beforesplit", {source: feature, target: targetFeature}
  358. );
  359. if(proceed !== false) {
  360. if(mutual) {
  361. parts = results[0];
  362. // handle parts that result from source splitting
  363. if(parts.length > 1) {
  364. // splice in new source parts
  365. parts.unshift(j, 1); // add args for splice below
  366. Array.prototype.splice.apply(sourceParts, parts);
  367. j += parts.length - 3;
  368. }
  369. results = results[1];
  370. }
  371. // handle parts that result from target splitting
  372. if(results.length > 1) {
  373. // splice in new target parts
  374. results.unshift(k, 1); // add args for splice below
  375. Array.prototype.splice.apply(targetParts, results);
  376. k += results.length - 3;
  377. }
  378. }
  379. }
  380. }
  381. }
  382. }
  383. if(targetParts && targetParts.length > 1) {
  384. this.geomsToFeatures(targetFeature, targetParts);
  385. this.events.triggerEvent("split", {
  386. original: targetFeature,
  387. features: targetParts
  388. });
  389. Array.prototype.push.apply(additions, targetParts);
  390. removals.push(targetFeature);
  391. targetSplit = true;
  392. }
  393. }
  394. }
  395. if(sourceParts && sourceParts.length > 1) {
  396. this.geomsToFeatures(feature, sourceParts);
  397. this.events.triggerEvent("split", {
  398. original: feature,
  399. features: sourceParts
  400. });
  401. Array.prototype.push.apply(additions, sourceParts);
  402. removals.push(feature);
  403. sourceSplit = true;
  404. }
  405. if(sourceSplit || targetSplit) {
  406. // remove and add feature events are suppressed
  407. // listen for split event on this control instead
  408. if(this.deferDelete) {
  409. // Set state instead of removing. Take care to avoid
  410. // setting delete for features that have not yet been
  411. // inserted - those should be destroyed immediately.
  412. var feat, destroys = [];
  413. for(var i=0, len=removals.length; i<len; ++i) {
  414. feat = removals[i];
  415. if(feat.state === OpenLayers.State.INSERT) {
  416. destroys.push(feat);
  417. } else {
  418. feat.state = OpenLayers.State.DELETE;
  419. this.layer.drawFeature(feat);
  420. }
  421. }
  422. this.layer.destroyFeatures(destroys, {silent: true});
  423. for(var i=0, len=additions.length; i<len; ++i) {
  424. additions[i].state = OpenLayers.State.INSERT;
  425. }
  426. } else {
  427. this.layer.destroyFeatures(removals, {silent: true});
  428. }
  429. this.layer.addFeatures(additions, {silent: true});
  430. this.events.triggerEvent("aftersplit", {
  431. source: feature,
  432. features: additions
  433. });
  434. }
  435. }
  436. return sourceSplit;
  437. },
  438. /**
  439. * Method: geomsToFeatures
  440. * Create new features given a template feature and a list of geometries.
  441. * The list of geometries is modified in place. The result will be
  442. * a list of new features.
  443. *
  444. * Parameters:
  445. * feature - {<OpenLayers.Feature.Vector>} The feature to be cloned.
  446. * geoms - {Array(<OpenLayers.Geometry>)} List of goemetries. This will
  447. * become a list of new features.
  448. */
  449. geomsToFeatures: function(feature, geoms) {
  450. var clone = feature.clone();
  451. delete clone.geometry;
  452. var newFeature;
  453. for(var i=0, len=geoms.length; i<len; ++i) {
  454. // turn results list from geoms to features
  455. newFeature = clone.clone();
  456. newFeature.geometry = geoms[i];
  457. newFeature.state = OpenLayers.State.INSERT;
  458. geoms[i] = newFeature;
  459. }
  460. },
  461. /**
  462. * Method: destroy
  463. * Clean up the control.
  464. */
  465. destroy: function() {
  466. if(this.active) {
  467. this.deactivate(); // TODO: this should be handled by the super
  468. }
  469. OpenLayers.Control.prototype.destroy.call(this);
  470. },
  471. CLASS_NAME: "OpenLayers.Control.Split"
  472. });