EditToolbar.Edit.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. /**
  2. * @class L.EditToolbar.Edit
  3. * @aka EditToolbar.Edit
  4. */
  5. L.EditToolbar.Edit = L.Handler.extend({
  6. statics: {
  7. TYPE: 'edit'
  8. },
  9. includes: L.Mixin.Events,
  10. // @method intialize(): void
  11. initialize: function (map, options) {
  12. L.Handler.prototype.initialize.call(this, map);
  13. L.setOptions(this, options);
  14. // Store the selectable layer group for ease of access
  15. this._featureGroup = options.featureGroup;
  16. if (!(this._featureGroup instanceof L.FeatureGroup)) {
  17. throw new Error('options.featureGroup must be a L.FeatureGroup');
  18. }
  19. this._uneditedLayerProps = {};
  20. // Save the type so super can fire, need to do this as cannot do this.TYPE :(
  21. this.type = L.EditToolbar.Edit.TYPE;
  22. },
  23. // @method enable(): void
  24. // Enable the edit toolbar
  25. enable: function () {
  26. if (this._enabled || !this._hasAvailableLayers()) {
  27. return;
  28. }
  29. this.fire('enabled', { handler: this.type });
  30. //this disable other handlers
  31. this._map.fire(L.Draw.Event.EDITSTART, { handler: this.type });
  32. //allow drawLayer to be updated before beginning edition.
  33. L.Handler.prototype.enable.call(this);
  34. this._featureGroup
  35. .on('layeradd', this._enableLayerEdit, this)
  36. .on('layerremove', this._disableLayerEdit, this);
  37. },
  38. // @method disable(): void
  39. // Disable the edit toolbar
  40. disable: function () {
  41. if (!this._enabled) {
  42. return;
  43. }
  44. this._featureGroup
  45. .off('layeradd', this._enableLayerEdit, this)
  46. .off('layerremove', this._disableLayerEdit, this);
  47. L.Handler.prototype.disable.call(this);
  48. this._map.fire(L.Draw.Event.EDITSTOP, { handler: this.type });
  49. this.fire('disabled', { handler: this.type });
  50. },
  51. // @method addHooks(): void
  52. // Add listener hooks for this handler
  53. addHooks: function () {
  54. var map = this._map;
  55. if (map) {
  56. map.getContainer().focus();
  57. this._featureGroup.eachLayer(this._enableLayerEdit, this);
  58. this._tooltip = new L.Draw.Tooltip(this._map);
  59. this._tooltip.updateContent({
  60. text: L.drawLocal.edit.handlers.edit.tooltip.text,
  61. subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext
  62. });
  63. // Quickly access the tooltip to update for intersection checking
  64. map._editTooltip = this._tooltip;
  65. this._updateTooltip();
  66. this._map
  67. .on('mousemove', this._onMouseMove, this)
  68. .on('touchmove', this._onMouseMove, this)
  69. .on('MSPointerMove', this._onMouseMove, this)
  70. .on(L.Draw.Event.EDITVERTEX, this._updateTooltip, this);
  71. }
  72. },
  73. // @method removeHooks(): void
  74. // Remove listener hooks for this handler
  75. removeHooks: function () {
  76. if (this._map) {
  77. // Clean up selected layers.
  78. this._featureGroup.eachLayer(this._disableLayerEdit, this);
  79. // Clear the backups of the original layers
  80. this._uneditedLayerProps = {};
  81. this._tooltip.dispose();
  82. this._tooltip = null;
  83. this._map
  84. .off('mousemove', this._onMouseMove, this)
  85. .off('touchmove', this._onMouseMove, this)
  86. .off('MSPointerMove', this._onMouseMove, this)
  87. .off(L.Draw.Event.EDITVERTEX, this._updateTooltip, this);
  88. }
  89. },
  90. // @method revertLayers(): void
  91. // Revert each layer's geometry changes
  92. revertLayers: function () {
  93. this._featureGroup.eachLayer(function (layer) {
  94. this._revertLayer(layer);
  95. }, this);
  96. },
  97. // @method save(): void
  98. // Save the layer geometries
  99. save: function () {
  100. var editedLayers = new L.LayerGroup();
  101. this._featureGroup.eachLayer(function (layer) {
  102. if (layer.edited) {
  103. editedLayers.addLayer(layer);
  104. layer.edited = false;
  105. }
  106. });
  107. this._map.fire(L.Draw.Event.EDITED, { layers: editedLayers });
  108. },
  109. _backupLayer: function (layer) {
  110. var id = L.Util.stamp(layer);
  111. if (!this._uneditedLayerProps[id]) {
  112. // Polyline, Polygon or Rectangle
  113. if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) {
  114. this._uneditedLayerProps[id] = {
  115. latlngs: L.LatLngUtil.cloneLatLngs(layer.getLatLngs())
  116. };
  117. } else if (layer instanceof L.Circle) {
  118. this._uneditedLayerProps[id] = {
  119. latlng: L.LatLngUtil.cloneLatLng(layer.getLatLng()),
  120. radius: layer.getRadius()
  121. };
  122. } else if (layer instanceof L.Marker) { // Marker
  123. this._uneditedLayerProps[id] = {
  124. latlng: L.LatLngUtil.cloneLatLng(layer.getLatLng())
  125. };
  126. }
  127. }
  128. },
  129. _getTooltipText: function () {
  130. return ({
  131. text: L.drawLocal.edit.handlers.edit.tooltip.text,
  132. subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext
  133. });
  134. },
  135. _updateTooltip: function () {
  136. this._tooltip.updateContent(this._getTooltipText());
  137. },
  138. _revertLayer: function (layer) {
  139. var id = L.Util.stamp(layer);
  140. layer.edited = false;
  141. if (this._uneditedLayerProps.hasOwnProperty(id)) {
  142. // Polyline, Polygon or Rectangle
  143. if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) {
  144. layer.setLatLngs(this._uneditedLayerProps[id].latlngs);
  145. } else if (layer instanceof L.Circle) {
  146. layer.setLatLng(this._uneditedLayerProps[id].latlng);
  147. layer.setRadius(this._uneditedLayerProps[id].radius);
  148. } else if (layer instanceof L.Marker) { // Marker
  149. layer.setLatLng(this._uneditedLayerProps[id].latlng);
  150. }
  151. layer.fire('revert-edited', { layer: layer });
  152. }
  153. },
  154. _enableLayerEdit: function (e) {
  155. var layer = e.layer || e.target || e,
  156. pathOptions, poly;
  157. // Back up this layer (if haven't before)
  158. this._backupLayer(layer);
  159. if (this.options.poly) {
  160. poly = L.Util.extend({}, this.options.poly);
  161. layer.options.poly = poly;
  162. }
  163. // Set different style for editing mode
  164. if (this.options.selectedPathOptions) {
  165. pathOptions = L.Util.extend({}, this.options.selectedPathOptions);
  166. // Use the existing color of the layer
  167. if (pathOptions.maintainColor) {
  168. pathOptions.color = layer.options.color;
  169. pathOptions.fillColor = layer.options.fillColor;
  170. }
  171. layer.options.original = L.extend({}, layer.options);
  172. layer.options.editing = pathOptions;
  173. }
  174. if (layer instanceof L.Marker) {
  175. if (layer.editing) {
  176. layer.editing.enable();
  177. }
  178. layer.dragging.enable();
  179. layer
  180. .on('dragend', this._onMarkerDragEnd)
  181. // #TODO: remove when leaflet finally fixes their draggable so it's touch friendly again.
  182. .on('touchmove', this._onTouchMove, this)
  183. .on('MSPointerMove', this._onTouchMove, this)
  184. .on('touchend', this._onMarkerDragEnd, this)
  185. .on('MSPointerUp', this._onMarkerDragEnd, this);
  186. } else {
  187. layer.editing.enable();
  188. }
  189. },
  190. _disableLayerEdit: function (e) {
  191. var layer = e.layer || e.target || e;
  192. layer.edited = false;
  193. if (layer.editing) {
  194. layer.editing.disable();
  195. }
  196. delete layer.options.editing;
  197. delete layer.options.original;
  198. // Reset layer styles to that of before select
  199. if (this._selectedPathOptions) {
  200. if (layer instanceof L.Marker) {
  201. this._toggleMarkerHighlight(layer);
  202. } else {
  203. // reset the layer style to what is was before being selected
  204. layer.setStyle(layer.options.previousOptions);
  205. // remove the cached options for the layer object
  206. delete layer.options.previousOptions;
  207. }
  208. }
  209. if (layer instanceof L.Marker) {
  210. layer.dragging.disable();
  211. layer
  212. .off('dragend', this._onMarkerDragEnd, this)
  213. .off('touchmove', this._onTouchMove, this)
  214. .off('MSPointerMove', this._onTouchMove, this)
  215. .off('touchend', this._onMarkerDragEnd, this)
  216. .off('MSPointerUp', this._onMarkerDragEnd, this);
  217. } else {
  218. layer.editing.disable();
  219. }
  220. },
  221. _onMouseMove: function (e) {
  222. this._tooltip.updatePosition(e.latlng);
  223. },
  224. _onMarkerDragEnd: function (e) {
  225. var layer = e.target;
  226. layer.edited = true;
  227. this._map.fire(L.Draw.Event.EDITMOVE, { layer: layer });
  228. },
  229. _onTouchMove: function (e) {
  230. var touchEvent = e.originalEvent.changedTouches[0],
  231. layerPoint = this._map.mouseEventToLayerPoint(touchEvent),
  232. latlng = this._map.layerPointToLatLng(layerPoint);
  233. e.target.setLatLng(latlng);
  234. },
  235. _hasAvailableLayers: function () {
  236. return this._featureGroup.getLayers().length !== 0;
  237. }
  238. });