ModifyFeature.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835
  1. /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
  2. * full list of contributors). Published under the 2-clause BSD license.
  3. * See license.txt in the OpenLayers distribution or repository for the
  4. * full text of the license. */
  5. /**
  6. * @requires OpenLayers/Control.js
  7. * @requires OpenLayers/Handler/Drag.js
  8. * @requires OpenLayers/Handler/Keyboard.js
  9. */
  10. /**
  11. * Class: OpenLayers.Control.ModifyFeature
  12. * Control to modify features. When activated, a click renders the vertices
  13. * of a feature - these vertices can then be dragged. By default, the
  14. * delete key will delete the vertex under the mouse. New features are
  15. * added by dragging "virtual vertices" between vertices. Create a new
  16. * control with the <OpenLayers.Control.ModifyFeature> constructor.
  17. *
  18. * Inherits From:
  19. * - <OpenLayers.Control>
  20. */
  21. OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
  22. /**
  23. * APIProperty: documentDrag
  24. * {Boolean} If set to true, dragging vertices will continue even if the
  25. * mouse cursor leaves the map viewport. Default is false.
  26. */
  27. documentDrag: false,
  28. /**
  29. * APIProperty: geometryTypes
  30. * {Array(String)} To restrict modification to a limited set of geometry
  31. * types, send a list of strings corresponding to the geometry class
  32. * names.
  33. */
  34. geometryTypes: null,
  35. /**
  36. * APIProperty: clickout
  37. * {Boolean} Unselect features when clicking outside any feature.
  38. * Default is true.
  39. */
  40. clickout: true,
  41. /**
  42. * APIProperty: toggle
  43. * {Boolean} Unselect a selected feature on click.
  44. * Default is true.
  45. */
  46. toggle: true,
  47. /**
  48. * APIProperty: standalone
  49. * {Boolean} Set to true to create a control without SelectFeature
  50. * capabilities. Default is false. If standalone is true, to modify
  51. * a feature, call the <selectFeature> method with the target feature.
  52. * Note that you must call the <unselectFeature> method to finish
  53. * feature modification in standalone mode (before starting to modify
  54. * another feature).
  55. */
  56. standalone: false,
  57. /**
  58. * Property: layer
  59. * {<OpenLayers.Layer.Vector>}
  60. */
  61. layer: null,
  62. /**
  63. * Property: feature
  64. * {<OpenLayers.Feature.Vector>} Feature currently available for modification.
  65. */
  66. feature: null,
  67. /**
  68. * Property: vertex
  69. * {<OpenLayers.Feature.Vector>} Vertex currently being modified.
  70. */
  71. vertex: null,
  72. /**
  73. * Property: vertices
  74. * {Array(<OpenLayers.Feature.Vector>)} Verticies currently available
  75. * for dragging.
  76. */
  77. vertices: null,
  78. /**
  79. * Property: virtualVertices
  80. * {Array(<OpenLayers.Feature.Vector>)} Virtual vertices in the middle
  81. * of each edge.
  82. */
  83. virtualVertices: null,
  84. /**
  85. * Property: handlers
  86. * {Object}
  87. */
  88. handlers: null,
  89. /**
  90. * APIProperty: deleteCodes
  91. * {Array(Integer)} Keycodes for deleting verticies. Set to null to disable
  92. * vertex deltion by keypress. If non-null, keypresses with codes
  93. * in this array will delete vertices under the mouse. Default
  94. * is 46 and 68, the 'delete' and lowercase 'd' keys.
  95. */
  96. deleteCodes: null,
  97. /**
  98. * APIProperty: virtualStyle
  99. * {Object} A symbolizer to be used for virtual vertices.
  100. */
  101. virtualStyle: null,
  102. /**
  103. * APIProperty: vertexRenderIntent
  104. * {String} The renderIntent to use for vertices. If no <virtualStyle> is
  105. * provided, this renderIntent will also be used for virtual vertices, with
  106. * a fillOpacity and strokeOpacity of 0.3. Default is null, which means
  107. * that the layer's default style will be used for vertices.
  108. */
  109. vertexRenderIntent: null,
  110. /**
  111. * APIProperty: mode
  112. * {Integer} Bitfields specifying the modification mode. Defaults to
  113. * OpenLayers.Control.ModifyFeature.RESHAPE. To set the mode to a
  114. * combination of options, use the | operator. For example, to allow
  115. * the control to both resize and rotate features, use the following
  116. * syntax
  117. * (code)
  118. * control.mode = OpenLayers.Control.ModifyFeature.RESIZE |
  119. * OpenLayers.Control.ModifyFeature.ROTATE;
  120. * (end)
  121. */
  122. mode: null,
  123. /**
  124. * APIProperty: createVertices
  125. * {Boolean} Create new vertices by dragging the virtual vertices
  126. * in the middle of each edge. Default is true.
  127. */
  128. createVertices: true,
  129. /**
  130. * Property: modified
  131. * {Boolean} The currently selected feature has been modified.
  132. */
  133. modified: false,
  134. /**
  135. * Property: radiusHandle
  136. * {<OpenLayers.Feature.Vector>} A handle for rotating/resizing a feature.
  137. */
  138. radiusHandle: null,
  139. /**
  140. * Property: dragHandle
  141. * {<OpenLayers.Feature.Vector>} A handle for dragging a feature.
  142. */
  143. dragHandle: null,
  144. /**
  145. * APIProperty: onModificationStart
  146. * {Function} *Deprecated*. Register for "beforefeaturemodified" instead.
  147. * The "beforefeaturemodified" event is triggered on the layer before
  148. * any modification begins.
  149. *
  150. * Optional function to be called when a feature is selected
  151. * to be modified. The function should expect to be called with a
  152. * feature. This could be used for example to allow to lock the
  153. * feature on server-side.
  154. */
  155. onModificationStart: function() {},
  156. /**
  157. * APIProperty: onModification
  158. * {Function} *Deprecated*. Register for "featuremodified" instead.
  159. * The "featuremodified" event is triggered on the layer with each
  160. * feature modification.
  161. *
  162. * Optional function to be called when a feature has been
  163. * modified. The function should expect to be called with a feature.
  164. */
  165. onModification: function() {},
  166. /**
  167. * APIProperty: onModificationEnd
  168. * {Function} *Deprecated*. Register for "afterfeaturemodified" instead.
  169. * The "afterfeaturemodified" event is triggered on the layer after
  170. * a feature has been modified.
  171. *
  172. * Optional function to be called when a feature is finished
  173. * being modified. The function should expect to be called with a
  174. * feature.
  175. */
  176. onModificationEnd: function() {},
  177. /**
  178. * Constructor: OpenLayers.Control.ModifyFeature
  179. * Create a new modify feature control.
  180. *
  181. * Parameters:
  182. * layer - {<OpenLayers.Layer.Vector>} Layer that contains features that
  183. * will be modified.
  184. * options - {Object} Optional object whose properties will be set on the
  185. * control.
  186. */
  187. initialize: function(layer, options) {
  188. options = options || {};
  189. this.layer = layer;
  190. this.vertices = [];
  191. this.virtualVertices = [];
  192. this.virtualStyle = OpenLayers.Util.extend({},
  193. this.layer.style ||
  194. this.layer.styleMap.createSymbolizer(null, options.vertexRenderIntent)
  195. );
  196. this.virtualStyle.fillOpacity = 0.3;
  197. this.virtualStyle.strokeOpacity = 0.3;
  198. this.deleteCodes = [46, 68];
  199. this.mode = OpenLayers.Control.ModifyFeature.RESHAPE;
  200. OpenLayers.Control.prototype.initialize.apply(this, [options]);
  201. if(!(OpenLayers.Util.isArray(this.deleteCodes))) {
  202. this.deleteCodes = [this.deleteCodes];
  203. }
  204. // configure the drag handler
  205. var dragCallbacks = {
  206. down: function(pixel) {
  207. this.vertex = null;
  208. var feature = this.layer.getFeatureFromEvent(
  209. this.handlers.drag.evt);
  210. if (feature) {
  211. this.dragStart(feature);
  212. } else if (this.clickout) {
  213. this._unselect = this.feature;
  214. }
  215. },
  216. move: function(pixel) {
  217. delete this._unselect;
  218. if (this.vertex) {
  219. this.dragVertex(this.vertex, pixel);
  220. }
  221. },
  222. up: function() {
  223. this.handlers.drag.stopDown = false;
  224. if (this._unselect) {
  225. this.unselectFeature(this._unselect);
  226. delete this._unselect;
  227. }
  228. },
  229. done: function(pixel) {
  230. if (this.vertex) {
  231. this.dragComplete(this.vertex);
  232. }
  233. }
  234. };
  235. var dragOptions = {
  236. documentDrag: this.documentDrag,
  237. stopDown: false
  238. };
  239. // configure the keyboard handler
  240. var keyboardOptions = {
  241. keydown: this.handleKeypress
  242. };
  243. this.handlers = {
  244. keyboard: new OpenLayers.Handler.Keyboard(this, keyboardOptions),
  245. drag: new OpenLayers.Handler.Drag(this, dragCallbacks, dragOptions)
  246. };
  247. },
  248. /**
  249. * APIMethod: destroy
  250. * Take care of things that are not handled in superclass.
  251. */
  252. destroy: function() {
  253. if (this.map) {
  254. this.map.events.un({
  255. "removelayer": this.handleMapEvents,
  256. "changelayer": this.handleMapEvents,
  257. scope: this
  258. });
  259. }
  260. this.layer = null;
  261. OpenLayers.Control.prototype.destroy.apply(this, []);
  262. },
  263. /**
  264. * APIMethod: activate
  265. * Activate the control.
  266. *
  267. * Returns:
  268. * {Boolean} Successfully activated the control.
  269. */
  270. activate: function() {
  271. this.moveLayerToTop();
  272. this.map.events.on({
  273. "removelayer": this.handleMapEvents,
  274. "changelayer": this.handleMapEvents,
  275. scope: this
  276. });
  277. return (this.handlers.keyboard.activate() &&
  278. this.handlers.drag.activate() &&
  279. OpenLayers.Control.prototype.activate.apply(this, arguments));
  280. },
  281. /**
  282. * APIMethod: deactivate
  283. * Deactivate the control.
  284. *
  285. * Returns:
  286. * {Boolean} Successfully deactivated the control.
  287. */
  288. deactivate: function() {
  289. var deactivated = false;
  290. // the return from the controls is unimportant in this case
  291. if(OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
  292. this.moveLayerBack();
  293. this.map.events.un({
  294. "removelayer": this.handleMapEvents,
  295. "changelayer": this.handleMapEvents,
  296. scope: this
  297. });
  298. this.layer.removeFeatures(this.vertices, {silent: true});
  299. this.layer.removeFeatures(this.virtualVertices, {silent: true});
  300. this.vertices = [];
  301. this.handlers.drag.deactivate();
  302. this.handlers.keyboard.deactivate();
  303. var feature = this.feature;
  304. if (feature && feature.geometry && feature.layer) {
  305. this.unselectFeature(feature);
  306. }
  307. deactivated = true;
  308. }
  309. return deactivated;
  310. },
  311. /**
  312. * Method: beforeSelectFeature
  313. * Called before a feature is selected.
  314. *
  315. * Parameters:
  316. * feature - {<OpenLayers.Feature.Vector>} The feature about to be selected.
  317. */
  318. beforeSelectFeature: function(feature) {
  319. return this.layer.events.triggerEvent(
  320. "beforefeaturemodified", {feature: feature}
  321. );
  322. },
  323. /**
  324. * APIMethod: selectFeature
  325. * Select a feature for modification in standalone mode. In non-standalone
  326. * mode, this method is called when a feature is selected by clicking.
  327. * Register a listener to the beforefeaturemodified event and return false
  328. * to prevent feature modification.
  329. *
  330. * Parameters:
  331. * feature - {<OpenLayers.Feature.Vector>} the selected feature.
  332. */
  333. selectFeature: function(feature) {
  334. if (this.feature === feature ||
  335. (this.geometryTypes && OpenLayers.Util.indexOf(this.geometryTypes,
  336. feature.geometry.CLASS_NAME) == -1)) {
  337. return;
  338. }
  339. if (this.beforeSelectFeature(feature) !== false) {
  340. if (this.feature) {
  341. this.unselectFeature(this.feature);
  342. }
  343. this.feature = feature;
  344. this.layer.selectedFeatures.push(feature);
  345. this.layer.drawFeature(feature, 'select');
  346. this.modified = false;
  347. this.resetVertices();
  348. this.onModificationStart(this.feature);
  349. }
  350. // keep track of geometry modifications
  351. var modified = feature.modified;
  352. if (feature.geometry && !(modified && modified.geometry)) {
  353. this._originalGeometry = feature.geometry.clone();
  354. }
  355. },
  356. /**
  357. * APIMethod: unselectFeature
  358. * Called when the select feature control unselects a feature.
  359. *
  360. * Parameters:
  361. * feature - {<OpenLayers.Feature.Vector>} The unselected feature.
  362. */
  363. unselectFeature: function(feature) {
  364. this.layer.removeFeatures(this.vertices, {silent: true});
  365. this.vertices = [];
  366. this.layer.destroyFeatures(this.virtualVertices, {silent: true});
  367. this.virtualVertices = [];
  368. if(this.dragHandle) {
  369. this.layer.destroyFeatures([this.dragHandle], {silent: true});
  370. delete this.dragHandle;
  371. }
  372. if(this.radiusHandle) {
  373. this.layer.destroyFeatures([this.radiusHandle], {silent: true});
  374. delete this.radiusHandle;
  375. }
  376. this.layer.drawFeature(this.feature, 'default');
  377. this.feature = null;
  378. OpenLayers.Util.removeItem(this.layer.selectedFeatures, feature);
  379. this.onModificationEnd(feature);
  380. this.layer.events.triggerEvent("afterfeaturemodified", {
  381. feature: feature,
  382. modified: this.modified
  383. });
  384. this.modified = false;
  385. },
  386. /**
  387. * Method: dragStart
  388. * Called by the drag handler before a feature is dragged. This method is
  389. * used to differentiate between points and vertices
  390. * of higher order geometries.
  391. *
  392. * Parameters:
  393. * feature - {<OpenLayers.Feature.Vector>} The point or vertex about to be
  394. * dragged.
  395. */
  396. dragStart: function(feature) {
  397. var isPoint = feature.geometry.CLASS_NAME ==
  398. 'OpenLayers.Geometry.Point';
  399. if (!this.standalone &&
  400. ((!feature._sketch && isPoint) || !feature._sketch)) {
  401. if (this.toggle && this.feature === feature) {
  402. // mark feature for unselection
  403. this._unselect = feature;
  404. }
  405. this.selectFeature(feature);
  406. }
  407. if (feature._sketch || isPoint) {
  408. // feature is a drag or virtual handle or point
  409. this.vertex = feature;
  410. this.handlers.drag.stopDown = true;
  411. }
  412. },
  413. /**
  414. * Method: dragVertex
  415. * Called by the drag handler with each drag move of a vertex.
  416. *
  417. * Parameters:
  418. * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged.
  419. * pixel - {<OpenLayers.Pixel>} Pixel location of the mouse event.
  420. */
  421. dragVertex: function(vertex, pixel) {
  422. var pos = this.map.getLonLatFromViewPortPx(pixel);
  423. var geom = vertex.geometry;
  424. geom.move(pos.lon - geom.x, pos.lat - geom.y);
  425. this.modified = true;
  426. /**
  427. * Five cases:
  428. * 1) dragging a simple point
  429. * 2) dragging a virtual vertex
  430. * 3) dragging a drag handle
  431. * 4) dragging a real vertex
  432. * 5) dragging a radius handle
  433. */
  434. if(this.feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
  435. // dragging a simple point
  436. this.layer.events.triggerEvent("vertexmodified", {
  437. vertex: vertex.geometry,
  438. feature: this.feature,
  439. pixel: pixel
  440. });
  441. } else {
  442. if(vertex._index) {
  443. // dragging a virtual vertex
  444. vertex.geometry.parent.addComponent(vertex.geometry,
  445. vertex._index);
  446. // move from virtual to real vertex
  447. delete vertex._index;
  448. OpenLayers.Util.removeItem(this.virtualVertices, vertex);
  449. this.vertices.push(vertex);
  450. } else if(vertex == this.dragHandle) {
  451. // dragging a drag handle
  452. this.layer.removeFeatures(this.vertices, {silent: true});
  453. this.vertices = [];
  454. if(this.radiusHandle) {
  455. this.layer.destroyFeatures([this.radiusHandle], {silent: true});
  456. this.radiusHandle = null;
  457. }
  458. } else if(vertex !== this.radiusHandle) {
  459. // dragging a real vertex
  460. this.layer.events.triggerEvent("vertexmodified", {
  461. vertex: vertex.geometry,
  462. feature: this.feature,
  463. pixel: pixel
  464. });
  465. }
  466. // dragging a radius handle - no special treatment
  467. if(this.virtualVertices.length > 0) {
  468. this.layer.destroyFeatures(this.virtualVertices, {silent: true});
  469. this.virtualVertices = [];
  470. }
  471. this.layer.drawFeature(this.feature, this.standalone ? undefined :
  472. 'select');
  473. }
  474. // keep the vertex on top so it gets the mouseout after dragging
  475. // this should be removed in favor of an option to draw under or
  476. // maintain node z-index
  477. this.layer.drawFeature(vertex);
  478. },
  479. /**
  480. * Method: dragComplete
  481. * Called by the drag handler when the feature dragging is complete.
  482. *
  483. * Parameters:
  484. * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged.
  485. */
  486. dragComplete: function(vertex) {
  487. this.resetVertices();
  488. this.setFeatureState();
  489. this.onModification(this.feature);
  490. this.layer.events.triggerEvent("featuremodified",
  491. {feature: this.feature});
  492. },
  493. /**
  494. * Method: setFeatureState
  495. * Called when the feature is modified. If the current state is not
  496. * INSERT or DELETE, the state is set to UPDATE.
  497. */
  498. setFeatureState: function() {
  499. if(this.feature.state != OpenLayers.State.INSERT &&
  500. this.feature.state != OpenLayers.State.DELETE) {
  501. this.feature.state = OpenLayers.State.UPDATE;
  502. if (this.modified && this._originalGeometry) {
  503. var feature = this.feature;
  504. feature.modified = OpenLayers.Util.extend(feature.modified, {
  505. geometry: this._originalGeometry
  506. });
  507. delete this._originalGeometry;
  508. }
  509. }
  510. },
  511. /**
  512. * Method: resetVertices
  513. */
  514. resetVertices: function() {
  515. if(this.vertices.length > 0) {
  516. this.layer.removeFeatures(this.vertices, {silent: true});
  517. this.vertices = [];
  518. }
  519. if(this.virtualVertices.length > 0) {
  520. this.layer.removeFeatures(this.virtualVertices, {silent: true});
  521. this.virtualVertices = [];
  522. }
  523. if(this.dragHandle) {
  524. this.layer.destroyFeatures([this.dragHandle], {silent: true});
  525. this.dragHandle = null;
  526. }
  527. if(this.radiusHandle) {
  528. this.layer.destroyFeatures([this.radiusHandle], {silent: true});
  529. this.radiusHandle = null;
  530. }
  531. if(this.feature &&
  532. this.feature.geometry.CLASS_NAME != "OpenLayers.Geometry.Point") {
  533. if((this.mode & OpenLayers.Control.ModifyFeature.DRAG)) {
  534. this.collectDragHandle();
  535. }
  536. if((this.mode & (OpenLayers.Control.ModifyFeature.ROTATE |
  537. OpenLayers.Control.ModifyFeature.RESIZE))) {
  538. this.collectRadiusHandle();
  539. }
  540. if(this.mode & OpenLayers.Control.ModifyFeature.RESHAPE){
  541. // Don't collect vertices when we're resizing
  542. if (!(this.mode & OpenLayers.Control.ModifyFeature.RESIZE)){
  543. this.collectVertices();
  544. }
  545. }
  546. }
  547. },
  548. /**
  549. * Method: handleKeypress
  550. * Called by the feature handler on keypress. This is used to delete
  551. * vertices. If the <deleteCode> property is set, vertices will
  552. * be deleted when a feature is selected for modification and
  553. * the mouse is over a vertex.
  554. *
  555. * Parameters:
  556. * evt - {Event} Keypress event.
  557. */
  558. handleKeypress: function(evt) {
  559. var code = evt.keyCode;
  560. // check for delete key
  561. if(this.feature &&
  562. OpenLayers.Util.indexOf(this.deleteCodes, code) != -1) {
  563. var vertex = this.layer.getFeatureFromEvent(this.handlers.drag.evt);
  564. if (vertex &&
  565. OpenLayers.Util.indexOf(this.vertices, vertex) != -1 &&
  566. !this.handlers.drag.dragging && vertex.geometry.parent) {
  567. // remove the vertex
  568. vertex.geometry.parent.removeComponent(vertex.geometry);
  569. this.layer.events.triggerEvent("vertexremoved", {
  570. vertex: vertex.geometry,
  571. feature: this.feature,
  572. pixel: evt.xy
  573. });
  574. this.layer.drawFeature(this.feature, this.standalone ?
  575. undefined : 'select');
  576. this.modified = true;
  577. this.resetVertices();
  578. this.setFeatureState();
  579. this.onModification(this.feature);
  580. this.layer.events.triggerEvent("featuremodified",
  581. {feature: this.feature});
  582. }
  583. }
  584. },
  585. /**
  586. * Method: collectVertices
  587. * Collect the vertices from the modifiable feature's geometry and push
  588. * them on to the control's vertices array.
  589. */
  590. collectVertices: function() {
  591. this.vertices = [];
  592. this.virtualVertices = [];
  593. var control = this;
  594. function collectComponentVertices(geometry) {
  595. var i, vertex, component, len;
  596. if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
  597. vertex = new OpenLayers.Feature.Vector(geometry);
  598. vertex._sketch = true;
  599. vertex.renderIntent = control.vertexRenderIntent;
  600. control.vertices.push(vertex);
  601. } else {
  602. var numVert = geometry.components.length;
  603. if(geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
  604. numVert -= 1;
  605. }
  606. for(i=0; i<numVert; ++i) {
  607. component = geometry.components[i];
  608. if(component.CLASS_NAME == "OpenLayers.Geometry.Point") {
  609. vertex = new OpenLayers.Feature.Vector(component);
  610. vertex._sketch = true;
  611. vertex.renderIntent = control.vertexRenderIntent;
  612. control.vertices.push(vertex);
  613. } else {
  614. collectComponentVertices(component);
  615. }
  616. }
  617. // add virtual vertices in the middle of each edge
  618. if (control.createVertices && geometry.CLASS_NAME != "OpenLayers.Geometry.MultiPoint") {
  619. for(i=0, len=geometry.components.length; i<len-1; ++i) {
  620. var prevVertex = geometry.components[i];
  621. var nextVertex = geometry.components[i + 1];
  622. if(prevVertex.CLASS_NAME == "OpenLayers.Geometry.Point" &&
  623. nextVertex.CLASS_NAME == "OpenLayers.Geometry.Point") {
  624. var x = (prevVertex.x + nextVertex.x) / 2;
  625. var y = (prevVertex.y + nextVertex.y) / 2;
  626. var point = new OpenLayers.Feature.Vector(
  627. new OpenLayers.Geometry.Point(x, y),
  628. null, control.virtualStyle
  629. );
  630. // set the virtual parent and intended index
  631. point.geometry.parent = geometry;
  632. point._index = i + 1;
  633. point._sketch = true;
  634. control.virtualVertices.push(point);
  635. }
  636. }
  637. }
  638. }
  639. }
  640. collectComponentVertices.call(this, this.feature.geometry);
  641. this.layer.addFeatures(this.virtualVertices, {silent: true});
  642. this.layer.addFeatures(this.vertices, {silent: true});
  643. },
  644. /**
  645. * Method: collectDragHandle
  646. * Collect the drag handle for the selected geometry.
  647. */
  648. collectDragHandle: function() {
  649. var geometry = this.feature.geometry;
  650. var center = geometry.getBounds().getCenterLonLat();
  651. var originGeometry = new OpenLayers.Geometry.Point(
  652. center.lon, center.lat
  653. );
  654. var origin = new OpenLayers.Feature.Vector(originGeometry);
  655. originGeometry.move = function(x, y) {
  656. OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
  657. geometry.move(x, y);
  658. };
  659. origin._sketch = true;
  660. this.dragHandle = origin;
  661. this.dragHandle.renderIntent = this.vertexRenderIntent;
  662. this.layer.addFeatures([this.dragHandle], {silent: true});
  663. },
  664. /**
  665. * Method: collectRadiusHandle
  666. * Collect the radius handle for the selected geometry.
  667. */
  668. collectRadiusHandle: function() {
  669. var geometry = this.feature.geometry;
  670. var bounds = geometry.getBounds();
  671. var center = bounds.getCenterLonLat();
  672. var originGeometry = new OpenLayers.Geometry.Point(
  673. center.lon, center.lat
  674. );
  675. var radiusGeometry = new OpenLayers.Geometry.Point(
  676. bounds.right, bounds.bottom
  677. );
  678. var radius = new OpenLayers.Feature.Vector(radiusGeometry);
  679. var resize = (this.mode & OpenLayers.Control.ModifyFeature.RESIZE);
  680. var reshape = (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE);
  681. var rotate = (this.mode & OpenLayers.Control.ModifyFeature.ROTATE);
  682. radiusGeometry.move = function(x, y) {
  683. OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
  684. var dx1 = this.x - originGeometry.x;
  685. var dy1 = this.y - originGeometry.y;
  686. var dx0 = dx1 - x;
  687. var dy0 = dy1 - y;
  688. if(rotate) {
  689. var a0 = Math.atan2(dy0, dx0);
  690. var a1 = Math.atan2(dy1, dx1);
  691. var angle = a1 - a0;
  692. angle *= 180 / Math.PI;
  693. geometry.rotate(angle, originGeometry);
  694. }
  695. if(resize) {
  696. var scale, ratio;
  697. // 'resize' together with 'reshape' implies that the aspect
  698. // ratio of the geometry will not be preserved whilst resizing
  699. if (reshape) {
  700. scale = dy1 / dy0;
  701. ratio = (dx1 / dx0) / scale;
  702. } else {
  703. var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0));
  704. var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1));
  705. scale = l1 / l0;
  706. }
  707. geometry.resize(scale, originGeometry, ratio);
  708. }
  709. };
  710. radius._sketch = true;
  711. this.radiusHandle = radius;
  712. this.radiusHandle.renderIntent = this.vertexRenderIntent;
  713. this.layer.addFeatures([this.radiusHandle], {silent: true});
  714. },
  715. /**
  716. * Method: setMap
  717. * Set the map property for the control and all handlers.
  718. *
  719. * Parameters:
  720. * map - {<OpenLayers.Map>} The control's map.
  721. */
  722. setMap: function(map) {
  723. this.handlers.drag.setMap(map);
  724. OpenLayers.Control.prototype.setMap.apply(this, arguments);
  725. },
  726. /**
  727. * Method: handleMapEvents
  728. *
  729. * Parameters:
  730. * evt - {Object}
  731. */
  732. handleMapEvents: function(evt) {
  733. if (evt.type == "removelayer" || evt.property == "order") {
  734. this.moveLayerToTop();
  735. }
  736. },
  737. /**
  738. * Method: moveLayerToTop
  739. * Moves the layer for this handler to the top, so mouse events can reach
  740. * it.
  741. */
  742. moveLayerToTop: function() {
  743. var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,
  744. this.layer.getZIndex()) + 1;
  745. this.layer.setZIndex(index);
  746. },
  747. /**
  748. * Method: moveLayerBack
  749. * Moves the layer back to the position determined by the map's layers
  750. * array.
  751. */
  752. moveLayerBack: function() {
  753. var index = this.layer.getZIndex() - 1;
  754. if (index >= this.map.Z_INDEX_BASE['Feature']) {
  755. this.layer.setZIndex(index);
  756. } else {
  757. this.map.setLayerZIndex(this.layer,
  758. this.map.getLayerIndex(this.layer));
  759. }
  760. },
  761. CLASS_NAME: "OpenLayers.Control.ModifyFeature"
  762. });
  763. /**
  764. * Constant: RESHAPE
  765. * {Integer} Constant used to make the control work in reshape mode
  766. */
  767. OpenLayers.Control.ModifyFeature.RESHAPE = 1;
  768. /**
  769. * Constant: RESIZE
  770. * {Integer} Constant used to make the control work in resize mode
  771. */
  772. OpenLayers.Control.ModifyFeature.RESIZE = 2;
  773. /**
  774. * Constant: ROTATE
  775. * {Integer} Constant used to make the control work in rotate mode
  776. */
  777. OpenLayers.Control.ModifyFeature.ROTATE = 4;
  778. /**
  779. * Constant: DRAG
  780. * {Integer} Constant used to make the control work in drag mode
  781. */
  782. OpenLayers.Control.ModifyFeature.DRAG = 8;