ModifyFeature.js 30 KB

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