| | |
| | | 'use strict' |
| | | const init = (L) => { |
| | | (function(factory, window) { |
| | | // define an AMD module that relies on 'leaflet' |
| | | if (typeof define === 'function' && window.define.amd) { |
| | | window.define(['leaflet'], factory) |
| | | (function (factory, window) { |
| | | // define an AMD module that relies on 'leaflet' |
| | | if (typeof define === 'function' && window.define.amd) { |
| | | window.define(['leaflet'], factory) |
| | | |
| | | // define a Common JS module that relies on 'leaflet' |
| | | } else if (typeof exports === 'object') { |
| | | module.exports = factory(require('leaflet')) |
| | | // define a Common JS module that relies on 'leaflet' |
| | | } else if (typeof exports === 'object') { |
| | | module.exports = factory(require('leaflet')) |
| | | } |
| | | |
| | | // attach your plugin to the global 'L' variable |
| | | if (typeof window !== 'undefined' && L) { |
| | | factory(L) |
| | | } |
| | | }(function (L) { |
| | | // 🍂miniclass CancelableEvent (Event objects) |
| | | // 🍂method cancel() |
| | | // Cancel any subsequent action. |
| | | |
| | | // 🍂miniclass VertexEvent (Event objects) |
| | | // 🍂property vertex: VertexMarker |
| | | // The vertex that fires the event. |
| | | |
| | | // 🍂miniclass ShapeEvent (Event objects) |
| | | // 🍂property shape: Array |
| | | // The shape (LatLngs array) subject of the action. |
| | | |
| | | // 🍂miniclass CancelableVertexEvent (Event objects) |
| | | // 🍂inherits VertexEvent |
| | | // 🍂inherits CancelableEvent |
| | | |
| | | // 🍂miniclass CancelableShapeEvent (Event objects) |
| | | // 🍂inherits ShapeEvent |
| | | // 🍂inherits CancelableEvent |
| | | |
| | | // 🍂miniclass LayerEvent (Event objects) |
| | | // 🍂property layer: object |
| | | // The Layer (Marker, Polyline…) subject of the action. |
| | | |
| | | // 🍂namespace Editable; 🍂class Editable; 🍂aka L.Editable |
| | | // Main edition handler. By default, it is attached to the map |
| | | // as `map.editTools` property. |
| | | // Leaflet.Editable is made to be fully extendable. You have three ways to customize |
| | | // the behaviour: using options, listening to events, or extending. |
| | | L.Editable = L.Evented.extend({ |
| | | |
| | | statics: { |
| | | FORWARD: 1, |
| | | BACKWARD: -1 |
| | | }, |
| | | |
| | | options: { |
| | | |
| | | // You can pass them when creating a map using the `editOptions` key. |
| | | // 🍂option zIndex: int = 1000 |
| | | // The default zIndex of the editing tools. |
| | | zIndex: 1000, |
| | | |
| | | // 🍂option polygonClass: class = L.Polygon |
| | | // Class to be used when creating a new Polygon. |
| | | polygonClass: L.Polygon, |
| | | |
| | | // 🍂option polylineClass: class = L.Polyline |
| | | // Class to be used when creating a new Polyline. |
| | | polylineClass: L.Polyline, |
| | | |
| | | // 🍂option markerClass: class = L.Marker |
| | | // Class to be used when creating a new Marker. |
| | | markerClass: L.Marker, |
| | | |
| | | // 🍂option rectangleClass: class = L.Rectangle |
| | | // Class to be used when creating a new Rectangle. |
| | | rectangleClass: L.Rectangle, |
| | | |
| | | // 🍂option circleClass: class = L.Circle |
| | | // Class to be used when creating a new Circle. |
| | | circleClass: L.Circle, |
| | | |
| | | // 🍂option drawingCSSClass: string = 'leaflet-editable-drawing' |
| | | // CSS class to be added to the map container while drawing. |
| | | drawingCSSClass: 'leaflet-editable-drawing', |
| | | |
| | | // 🍂option drawingCursor: const = 'crosshair' |
| | | // Cursor mode set to the map while drawing. |
| | | drawingCursor: 'crosshair', |
| | | |
| | | // 🍂option editLayer: Layer = new L.LayerGroup() |
| | | // Layer used to store edit tools (vertex, line guide…). |
| | | editLayer: undefined, |
| | | |
| | | // 🍂option featuresLayer: Layer = new L.LayerGroup() |
| | | // Default layer used to store drawn features (Marker, Polyline…). |
| | | featuresLayer: undefined, |
| | | |
| | | // 🍂option polylineEditorClass: class = PolylineEditor |
| | | // Class to be used as Polyline editor. |
| | | polylineEditorClass: undefined, |
| | | |
| | | // 🍂option polygonEditorClass: class = PolygonEditor |
| | | // Class to be used as Polygon editor. |
| | | polygonEditorClass: undefined, |
| | | |
| | | // 🍂option markerEditorClass: class = MarkerEditor |
| | | // Class to be used as Marker editor. |
| | | markerEditorClass: undefined, |
| | | |
| | | // 🍂option rectangleEditorClass: class = RectangleEditor |
| | | // Class to be used as Rectangle editor. |
| | | rectangleEditorClass: undefined, |
| | | |
| | | // 🍂option circleEditorClass: class = CircleEditor |
| | | // Class to be used as Circle editor. |
| | | circleEditorClass: undefined, |
| | | |
| | | // 🍂option lineGuideOptions: hash = {} |
| | | // Options to be passed to the line guides. |
| | | lineGuideOptions: {}, |
| | | |
| | | // 🍂option skipMiddleMarkers: boolean = false |
| | | // Set this to true if you don't want middle markers. |
| | | skipMiddleMarkers: false |
| | | |
| | | }, |
| | | |
| | | initialize: function (map, options) { |
| | | L.setOptions(this, options) |
| | | this._lastZIndex = this.options.zIndex |
| | | this.map = map |
| | | this.editLayer = this.createEditLayer() |
| | | this.featuresLayer = this.createFeaturesLayer() |
| | | this.forwardLineGuide = this.createLineGuide() |
| | | this.backwardLineGuide = this.createLineGuide() |
| | | }, |
| | | |
| | | fireAndForward: function (type, e) { |
| | | e = e || {} |
| | | e.editTools = this |
| | | this.fire(type, e) |
| | | this.map.fire(type, e) |
| | | }, |
| | | |
| | | createLineGuide: function () { |
| | | const options = L.extend({ |
| | | dashArray: '5,10', |
| | | weight: 1, |
| | | interactive: false |
| | | }, this.options.lineGuideOptions) |
| | | return L.polyline([], options) |
| | | }, |
| | | |
| | | createVertexIcon: function (options) { |
| | | return L.Browser.mobile && L.Browser.touch ? new L.Editable.TouchVertexIcon(options) : new L.Editable.VertexIcon(options) |
| | | }, |
| | | |
| | | createEditLayer: function () { |
| | | return this.options.editLayer || new L.LayerGroup().addTo(this.map) |
| | | }, |
| | | |
| | | createFeaturesLayer: function () { |
| | | return this.options.featuresLayer || new L.LayerGroup().addTo(this.map) |
| | | }, |
| | | |
| | | moveForwardLineGuide: function (latlng) { |
| | | if (this.forwardLineGuide._latlngs.length) { |
| | | this.forwardLineGuide._latlngs[1] = latlng |
| | | this.forwardLineGuide._bounds.extend(latlng) |
| | | this.forwardLineGuide.redraw() |
| | | } |
| | | }, |
| | | |
| | | // attach your plugin to the global 'L' variable |
| | | if (typeof window !== 'undefined' && L) { |
| | | factory(L) |
| | | moveBackwardLineGuide: function (latlng) { |
| | | if (this.backwardLineGuide._latlngs.length) { |
| | | this.backwardLineGuide._latlngs[1] = latlng |
| | | this.backwardLineGuide._bounds.extend(latlng) |
| | | this.backwardLineGuide.redraw() |
| | | } |
| | | }(function(L) { |
| | | // 🍂miniclass CancelableEvent (Event objects) |
| | | // 🍂method cancel() |
| | | // Cancel any subsequent action. |
| | | }, |
| | | |
| | | // 🍂miniclass VertexEvent (Event objects) |
| | | // 🍂property vertex: VertexMarker |
| | | // The vertex that fires the event. |
| | | anchorForwardLineGuide: function (latlng) { |
| | | this.forwardLineGuide._latlngs[0] = latlng |
| | | this.forwardLineGuide._bounds.extend(latlng) |
| | | this.forwardLineGuide.redraw() |
| | | }, |
| | | |
| | | // 🍂miniclass ShapeEvent (Event objects) |
| | | // 🍂property shape: Array |
| | | // The shape (LatLngs array) subject of the action. |
| | | anchorBackwardLineGuide: function (latlng) { |
| | | this.backwardLineGuide._latlngs[0] = latlng |
| | | this.backwardLineGuide._bounds.extend(latlng) |
| | | this.backwardLineGuide.redraw() |
| | | }, |
| | | |
| | | // 🍂miniclass CancelableVertexEvent (Event objects) |
| | | // 🍂inherits VertexEvent |
| | | // 🍂inherits CancelableEvent |
| | | attachForwardLineGuide: function () { |
| | | this.editLayer.addLayer(this.forwardLineGuide) |
| | | }, |
| | | |
| | | // 🍂miniclass CancelableShapeEvent (Event objects) |
| | | // 🍂inherits ShapeEvent |
| | | // 🍂inherits CancelableEvent |
| | | attachBackwardLineGuide: function () { |
| | | this.editLayer.addLayer(this.backwardLineGuide) |
| | | }, |
| | | |
| | | // 🍂miniclass LayerEvent (Event objects) |
| | | // 🍂property layer: object |
| | | // The Layer (Marker, Polyline…) subject of the action. |
| | | detachForwardLineGuide: function () { |
| | | this.forwardLineGuide.setLatLngs([]) |
| | | this.editLayer.removeLayer(this.forwardLineGuide) |
| | | }, |
| | | |
| | | // 🍂namespace Editable; 🍂class Editable; 🍂aka L.Editable |
| | | // Main edition handler. By default, it is attached to the map |
| | | // as `map.editTools` property. |
| | | // Leaflet.Editable is made to be fully extendable. You have three ways to customize |
| | | // the behaviour: using options, listening to events, or extending. |
| | | L.Editable = L.Evented.extend({ |
| | | detachBackwardLineGuide: function () { |
| | | this.backwardLineGuide.setLatLngs([]) |
| | | this.editLayer.removeLayer(this.backwardLineGuide) |
| | | }, |
| | | |
| | | statics: { |
| | | FORWARD: 1, |
| | | BACKWARD: -1 |
| | | }, |
| | | |
| | | options: { |
| | | |
| | | // You can pass them when creating a map using the `editOptions` key. |
| | | // 🍂option zIndex: int = 1000 |
| | | // The default zIndex of the editing tools. |
| | | zIndex: 1000, |
| | | |
| | | // 🍂option polygonClass: class = L.Polygon |
| | | // Class to be used when creating a new Polygon. |
| | | polygonClass: L.Polygon, |
| | | |
| | | // 🍂option polylineClass: class = L.Polyline |
| | | // Class to be used when creating a new Polyline. |
| | | polylineClass: L.Polyline, |
| | | |
| | | // 🍂option markerClass: class = L.Marker |
| | | // Class to be used when creating a new Marker. |
| | | markerClass: L.Marker, |
| | | |
| | | // 🍂option rectangleClass: class = L.Rectangle |
| | | // Class to be used when creating a new Rectangle. |
| | | rectangleClass: L.Rectangle, |
| | | |
| | | // 🍂option circleClass: class = L.Circle |
| | | // Class to be used when creating a new Circle. |
| | | circleClass: L.Circle, |
| | | |
| | | // 🍂option drawingCSSClass: string = 'leaflet-editable-drawing' |
| | | // CSS class to be added to the map container while drawing. |
| | | drawingCSSClass: 'leaflet-editable-drawing', |
| | | |
| | | // 🍂option drawingCursor: const = 'crosshair' |
| | | // Cursor mode set to the map while drawing. |
| | | drawingCursor: 'crosshair', |
| | | |
| | | // 🍂option editLayer: Layer = new L.LayerGroup() |
| | | // Layer used to store edit tools (vertex, line guide…). |
| | | editLayer: undefined, |
| | | |
| | | // 🍂option featuresLayer: Layer = new L.LayerGroup() |
| | | // Default layer used to store drawn features (Marker, Polyline…). |
| | | featuresLayer: undefined, |
| | | |
| | | // 🍂option polylineEditorClass: class = PolylineEditor |
| | | // Class to be used as Polyline editor. |
| | | polylineEditorClass: undefined, |
| | | |
| | | // 🍂option polygonEditorClass: class = PolygonEditor |
| | | // Class to be used as Polygon editor. |
| | | polygonEditorClass: undefined, |
| | | |
| | | // 🍂option markerEditorClass: class = MarkerEditor |
| | | // Class to be used as Marker editor. |
| | | markerEditorClass: undefined, |
| | | |
| | | // 🍂option rectangleEditorClass: class = RectangleEditor |
| | | // Class to be used as Rectangle editor. |
| | | rectangleEditorClass: undefined, |
| | | |
| | | // 🍂option circleEditorClass: class = CircleEditor |
| | | // Class to be used as Circle editor. |
| | | circleEditorClass: undefined, |
| | | |
| | | // 🍂option lineGuideOptions: hash = {} |
| | | // Options to be passed to the line guides. |
| | | lineGuideOptions: {}, |
| | | |
| | | // 🍂option skipMiddleMarkers: boolean = false |
| | | // Set this to true if you don't want middle markers. |
| | | skipMiddleMarkers: false |
| | | |
| | | }, |
| | | |
| | | initialize: function(map, options) { |
| | | L.setOptions(this, options) |
| | | this._lastZIndex = this.options.zIndex |
| | | this.map = map |
| | | this.editLayer = this.createEditLayer() |
| | | this.featuresLayer = this.createFeaturesLayer() |
| | | this.forwardLineGuide = this.createLineGuide() |
| | | this.backwardLineGuide = this.createLineGuide() |
| | | }, |
| | | |
| | | fireAndForward: function(type, e) { |
| | | e = e || {} |
| | | e.editTools = this |
| | | this.fire(type, e) |
| | | this.map.fire(type, e) |
| | | }, |
| | | |
| | | createLineGuide: function() { |
| | | let options = L.extend({ |
| | | dashArray: '5,10', |
| | | weight: 1, |
| | | interactive: false |
| | | }, this.options.lineGuideOptions) |
| | | return L.polyline([], options) |
| | | }, |
| | | |
| | | createVertexIcon: function(options) { |
| | | return L.Browser.mobile && L.Browser.touch ? new L.Editable.TouchVertexIcon(options) : new L.Editable.VertexIcon(options) |
| | | }, |
| | | |
| | | createEditLayer: function() { |
| | | return this.options.editLayer || new L.LayerGroup().addTo(this.map) |
| | | }, |
| | | |
| | | createFeaturesLayer: function() { |
| | | return this.options.featuresLayer || new L.LayerGroup().addTo(this.map) |
| | | }, |
| | | |
| | | moveForwardLineGuide: function(latlng) { |
| | | if (this.forwardLineGuide._latlngs.length) { |
| | | this.forwardLineGuide._latlngs[1] = latlng |
| | | this.forwardLineGuide._bounds.extend(latlng) |
| | | this.forwardLineGuide.redraw() |
| | | } |
| | | }, |
| | | |
| | | moveBackwardLineGuide: function(latlng) { |
| | | if (this.backwardLineGuide._latlngs.length) { |
| | | this.backwardLineGuide._latlngs[1] = latlng |
| | | this.backwardLineGuide._bounds.extend(latlng) |
| | | this.backwardLineGuide.redraw() |
| | | } |
| | | }, |
| | | |
| | | anchorForwardLineGuide: function(latlng) { |
| | | this.forwardLineGuide._latlngs[0] = latlng |
| | | this.forwardLineGuide._bounds.extend(latlng) |
| | | this.forwardLineGuide.redraw() |
| | | }, |
| | | |
| | | anchorBackwardLineGuide: function(latlng) { |
| | | this.backwardLineGuide._latlngs[0] = latlng |
| | | this.backwardLineGuide._bounds.extend(latlng) |
| | | this.backwardLineGuide.redraw() |
| | | }, |
| | | |
| | | attachForwardLineGuide: function() { |
| | | this.editLayer.addLayer(this.forwardLineGuide) |
| | | }, |
| | | |
| | | attachBackwardLineGuide: function() { |
| | | this.editLayer.addLayer(this.backwardLineGuide) |
| | | }, |
| | | |
| | | detachForwardLineGuide: function() { |
| | | this.forwardLineGuide.setLatLngs([]) |
| | | this.editLayer.removeLayer(this.forwardLineGuide) |
| | | }, |
| | | |
| | | detachBackwardLineGuide: function() { |
| | | this.backwardLineGuide.setLatLngs([]) |
| | | this.editLayer.removeLayer(this.backwardLineGuide) |
| | | }, |
| | | |
| | | blockEvents: function() { |
| | | // Hack: force map not to listen to other layers events while drawing. |
| | | if (!this._oldTargets) { |
| | | this._oldTargets = this.map._targets |
| | | this.map._targets = {} |
| | | } |
| | | }, |
| | | |
| | | unblockEvents: function() { |
| | | if (this._oldTargets) { |
| | | // Reset, but keep targets created while drawing. |
| | | this.map._targets = L.extend(this.map._targets, this._oldTargets) |
| | | delete this._oldTargets |
| | | } |
| | | }, |
| | | |
| | | registerForDrawing: function(editor) { |
| | | if (this._drawingEditor) this.unregisterForDrawing(this._drawingEditor) |
| | | this.blockEvents() |
| | | editor.reset() // Make sure editor tools still receive events. |
| | | this._drawingEditor = editor |
| | | this.map.on('mousemove touchmove', editor.onDrawingMouseMove, editor) |
| | | this.map.on('mousedown', this.onMousedown, this) |
| | | this.map.on('mouseup', this.onMouseup, this) |
| | | L.DomUtil.addClass(this.map._container, this.options.drawingCSSClass) |
| | | this.defaultMapCursor = this.map._container.style.cursor |
| | | this.map._container.style.cursor = this.options.drawingCursor |
| | | }, |
| | | |
| | | unregisterForDrawing: function(editor) { |
| | | this.unblockEvents() |
| | | L.DomUtil.removeClass(this.map._container, this.options.drawingCSSClass) |
| | | this.map._container.style.cursor = this.defaultMapCursor |
| | | editor = editor || this._drawingEditor |
| | | if (!editor) return |
| | | this.map.off('mousemove touchmove', editor.onDrawingMouseMove, editor) |
| | | this.map.off('mousedown', this.onMousedown, this) |
| | | this.map.off('mouseup', this.onMouseup, this) |
| | | if (editor !== this._drawingEditor) return |
| | | delete this._drawingEditor |
| | | if (editor._drawing) editor.cancelDrawing() |
| | | }, |
| | | |
| | | onMousedown: function(e) { |
| | | if (e.originalEvent.which !== 1) return |
| | | this._mouseDown = e |
| | | this._drawingEditor.onDrawingMouseDown(e) |
| | | }, |
| | | |
| | | onMouseup: function(e) { |
| | | if (this._mouseDown) { |
| | | let editor = this._drawingEditor |
| | | let mouseDown = this._mouseDown |
| | | this._mouseDown = null |
| | | editor.onDrawingMouseUp(e) |
| | | if (this._drawingEditor !== editor) return // onDrawingMouseUp may call unregisterFromDrawing. |
| | | let origin = L.point(mouseDown.originalEvent.clientX, mouseDown.originalEvent.clientY) |
| | | let distance = L.point(e.originalEvent.clientX, e.originalEvent.clientY).distanceTo(origin) |
| | | if (Math.abs(distance) < 9 * (window.devicePixelRatio || 1)) this._drawingEditor.onDrawingClick(e) |
| | | } |
| | | }, |
| | | |
| | | // 🍂section Public methods |
| | | // You will generally access them by the `map.editTools` |
| | | // instance: |
| | | // |
| | | // `map.editTools.startPolyline();` |
| | | |
| | | // 🍂method drawing(): boolean |
| | | // Return true if any drawing action is ongoing. |
| | | drawing: function() { |
| | | return this._drawingEditor && this._drawingEditor.drawing() |
| | | }, |
| | | |
| | | // 🍂method stopDrawing() |
| | | // When you need to stop any ongoing drawing, without needing to know which editor is active. |
| | | stopDrawing: function() { |
| | | this.unregisterForDrawing() |
| | | }, |
| | | |
| | | // 🍂method commitDrawing() |
| | | // When you need to commit any ongoing drawing, without needing to know which editor is active. |
| | | commitDrawing: function(e) { |
| | | if (!this._drawingEditor) return |
| | | this._drawingEditor.commitDrawing(e) |
| | | }, |
| | | |
| | | connectCreatedToMap: function(layer) { |
| | | return this.featuresLayer.addLayer(layer) |
| | | }, |
| | | |
| | | // 🍂method startPolyline(latlng: L.LatLng, options: hash): L.Polyline |
| | | // Start drawing a Polyline. If `latlng` is given, a first point will be added. In any case, continuing on user click. |
| | | // If `options` is given, it will be passed to the Polyline class constructor. |
| | | startPolyline: function(latlng, options) { |
| | | let line = this.createPolyline([], options) |
| | | line.enableEdit(this.map).newShape(latlng) |
| | | return line |
| | | }, |
| | | |
| | | // 🍂method startPolygon(latlng: L.LatLng, options: hash): L.Polygon |
| | | // Start drawing a Polygon. If `latlng` is given, a first point will be added. In any case, continuing on user click. |
| | | // If `options` is given, it will be passed to the Polygon class constructor. |
| | | startPolygon: function(latlng, options) { |
| | | let polygon = this.createPolygon([], options) |
| | | polygon.enableEdit(this.map).newShape(latlng) |
| | | return polygon |
| | | }, |
| | | |
| | | // 🍂method startMarker(latlng: L.LatLng, options: hash): L.Marker |
| | | // Start adding a Marker. If `latlng` is given, the Marker will be shown first at this point. |
| | | // In any case, it will follow the user mouse, and will have a final `latlng` on next click (or touch). |
| | | // If `options` is given, it will be passed to the Marker class constructor. |
| | | startMarker: function(latlng, options) { |
| | | latlng = latlng || this.map.getCenter().clone() |
| | | let marker = this.createMarker(latlng, options) |
| | | marker.enableEdit(this.map).startDrawing() |
| | | return marker |
| | | }, |
| | | |
| | | // 🍂method startRectangle(latlng: L.LatLng, options: hash): L.Rectangle |
| | | // Start drawing a Rectangle. If `latlng` is given, the Rectangle anchor will be added. In any case, continuing on user drag. |
| | | // If `options` is given, it will be passed to the Rectangle class constructor. |
| | | startRectangle: function(latlng, options) { |
| | | let corner = latlng || L.latLng([0, 0]) |
| | | let bounds = new L.LatLngBounds(corner, corner) |
| | | let rectangle = this.createRectangle(bounds, options) |
| | | rectangle.enableEdit(this.map).startDrawing() |
| | | return rectangle |
| | | }, |
| | | |
| | | // 🍂method startCircle(latlng: L.LatLng, options: hash): L.Circle |
| | | // Start drawing a Circle. If `latlng` is given, the Circle anchor will be added. In any case, continuing on user drag. |
| | | // If `options` is given, it will be passed to the Circle class constructor. |
| | | startCircle: function(latlng, options) { |
| | | latlng = latlng || this.map.getCenter().clone() |
| | | let circle = this.createCircle(latlng, options) |
| | | circle.enableEdit(this.map).startDrawing() |
| | | return circle |
| | | }, |
| | | |
| | | startHole: function(editor, latlng) { |
| | | editor.newHole(latlng) |
| | | }, |
| | | |
| | | createLayer: function(Klass, latlngs, options) { |
| | | options = L.Util.extend({ editOptions: { editTools: this } }, options) |
| | | let layer = new Klass(latlngs, options) |
| | | // 🍂namespace Editable |
| | | // 🍂event editable:created: LayerEvent |
| | | // Fired when a new feature (Marker, Polyline…) is created. |
| | | this.fireAndForward('editable:created', { layer: layer }) |
| | | return layer |
| | | }, |
| | | |
| | | createPolyline: function(latlngs, options) { |
| | | return this.createLayer((options && options.polylineClass) || this.options.polylineClass, latlngs, options) |
| | | }, |
| | | |
| | | createPolygon: function(latlngs, options) { |
| | | return this.createLayer((options && options.polygonClass) || this.options.polygonClass, latlngs, options) |
| | | }, |
| | | |
| | | createMarker: function(latlng, options) { |
| | | return this.createLayer((options && options.markerClass) || this.options.markerClass, latlng, options) |
| | | }, |
| | | |
| | | createRectangle: function(bounds, options) { |
| | | return this.createLayer((options && options.rectangleClass) || this.options.rectangleClass, bounds, options) |
| | | }, |
| | | |
| | | createCircle: function(latlng, options) { |
| | | return this.createLayer((options && options.circleClass) || this.options.circleClass, latlng, options) |
| | | } |
| | | |
| | | }) |
| | | |
| | | L.extend(L.Editable, { |
| | | |
| | | makeCancellable: function(e) { |
| | | e.cancel = function() { |
| | | e._cancelled = true |
| | | } |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 🍂namespace Map; 🍂class Map |
| | | // Leaflet.Editable add options and events to the `L.Map` object. |
| | | // See `Editable` events for the list of events fired on the Map. |
| | | // 🍂example |
| | | // |
| | | // ```js |
| | | // let map = L.map('map', { |
| | | // editable: true, |
| | | // editOptions: { |
| | | // … |
| | | // } |
| | | // }); |
| | | // ``` |
| | | // 🍂section Editable Map Options |
| | | L.Map.mergeOptions({ |
| | | |
| | | // 🍂namespace Map |
| | | // 🍂section Map Options |
| | | // 🍂option EditToolsClass: class = L.Editable |
| | | // Class to be used as vertex, for path editing. |
| | | EditToolsClass: L.Editable, |
| | | |
| | | // 🍂option editable: boolean = false |
| | | // Whether to create a L.Editable instance at map init. |
| | | editable: false, |
| | | |
| | | // 🍂option editOptions: hash = {} |
| | | // Options to pass to L.Editable when instantiating. |
| | | editOptions: {} |
| | | |
| | | }) |
| | | |
| | | L.Map.addInitHook(function() { |
| | | this.whenReady(function() { |
| | | if (this.options.editable) { |
| | | this.editTools = new this.options.EditToolsClass(this, this.options.editOptions) |
| | | } |
| | | }) |
| | | }) |
| | | |
| | | L.Editable.VertexIcon = L.DivIcon.extend({ |
| | | |
| | | options: { |
| | | iconSize: new L.Point(8, 8) |
| | | } |
| | | |
| | | }) |
| | | |
| | | L.Editable.TouchVertexIcon = L.Editable.VertexIcon.extend({ |
| | | |
| | | options: { |
| | | iconSize: new L.Point(20, 20) |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 🍂namespace Editable; 🍂class VertexMarker; Handler for dragging path vertices. |
| | | L.Editable.VertexMarker = L.Marker.extend({ |
| | | |
| | | options: { |
| | | draggable: true, |
| | | className: 'leaflet-vertex-icon leaflet-custom-icon' |
| | | }, |
| | | |
| | | // 🍂section Public methods |
| | | // The marker used to handle path vertex. You will usually interact with a `VertexMarker` |
| | | // instance when listening for events like `editable:vertex:ctrlclick`. |
| | | |
| | | initialize: function(latlng, latlngs, editor, options) { |
| | | // We don't use this._latlng, because on drag Leaflet replace it while |
| | | // we want to keep reference. |
| | | this.latlng = latlng |
| | | this.latlngs = latlngs |
| | | this.editor = editor |
| | | L.Marker.prototype.initialize.call(this, latlng, options) |
| | | this.options.icon = this.editor.tools.createVertexIcon({ className: this.options.className }) |
| | | this.latlng.__vertex = this |
| | | this.editor.editLayer.addLayer(this) |
| | | this.setZIndexOffset(editor.tools._lastZIndex + 1) |
| | | }, |
| | | |
| | | onAdd: function(map) { |
| | | L.Marker.prototype.onAdd.call(this, map) |
| | | this.on('drag', this.onDrag) |
| | | this.on('dragstart', this.onDragStart) |
| | | this.on('dragend', this.onDragEnd) |
| | | this.on('mouseup', this.onMouseup) |
| | | this.on('click', this.onClick) |
| | | this.on('contextmenu', this.onContextMenu) |
| | | this.on('mousedown touchstart', this.onMouseDown) |
| | | this.on('mouseover', this.onMouseOver) |
| | | this.on('mouseout', this.onMouseOut) |
| | | this.addMiddleMarkers() |
| | | }, |
| | | |
| | | onRemove: function(map) { |
| | | if (this.middleMarker) this.middleMarker.delete() |
| | | delete this.latlng.__vertex |
| | | this.off('drag', this.onDrag) |
| | | this.off('dragstart', this.onDragStart) |
| | | this.off('dragend', this.onDragEnd) |
| | | this.off('mouseup', this.onMouseup) |
| | | this.off('click', this.onClick) |
| | | this.off('contextmenu', this.onContextMenu) |
| | | this.off('mousedown touchstart', this.onMouseDown) |
| | | this.off('mouseover', this.onMouseOver) |
| | | this.off('mouseout', this.onMouseOut) |
| | | L.Marker.prototype.onRemove.call(this, map) |
| | | }, |
| | | |
| | | onDrag: function(e) { |
| | | e.vertex = this |
| | | this.editor.onVertexMarkerDrag(e) |
| | | let iconPos = L.DomUtil.getPosition(this._icon) |
| | | let latlng = this._map.layerPointToLatLng(iconPos) |
| | | this.latlng.update(latlng) |
| | | this._latlng = this.latlng // Push back to Leaflet our reference. |
| | | this.editor.refresh() |
| | | if (this.middleMarker) this.middleMarker.updateLatLng() |
| | | let next = this.getNext() |
| | | if (next && next.middleMarker) next.middleMarker.updateLatLng() |
| | | }, |
| | | |
| | | onDragStart: function(e) { |
| | | e.vertex = this |
| | | this.editor.onVertexMarkerDragStart(e) |
| | | }, |
| | | |
| | | onDragEnd: function(e) { |
| | | e.vertex = this |
| | | this.editor.onVertexMarkerDragEnd(e) |
| | | }, |
| | | |
| | | onClick: function(e) { |
| | | e.vertex = this |
| | | this.editor.onVertexMarkerClick(e) |
| | | }, |
| | | |
| | | onMouseup: function(e) { |
| | | L.DomEvent.stop(e) |
| | | e.vertex = this |
| | | this.editor.map.fire('mouseup', e) |
| | | }, |
| | | |
| | | onContextMenu: function(e) { |
| | | e.vertex = this |
| | | this.editor.onVertexMarkerContextMenu(e) |
| | | }, |
| | | |
| | | onMouseDown: function(e) { |
| | | e.vertex = this |
| | | this.editor.onVertexMarkerMouseDown(e) |
| | | }, |
| | | |
| | | onMouseOver: function(e) { |
| | | e.vertex = this |
| | | this.editor.onVertexMarkerMouseOver(e) |
| | | }, |
| | | |
| | | onMouseOut: function(e) { |
| | | e.vertex = this |
| | | this.editor.onVertexMarkerMouseOut(e) |
| | | }, |
| | | |
| | | // 🍂method delete() |
| | | // Delete a vertex and the related LatLng. |
| | | delete: function() { |
| | | let next = this.getNext() // Compute before changing latlng |
| | | this.latlngs.splice(this.getIndex(), 1) |
| | | this.editor.editLayer.removeLayer(this) |
| | | this.editor.onVertexDeleted({ latlng: this.latlng, vertex: this }) |
| | | if (!this.latlngs.length) this.editor.deleteShape(this.latlngs) |
| | | if (next) next.resetMiddleMarker() |
| | | this.editor.refresh() |
| | | }, |
| | | |
| | | // 🍂method getIndex(): int |
| | | // Get the index of the current vertex among others of the same LatLngs group. |
| | | getIndex: function() { |
| | | return this.latlngs.indexOf(this.latlng) |
| | | }, |
| | | |
| | | // 🍂method getLastIndex(): int |
| | | // Get last vertex index of the LatLngs group of the current vertex. |
| | | getLastIndex: function() { |
| | | return this.latlngs.length - 1 |
| | | }, |
| | | |
| | | // 🍂method getPrevious(): VertexMarker |
| | | // Get the previous VertexMarker in the same LatLngs group. |
| | | getPrevious: function() { |
| | | if (this.latlngs.length < 2) return |
| | | let index = this.getIndex() |
| | | let previousIndex = index - 1 |
| | | if (index === 0 && this.editor.CLOSED) previousIndex = this.getLastIndex() |
| | | let previous = this.latlngs[previousIndex] |
| | | if (previous) return previous.__vertex |
| | | }, |
| | | |
| | | // 🍂method getNext(): VertexMarker |
| | | // Get the next VertexMarker in the same LatLngs group. |
| | | getNext: function() { |
| | | if (this.latlngs.length < 2) return |
| | | let index = this.getIndex() |
| | | let nextIndex = index + 1 |
| | | if (index === this.getLastIndex() && this.editor.CLOSED) nextIndex = 0 |
| | | let next = this.latlngs[nextIndex] |
| | | if (next) return next.__vertex |
| | | }, |
| | | |
| | | addMiddleMarker: function(previous) { |
| | | if (!this.editor.hasMiddleMarkers()) return |
| | | previous = previous || this.getPrevious() |
| | | if (previous && !this.middleMarker) this.middleMarker = this.editor.addMiddleMarker(previous, this, this.latlngs, this.editor) |
| | | }, |
| | | |
| | | addMiddleMarkers: function() { |
| | | if (!this.editor.hasMiddleMarkers()) return |
| | | let previous = this.getPrevious() |
| | | if (previous) this.addMiddleMarker(previous) |
| | | let next = this.getNext() |
| | | if (next) next.resetMiddleMarker() |
| | | }, |
| | | |
| | | resetMiddleMarker: function() { |
| | | if (this.middleMarker) this.middleMarker.delete() |
| | | this.addMiddleMarker() |
| | | }, |
| | | |
| | | // 🍂method split() |
| | | // Split the vertex LatLngs group at its index, if possible. |
| | | split: function() { |
| | | if (!this.editor.splitShape) return // Only for PolylineEditor |
| | | this.editor.splitShape(this.latlngs, this.getIndex()) |
| | | }, |
| | | |
| | | // 🍂method continue() |
| | | // Continue the vertex LatLngs from this vertex. Only active for first and last vertices of a Polyline. |
| | | continue: function() { |
| | | if (!this.editor.continueBackward) return // Only for PolylineEditor |
| | | let index = this.getIndex() |
| | | if (index === 0) this.editor.continueBackward(this.latlngs) |
| | | else if (index === this.getLastIndex()) this.editor.continueForward(this.latlngs) |
| | | } |
| | | |
| | | }) |
| | | |
| | | L.Editable.mergeOptions({ |
| | | |
| | | // 🍂namespace Editable |
| | | // 🍂option VertexMarkerClass: class = VertexMarker |
| | | // Class to be used as vertex, for path editing. |
| | | VertexMarkerClass: L.Editable.VertexMarker |
| | | |
| | | }) |
| | | |
| | | L.Editable.MiddleMarker = L.Marker.extend({ |
| | | |
| | | options: { |
| | | opacity: 0.5, |
| | | className: 'leaflet-div-icon leaflet-middle-icon', |
| | | draggable: true |
| | | }, |
| | | |
| | | initialize: function(left, right, latlngs, editor, options) { |
| | | this.left = left |
| | | this.right = right |
| | | this.editor = editor |
| | | this.latlngs = latlngs |
| | | L.Marker.prototype.initialize.call(this, this.computeLatLng(), options) |
| | | this._opacity = this.options.opacity |
| | | this.options.icon = this.editor.tools.createVertexIcon({ className: this.options.className }) |
| | | this.editor.editLayer.addLayer(this) |
| | | this.setVisibility() |
| | | }, |
| | | |
| | | setVisibility: function() { |
| | | let leftPoint = this._map.latLngToContainerPoint(this.left.latlng) |
| | | let rightPoint = this._map.latLngToContainerPoint(this.right.latlng) |
| | | let size = L.point(this.options.icon.options.iconSize) |
| | | if (leftPoint.distanceTo(rightPoint) < size.x * 3) this.hide() |
| | | else this.show() |
| | | }, |
| | | |
| | | show: function() { |
| | | this.setOpacity(this._opacity) |
| | | }, |
| | | |
| | | hide: function() { |
| | | this.setOpacity(0) |
| | | }, |
| | | |
| | | updateLatLng: function() { |
| | | this.setLatLng(this.computeLatLng()) |
| | | this.setVisibility() |
| | | }, |
| | | |
| | | computeLatLng: function() { |
| | | let leftPoint = this.editor.map.latLngToContainerPoint(this.left.latlng) |
| | | let rightPoint = this.editor.map.latLngToContainerPoint(this.right.latlng) |
| | | let y = (leftPoint.y + rightPoint.y) / 2 |
| | | let x = (leftPoint.x + rightPoint.x) / 2 |
| | | return this.editor.map.containerPointToLatLng([x, y]) |
| | | }, |
| | | |
| | | onAdd: function(map) { |
| | | L.Marker.prototype.onAdd.call(this, map) |
| | | L.DomEvent.on(this._icon, 'mousedown touchstart', this.onMouseDown, this) |
| | | map.on('zoomend', this.setVisibility, this) |
| | | }, |
| | | |
| | | onRemove: function(map) { |
| | | delete this.right.middleMarker |
| | | L.DomEvent.off(this._icon, 'mousedown touchstart', this.onMouseDown, this) |
| | | map.off('zoomend', this.setVisibility, this) |
| | | L.Marker.prototype.onRemove.call(this, map) |
| | | }, |
| | | |
| | | onMouseDown: function(e) { |
| | | let iconPos = L.DomUtil.getPosition(this._icon) |
| | | let latlng = this.editor.map.layerPointToLatLng(iconPos) |
| | | e = { |
| | | originalEvent: e, |
| | | latlng: latlng |
| | | } |
| | | if (this.options.opacity === 0) return |
| | | L.Editable.makeCancellable(e) |
| | | this.editor.onMiddleMarkerMouseDown(e) |
| | | if (e._cancelled) return |
| | | this.latlngs.splice(this.index(), 0, e.latlng) |
| | | this.editor.refresh() |
| | | let icon = this._icon |
| | | let marker = this.editor.addVertexMarker(e.latlng, this.latlngs) |
| | | this.editor.onNewVertex(marker) |
| | | /* Hack to workaround browser not firing touchend when element is no more on DOM */ |
| | | let parent = marker._icon.parentNode |
| | | parent.removeChild(marker._icon) |
| | | marker._icon = icon |
| | | parent.appendChild(marker._icon) |
| | | marker._initIcon() |
| | | marker._initInteraction() |
| | | marker.setOpacity(1) |
| | | /* End hack */ |
| | | // Transfer ongoing dragging to real marker |
| | | L.Draggable._dragging = false |
| | | marker.dragging._draggable._onDown(e.originalEvent) |
| | | this.delete() |
| | | }, |
| | | |
| | | delete: function() { |
| | | this.editor.editLayer.removeLayer(this) |
| | | }, |
| | | |
| | | index: function() { |
| | | return this.latlngs.indexOf(this.right.latlng) |
| | | } |
| | | |
| | | }) |
| | | |
| | | L.Editable.mergeOptions({ |
| | | |
| | | // 🍂namespace Editable |
| | | // 🍂option MiddleMarkerClass: class = VertexMarker |
| | | // Class to be used as middle vertex, pulled by the user to create a new point in the middle of a path. |
| | | MiddleMarkerClass: L.Editable.MiddleMarker |
| | | |
| | | }) |
| | | |
| | | // 🍂namespace Editable; 🍂class BaseEditor; 🍂aka L.Editable.BaseEditor |
| | | // When editing a feature (Marker, Polyline…), an editor is attached to it. This |
| | | // editor basically knows how to handle the edition. |
| | | L.Editable.BaseEditor = L.Handler.extend({ |
| | | |
| | | initialize: function(map, feature, options) { |
| | | L.setOptions(this, options) |
| | | this.map = map |
| | | this.feature = feature |
| | | this.feature.editor = this |
| | | this.editLayer = new L.LayerGroup() |
| | | this.tools = this.options.editTools || map.editTools |
| | | }, |
| | | |
| | | // 🍂method enable(): this |
| | | // Set up the drawing tools for the feature to be editable. |
| | | addHooks: function() { |
| | | if (this.isConnected()) this.onFeatureAdd() |
| | | else this.feature.once('add', this.onFeatureAdd, this) |
| | | this.onEnable() |
| | | this.feature.on(this._getEvents(), this) |
| | | }, |
| | | |
| | | // 🍂method disable(): this |
| | | // Remove the drawing tools for the feature. |
| | | removeHooks: function() { |
| | | this.feature.off(this._getEvents(), this) |
| | | if (this.feature.dragging) this.feature.dragging.disable() |
| | | this.editLayer.clearLayers() |
| | | this.tools.editLayer.removeLayer(this.editLayer) |
| | | this.onDisable() |
| | | if (this._drawing) this.cancelDrawing() |
| | | }, |
| | | |
| | | // 🍂method drawing(): boolean |
| | | // Return true if any drawing action is ongoing with this editor. |
| | | drawing: function() { |
| | | return !!this._drawing |
| | | }, |
| | | |
| | | reset: function() { |
| | | }, |
| | | |
| | | onFeatureAdd: function() { |
| | | this.tools.editLayer.addLayer(this.editLayer) |
| | | if (this.feature.dragging) this.feature.dragging.enable() |
| | | }, |
| | | |
| | | hasMiddleMarkers: function() { |
| | | return !this.options.skipMiddleMarkers && !this.tools.options.skipMiddleMarkers |
| | | }, |
| | | |
| | | fireAndForward: function(type, e) { |
| | | e = e || {} |
| | | e.layer = this.feature |
| | | this.feature.fire(type, e) |
| | | this.tools.fireAndForward(type, e) |
| | | }, |
| | | |
| | | onEnable: function() { |
| | | // 🍂namespace Editable |
| | | // 🍂event editable:enable: Event |
| | | // Fired when an existing feature is ready to be edited. |
| | | this.fireAndForward('editable:enable') |
| | | }, |
| | | |
| | | onDisable: function() { |
| | | // 🍂namespace Editable |
| | | // 🍂event editable:disable: Event |
| | | // Fired when an existing feature is not ready anymore to be edited. |
| | | this.fireAndForward('editable:disable') |
| | | }, |
| | | |
| | | onEditing: function() { |
| | | // 🍂namespace Editable |
| | | // 🍂event editable:editing: Event |
| | | // Fired as soon as any change is made to the feature geometry. |
| | | this.fireAndForward('editable:editing') |
| | | }, |
| | | |
| | | onStartDrawing: function() { |
| | | // 🍂namespace Editable |
| | | // 🍂section Drawing events |
| | | // 🍂event editable:drawing:start: Event |
| | | // Fired when a feature is to be drawn. |
| | | this.fireAndForward('editable:drawing:start') |
| | | }, |
| | | |
| | | onEndDrawing: function() { |
| | | // 🍂namespace Editable |
| | | // 🍂section Drawing events |
| | | // 🍂event editable:drawing:end: Event |
| | | // Fired when a feature is not drawn anymore. |
| | | this.fireAndForward('editable:drawing:end') |
| | | }, |
| | | |
| | | onCancelDrawing: function() { |
| | | // 🍂namespace Editable |
| | | // 🍂section Drawing events |
| | | // 🍂event editable:drawing:cancel: Event |
| | | // Fired when user cancel drawing while a feature is being drawn. |
| | | this.fireAndForward('editable:drawing:cancel') |
| | | }, |
| | | |
| | | onCommitDrawing: function(e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Drawing events |
| | | // 🍂event editable:drawing:commit: Event |
| | | // Fired when user finish drawing a feature. |
| | | this.fireAndForward('editable:drawing:commit', e) |
| | | }, |
| | | |
| | | onDrawingMouseDown: function(e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Drawing events |
| | | // 🍂event editable:drawing:mousedown: Event |
| | | // Fired when user `mousedown` while drawing. |
| | | this.fireAndForward('editable:drawing:mousedown', e) |
| | | }, |
| | | |
| | | onDrawingMouseUp: function(e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Drawing events |
| | | // 🍂event editable:drawing:mouseup: Event |
| | | // Fired when user `mouseup` while drawing. |
| | | this.fireAndForward('editable:drawing:mouseup', e) |
| | | }, |
| | | |
| | | startDrawing: function() { |
| | | if (!this._drawing) this._drawing = L.Editable.FORWARD |
| | | this.tools.registerForDrawing(this) |
| | | this.onStartDrawing() |
| | | }, |
| | | |
| | | commitDrawing: function(e) { |
| | | this.onCommitDrawing(e) |
| | | this.endDrawing() |
| | | }, |
| | | |
| | | cancelDrawing: function() { |
| | | // If called during a vertex drag, the vertex will be removed before |
| | | // the mouseup fires on it. This is a workaround. Maybe better fix is |
| | | // To have L.Draggable reset it's status on disable (Leaflet side). |
| | | L.Draggable._dragging = false |
| | | this.onCancelDrawing() |
| | | this.endDrawing() |
| | | }, |
| | | |
| | | endDrawing: function() { |
| | | this._drawing = false |
| | | this.tools.unregisterForDrawing(this) |
| | | this.onEndDrawing() |
| | | }, |
| | | |
| | | onDrawingClick: function(e) { |
| | | if (!this.drawing()) return |
| | | L.Editable.makeCancellable(e) |
| | | // 🍂namespace Editable |
| | | // 🍂section Drawing events |
| | | // 🍂event editable:drawing:click: CancelableEvent |
| | | // Fired when user `click` while drawing, before any internal action is being processed. |
| | | this.fireAndForward('editable:drawing:click', e) |
| | | if (e._cancelled) return |
| | | if (!this.isConnected()) this.connect(e) |
| | | this.processDrawingClick(e) |
| | | }, |
| | | |
| | | isConnected: function() { |
| | | return this.map.hasLayer(this.feature) |
| | | }, |
| | | |
| | | connect: function() { |
| | | this.tools.connectCreatedToMap(this.feature) |
| | | this.tools.editLayer.addLayer(this.editLayer) |
| | | }, |
| | | |
| | | onMove: function(e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Drawing events |
| | | // 🍂event editable:drawing:move: Event |
| | | // Fired when `move` mouse while drawing, while dragging a marker, and while dragging a vertex. |
| | | this.fireAndForward('editable:drawing:move', e) |
| | | }, |
| | | |
| | | onDrawingMouseMove: function(e) { |
| | | this.onMove(e) |
| | | }, |
| | | |
| | | _getEvents: function() { |
| | | return { |
| | | dragstart: this.onDragStart, |
| | | drag: this.onDrag, |
| | | dragend: this.onDragEnd, |
| | | remove: this.disable |
| | | } |
| | | }, |
| | | |
| | | onDragStart: function(e) { |
| | | this.onEditing() |
| | | // 🍂namespace Editable |
| | | // 🍂event editable:dragstart: Event |
| | | // Fired before a path feature is dragged. |
| | | this.fireAndForward('editable:dragstart', e) |
| | | }, |
| | | |
| | | onDrag: function(e) { |
| | | this.onMove(e) |
| | | // 🍂namespace Editable |
| | | // 🍂event editable:drag: Event |
| | | // Fired when a path feature is being dragged. |
| | | this.fireAndForward('editable:drag', e) |
| | | }, |
| | | |
| | | onDragEnd: function(e) { |
| | | // 🍂namespace Editable |
| | | // 🍂event editable:dragend: Event |
| | | // Fired after a path feature has been dragged. |
| | | this.fireAndForward('editable:dragend', e) |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 🍂namespace Editable; 🍂class MarkerEditor; 🍂aka L.Editable.MarkerEditor |
| | | // 🍂inherits BaseEditor |
| | | // Editor for Marker. |
| | | L.Editable.MarkerEditor = L.Editable.BaseEditor.extend({ |
| | | |
| | | onDrawingMouseMove: function(e) { |
| | | L.Editable.BaseEditor.prototype.onDrawingMouseMove.call(this, e) |
| | | if (this._drawing) this.feature.setLatLng(e.latlng) |
| | | }, |
| | | |
| | | processDrawingClick: function(e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Drawing events |
| | | // 🍂event editable:drawing:clicked: Event |
| | | // Fired when user `click` while drawing, after all internal actions. |
| | | this.fireAndForward('editable:drawing:clicked', e) |
| | | this.commitDrawing(e) |
| | | }, |
| | | |
| | | connect: function(e) { |
| | | // On touch, the latlng has not been updated because there is |
| | | // no mousemove. |
| | | if (e) this.feature._latlng = e.latlng |
| | | L.Editable.BaseEditor.prototype.connect.call(this, e) |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 🍂namespace Editable; 🍂class PathEditor; 🍂aka L.Editable.PathEditor |
| | | // 🍂inherits BaseEditor |
| | | // Base class for all path editors. |
| | | L.Editable.PathEditor = L.Editable.BaseEditor.extend({ |
| | | |
| | | CLOSED: false, |
| | | MIN_VERTEX: 2, |
| | | |
| | | addHooks: function() { |
| | | L.Editable.BaseEditor.prototype.addHooks.call(this) |
| | | if (this.feature) this.initVertexMarkers() |
| | | return this |
| | | }, |
| | | |
| | | initVertexMarkers: function(latlngs) { |
| | | if (!this.enabled()) return |
| | | latlngs = latlngs || this.getLatLngs() |
| | | if (isFlat(latlngs)) this.addVertexMarkers(latlngs) |
| | | else for (let i = 0; i < latlngs.length; i++) this.initVertexMarkers(latlngs[i]) |
| | | }, |
| | | |
| | | getLatLngs: function() { |
| | | return this.feature.getLatLngs() |
| | | }, |
| | | |
| | | // 🍂method reset() |
| | | // Rebuild edit elements (Vertex, MiddleMarker, etc.). |
| | | reset: function() { |
| | | this.editLayer.clearLayers() |
| | | this.initVertexMarkers() |
| | | }, |
| | | |
| | | addVertexMarker: function(latlng, latlngs) { |
| | | return new this.tools.options.VertexMarkerClass(latlng, latlngs, this) |
| | | }, |
| | | |
| | | onNewVertex: function(vertex) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:new: VertexEvent |
| | | // Fired when a new vertex is created. |
| | | this.fireAndForward('editable:vertex:new', { latlng: vertex.latlng, vertex: vertex }) |
| | | }, |
| | | |
| | | addVertexMarkers: function(latlngs) { |
| | | for (let i = 0; i < latlngs.length; i++) { |
| | | this.addVertexMarker(latlngs[i], latlngs) |
| | | } |
| | | }, |
| | | |
| | | refreshVertexMarkers: function(latlngs) { |
| | | latlngs = latlngs || this.getDefaultLatLngs() |
| | | for (let i = 0; i < latlngs.length; i++) { |
| | | latlngs[i].__vertex.update() |
| | | } |
| | | }, |
| | | |
| | | addMiddleMarker: function(left, right, latlngs) { |
| | | return new this.tools.options.MiddleMarkerClass(left, right, latlngs, this) |
| | | }, |
| | | |
| | | onVertexMarkerClick: function(e) { |
| | | L.Editable.makeCancellable(e) |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:click: CancelableVertexEvent |
| | | // Fired when a `click` is issued on a vertex, before any internal action is being processed. |
| | | this.fireAndForward('editable:vertex:click', e) |
| | | if (e._cancelled) return |
| | | if (this.tools.drawing() && this.tools._drawingEditor !== this) return |
| | | let index = e.vertex.getIndex() |
| | | let commit |
| | | if (e.originalEvent.ctrlKey) { |
| | | this.onVertexMarkerCtrlClick(e) |
| | | } else if (e.originalEvent.altKey) { |
| | | this.onVertexMarkerAltClick(e) |
| | | } else if (e.originalEvent.shiftKey) { |
| | | this.onVertexMarkerShiftClick(e) |
| | | } else if (e.originalEvent.metaKey) { |
| | | this.onVertexMarkerMetaKeyClick(e) |
| | | } else if (index === e.vertex.getLastIndex() && this._drawing === L.Editable.FORWARD) { |
| | | if (index >= this.MIN_VERTEX - 1) commit = true |
| | | } else if (index === 0 && this._drawing === L.Editable.BACKWARD && this._drawnLatLngs.length >= this.MIN_VERTEX) { |
| | | commit = true |
| | | } else if (index === 0 && this._drawing === L.Editable.FORWARD && this._drawnLatLngs.length >= this.MIN_VERTEX && this.CLOSED) { |
| | | commit = true // Allow to close on first point also for polygons |
| | | } else { |
| | | this.onVertexRawMarkerClick(e) |
| | | } |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:clicked: VertexEvent |
| | | // Fired when a `click` is issued on a vertex, after all internal actions. |
| | | this.fireAndForward('editable:vertex:clicked', e) |
| | | if (commit) this.commitDrawing(e) |
| | | }, |
| | | |
| | | onVertexRawMarkerClick: function(e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:rawclick: CancelableVertexEvent |
| | | // Fired when a `click` is issued on a vertex without any special key and without being in drawing mode. |
| | | this.fireAndForward('editable:vertex:rawclick', e) |
| | | if (e._cancelled) return |
| | | if (!this.vertexCanBeDeleted(e.vertex)) return |
| | | e.vertex.delete() |
| | | }, |
| | | |
| | | vertexCanBeDeleted: function(vertex) { |
| | | return vertex.latlngs.length > this.MIN_VERTEX |
| | | }, |
| | | |
| | | onVertexDeleted: function(e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:deleted: VertexEvent |
| | | // Fired after a vertex has been deleted by user. |
| | | this.fireAndForward('editable:vertex:deleted', e) |
| | | }, |
| | | |
| | | onVertexMarkerCtrlClick: function(e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:ctrlclick: VertexEvent |
| | | // Fired when a `click` with `ctrlKey` is issued on a vertex. |
| | | this.fireAndForward('editable:vertex:ctrlclick', e) |
| | | }, |
| | | |
| | | onVertexMarkerShiftClick: function(e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:shiftclick: VertexEvent |
| | | // Fired when a `click` with `shiftKey` is issued on a vertex. |
| | | this.fireAndForward('editable:vertex:shiftclick', e) |
| | | }, |
| | | |
| | | onVertexMarkerMetaKeyClick: function(e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:metakeyclick: VertexEvent |
| | | // Fired when a `click` with `metaKey` is issued on a vertex. |
| | | this.fireAndForward('editable:vertex:metakeyclick', e) |
| | | }, |
| | | |
| | | onVertexMarkerAltClick: function(e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:altclick: VertexEvent |
| | | // Fired when a `click` with `altKey` is issued on a vertex. |
| | | this.fireAndForward('editable:vertex:altclick', e) |
| | | }, |
| | | |
| | | onVertexMarkerContextMenu: function(e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:contextmenu: VertexEvent |
| | | // Fired when a `contextmenu` is issued on a vertex. |
| | | this.fireAndForward('editable:vertex:contextmenu', e) |
| | | }, |
| | | |
| | | onVertexMarkerMouseDown: function(e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:mousedown: VertexEvent |
| | | // Fired when user `mousedown` a vertex. |
| | | this.fireAndForward('editable:vertex:mousedown', e) |
| | | }, |
| | | |
| | | onVertexMarkerMouseOver: function(e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:mouseover: VertexEvent |
| | | // Fired when a user's mouse enters the vertex |
| | | this.fireAndForward('editable:vertex:mouseover', e) |
| | | }, |
| | | |
| | | onVertexMarkerMouseOut: function(e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:mouseout: VertexEvent |
| | | // Fired when a user's mouse leaves the vertex |
| | | this.fireAndForward('editable:vertex:mouseout', e) |
| | | }, |
| | | |
| | | onMiddleMarkerMouseDown: function(e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section MiddleMarker events |
| | | // 🍂event editable:middlemarker:mousedown: VertexEvent |
| | | // Fired when user `mousedown` a middle marker. |
| | | this.fireAndForward('editable:middlemarker:mousedown', e) |
| | | }, |
| | | |
| | | onVertexMarkerDrag: function(e) { |
| | | this.onMove(e) |
| | | if (this.feature._bounds) this.extendBounds(e) |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:drag: VertexEvent |
| | | // Fired when a vertex is dragged by user. |
| | | this.fireAndForward('editable:vertex:drag', e) |
| | | }, |
| | | |
| | | onVertexMarkerDragStart: function(e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:dragstart: VertexEvent |
| | | // Fired before a vertex is dragged by user. |
| | | this.fireAndForward('editable:vertex:dragstart', e) |
| | | }, |
| | | |
| | | onVertexMarkerDragEnd: function(e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:dragend: VertexEvent |
| | | // Fired after a vertex is dragged by user. |
| | | this.fireAndForward('editable:vertex:dragend', e) |
| | | }, |
| | | |
| | | setDrawnLatLngs: function(latlngs) { |
| | | this._drawnLatLngs = latlngs || this.getDefaultLatLngs() |
| | | }, |
| | | |
| | | startDrawing: function() { |
| | | if (!this._drawnLatLngs) this.setDrawnLatLngs() |
| | | L.Editable.BaseEditor.prototype.startDrawing.call(this) |
| | | }, |
| | | |
| | | startDrawingForward: function() { |
| | | this.startDrawing() |
| | | }, |
| | | |
| | | endDrawing: function() { |
| | | this.tools.detachForwardLineGuide() |
| | | this.tools.detachBackwardLineGuide() |
| | | if (this._drawnLatLngs && this._drawnLatLngs.length < this.MIN_VERTEX) this.deleteShape(this._drawnLatLngs) |
| | | L.Editable.BaseEditor.prototype.endDrawing.call(this) |
| | | delete this._drawnLatLngs |
| | | }, |
| | | |
| | | addLatLng: function(latlng) { |
| | | if (this._drawing === L.Editable.FORWARD) this._drawnLatLngs.push(latlng) |
| | | else this._drawnLatLngs.unshift(latlng) |
| | | this.feature._bounds.extend(latlng) |
| | | let vertex = this.addVertexMarker(latlng, this._drawnLatLngs) |
| | | this.onNewVertex(vertex) |
| | | this.refresh() |
| | | }, |
| | | |
| | | newPointForward: function(latlng) { |
| | | this.addLatLng(latlng) |
| | | this.tools.attachForwardLineGuide() |
| | | this.tools.anchorForwardLineGuide(latlng) |
| | | }, |
| | | |
| | | newPointBackward: function(latlng) { |
| | | this.addLatLng(latlng) |
| | | this.tools.anchorBackwardLineGuide(latlng) |
| | | }, |
| | | |
| | | // 🍂namespace PathEditor |
| | | // 🍂method push() |
| | | // Programmatically add a point while drawing. |
| | | push: function(latlng) { |
| | | if (!latlng) return console.error('L.Editable.PathEditor.push expect a valid latlng as parameter') |
| | | if (this._drawing === L.Editable.FORWARD) this.newPointForward(latlng) |
| | | else this.newPointBackward(latlng) |
| | | }, |
| | | |
| | | removeLatLng: function(latlng) { |
| | | latlng.__vertex.delete() |
| | | this.refresh() |
| | | }, |
| | | |
| | | // 🍂method pop(): L.LatLng or null |
| | | // Programmatically remove last point (if any) while drawing. |
| | | pop: function() { |
| | | if (this._drawnLatLngs.length <= 1) return |
| | | let latlng |
| | | if (this._drawing === L.Editable.FORWARD) latlng = this._drawnLatLngs[this._drawnLatLngs.length - 1] |
| | | else latlng = this._drawnLatLngs[0] |
| | | this.removeLatLng(latlng) |
| | | if (this._drawing === L.Editable.FORWARD) this.tools.anchorForwardLineGuide(this._drawnLatLngs[this._drawnLatLngs.length - 1]) |
| | | else this.tools.anchorForwardLineGuide(this._drawnLatLngs[0]) |
| | | return latlng |
| | | }, |
| | | |
| | | processDrawingClick: function(e) { |
| | | if (e.vertex && e.vertex.editor === this) return |
| | | if (this._drawing === L.Editable.FORWARD) this.newPointForward(e.latlng) |
| | | else this.newPointBackward(e.latlng) |
| | | this.fireAndForward('editable:drawing:clicked', e) |
| | | }, |
| | | |
| | | onDrawingMouseMove: function(e) { |
| | | L.Editable.BaseEditor.prototype.onDrawingMouseMove.call(this, e) |
| | | if (this._drawing) { |
| | | this.tools.moveForwardLineGuide(e.latlng) |
| | | this.tools.moveBackwardLineGuide(e.latlng) |
| | | } |
| | | }, |
| | | |
| | | refresh: function() { |
| | | this.feature.redraw() |
| | | this.onEditing() |
| | | }, |
| | | |
| | | // 🍂namespace PathEditor |
| | | // 🍂method newShape(latlng?: L.LatLng) |
| | | // Add a new shape (Polyline, Polygon) in a multi, and setup up drawing tools to draw it; |
| | | // if optional `latlng` is given, start a path at this point. |
| | | newShape: function(latlng) { |
| | | let shape = this.addNewEmptyShape() |
| | | if (!shape) return |
| | | this.setDrawnLatLngs(shape[0] || shape) // Polygon or polyline |
| | | this.startDrawingForward() |
| | | // 🍂namespace Editable |
| | | // 🍂section Shape events |
| | | // 🍂event editable:shape:new: ShapeEvent |
| | | // Fired when a new shape is created in a multi (Polygon or Polyline). |
| | | this.fireAndForward('editable:shape:new', { shape: shape }) |
| | | if (latlng) this.newPointForward(latlng) |
| | | }, |
| | | |
| | | deleteShape: function(shape, latlngs) { |
| | | let e = { shape: shape } |
| | | L.Editable.makeCancellable(e) |
| | | // 🍂namespace Editable |
| | | // 🍂section Shape events |
| | | // 🍂event editable:shape:delete: CancelableShapeEvent |
| | | // Fired before a new shape is deleted in a multi (Polygon or Polyline). |
| | | this.fireAndForward('editable:shape:delete', e) |
| | | if (e._cancelled) return |
| | | shape = this._deleteShape(shape, latlngs) |
| | | if (this.ensureNotFlat) this.ensureNotFlat() // Polygon. |
| | | this.feature.setLatLngs(this.getLatLngs()) // Force bounds reset. |
| | | this.refresh() |
| | | this.reset() |
| | | // 🍂namespace Editable |
| | | // 🍂section Shape events |
| | | // 🍂event editable:shape:deleted: ShapeEvent |
| | | // Fired after a new shape is deleted in a multi (Polygon or Polyline). |
| | | this.fireAndForward('editable:shape:deleted', { shape: shape }) |
| | | return shape |
| | | }, |
| | | |
| | | _deleteShape: function(shape, latlngs) { |
| | | latlngs = latlngs || this.getLatLngs() |
| | | if (!latlngs.length) return |
| | | let self = this |
| | | let inplaceDelete = function(latlngs, shape) { |
| | | // Called when deleting a flat latlngs |
| | | shape = latlngs.splice(0, Number.MAX_VALUE) |
| | | return shape |
| | | } |
| | | let spliceDelete = function(latlngs, shape) { |
| | | // Called when removing a latlngs inside an array |
| | | latlngs.splice(latlngs.indexOf(shape), 1) |
| | | if (!latlngs.length) self._deleteShape(latlngs) |
| | | return shape |
| | | } |
| | | if (latlngs === shape) return inplaceDelete(latlngs, shape) |
| | | for (let i = 0; i < latlngs.length; i++) { |
| | | if (latlngs[i] === shape) return spliceDelete(latlngs, shape) |
| | | else if (latlngs[i].indexOf(shape) !== -1) return spliceDelete(latlngs[i], shape) |
| | | } |
| | | }, |
| | | |
| | | // 🍂namespace PathEditor |
| | | // 🍂method deleteShapeAt(latlng: L.LatLng): Array |
| | | // Remove a path shape at the given `latlng`. |
| | | deleteShapeAt: function(latlng) { |
| | | let shape = this.feature.shapeAt(latlng) |
| | | if (shape) return this.deleteShape(shape) |
| | | }, |
| | | |
| | | // 🍂method appendShape(shape: Array) |
| | | // Append a new shape to the Polygon or Polyline. |
| | | appendShape: function(shape) { |
| | | this.insertShape(shape) |
| | | }, |
| | | |
| | | // 🍂method prependShape(shape: Array) |
| | | // Prepend a new shape to the Polygon or Polyline. |
| | | prependShape: function(shape) { |
| | | this.insertShape(shape, 0) |
| | | }, |
| | | |
| | | // 🍂method insertShape(shape: Array, index: int) |
| | | // Insert a new shape to the Polygon or Polyline at given index (default is to append). |
| | | insertShape: function(shape, index) { |
| | | this.ensureMulti() |
| | | shape = this.formatShape(shape) |
| | | if (typeof index === 'undefined') index = this.feature._latlngs.length |
| | | this.feature._latlngs.splice(index, 0, shape) |
| | | this.feature.redraw() |
| | | if (this._enabled) this.reset() |
| | | }, |
| | | |
| | | extendBounds: function(e) { |
| | | this.feature._bounds.extend(e.vertex.latlng) |
| | | }, |
| | | |
| | | onDragStart: function(e) { |
| | | this.editLayer.clearLayers() |
| | | L.Editable.BaseEditor.prototype.onDragStart.call(this, e) |
| | | }, |
| | | |
| | | onDragEnd: function(e) { |
| | | this.initVertexMarkers() |
| | | L.Editable.BaseEditor.prototype.onDragEnd.call(this, e) |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 🍂namespace Editable; 🍂class PolylineEditor; 🍂aka L.Editable.PolylineEditor |
| | | // 🍂inherits PathEditor |
| | | L.Editable.PolylineEditor = L.Editable.PathEditor.extend({ |
| | | |
| | | startDrawingBackward: function() { |
| | | this._drawing = L.Editable.BACKWARD |
| | | this.startDrawing() |
| | | }, |
| | | |
| | | // 🍂method continueBackward(latlngs?: Array) |
| | | // Set up drawing tools to continue the line backward. |
| | | continueBackward: function(latlngs) { |
| | | if (this.drawing()) return |
| | | latlngs = latlngs || this.getDefaultLatLngs() |
| | | this.setDrawnLatLngs(latlngs) |
| | | if (latlngs.length > 0) { |
| | | this.tools.attachBackwardLineGuide() |
| | | this.tools.anchorBackwardLineGuide(latlngs[0]) |
| | | } |
| | | this.startDrawingBackward() |
| | | }, |
| | | |
| | | // 🍂method continueForward(latlngs?: Array) |
| | | // Set up drawing tools to continue the line forward. |
| | | continueForward: function(latlngs) { |
| | | if (this.drawing()) return |
| | | latlngs = latlngs || this.getDefaultLatLngs() |
| | | this.setDrawnLatLngs(latlngs) |
| | | if (latlngs.length > 0) { |
| | | this.tools.attachForwardLineGuide() |
| | | this.tools.anchorForwardLineGuide(latlngs[latlngs.length - 1]) |
| | | } |
| | | this.startDrawingForward() |
| | | }, |
| | | |
| | | getDefaultLatLngs: function(latlngs) { |
| | | latlngs = latlngs || this.feature._latlngs |
| | | if (!latlngs.length || latlngs[0] instanceof L.LatLng) return latlngs |
| | | else return this.getDefaultLatLngs(latlngs[0]) |
| | | }, |
| | | |
| | | ensureMulti: function() { |
| | | if (this.feature._latlngs.length && isFlat(this.feature._latlngs)) { |
| | | this.feature._latlngs = [this.feature._latlngs] |
| | | } |
| | | }, |
| | | |
| | | addNewEmptyShape: function() { |
| | | if (this.feature._latlngs.length) { |
| | | let shape = [] |
| | | this.appendShape(shape) |
| | | return shape |
| | | } else { |
| | | return this.feature._latlngs |
| | | } |
| | | }, |
| | | |
| | | formatShape: function(shape) { |
| | | if (isFlat(shape)) return shape |
| | | else if (shape[0]) return this.formatShape(shape[0]) |
| | | }, |
| | | |
| | | // 🍂method splitShape(latlngs?: Array, index: int) |
| | | // Split the given `latlngs` shape at index `index` and integrate new shape in instance `latlngs`. |
| | | splitShape: function(shape, index) { |
| | | if (!index || index >= shape.length - 1) return |
| | | this.ensureMulti() |
| | | let shapeIndex = this.feature._latlngs.indexOf(shape) |
| | | if (shapeIndex === -1) return |
| | | let first = shape.slice(0, index + 1) |
| | | let second = shape.slice(index) |
| | | // We deal with reference, we don't want twice the same latlng around. |
| | | second[0] = L.latLng(second[0].lat, second[0].lng, second[0].alt) |
| | | this.feature._latlngs.splice(shapeIndex, 1, first, second) |
| | | this.refresh() |
| | | this.reset() |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 🍂namespace Editable; 🍂class PolygonEditor; 🍂aka L.Editable.PolygonEditor |
| | | // 🍂inherits PathEditor |
| | | L.Editable.PolygonEditor = L.Editable.PathEditor.extend({ |
| | | |
| | | CLOSED: true, |
| | | MIN_VERTEX: 3, |
| | | |
| | | newPointForward: function(latlng) { |
| | | L.Editable.PathEditor.prototype.newPointForward.call(this, latlng) |
| | | if (!this.tools.backwardLineGuide._latlngs.length) this.tools.anchorBackwardLineGuide(latlng) |
| | | if (this._drawnLatLngs.length === 2) this.tools.attachBackwardLineGuide() |
| | | }, |
| | | |
| | | addNewEmptyHole: function(latlng) { |
| | | this.ensureNotFlat() |
| | | let latlngs = this.feature.shapeAt(latlng) |
| | | if (!latlngs) return |
| | | let holes = [] |
| | | latlngs.push(holes) |
| | | return holes |
| | | }, |
| | | |
| | | // 🍂method newHole(latlng?: L.LatLng, index: int) |
| | | // Set up drawing tools for creating a new hole on the Polygon. If the `latlng` param is given, a first point is created. |
| | | newHole: function(latlng) { |
| | | let holes = this.addNewEmptyHole(latlng) |
| | | if (!holes) return |
| | | this.setDrawnLatLngs(holes) |
| | | this.startDrawingForward() |
| | | if (latlng) this.newPointForward(latlng) |
| | | }, |
| | | |
| | | addNewEmptyShape: function() { |
| | | if (this.feature._latlngs.length && this.feature._latlngs[0].length) { |
| | | let shape = [] |
| | | this.appendShape(shape) |
| | | return shape |
| | | } else { |
| | | return this.feature._latlngs |
| | | } |
| | | }, |
| | | |
| | | ensureMulti: function() { |
| | | if (this.feature._latlngs.length && isFlat(this.feature._latlngs[0])) { |
| | | this.feature._latlngs = [this.feature._latlngs] |
| | | } |
| | | }, |
| | | |
| | | ensureNotFlat: function() { |
| | | if (!this.feature._latlngs.length || isFlat(this.feature._latlngs)) this.feature._latlngs = [this.feature._latlngs] |
| | | }, |
| | | |
| | | vertexCanBeDeleted: function(vertex) { |
| | | let parent = this.feature.parentShape(vertex.latlngs) |
| | | let idx = L.Util.indexOf(parent, vertex.latlngs) |
| | | if (idx > 0) return true // Holes can be totally deleted without removing the layer itself. |
| | | return L.Editable.PathEditor.prototype.vertexCanBeDeleted.call(this, vertex) |
| | | }, |
| | | |
| | | getDefaultLatLngs: function() { |
| | | if (!this.feature._latlngs.length) this.feature._latlngs.push([]) |
| | | return this.feature._latlngs[0] |
| | | }, |
| | | |
| | | formatShape: function(shape) { |
| | | // [[1, 2], [3, 4]] => must be nested |
| | | // [] => must be nested |
| | | // [[]] => is already nested |
| | | if (isFlat(shape) && (!shape[0] || shape[0].length !== 0)) return [shape] |
| | | else return shape |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 🍂namespace Editable; 🍂class RectangleEditor; 🍂aka L.Editable.RectangleEditor |
| | | // 🍂inherits PathEditor |
| | | L.Editable.RectangleEditor = L.Editable.PathEditor.extend({ |
| | | |
| | | CLOSED: true, |
| | | MIN_VERTEX: 4, |
| | | |
| | | options: { |
| | | skipMiddleMarkers: true |
| | | }, |
| | | |
| | | extendBounds: function(e) { |
| | | let index = e.vertex.getIndex() |
| | | let next = e.vertex.getNext() |
| | | let previous = e.vertex.getPrevious() |
| | | let oppositeIndex = (index + 2) % 4 |
| | | let opposite = e.vertex.latlngs[oppositeIndex] |
| | | let bounds = new L.LatLngBounds(e.latlng, opposite) |
| | | // Update latlngs by hand to preserve order. |
| | | previous.latlng.update([e.latlng.lat, opposite.lng]) |
| | | next.latlng.update([opposite.lat, e.latlng.lng]) |
| | | this.updateBounds(bounds) |
| | | this.refreshVertexMarkers() |
| | | }, |
| | | |
| | | onDrawingMouseDown: function(e) { |
| | | L.Editable.PathEditor.prototype.onDrawingMouseDown.call(this, e) |
| | | this.connect() |
| | | let latlngs = this.getDefaultLatLngs() |
| | | // L.Polygon._convertLatLngs removes last latlng if it equals first point, |
| | | // which is the case here as all latlngs are [0, 0] |
| | | if (latlngs.length === 3) latlngs.push(e.latlng) |
| | | let bounds = new L.LatLngBounds(e.latlng, e.latlng) |
| | | this.updateBounds(bounds) |
| | | this.updateLatLngs(bounds) |
| | | this.refresh() |
| | | this.reset() |
| | | // Stop dragging map. |
| | | // L.Draggable has two workflows: |
| | | // - mousedown => mousemove => mouseup |
| | | // - touchstart => touchmove => touchend |
| | | // Problem: L.Map.Tap does not allow us to listen to touchstart, so we only |
| | | // can deal with mousedown, but then when in a touch device, we are dealing with |
| | | // simulated events (actually simulated by L.Map.Tap), which are no more taken |
| | | // into account by L.Draggable. |
| | | // Ref.: https://github.com/Leaflet/Leaflet.Editable/issues/103 |
| | | e.originalEvent._simulated = false |
| | | this.map.dragging._draggable._onUp(e.originalEvent) |
| | | // Now transfer ongoing drag action to the bottom right corner. |
| | | // Should we refine which corner will handle the drag according to |
| | | // drag direction? |
| | | latlngs[3].__vertex.dragging._draggable._onDown(e.originalEvent) |
| | | }, |
| | | |
| | | onDrawingMouseUp: function(e) { |
| | | this.commitDrawing(e) |
| | | e.originalEvent._simulated = false |
| | | L.Editable.PathEditor.prototype.onDrawingMouseUp.call(this, e) |
| | | }, |
| | | |
| | | onDrawingMouseMove: function(e) { |
| | | e.originalEvent._simulated = false |
| | | L.Editable.PathEditor.prototype.onDrawingMouseMove.call(this, e) |
| | | }, |
| | | |
| | | getDefaultLatLngs: function(latlngs) { |
| | | return latlngs || this.feature._latlngs[0] |
| | | }, |
| | | |
| | | updateBounds: function(bounds) { |
| | | this.feature._bounds = bounds |
| | | }, |
| | | |
| | | updateLatLngs: function(bounds) { |
| | | let latlngs = this.getDefaultLatLngs() |
| | | let newLatlngs = this.feature._boundsToLatLngs(bounds) |
| | | // Keep references. |
| | | for (let i = 0; i < latlngs.length; i++) { |
| | | latlngs[i].update(newLatlngs[i]) |
| | | } |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 🍂namespace Editable; 🍂class CircleEditor; 🍂aka L.Editable.CircleEditor |
| | | // 🍂inherits PathEditor |
| | | L.Editable.CircleEditor = L.Editable.PathEditor.extend({ |
| | | |
| | | MIN_VERTEX: 2, |
| | | |
| | | options: { |
| | | skipMiddleMarkers: true |
| | | }, |
| | | |
| | | initialize: function(map, feature, options) { |
| | | L.Editable.PathEditor.prototype.initialize.call(this, map, feature, options) |
| | | // C.Y.B Modify |
| | | |
| | | let latlng = this.computeResizeLatLng() |
| | | |
| | | // latlng.lat = latlng.lat-0.007855; |
| | | // latlng.lng= latlng.lng-0.1; |
| | | this._resizeLatLng = latlng |
| | | // 原始方法 |
| | | // this._resizeLatLng = this.computeResizeLatLng(); |
| | | }, |
| | | |
| | | computeResizeLatLng: function() { |
| | | // While circle is not added to the map, _radius is not set. |
| | | // let delta = (this.feature._radius || this.feature._mRadius) * Math.cos(Math.PI / 4) |
| | | let delta = (this.feature._radius || this.feature._mRadius) |
| | | let point = this.map.project(this.feature._latlng) |
| | | // return this.map.unproject([point.x + delta, point.y - delta]) |
| | | return this.map.unproject([point.x + delta, point.y]) |
| | | }, |
| | | |
| | | updateResizeLatLng: function() { |
| | | this._resizeLatLng.update(this.computeResizeLatLng()) |
| | | this._resizeLatLng.__vertex.update() |
| | | }, |
| | | |
| | | getLatLngs: function() { |
| | | return [this.feature._latlng, this._resizeLatLng] |
| | | }, |
| | | |
| | | getDefaultLatLngs: function() { |
| | | return this.getLatLngs() |
| | | }, |
| | | |
| | | onVertexMarkerDrag: function(e) { |
| | | if (e.vertex.getIndex() === 1) this.resize(e) |
| | | else this.updateResizeLatLng(e) |
| | | L.Editable.PathEditor.prototype.onVertexMarkerDrag.call(this, e) |
| | | }, |
| | | |
| | | resize: function(e) { |
| | | let radius = this.feature._latlng.distanceTo(e.latlng) |
| | | this.feature.setRadius(radius) |
| | | }, |
| | | |
| | | onDrawingMouseDown: function(e) { |
| | | L.Editable.PathEditor.prototype.onDrawingMouseDown.call(this, e) |
| | | this._resizeLatLng.update(e.latlng) |
| | | this.feature._latlng.update(e.latlng) |
| | | this.connect() |
| | | // Stop dragging map. |
| | | e.originalEvent._simulated = false |
| | | this.map.dragging._draggable._onUp(e.originalEvent) |
| | | // Now transfer ongoing drag action to the radius handler. |
| | | this._resizeLatLng.__vertex.dragging._draggable._onDown(e.originalEvent) |
| | | }, |
| | | |
| | | onDrawingMouseUp: function(e) { |
| | | this.commitDrawing(e) |
| | | e.originalEvent._simulated = false |
| | | L.Editable.PathEditor.prototype.onDrawingMouseUp.call(this, e) |
| | | }, |
| | | |
| | | onDrawingMouseMove: function(e) { |
| | | e.originalEvent._simulated = false |
| | | L.Editable.PathEditor.prototype.onDrawingMouseMove.call(this, e) |
| | | }, |
| | | |
| | | onDrag: function(e) { |
| | | L.Editable.PathEditor.prototype.onDrag.call(this, e) |
| | | this.feature.dragging.updateLatLng(this._resizeLatLng) |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 🍂namespace Editable; 🍂class EditableMixin |
| | | // `EditableMixin` is included to `L.Polyline`, `L.Polygon`, `L.Rectangle`, `L.Circle` |
| | | // and `L.Marker`. It adds some methods to them. |
| | | // *When editing is enabled, the editor is accessible on the instance with the |
| | | // `editor` property.* |
| | | let EditableMixin = { |
| | | |
| | | createEditor: function(map) { |
| | | map = map || this._map |
| | | let tools = (this.options.editOptions || {}).editTools || map.editTools |
| | | if (!tools) throw Error('Unable to detect Editable instance.') |
| | | let Klass = this.options.editorClass || this.getEditorClass(tools) |
| | | return new Klass(map, this, this.options.editOptions) |
| | | }, |
| | | |
| | | // 🍂method enableEdit(map?: L.Map): this.editor |
| | | // Enable editing, by creating an editor if not existing, and then calling `enable` on it. |
| | | enableEdit: function(map) { |
| | | if (!this.editor) this.createEditor(map) |
| | | this.editor.enable() |
| | | return this.editor |
| | | }, |
| | | |
| | | // 🍂method editEnabled(): boolean |
| | | // Return true if current instance has an editor attached, and this editor is enabled. |
| | | editEnabled: function() { |
| | | return this.editor && this.editor.enabled() |
| | | }, |
| | | |
| | | // 🍂method disableEdit() |
| | | // Disable editing, also remove the editor property reference. |
| | | disableEdit: function() { |
| | | if (this.editor) { |
| | | this.editor.disable() |
| | | delete this.editor |
| | | } |
| | | }, |
| | | |
| | | // 🍂method toggleEdit() |
| | | // Enable or disable editing, according to current status. |
| | | toggleEdit: function() { |
| | | if (this.editEnabled()) this.disableEdit() |
| | | else this.enableEdit() |
| | | }, |
| | | |
| | | _onEditableAdd: function() { |
| | | if (this.editor) this.enableEdit() |
| | | } |
| | | |
| | | blockEvents: function () { |
| | | // Hack: force map not to listen to other layers events while drawing. |
| | | if (!this._oldTargets) { |
| | | this._oldTargets = this.map._targets |
| | | this.map._targets = {} |
| | | } |
| | | }, |
| | | |
| | | let PolylineMixin = { |
| | | |
| | | getEditorClass: function(tools) { |
| | | return (tools && tools.options.polylineEditorClass) ? tools.options.polylineEditorClass : L.Editable.PolylineEditor |
| | | }, |
| | | |
| | | shapeAt: function(latlng, latlngs) { |
| | | // We can have those cases: |
| | | // - latlngs are just a flat array of latlngs, use this |
| | | // - latlngs is an array of arrays of latlngs, loop over |
| | | let shape = null |
| | | latlngs = latlngs || this._latlngs |
| | | if (!latlngs.length) return shape |
| | | else if (isFlat(latlngs) && this.isInLatLngs(latlng, latlngs)) shape = latlngs |
| | | else for (let i = 0; i < latlngs.length; i++) if (this.isInLatLngs(latlng, latlngs[i])) return latlngs[i] |
| | | return shape |
| | | }, |
| | | |
| | | isInLatLngs: function(l, latlngs) { |
| | | if (!latlngs) return false |
| | | let i, k, len |
| | | let part = [] |
| | | let p |
| | | let w = this._clickTolerance() |
| | | this._projectLatlngs(latlngs, part, this._pxBounds) |
| | | part = part[0] |
| | | p = this._map.latLngToLayerPoint(l) |
| | | |
| | | if (!this._pxBounds.contains(p)) { |
| | | return false |
| | | } |
| | | for (i = 1, len = part.length, k = 0; i < len; k = i++) { |
| | | if (L.LineUtil.pointToSegmentDistance(p, part[k], part[i]) <= w) { |
| | | return true |
| | | } |
| | | } |
| | | return false |
| | | } |
| | | |
| | | unblockEvents: function () { |
| | | if (this._oldTargets) { |
| | | // Reset, but keep targets created while drawing. |
| | | this.map._targets = L.extend(this.map._targets, this._oldTargets) |
| | | delete this._oldTargets |
| | | } |
| | | }, |
| | | |
| | | let PolygonMixin = { |
| | | registerForDrawing: function (editor) { |
| | | if (this._drawingEditor) this.unregisterForDrawing(this._drawingEditor) |
| | | this.blockEvents() |
| | | editor.reset() // Make sure editor tools still receive events. |
| | | this._drawingEditor = editor |
| | | this.map.on('mousemove touchmove', editor.onDrawingMouseMove, editor) |
| | | this.map.on('mousedown', this.onMousedown, this) |
| | | this.map.on('mouseup', this.onMouseup, this) |
| | | L.DomUtil.addClass(this.map._container, this.options.drawingCSSClass) |
| | | this.defaultMapCursor = this.map._container.style.cursor |
| | | this.map._container.style.cursor = this.options.drawingCursor |
| | | }, |
| | | |
| | | getEditorClass: function(tools) { |
| | | return (tools && tools.options.polygonEditorClass) ? tools.options.polygonEditorClass : L.Editable.PolygonEditor |
| | | }, |
| | | unregisterForDrawing: function (editor) { |
| | | this.unblockEvents() |
| | | L.DomUtil.removeClass(this.map._container, this.options.drawingCSSClass) |
| | | this.map._container.style.cursor = this.defaultMapCursor |
| | | editor = editor || this._drawingEditor |
| | | if (!editor) return |
| | | this.map.off('mousemove touchmove', editor.onDrawingMouseMove, editor) |
| | | this.map.off('mousedown', this.onMousedown, this) |
| | | this.map.off('mouseup', this.onMouseup, this) |
| | | if (editor !== this._drawingEditor) return |
| | | delete this._drawingEditor |
| | | if (editor._drawing) editor.cancelDrawing() |
| | | }, |
| | | |
| | | shapeAt: function(latlng, latlngs) { |
| | | // We can have those cases: |
| | | // - latlngs are just a flat array of latlngs, use this |
| | | // - latlngs is an array of arrays of latlngs, this is a simple polygon (maybe with holes), use the first |
| | | // - latlngs is an array of arrays of arrays, this is a multi, loop over |
| | | let shape = null |
| | | latlngs = latlngs || this._latlngs |
| | | if (!latlngs.length) return shape |
| | | else if (isFlat(latlngs) && this.isInLatLngs(latlng, latlngs)) shape = latlngs |
| | | else if (isFlat(latlngs[0]) && this.isInLatLngs(latlng, latlngs[0])) shape = latlngs |
| | | else for (let i = 0; i < latlngs.length; i++) if (this.isInLatLngs(latlng, latlngs[i][0])) return latlngs[i] |
| | | return shape |
| | | }, |
| | | onMousedown: function (e) { |
| | | if (e.originalEvent.which !== 1) return |
| | | this._mouseDown = e |
| | | this._drawingEditor.onDrawingMouseDown(e) |
| | | }, |
| | | |
| | | isInLatLngs: function(l, latlngs) { |
| | | let inside = false |
| | | let l1 |
| | | let l2 |
| | | let j |
| | | let k |
| | | let len2 |
| | | for (j = 0, len2 = latlngs.length, k = len2 - 1; j < len2; k = j++) { |
| | | l1 = latlngs[j] |
| | | l2 = latlngs[k] |
| | | onMouseup: function (e) { |
| | | if (this._mouseDown) { |
| | | const editor = this._drawingEditor |
| | | const mouseDown = this._mouseDown |
| | | this._mouseDown = null |
| | | editor.onDrawingMouseUp(e) |
| | | if (this._drawingEditor !== editor) return // onDrawingMouseUp may call unregisterFromDrawing. |
| | | const origin = L.point(mouseDown.originalEvent.clientX, mouseDown.originalEvent.clientY) |
| | | const distance = L.point(e.originalEvent.clientX, e.originalEvent.clientY).distanceTo(origin) |
| | | if (Math.abs(distance) < 9 * (window.devicePixelRatio || 1)) this._drawingEditor.onDrawingClick(e) |
| | | } |
| | | }, |
| | | |
| | | if (((l1.lat > l.lat) !== (l2.lat > l.lat)) && |
| | | // 🍂section Public methods |
| | | // You will generally access them by the `map.editTools` |
| | | // instance: |
| | | // |
| | | // `map.editTools.startPolyline();` |
| | | |
| | | // 🍂method drawing(): boolean |
| | | // Return true if any drawing action is ongoing. |
| | | drawing: function () { |
| | | return this._drawingEditor && this._drawingEditor.drawing() |
| | | }, |
| | | |
| | | // 🍂method stopDrawing() |
| | | // When you need to stop any ongoing drawing, without needing to know which editor is active. |
| | | stopDrawing: function () { |
| | | this.unregisterForDrawing() |
| | | }, |
| | | |
| | | // 🍂method commitDrawing() |
| | | // When you need to commit any ongoing drawing, without needing to know which editor is active. |
| | | commitDrawing: function (e) { |
| | | if (!this._drawingEditor) return |
| | | this._drawingEditor.commitDrawing(e) |
| | | }, |
| | | |
| | | connectCreatedToMap: function (layer) { |
| | | return this.featuresLayer.addLayer(layer) |
| | | }, |
| | | |
| | | // 🍂method startPolyline(latlng: L.LatLng, options: hash): L.Polyline |
| | | // Start drawing a Polyline. If `latlng` is given, a first point will be added. In any case, continuing on user click. |
| | | // If `options` is given, it will be passed to the Polyline class constructor. |
| | | startPolyline: function (latlng, options) { |
| | | const line = this.createPolyline([], options) |
| | | line.enableEdit(this.map).newShape(latlng) |
| | | return line |
| | | }, |
| | | |
| | | // 🍂method startPolygon(latlng: L.LatLng, options: hash): L.Polygon |
| | | // Start drawing a Polygon. If `latlng` is given, a first point will be added. In any case, continuing on user click. |
| | | // If `options` is given, it will be passed to the Polygon class constructor. |
| | | startPolygon: function (latlng, options) { |
| | | const polygon = this.createPolygon([], options) |
| | | polygon.enableEdit(this.map).newShape(latlng) |
| | | return polygon |
| | | }, |
| | | |
| | | // 🍂method startMarker(latlng: L.LatLng, options: hash): L.Marker |
| | | // Start adding a Marker. If `latlng` is given, the Marker will be shown first at this point. |
| | | // In any case, it will follow the user mouse, and will have a final `latlng` on next click (or touch). |
| | | // If `options` is given, it will be passed to the Marker class constructor. |
| | | startMarker: function (latlng, options) { |
| | | latlng = latlng || this.map.getCenter().clone() |
| | | const marker = this.createMarker(latlng, options) |
| | | marker.enableEdit(this.map).startDrawing() |
| | | return marker |
| | | }, |
| | | |
| | | // 🍂method startRectangle(latlng: L.LatLng, options: hash): L.Rectangle |
| | | // Start drawing a Rectangle. If `latlng` is given, the Rectangle anchor will be added. In any case, continuing on user drag. |
| | | // If `options` is given, it will be passed to the Rectangle class constructor. |
| | | startRectangle: function (latlng, options) { |
| | | const corner = latlng || L.latLng([0, 0]) |
| | | const bounds = new L.LatLngBounds(corner, corner) |
| | | const rectangle = this.createRectangle(bounds, options) |
| | | rectangle.enableEdit(this.map).startDrawing() |
| | | return rectangle |
| | | }, |
| | | |
| | | // 🍂method startCircle(latlng: L.LatLng, options: hash): L.Circle |
| | | // Start drawing a Circle. If `latlng` is given, the Circle anchor will be added. In any case, continuing on user drag. |
| | | // If `options` is given, it will be passed to the Circle class constructor. |
| | | startCircle: function (latlng, options) { |
| | | latlng = latlng || this.map.getCenter().clone() |
| | | const circle = this.createCircle(latlng, options) |
| | | circle.enableEdit(this.map).startDrawing() |
| | | return circle |
| | | }, |
| | | |
| | | startHole: function (editor, latlng) { |
| | | editor.newHole(latlng) |
| | | }, |
| | | |
| | | createLayer: function (Klass, latlngs, options) { |
| | | options = L.Util.extend({ editOptions: { editTools: this } }, options) |
| | | const layer = new Klass(latlngs, options) |
| | | // 🍂namespace Editable |
| | | // 🍂event editable:created: LayerEvent |
| | | // Fired when a new feature (Marker, Polyline…) is created. |
| | | this.fireAndForward('editable:created', { layer: layer }) |
| | | return layer |
| | | }, |
| | | |
| | | createPolyline: function (latlngs, options) { |
| | | return this.createLayer((options && options.polylineClass) || this.options.polylineClass, latlngs, options) |
| | | }, |
| | | |
| | | createPolygon: function (latlngs, options) { |
| | | return this.createLayer((options && options.polygonClass) || this.options.polygonClass, latlngs, options) |
| | | }, |
| | | |
| | | createMarker: function (latlng, options) { |
| | | return this.createLayer((options && options.markerClass) || this.options.markerClass, latlng, options) |
| | | }, |
| | | |
| | | createRectangle: function (bounds, options) { |
| | | return this.createLayer((options && options.rectangleClass) || this.options.rectangleClass, bounds, options) |
| | | }, |
| | | |
| | | createCircle: function (latlng, options) { |
| | | return this.createLayer((options && options.circleClass) || this.options.circleClass, latlng, options) |
| | | } |
| | | |
| | | }) |
| | | |
| | | L.extend(L.Editable, { |
| | | |
| | | makeCancellable: function (e) { |
| | | e.cancel = function () { |
| | | e._cancelled = true |
| | | } |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 🍂namespace Map; 🍂class Map |
| | | // Leaflet.Editable add options and events to the `L.Map` object. |
| | | // See `Editable` events for the list of events fired on the Map. |
| | | // 🍂example |
| | | // |
| | | // ```js |
| | | // let map = L.map('map', { |
| | | // editable: true, |
| | | // editOptions: { |
| | | // … |
| | | // } |
| | | // }); |
| | | // ``` |
| | | // 🍂section Editable Map Options |
| | | L.Map.mergeOptions({ |
| | | |
| | | // 🍂namespace Map |
| | | // 🍂section Map Options |
| | | // 🍂option EditToolsClass: class = L.Editable |
| | | // Class to be used as vertex, for path editing. |
| | | EditToolsClass: L.Editable, |
| | | |
| | | // 🍂option editable: boolean = false |
| | | // Whether to create a L.Editable instance at map init. |
| | | editable: false, |
| | | |
| | | // 🍂option editOptions: hash = {} |
| | | // Options to pass to L.Editable when instantiating. |
| | | editOptions: {} |
| | | |
| | | }) |
| | | |
| | | L.Map.addInitHook(function () { |
| | | this.whenReady(function () { |
| | | if (this.options.editable) { |
| | | this.editTools = new this.options.EditToolsClass(this, this.options.editOptions) |
| | | } |
| | | }) |
| | | }) |
| | | |
| | | L.Editable.VertexIcon = L.DivIcon.extend({ |
| | | |
| | | options: { |
| | | iconSize: new L.Point(8, 8) |
| | | } |
| | | |
| | | }) |
| | | |
| | | L.Editable.TouchVertexIcon = L.Editable.VertexIcon.extend({ |
| | | |
| | | options: { |
| | | iconSize: new L.Point(20, 20) |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 🍂namespace Editable; 🍂class VertexMarker; Handler for dragging path vertices. |
| | | L.Editable.VertexMarker = L.Marker.extend({ |
| | | |
| | | options: { |
| | | draggable: true, |
| | | className: 'leaflet-vertex-icon leaflet-custom-icon' |
| | | }, |
| | | |
| | | // 🍂section Public methods |
| | | // The marker used to handle path vertex. You will usually interact with a `VertexMarker` |
| | | // instance when listening for events like `editable:vertex:ctrlclick`. |
| | | |
| | | initialize: function (latlng, latlngs, editor, options) { |
| | | // We don't use this._latlng, because on drag Leaflet replace it while |
| | | // we want to keep reference. |
| | | this.latlng = latlng |
| | | this.latlngs = latlngs |
| | | this.editor = editor |
| | | L.Marker.prototype.initialize.call(this, latlng, options) |
| | | this.options.icon = this.editor.tools.createVertexIcon({ className: this.options.className }) |
| | | this.latlng.__vertex = this |
| | | this.editor.editLayer.addLayer(this) |
| | | this.setZIndexOffset(editor.tools._lastZIndex + 1) |
| | | }, |
| | | |
| | | onAdd: function (map) { |
| | | L.Marker.prototype.onAdd.call(this, map) |
| | | this.on('drag', this.onDrag) |
| | | this.on('dragstart', this.onDragStart) |
| | | this.on('dragend', this.onDragEnd) |
| | | this.on('mouseup', this.onMouseup) |
| | | this.on('click', this.onClick) |
| | | this.on('contextmenu', this.onContextMenu) |
| | | this.on('mousedown touchstart', this.onMouseDown) |
| | | this.on('mouseover', this.onMouseOver) |
| | | this.on('mouseout', this.onMouseOut) |
| | | this.addMiddleMarkers() |
| | | }, |
| | | |
| | | onRemove: function (map) { |
| | | if (this.middleMarker) this.middleMarker.delete() |
| | | delete this.latlng.__vertex |
| | | this.off('drag', this.onDrag) |
| | | this.off('dragstart', this.onDragStart) |
| | | this.off('dragend', this.onDragEnd) |
| | | this.off('mouseup', this.onMouseup) |
| | | this.off('click', this.onClick) |
| | | this.off('contextmenu', this.onContextMenu) |
| | | this.off('mousedown touchstart', this.onMouseDown) |
| | | this.off('mouseover', this.onMouseOver) |
| | | this.off('mouseout', this.onMouseOut) |
| | | L.Marker.prototype.onRemove.call(this, map) |
| | | }, |
| | | |
| | | onDrag: function (e) { |
| | | e.vertex = this |
| | | this.editor.onVertexMarkerDrag(e) |
| | | const iconPos = L.DomUtil.getPosition(this._icon) |
| | | const latlng = this._map.layerPointToLatLng(iconPos) |
| | | this.latlng.update(latlng) |
| | | this._latlng = this.latlng // Push back to Leaflet our reference. |
| | | this.editor.refresh() |
| | | if (this.middleMarker) this.middleMarker.updateLatLng() |
| | | const next = this.getNext() |
| | | if (next && next.middleMarker) next.middleMarker.updateLatLng() |
| | | }, |
| | | |
| | | onDragStart: function (e) { |
| | | e.vertex = this |
| | | this.editor.onVertexMarkerDragStart(e) |
| | | }, |
| | | |
| | | onDragEnd: function (e) { |
| | | e.vertex = this |
| | | this.editor.onVertexMarkerDragEnd(e) |
| | | }, |
| | | |
| | | onClick: function (e) { |
| | | e.vertex = this |
| | | this.editor.onVertexMarkerClick(e) |
| | | }, |
| | | |
| | | onMouseup: function (e) { |
| | | L.DomEvent.stop(e) |
| | | e.vertex = this |
| | | this.editor.map.fire('mouseup', e) |
| | | }, |
| | | |
| | | onContextMenu: function (e) { |
| | | e.vertex = this |
| | | this.editor.onVertexMarkerContextMenu(e) |
| | | }, |
| | | |
| | | onMouseDown: function (e) { |
| | | e.vertex = this |
| | | this.editor.onVertexMarkerMouseDown(e) |
| | | }, |
| | | |
| | | onMouseOver: function (e) { |
| | | e.vertex = this |
| | | this.editor.onVertexMarkerMouseOver(e) |
| | | }, |
| | | |
| | | onMouseOut: function (e) { |
| | | e.vertex = this |
| | | this.editor.onVertexMarkerMouseOut(e) |
| | | }, |
| | | |
| | | // 🍂method delete() |
| | | // Delete a vertex and the related LatLng. |
| | | delete: function () { |
| | | const next = this.getNext() // Compute before changing latlng |
| | | this.latlngs.splice(this.getIndex(), 1) |
| | | this.editor.editLayer.removeLayer(this) |
| | | this.editor.onVertexDeleted({ latlng: this.latlng, vertex: this }) |
| | | if (!this.latlngs.length) this.editor.deleteShape(this.latlngs) |
| | | if (next) next.resetMiddleMarker() |
| | | this.editor.refresh() |
| | | }, |
| | | |
| | | // 🍂method getIndex(): int |
| | | // Get the index of the current vertex among others of the same LatLngs group. |
| | | getIndex: function () { |
| | | return this.latlngs.indexOf(this.latlng) |
| | | }, |
| | | |
| | | // 🍂method getLastIndex(): int |
| | | // Get last vertex index of the LatLngs group of the current vertex. |
| | | getLastIndex: function () { |
| | | return this.latlngs.length - 1 |
| | | }, |
| | | |
| | | // 🍂method getPrevious(): VertexMarker |
| | | // Get the previous VertexMarker in the same LatLngs group. |
| | | getPrevious: function () { |
| | | if (this.latlngs.length < 2) return |
| | | const index = this.getIndex() |
| | | let previousIndex = index - 1 |
| | | if (index === 0 && this.editor.CLOSED) previousIndex = this.getLastIndex() |
| | | const previous = this.latlngs[previousIndex] |
| | | if (previous) return previous.__vertex |
| | | }, |
| | | |
| | | // 🍂method getNext(): VertexMarker |
| | | // Get the next VertexMarker in the same LatLngs group. |
| | | getNext: function () { |
| | | if (this.latlngs.length < 2) return |
| | | const index = this.getIndex() |
| | | let nextIndex = index + 1 |
| | | if (index === this.getLastIndex() && this.editor.CLOSED) nextIndex = 0 |
| | | const next = this.latlngs[nextIndex] |
| | | if (next) return next.__vertex |
| | | }, |
| | | |
| | | addMiddleMarker: function (previous) { |
| | | if (!this.editor.hasMiddleMarkers()) return |
| | | previous = previous || this.getPrevious() |
| | | if (previous && !this.middleMarker) this.middleMarker = this.editor.addMiddleMarker(previous, this, this.latlngs, this.editor) |
| | | }, |
| | | |
| | | addMiddleMarkers: function () { |
| | | if (!this.editor.hasMiddleMarkers()) return |
| | | const previous = this.getPrevious() |
| | | if (previous) this.addMiddleMarker(previous) |
| | | const next = this.getNext() |
| | | if (next) next.resetMiddleMarker() |
| | | }, |
| | | |
| | | resetMiddleMarker: function () { |
| | | if (this.middleMarker) this.middleMarker.delete() |
| | | this.addMiddleMarker() |
| | | }, |
| | | |
| | | // 🍂method split() |
| | | // Split the vertex LatLngs group at its index, if possible. |
| | | split: function () { |
| | | if (!this.editor.splitShape) return // Only for PolylineEditor |
| | | this.editor.splitShape(this.latlngs, this.getIndex()) |
| | | }, |
| | | |
| | | // 🍂method continue() |
| | | // Continue the vertex LatLngs from this vertex. Only active for first and last vertices of a Polyline. |
| | | continue: function () { |
| | | if (!this.editor.continueBackward) return // Only for PolylineEditor |
| | | const index = this.getIndex() |
| | | if (index === 0) this.editor.continueBackward(this.latlngs) |
| | | else if (index === this.getLastIndex()) this.editor.continueForward(this.latlngs) |
| | | } |
| | | |
| | | }) |
| | | |
| | | L.Editable.mergeOptions({ |
| | | |
| | | // 🍂namespace Editable |
| | | // 🍂option VertexMarkerClass: class = VertexMarker |
| | | // Class to be used as vertex, for path editing. |
| | | VertexMarkerClass: L.Editable.VertexMarker |
| | | |
| | | }) |
| | | |
| | | L.Editable.MiddleMarker = L.Marker.extend({ |
| | | |
| | | options: { |
| | | opacity: 0.5, |
| | | className: 'leaflet-div-icon leaflet-middle-icon', |
| | | draggable: true |
| | | }, |
| | | |
| | | initialize: function (left, right, latlngs, editor, options) { |
| | | this.left = left |
| | | this.right = right |
| | | this.editor = editor |
| | | this.latlngs = latlngs |
| | | L.Marker.prototype.initialize.call(this, this.computeLatLng(), options) |
| | | this._opacity = this.options.opacity |
| | | this.options.icon = this.editor.tools.createVertexIcon({ className: this.options.className }) |
| | | this.editor.editLayer.addLayer(this) |
| | | this.setVisibility() |
| | | }, |
| | | |
| | | setVisibility: function () { |
| | | const leftPoint = this._map.latLngToContainerPoint(this.left.latlng) |
| | | const rightPoint = this._map.latLngToContainerPoint(this.right.latlng) |
| | | const size = L.point(this.options.icon.options.iconSize) |
| | | if (leftPoint.distanceTo(rightPoint) < size.x * 3) this.hide() |
| | | else this.show() |
| | | }, |
| | | |
| | | show: function () { |
| | | this.setOpacity(this._opacity) |
| | | }, |
| | | |
| | | hide: function () { |
| | | this.setOpacity(0) |
| | | }, |
| | | |
| | | updateLatLng: function () { |
| | | this.setLatLng(this.computeLatLng()) |
| | | this.setVisibility() |
| | | }, |
| | | |
| | | computeLatLng: function () { |
| | | const leftPoint = this.editor.map.latLngToContainerPoint(this.left.latlng) |
| | | const rightPoint = this.editor.map.latLngToContainerPoint(this.right.latlng) |
| | | const y = (leftPoint.y + rightPoint.y) / 2 |
| | | const x = (leftPoint.x + rightPoint.x) / 2 |
| | | return this.editor.map.containerPointToLatLng([x, y]) |
| | | }, |
| | | |
| | | onAdd: function (map) { |
| | | L.Marker.prototype.onAdd.call(this, map) |
| | | L.DomEvent.on(this._icon, 'mousedown touchstart', this.onMouseDown, this) |
| | | map.on('zoomend', this.setVisibility, this) |
| | | }, |
| | | |
| | | onRemove: function (map) { |
| | | delete this.right.middleMarker |
| | | L.DomEvent.off(this._icon, 'mousedown touchstart', this.onMouseDown, this) |
| | | map.off('zoomend', this.setVisibility, this) |
| | | L.Marker.prototype.onRemove.call(this, map) |
| | | }, |
| | | |
| | | onMouseDown: function (e) { |
| | | const iconPos = L.DomUtil.getPosition(this._icon) |
| | | const latlng = this.editor.map.layerPointToLatLng(iconPos) |
| | | e = { |
| | | originalEvent: e, |
| | | latlng: latlng |
| | | } |
| | | if (this.options.opacity === 0) return |
| | | L.Editable.makeCancellable(e) |
| | | this.editor.onMiddleMarkerMouseDown(e) |
| | | if (e._cancelled) return |
| | | this.latlngs.splice(this.index(), 0, e.latlng) |
| | | this.editor.refresh() |
| | | const icon = this._icon |
| | | const marker = this.editor.addVertexMarker(e.latlng, this.latlngs) |
| | | this.editor.onNewVertex(marker) |
| | | /* Hack to workaround browser not firing touchend when element is no more on DOM */ |
| | | const parent = marker._icon.parentNode |
| | | parent.removeChild(marker._icon) |
| | | marker._icon = icon |
| | | parent.appendChild(marker._icon) |
| | | marker._initIcon() |
| | | marker._initInteraction() |
| | | marker.setOpacity(1) |
| | | /* End hack */ |
| | | // Transfer ongoing dragging to real marker |
| | | L.Draggable._dragging = false |
| | | marker.dragging._draggable._onDown(e.originalEvent) |
| | | this.delete() |
| | | }, |
| | | |
| | | delete: function () { |
| | | this.editor.editLayer.removeLayer(this) |
| | | }, |
| | | |
| | | index: function () { |
| | | return this.latlngs.indexOf(this.right.latlng) |
| | | } |
| | | |
| | | }) |
| | | |
| | | L.Editable.mergeOptions({ |
| | | |
| | | // 🍂namespace Editable |
| | | // 🍂option MiddleMarkerClass: class = VertexMarker |
| | | // Class to be used as middle vertex, pulled by the user to create a new point in the middle of a path. |
| | | MiddleMarkerClass: L.Editable.MiddleMarker |
| | | |
| | | }) |
| | | |
| | | // 🍂namespace Editable; 🍂class BaseEditor; 🍂aka L.Editable.BaseEditor |
| | | // When editing a feature (Marker, Polyline…), an editor is attached to it. This |
| | | // editor basically knows how to handle the edition. |
| | | L.Editable.BaseEditor = L.Handler.extend({ |
| | | |
| | | initialize: function (map, feature, options) { |
| | | L.setOptions(this, options) |
| | | this.map = map |
| | | this.feature = feature |
| | | this.feature.editor = this |
| | | this.editLayer = new L.LayerGroup() |
| | | this.tools = this.options.editTools || map.editTools |
| | | }, |
| | | |
| | | // 🍂method enable(): this |
| | | // Set up the drawing tools for the feature to be editable. |
| | | addHooks: function () { |
| | | if (this.isConnected()) this.onFeatureAdd() |
| | | else this.feature.once('add', this.onFeatureAdd, this) |
| | | this.onEnable() |
| | | this.feature.on(this._getEvents(), this) |
| | | }, |
| | | |
| | | // 🍂method disable(): this |
| | | // Remove the drawing tools for the feature. |
| | | removeHooks: function () { |
| | | this.feature.off(this._getEvents(), this) |
| | | if (this.feature.dragging) this.feature.dragging.disable() |
| | | this.editLayer.clearLayers() |
| | | this.tools.editLayer.removeLayer(this.editLayer) |
| | | this.onDisable() |
| | | if (this._drawing) this.cancelDrawing() |
| | | }, |
| | | |
| | | // 🍂method drawing(): boolean |
| | | // Return true if any drawing action is ongoing with this editor. |
| | | drawing: function () { |
| | | return !!this._drawing |
| | | }, |
| | | |
| | | reset: function () { |
| | | }, |
| | | |
| | | onFeatureAdd: function () { |
| | | this.tools.editLayer.addLayer(this.editLayer) |
| | | if (this.feature.dragging) this.feature.dragging.enable() |
| | | }, |
| | | |
| | | hasMiddleMarkers: function () { |
| | | return !this.options.skipMiddleMarkers && !this.tools.options.skipMiddleMarkers |
| | | }, |
| | | |
| | | fireAndForward: function (type, e) { |
| | | e = e || {} |
| | | e.layer = this.feature |
| | | this.feature.fire(type, e) |
| | | this.tools.fireAndForward(type, e) |
| | | }, |
| | | |
| | | onEnable: function () { |
| | | // 🍂namespace Editable |
| | | // 🍂event editable:enable: Event |
| | | // Fired when an existing feature is ready to be edited. |
| | | this.fireAndForward('editable:enable') |
| | | }, |
| | | |
| | | onDisable: function () { |
| | | // 🍂namespace Editable |
| | | // 🍂event editable:disable: Event |
| | | // Fired when an existing feature is not ready anymore to be edited. |
| | | this.fireAndForward('editable:disable') |
| | | }, |
| | | |
| | | onEditing: function () { |
| | | // 🍂namespace Editable |
| | | // 🍂event editable:editing: Event |
| | | // Fired as soon as any change is made to the feature geometry. |
| | | this.fireAndForward('editable:editing') |
| | | }, |
| | | |
| | | onStartDrawing: function () { |
| | | // 🍂namespace Editable |
| | | // 🍂section Drawing events |
| | | // 🍂event editable:drawing:start: Event |
| | | // Fired when a feature is to be drawn. |
| | | this.fireAndForward('editable:drawing:start') |
| | | }, |
| | | |
| | | onEndDrawing: function () { |
| | | // 🍂namespace Editable |
| | | // 🍂section Drawing events |
| | | // 🍂event editable:drawing:end: Event |
| | | // Fired when a feature is not drawn anymore. |
| | | this.fireAndForward('editable:drawing:end') |
| | | }, |
| | | |
| | | onCancelDrawing: function () { |
| | | // 🍂namespace Editable |
| | | // 🍂section Drawing events |
| | | // 🍂event editable:drawing:cancel: Event |
| | | // Fired when user cancel drawing while a feature is being drawn. |
| | | this.fireAndForward('editable:drawing:cancel') |
| | | }, |
| | | |
| | | onCommitDrawing: function (e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Drawing events |
| | | // 🍂event editable:drawing:commit: Event |
| | | // Fired when user finish drawing a feature. |
| | | this.fireAndForward('editable:drawing:commit', e) |
| | | }, |
| | | |
| | | onDrawingMouseDown: function (e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Drawing events |
| | | // 🍂event editable:drawing:mousedown: Event |
| | | // Fired when user `mousedown` while drawing. |
| | | this.fireAndForward('editable:drawing:mousedown', e) |
| | | }, |
| | | |
| | | onDrawingMouseUp: function (e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Drawing events |
| | | // 🍂event editable:drawing:mouseup: Event |
| | | // Fired when user `mouseup` while drawing. |
| | | this.fireAndForward('editable:drawing:mouseup', e) |
| | | }, |
| | | |
| | | startDrawing: function () { |
| | | if (!this._drawing) this._drawing = L.Editable.FORWARD |
| | | this.tools.registerForDrawing(this) |
| | | this.onStartDrawing() |
| | | }, |
| | | |
| | | commitDrawing: function (e) { |
| | | this.onCommitDrawing(e) |
| | | this.endDrawing() |
| | | }, |
| | | |
| | | cancelDrawing: function () { |
| | | // If called during a vertex drag, the vertex will be removed before |
| | | // the mouseup fires on it. This is a workaround. Maybe better fix is |
| | | // To have L.Draggable reset it's status on disable (Leaflet side). |
| | | L.Draggable._dragging = false |
| | | this.onCancelDrawing() |
| | | this.endDrawing() |
| | | }, |
| | | |
| | | endDrawing: function () { |
| | | this._drawing = false |
| | | this.tools.unregisterForDrawing(this) |
| | | this.onEndDrawing() |
| | | }, |
| | | |
| | | onDrawingClick: function (e) { |
| | | if (!this.drawing()) return |
| | | L.Editable.makeCancellable(e) |
| | | // 🍂namespace Editable |
| | | // 🍂section Drawing events |
| | | // 🍂event editable:drawing:click: CancelableEvent |
| | | // Fired when user `click` while drawing, before any internal action is being processed. |
| | | this.fireAndForward('editable:drawing:click', e) |
| | | if (e._cancelled) return |
| | | if (!this.isConnected()) this.connect(e) |
| | | this.processDrawingClick(e) |
| | | }, |
| | | |
| | | isConnected: function () { |
| | | return this.map.hasLayer(this.feature) |
| | | }, |
| | | |
| | | connect: function () { |
| | | this.tools.connectCreatedToMap(this.feature) |
| | | this.tools.editLayer.addLayer(this.editLayer) |
| | | }, |
| | | |
| | | onMove: function (e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Drawing events |
| | | // 🍂event editable:drawing:move: Event |
| | | // Fired when `move` mouse while drawing, while dragging a marker, and while dragging a vertex. |
| | | this.fireAndForward('editable:drawing:move', e) |
| | | }, |
| | | |
| | | onDrawingMouseMove: function (e) { |
| | | this.onMove(e) |
| | | }, |
| | | |
| | | _getEvents: function () { |
| | | return { |
| | | dragstart: this.onDragStart, |
| | | drag: this.onDrag, |
| | | dragend: this.onDragEnd, |
| | | remove: this.disable |
| | | } |
| | | }, |
| | | |
| | | onDragStart: function (e) { |
| | | this.onEditing() |
| | | // 🍂namespace Editable |
| | | // 🍂event editable:dragstart: Event |
| | | // Fired before a path feature is dragged. |
| | | this.fireAndForward('editable:dragstart', e) |
| | | }, |
| | | |
| | | onDrag: function (e) { |
| | | this.onMove(e) |
| | | // 🍂namespace Editable |
| | | // 🍂event editable:drag: Event |
| | | // Fired when a path feature is being dragged. |
| | | this.fireAndForward('editable:drag', e) |
| | | }, |
| | | |
| | | onDragEnd: function (e) { |
| | | // 🍂namespace Editable |
| | | // 🍂event editable:dragend: Event |
| | | // Fired after a path feature has been dragged. |
| | | this.fireAndForward('editable:dragend', e) |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 🍂namespace Editable; 🍂class MarkerEditor; 🍂aka L.Editable.MarkerEditor |
| | | // 🍂inherits BaseEditor |
| | | // Editor for Marker. |
| | | L.Editable.MarkerEditor = L.Editable.BaseEditor.extend({ |
| | | |
| | | onDrawingMouseMove: function (e) { |
| | | L.Editable.BaseEditor.prototype.onDrawingMouseMove.call(this, e) |
| | | if (this._drawing) this.feature.setLatLng(e.latlng) |
| | | }, |
| | | |
| | | processDrawingClick: function (e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Drawing events |
| | | // 🍂event editable:drawing:clicked: Event |
| | | // Fired when user `click` while drawing, after all internal actions. |
| | | this.fireAndForward('editable:drawing:clicked', e) |
| | | this.commitDrawing(e) |
| | | }, |
| | | |
| | | connect: function (e) { |
| | | // On touch, the latlng has not been updated because there is |
| | | // no mousemove. |
| | | if (e) this.feature._latlng = e.latlng |
| | | L.Editable.BaseEditor.prototype.connect.call(this, e) |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 🍂namespace Editable; 🍂class PathEditor; 🍂aka L.Editable.PathEditor |
| | | // 🍂inherits BaseEditor |
| | | // Base class for all path editors. |
| | | L.Editable.PathEditor = L.Editable.BaseEditor.extend({ |
| | | |
| | | CLOSED: false, |
| | | MIN_VERTEX: 2, |
| | | |
| | | addHooks: function () { |
| | | L.Editable.BaseEditor.prototype.addHooks.call(this) |
| | | if (this.feature) this.initVertexMarkers() |
| | | return this |
| | | }, |
| | | |
| | | initVertexMarkers: function (latlngs) { |
| | | if (!this.enabled()) return |
| | | latlngs = latlngs || this.getLatLngs() |
| | | if (isFlat(latlngs)) this.addVertexMarkers(latlngs) |
| | | else for (let i = 0; i < latlngs.length; i++) this.initVertexMarkers(latlngs[i]) |
| | | }, |
| | | |
| | | getLatLngs: function () { |
| | | return this.feature.getLatLngs() |
| | | }, |
| | | |
| | | // 🍂method reset() |
| | | // Rebuild edit elements (Vertex, MiddleMarker, etc.). |
| | | reset: function () { |
| | | this.editLayer.clearLayers() |
| | | this.initVertexMarkers() |
| | | }, |
| | | |
| | | addVertexMarker: function (latlng, latlngs) { |
| | | return new this.tools.options.VertexMarkerClass(latlng, latlngs, this) |
| | | }, |
| | | |
| | | onNewVertex: function (vertex) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:new: VertexEvent |
| | | // Fired when a new vertex is created. |
| | | this.fireAndForward('editable:vertex:new', { latlng: vertex.latlng, vertex: vertex }) |
| | | }, |
| | | |
| | | addVertexMarkers: function (latlngs) { |
| | | for (let i = 0; i < latlngs.length; i++) { |
| | | this.addVertexMarker(latlngs[i], latlngs) |
| | | } |
| | | }, |
| | | |
| | | refreshVertexMarkers: function (latlngs) { |
| | | latlngs = latlngs || this.getDefaultLatLngs() |
| | | for (let i = 0; i < latlngs.length; i++) { |
| | | latlngs[i].__vertex.update() |
| | | } |
| | | }, |
| | | |
| | | addMiddleMarker: function (left, right, latlngs) { |
| | | return new this.tools.options.MiddleMarkerClass(left, right, latlngs, this) |
| | | }, |
| | | |
| | | onVertexMarkerClick: function (e) { |
| | | L.Editable.makeCancellable(e) |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:click: CancelableVertexEvent |
| | | // Fired when a `click` is issued on a vertex, before any internal action is being processed. |
| | | this.fireAndForward('editable:vertex:click', e) |
| | | if (e._cancelled) return |
| | | if (this.tools.drawing() && this.tools._drawingEditor !== this) return |
| | | const index = e.vertex.getIndex() |
| | | let commit |
| | | if (e.originalEvent.ctrlKey) { |
| | | this.onVertexMarkerCtrlClick(e) |
| | | } else if (e.originalEvent.altKey) { |
| | | this.onVertexMarkerAltClick(e) |
| | | } else if (e.originalEvent.shiftKey) { |
| | | this.onVertexMarkerShiftClick(e) |
| | | } else if (e.originalEvent.metaKey) { |
| | | this.onVertexMarkerMetaKeyClick(e) |
| | | } else if (index === e.vertex.getLastIndex() && this._drawing === L.Editable.FORWARD) { |
| | | if (index >= this.MIN_VERTEX - 1) commit = true |
| | | } else if (index === 0 && this._drawing === L.Editable.BACKWARD && this._drawnLatLngs.length >= this.MIN_VERTEX) { |
| | | commit = true |
| | | } else if (index === 0 && this._drawing === L.Editable.FORWARD && this._drawnLatLngs.length >= this.MIN_VERTEX && this.CLOSED) { |
| | | commit = true // Allow to close on first point also for polygons |
| | | } else { |
| | | this.onVertexRawMarkerClick(e) |
| | | } |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:clicked: VertexEvent |
| | | // Fired when a `click` is issued on a vertex, after all internal actions. |
| | | this.fireAndForward('editable:vertex:clicked', e) |
| | | if (commit) this.commitDrawing(e) |
| | | }, |
| | | |
| | | onVertexRawMarkerClick: function (e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:rawclick: CancelableVertexEvent |
| | | // Fired when a `click` is issued on a vertex without any special key and without being in drawing mode. |
| | | this.fireAndForward('editable:vertex:rawclick', e) |
| | | if (e._cancelled) return |
| | | if (!this.vertexCanBeDeleted(e.vertex)) return |
| | | e.vertex.delete() |
| | | }, |
| | | |
| | | vertexCanBeDeleted: function (vertex) { |
| | | return vertex.latlngs.length > this.MIN_VERTEX |
| | | }, |
| | | |
| | | onVertexDeleted: function (e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:deleted: VertexEvent |
| | | // Fired after a vertex has been deleted by user. |
| | | this.fireAndForward('editable:vertex:deleted', e) |
| | | }, |
| | | |
| | | onVertexMarkerCtrlClick: function (e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:ctrlclick: VertexEvent |
| | | // Fired when a `click` with `ctrlKey` is issued on a vertex. |
| | | this.fireAndForward('editable:vertex:ctrlclick', e) |
| | | }, |
| | | |
| | | onVertexMarkerShiftClick: function (e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:shiftclick: VertexEvent |
| | | // Fired when a `click` with `shiftKey` is issued on a vertex. |
| | | this.fireAndForward('editable:vertex:shiftclick', e) |
| | | }, |
| | | |
| | | onVertexMarkerMetaKeyClick: function (e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:metakeyclick: VertexEvent |
| | | // Fired when a `click` with `metaKey` is issued on a vertex. |
| | | this.fireAndForward('editable:vertex:metakeyclick', e) |
| | | }, |
| | | |
| | | onVertexMarkerAltClick: function (e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:altclick: VertexEvent |
| | | // Fired when a `click` with `altKey` is issued on a vertex. |
| | | this.fireAndForward('editable:vertex:altclick', e) |
| | | }, |
| | | |
| | | onVertexMarkerContextMenu: function (e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:contextmenu: VertexEvent |
| | | // Fired when a `contextmenu` is issued on a vertex. |
| | | this.fireAndForward('editable:vertex:contextmenu', e) |
| | | }, |
| | | |
| | | onVertexMarkerMouseDown: function (e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:mousedown: VertexEvent |
| | | // Fired when user `mousedown` a vertex. |
| | | this.fireAndForward('editable:vertex:mousedown', e) |
| | | }, |
| | | |
| | | onVertexMarkerMouseOver: function (e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:mouseover: VertexEvent |
| | | // Fired when a user's mouse enters the vertex |
| | | this.fireAndForward('editable:vertex:mouseover', e) |
| | | }, |
| | | |
| | | onVertexMarkerMouseOut: function (e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:mouseout: VertexEvent |
| | | // Fired when a user's mouse leaves the vertex |
| | | this.fireAndForward('editable:vertex:mouseout', e) |
| | | }, |
| | | |
| | | onMiddleMarkerMouseDown: function (e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section MiddleMarker events |
| | | // 🍂event editable:middlemarker:mousedown: VertexEvent |
| | | // Fired when user `mousedown` a middle marker. |
| | | this.fireAndForward('editable:middlemarker:mousedown', e) |
| | | }, |
| | | |
| | | onVertexMarkerDrag: function (e) { |
| | | this.onMove(e) |
| | | if (this.feature._bounds) this.extendBounds(e) |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:drag: VertexEvent |
| | | // Fired when a vertex is dragged by user. |
| | | this.fireAndForward('editable:vertex:drag', e) |
| | | }, |
| | | |
| | | onVertexMarkerDragStart: function (e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:dragstart: VertexEvent |
| | | // Fired before a vertex is dragged by user. |
| | | this.fireAndForward('editable:vertex:dragstart', e) |
| | | }, |
| | | |
| | | onVertexMarkerDragEnd: function (e) { |
| | | // 🍂namespace Editable |
| | | // 🍂section Vertex events |
| | | // 🍂event editable:vertex:dragend: VertexEvent |
| | | // Fired after a vertex is dragged by user. |
| | | this.fireAndForward('editable:vertex:dragend', e) |
| | | }, |
| | | |
| | | setDrawnLatLngs: function (latlngs) { |
| | | this._drawnLatLngs = latlngs || this.getDefaultLatLngs() |
| | | }, |
| | | |
| | | startDrawing: function () { |
| | | if (!this._drawnLatLngs) this.setDrawnLatLngs() |
| | | L.Editable.BaseEditor.prototype.startDrawing.call(this) |
| | | }, |
| | | |
| | | startDrawingForward: function () { |
| | | this.startDrawing() |
| | | }, |
| | | |
| | | endDrawing: function () { |
| | | this.tools.detachForwardLineGuide() |
| | | this.tools.detachBackwardLineGuide() |
| | | if (this._drawnLatLngs && this._drawnLatLngs.length < this.MIN_VERTEX) this.deleteShape(this._drawnLatLngs) |
| | | L.Editable.BaseEditor.prototype.endDrawing.call(this) |
| | | delete this._drawnLatLngs |
| | | }, |
| | | |
| | | addLatLng: function (latlng) { |
| | | if (this._drawing === L.Editable.FORWARD) this._drawnLatLngs.push(latlng) |
| | | else this._drawnLatLngs.unshift(latlng) |
| | | this.feature._bounds.extend(latlng) |
| | | const vertex = this.addVertexMarker(latlng, this._drawnLatLngs) |
| | | this.onNewVertex(vertex) |
| | | this.refresh() |
| | | }, |
| | | |
| | | newPointForward: function (latlng) { |
| | | this.addLatLng(latlng) |
| | | this.tools.attachForwardLineGuide() |
| | | this.tools.anchorForwardLineGuide(latlng) |
| | | }, |
| | | |
| | | newPointBackward: function (latlng) { |
| | | this.addLatLng(latlng) |
| | | this.tools.anchorBackwardLineGuide(latlng) |
| | | }, |
| | | |
| | | // 🍂namespace PathEditor |
| | | // 🍂method push() |
| | | // Programmatically add a point while drawing. |
| | | push: function (latlng) { |
| | | if (!latlng) return console.error('L.Editable.PathEditor.push expect a valid latlng as parameter') |
| | | if (this._drawing === L.Editable.FORWARD) this.newPointForward(latlng) |
| | | else this.newPointBackward(latlng) |
| | | }, |
| | | |
| | | removeLatLng: function (latlng) { |
| | | latlng.__vertex.delete() |
| | | this.refresh() |
| | | }, |
| | | |
| | | // 🍂method pop(): L.LatLng or null |
| | | // Programmatically remove last point (if any) while drawing. |
| | | pop: function () { |
| | | if (this._drawnLatLngs.length <= 1) return |
| | | let latlng |
| | | if (this._drawing === L.Editable.FORWARD) latlng = this._drawnLatLngs[this._drawnLatLngs.length - 1] |
| | | else latlng = this._drawnLatLngs[0] |
| | | this.removeLatLng(latlng) |
| | | if (this._drawing === L.Editable.FORWARD) this.tools.anchorForwardLineGuide(this._drawnLatLngs[this._drawnLatLngs.length - 1]) |
| | | else this.tools.anchorForwardLineGuide(this._drawnLatLngs[0]) |
| | | return latlng |
| | | }, |
| | | |
| | | processDrawingClick: function (e) { |
| | | if (e.vertex && e.vertex.editor === this) return |
| | | if (this._drawing === L.Editable.FORWARD) this.newPointForward(e.latlng) |
| | | else this.newPointBackward(e.latlng) |
| | | this.fireAndForward('editable:drawing:clicked', e) |
| | | }, |
| | | |
| | | onDrawingMouseMove: function (e) { |
| | | L.Editable.BaseEditor.prototype.onDrawingMouseMove.call(this, e) |
| | | if (this._drawing) { |
| | | this.tools.moveForwardLineGuide(e.latlng) |
| | | this.tools.moveBackwardLineGuide(e.latlng) |
| | | } |
| | | }, |
| | | |
| | | refresh: function () { |
| | | this.feature.redraw() |
| | | this.onEditing() |
| | | }, |
| | | |
| | | // 🍂namespace PathEditor |
| | | // 🍂method newShape(latlng?: L.LatLng) |
| | | // Add a new shape (Polyline, Polygon) in a multi, and setup up drawing tools to draw it; |
| | | // if optional `latlng` is given, start a path at this point. |
| | | newShape: function (latlng) { |
| | | const shape = this.addNewEmptyShape() |
| | | if (!shape) return |
| | | this.setDrawnLatLngs(shape[0] || shape) // Polygon or polyline |
| | | this.startDrawingForward() |
| | | // 🍂namespace Editable |
| | | // 🍂section Shape events |
| | | // 🍂event editable:shape:new: ShapeEvent |
| | | // Fired when a new shape is created in a multi (Polygon or Polyline). |
| | | this.fireAndForward('editable:shape:new', { shape: shape }) |
| | | if (latlng) this.newPointForward(latlng) |
| | | }, |
| | | |
| | | deleteShape: function (shape, latlngs) { |
| | | const e = { shape: shape } |
| | | L.Editable.makeCancellable(e) |
| | | // 🍂namespace Editable |
| | | // 🍂section Shape events |
| | | // 🍂event editable:shape:delete: CancelableShapeEvent |
| | | // Fired before a new shape is deleted in a multi (Polygon or Polyline). |
| | | this.fireAndForward('editable:shape:delete', e) |
| | | if (e._cancelled) return |
| | | shape = this._deleteShape(shape, latlngs) |
| | | if (this.ensureNotFlat) this.ensureNotFlat() // Polygon. |
| | | this.feature.setLatLngs(this.getLatLngs()) // Force bounds reset. |
| | | this.refresh() |
| | | this.reset() |
| | | // 🍂namespace Editable |
| | | // 🍂section Shape events |
| | | // 🍂event editable:shape:deleted: ShapeEvent |
| | | // Fired after a new shape is deleted in a multi (Polygon or Polyline). |
| | | this.fireAndForward('editable:shape:deleted', { shape: shape }) |
| | | return shape |
| | | }, |
| | | |
| | | _deleteShape: function (shape, latlngs) { |
| | | latlngs = latlngs || this.getLatLngs() |
| | | if (!latlngs.length) return |
| | | const self = this |
| | | const inplaceDelete = function (latlngs, shape) { |
| | | // Called when deleting a flat latlngs |
| | | shape = latlngs.splice(0, Number.MAX_VALUE) |
| | | return shape |
| | | } |
| | | const spliceDelete = function (latlngs, shape) { |
| | | // Called when removing a latlngs inside an array |
| | | latlngs.splice(latlngs.indexOf(shape), 1) |
| | | if (!latlngs.length) self._deleteShape(latlngs) |
| | | return shape |
| | | } |
| | | if (latlngs === shape) return inplaceDelete(latlngs, shape) |
| | | for (let i = 0; i < latlngs.length; i++) { |
| | | if (latlngs[i] === shape) return spliceDelete(latlngs, shape) |
| | | else if (latlngs[i].indexOf(shape) !== -1) return spliceDelete(latlngs[i], shape) |
| | | } |
| | | }, |
| | | |
| | | // 🍂namespace PathEditor |
| | | // 🍂method deleteShapeAt(latlng: L.LatLng): Array |
| | | // Remove a path shape at the given `latlng`. |
| | | deleteShapeAt: function (latlng) { |
| | | const shape = this.feature.shapeAt(latlng) |
| | | if (shape) return this.deleteShape(shape) |
| | | }, |
| | | |
| | | // 🍂method appendShape(shape: Array) |
| | | // Append a new shape to the Polygon or Polyline. |
| | | appendShape: function (shape) { |
| | | this.insertShape(shape) |
| | | }, |
| | | |
| | | // 🍂method prependShape(shape: Array) |
| | | // Prepend a new shape to the Polygon or Polyline. |
| | | prependShape: function (shape) { |
| | | this.insertShape(shape, 0) |
| | | }, |
| | | |
| | | // 🍂method insertShape(shape: Array, index: int) |
| | | // Insert a new shape to the Polygon or Polyline at given index (default is to append). |
| | | insertShape: function (shape, index) { |
| | | this.ensureMulti() |
| | | shape = this.formatShape(shape) |
| | | if (typeof index === 'undefined') index = this.feature._latlngs.length |
| | | this.feature._latlngs.splice(index, 0, shape) |
| | | this.feature.redraw() |
| | | if (this._enabled) this.reset() |
| | | }, |
| | | |
| | | extendBounds: function (e) { |
| | | this.feature._bounds.extend(e.vertex.latlng) |
| | | }, |
| | | |
| | | onDragStart: function (e) { |
| | | this.editLayer.clearLayers() |
| | | L.Editable.BaseEditor.prototype.onDragStart.call(this, e) |
| | | }, |
| | | |
| | | onDragEnd: function (e) { |
| | | this.initVertexMarkers() |
| | | L.Editable.BaseEditor.prototype.onDragEnd.call(this, e) |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 🍂namespace Editable; 🍂class PolylineEditor; 🍂aka L.Editable.PolylineEditor |
| | | // 🍂inherits PathEditor |
| | | L.Editable.PolylineEditor = L.Editable.PathEditor.extend({ |
| | | |
| | | startDrawingBackward: function () { |
| | | this._drawing = L.Editable.BACKWARD |
| | | this.startDrawing() |
| | | }, |
| | | |
| | | // 🍂method continueBackward(latlngs?: Array) |
| | | // Set up drawing tools to continue the line backward. |
| | | continueBackward: function (latlngs) { |
| | | if (this.drawing()) return |
| | | latlngs = latlngs || this.getDefaultLatLngs() |
| | | this.setDrawnLatLngs(latlngs) |
| | | if (latlngs.length > 0) { |
| | | this.tools.attachBackwardLineGuide() |
| | | this.tools.anchorBackwardLineGuide(latlngs[0]) |
| | | } |
| | | this.startDrawingBackward() |
| | | }, |
| | | |
| | | // 🍂method continueForward(latlngs?: Array) |
| | | // Set up drawing tools to continue the line forward. |
| | | continueForward: function (latlngs) { |
| | | if (this.drawing()) return |
| | | latlngs = latlngs || this.getDefaultLatLngs() |
| | | this.setDrawnLatLngs(latlngs) |
| | | if (latlngs.length > 0) { |
| | | this.tools.attachForwardLineGuide() |
| | | this.tools.anchorForwardLineGuide(latlngs[latlngs.length - 1]) |
| | | } |
| | | this.startDrawingForward() |
| | | }, |
| | | |
| | | getDefaultLatLngs: function (latlngs) { |
| | | latlngs = latlngs || this.feature._latlngs |
| | | if (!latlngs.length || latlngs[0] instanceof L.LatLng) return latlngs |
| | | else return this.getDefaultLatLngs(latlngs[0]) |
| | | }, |
| | | |
| | | ensureMulti: function () { |
| | | if (this.feature._latlngs.length && isFlat(this.feature._latlngs)) { |
| | | this.feature._latlngs = [this.feature._latlngs] |
| | | } |
| | | }, |
| | | |
| | | addNewEmptyShape: function () { |
| | | if (this.feature._latlngs.length) { |
| | | const shape = [] |
| | | this.appendShape(shape) |
| | | return shape |
| | | } else { |
| | | return this.feature._latlngs |
| | | } |
| | | }, |
| | | |
| | | formatShape: function (shape) { |
| | | if (isFlat(shape)) return shape |
| | | else if (shape[0]) return this.formatShape(shape[0]) |
| | | }, |
| | | |
| | | // 🍂method splitShape(latlngs?: Array, index: int) |
| | | // Split the given `latlngs` shape at index `index` and integrate new shape in instance `latlngs`. |
| | | splitShape: function (shape, index) { |
| | | if (!index || index >= shape.length - 1) return |
| | | this.ensureMulti() |
| | | const shapeIndex = this.feature._latlngs.indexOf(shape) |
| | | if (shapeIndex === -1) return |
| | | const first = shape.slice(0, index + 1) |
| | | const second = shape.slice(index) |
| | | // We deal with reference, we don't want twice the same latlng around. |
| | | second[0] = L.latLng(second[0].lat, second[0].lng, second[0].alt) |
| | | this.feature._latlngs.splice(shapeIndex, 1, first, second) |
| | | this.refresh() |
| | | this.reset() |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 🍂namespace Editable; 🍂class PolygonEditor; 🍂aka L.Editable.PolygonEditor |
| | | // 🍂inherits PathEditor |
| | | L.Editable.PolygonEditor = L.Editable.PathEditor.extend({ |
| | | |
| | | CLOSED: true, |
| | | MIN_VERTEX: 3, |
| | | |
| | | newPointForward: function (latlng) { |
| | | L.Editable.PathEditor.prototype.newPointForward.call(this, latlng) |
| | | if (!this.tools.backwardLineGuide._latlngs.length) this.tools.anchorBackwardLineGuide(latlng) |
| | | if (this._drawnLatLngs.length === 2) this.tools.attachBackwardLineGuide() |
| | | }, |
| | | |
| | | addNewEmptyHole: function (latlng) { |
| | | this.ensureNotFlat() |
| | | const latlngs = this.feature.shapeAt(latlng) |
| | | if (!latlngs) return |
| | | const holes = [] |
| | | latlngs.push(holes) |
| | | return holes |
| | | }, |
| | | |
| | | // 🍂method newHole(latlng?: L.LatLng, index: int) |
| | | // Set up drawing tools for creating a new hole on the Polygon. If the `latlng` param is given, a first point is created. |
| | | newHole: function (latlng) { |
| | | const holes = this.addNewEmptyHole(latlng) |
| | | if (!holes) return |
| | | this.setDrawnLatLngs(holes) |
| | | this.startDrawingForward() |
| | | if (latlng) this.newPointForward(latlng) |
| | | }, |
| | | |
| | | addNewEmptyShape: function () { |
| | | if (this.feature._latlngs.length && this.feature._latlngs[0].length) { |
| | | const shape = [] |
| | | this.appendShape(shape) |
| | | return shape |
| | | } else { |
| | | return this.feature._latlngs |
| | | } |
| | | }, |
| | | |
| | | ensureMulti: function () { |
| | | if (this.feature._latlngs.length && isFlat(this.feature._latlngs[0])) { |
| | | this.feature._latlngs = [this.feature._latlngs] |
| | | } |
| | | }, |
| | | |
| | | ensureNotFlat: function () { |
| | | if (!this.feature._latlngs.length || isFlat(this.feature._latlngs)) this.feature._latlngs = [this.feature._latlngs] |
| | | }, |
| | | |
| | | vertexCanBeDeleted: function (vertex) { |
| | | const parent = this.feature.parentShape(vertex.latlngs) |
| | | const idx = L.Util.indexOf(parent, vertex.latlngs) |
| | | if (idx > 0) return true // Holes can be totally deleted without removing the layer itself. |
| | | return L.Editable.PathEditor.prototype.vertexCanBeDeleted.call(this, vertex) |
| | | }, |
| | | |
| | | getDefaultLatLngs: function () { |
| | | if (!this.feature._latlngs.length) this.feature._latlngs.push([]) |
| | | return this.feature._latlngs[0] |
| | | }, |
| | | |
| | | formatShape: function (shape) { |
| | | // [[1, 2], [3, 4]] => must be nested |
| | | // [] => must be nested |
| | | // [[]] => is already nested |
| | | if (isFlat(shape) && (!shape[0] || shape[0].length !== 0)) return [shape] |
| | | else return shape |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 🍂namespace Editable; 🍂class RectangleEditor; 🍂aka L.Editable.RectangleEditor |
| | | // 🍂inherits PathEditor |
| | | L.Editable.RectangleEditor = L.Editable.PathEditor.extend({ |
| | | |
| | | CLOSED: true, |
| | | MIN_VERTEX: 4, |
| | | |
| | | options: { |
| | | skipMiddleMarkers: true |
| | | }, |
| | | |
| | | extendBounds: function (e) { |
| | | const index = e.vertex.getIndex() |
| | | const next = e.vertex.getNext() |
| | | const previous = e.vertex.getPrevious() |
| | | const oppositeIndex = (index + 2) % 4 |
| | | const opposite = e.vertex.latlngs[oppositeIndex] |
| | | const bounds = new L.LatLngBounds(e.latlng, opposite) |
| | | // Update latlngs by hand to preserve order. |
| | | previous.latlng.update([e.latlng.lat, opposite.lng]) |
| | | next.latlng.update([opposite.lat, e.latlng.lng]) |
| | | this.updateBounds(bounds) |
| | | this.refreshVertexMarkers() |
| | | }, |
| | | |
| | | onDrawingMouseDown: function (e) { |
| | | L.Editable.PathEditor.prototype.onDrawingMouseDown.call(this, e) |
| | | this.connect() |
| | | const latlngs = this.getDefaultLatLngs() |
| | | // L.Polygon._convertLatLngs removes last latlng if it equals first point, |
| | | // which is the case here as all latlngs are [0, 0] |
| | | if (latlngs.length === 3) latlngs.push(e.latlng) |
| | | const bounds = new L.LatLngBounds(e.latlng, e.latlng) |
| | | this.updateBounds(bounds) |
| | | this.updateLatLngs(bounds) |
| | | this.refresh() |
| | | this.reset() |
| | | // Stop dragging map. |
| | | // L.Draggable has two workflows: |
| | | // - mousedown => mousemove => mouseup |
| | | // - touchstart => touchmove => touchend |
| | | // Problem: L.Map.Tap does not allow us to listen to touchstart, so we only |
| | | // can deal with mousedown, but then when in a touch device, we are dealing with |
| | | // simulated events (actually simulated by L.Map.Tap), which are no more taken |
| | | // into account by L.Draggable. |
| | | // Ref.: https://github.com/Leaflet/Leaflet.Editable/issues/103 |
| | | e.originalEvent._simulated = false |
| | | this.map.dragging._draggable._onUp(e.originalEvent) |
| | | // Now transfer ongoing drag action to the bottom right corner. |
| | | // Should we refine which corner will handle the drag according to |
| | | // drag direction? |
| | | latlngs[3].__vertex.dragging._draggable._onDown(e.originalEvent) |
| | | }, |
| | | |
| | | onDrawingMouseUp: function (e) { |
| | | this.commitDrawing(e) |
| | | e.originalEvent._simulated = false |
| | | L.Editable.PathEditor.prototype.onDrawingMouseUp.call(this, e) |
| | | }, |
| | | |
| | | onDrawingMouseMove: function (e) { |
| | | e.originalEvent._simulated = false |
| | | L.Editable.PathEditor.prototype.onDrawingMouseMove.call(this, e) |
| | | }, |
| | | |
| | | getDefaultLatLngs: function (latlngs) { |
| | | return latlngs || this.feature._latlngs[0] |
| | | }, |
| | | |
| | | updateBounds: function (bounds) { |
| | | this.feature._bounds = bounds |
| | | }, |
| | | |
| | | updateLatLngs: function (bounds) { |
| | | const latlngs = this.getDefaultLatLngs() |
| | | const newLatlngs = this.feature._boundsToLatLngs(bounds) |
| | | // Keep references. |
| | | for (let i = 0; i < latlngs.length; i++) { |
| | | latlngs[i].update(newLatlngs[i]) |
| | | } |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 🍂namespace Editable; 🍂class CircleEditor; 🍂aka L.Editable.CircleEditor |
| | | // 🍂inherits PathEditor |
| | | L.Editable.CircleEditor = L.Editable.PathEditor.extend({ |
| | | |
| | | MIN_VERTEX: 2, |
| | | |
| | | options: { |
| | | skipMiddleMarkers: true |
| | | }, |
| | | |
| | | initialize: function (map, feature, options) { |
| | | L.Editable.PathEditor.prototype.initialize.call(this, map, feature, options) |
| | | // C.Y.B Modify |
| | | |
| | | const latlng = this.computeResizeLatLng() |
| | | |
| | | // latlng.lat = latlng.lat-0.007855; |
| | | // latlng.lng= latlng.lng-0.1; |
| | | this._resizeLatLng = latlng |
| | | // 原始方法 |
| | | // this._resizeLatLng = this.computeResizeLatLng(); |
| | | }, |
| | | |
| | | computeResizeLatLng: function () { |
| | | // While circle is not added to the map, _radius is not set. |
| | | // let delta = (this.feature._radius || this.feature._mRadius) * Math.cos(Math.PI / 4) |
| | | const delta = (this.feature._radius || this.feature._mRadius) |
| | | const point = this.map.project(this.feature._latlng) |
| | | // return this.map.unproject([point.x + delta, point.y - delta]) |
| | | return this.map.unproject([point.x + delta, point.y]) |
| | | }, |
| | | |
| | | updateResizeLatLng: function () { |
| | | this._resizeLatLng.update(this.computeResizeLatLng()) |
| | | this._resizeLatLng.__vertex.update() |
| | | }, |
| | | |
| | | getLatLngs: function () { |
| | | return [this.feature._latlng, this._resizeLatLng] |
| | | }, |
| | | |
| | | getDefaultLatLngs: function () { |
| | | return this.getLatLngs() |
| | | }, |
| | | |
| | | onVertexMarkerDrag: function (e) { |
| | | if (e.vertex.getIndex() === 1) this.resize(e) |
| | | else this.updateResizeLatLng(e) |
| | | L.Editable.PathEditor.prototype.onVertexMarkerDrag.call(this, e) |
| | | }, |
| | | |
| | | resize: function (e) { |
| | | const radius = this.feature._latlng.distanceTo(e.latlng) |
| | | this.feature.setRadius(radius) |
| | | }, |
| | | |
| | | onDrawingMouseDown: function (e) { |
| | | L.Editable.PathEditor.prototype.onDrawingMouseDown.call(this, e) |
| | | this._resizeLatLng.update(e.latlng) |
| | | this.feature._latlng.update(e.latlng) |
| | | this.connect() |
| | | // Stop dragging map. |
| | | e.originalEvent._simulated = false |
| | | this.map.dragging._draggable._onUp(e.originalEvent) |
| | | // Now transfer ongoing drag action to the radius handler. |
| | | this._resizeLatLng.__vertex.dragging._draggable._onDown(e.originalEvent) |
| | | }, |
| | | |
| | | onDrawingMouseUp: function (e) { |
| | | this.commitDrawing(e) |
| | | e.originalEvent._simulated = false |
| | | L.Editable.PathEditor.prototype.onDrawingMouseUp.call(this, e) |
| | | }, |
| | | |
| | | onDrawingMouseMove: function (e) { |
| | | e.originalEvent._simulated = false |
| | | L.Editable.PathEditor.prototype.onDrawingMouseMove.call(this, e) |
| | | }, |
| | | |
| | | onDrag: function (e) { |
| | | L.Editable.PathEditor.prototype.onDrag.call(this, e) |
| | | this.feature.dragging.updateLatLng(this._resizeLatLng) |
| | | } |
| | | |
| | | }) |
| | | |
| | | // 🍂namespace Editable; 🍂class EditableMixin |
| | | // `EditableMixin` is included to `L.Polyline`, `L.Polygon`, `L.Rectangle`, `L.Circle` |
| | | // and `L.Marker`. It adds some methods to them. |
| | | // *When editing is enabled, the editor is accessible on the instance with the |
| | | // `editor` property.* |
| | | const EditableMixin = { |
| | | |
| | | createEditor: function (map) { |
| | | map = map || this._map |
| | | const tools = (this.options.editOptions || {}).editTools || map.editTools |
| | | if (!tools) throw Error('Unable to detect Editable instance.') |
| | | const Klass = this.options.editorClass || this.getEditorClass(tools) |
| | | return new Klass(map, this, this.options.editOptions) |
| | | }, |
| | | |
| | | // 🍂method enableEdit(map?: L.Map): this.editor |
| | | // Enable editing, by creating an editor if not existing, and then calling `enable` on it. |
| | | enableEdit: function (map) { |
| | | if (!this.editor) this.createEditor(map) |
| | | this.editor.enable() |
| | | return this.editor |
| | | }, |
| | | |
| | | // 🍂method editEnabled(): boolean |
| | | // Return true if current instance has an editor attached, and this editor is enabled. |
| | | editEnabled: function () { |
| | | return this.editor && this.editor.enabled() |
| | | }, |
| | | |
| | | // 🍂method disableEdit() |
| | | // Disable editing, also remove the editor property reference. |
| | | disableEdit: function () { |
| | | if (this.editor) { |
| | | this.editor.disable() |
| | | delete this.editor |
| | | } |
| | | }, |
| | | |
| | | // 🍂method toggleEdit() |
| | | // Enable or disable editing, according to current status. |
| | | toggleEdit: function () { |
| | | if (this.editEnabled()) this.disableEdit() |
| | | else this.enableEdit() |
| | | }, |
| | | |
| | | _onEditableAdd: function () { |
| | | if (this.editor) this.enableEdit() |
| | | } |
| | | |
| | | } |
| | | |
| | | const PolylineMixin = { |
| | | |
| | | getEditorClass: function (tools) { |
| | | return (tools && tools.options.polylineEditorClass) ? tools.options.polylineEditorClass : L.Editable.PolylineEditor |
| | | }, |
| | | |
| | | shapeAt: function (latlng, latlngs) { |
| | | // We can have those cases: |
| | | // - latlngs are just a flat array of latlngs, use this |
| | | // - latlngs is an array of arrays of latlngs, loop over |
| | | let shape = null |
| | | latlngs = latlngs || this._latlngs |
| | | if (!latlngs.length) return shape |
| | | else if (isFlat(latlngs) && this.isInLatLngs(latlng, latlngs)) shape = latlngs |
| | | else for (let i = 0; i < latlngs.length; i++) if (this.isInLatLngs(latlng, latlngs[i])) return latlngs[i] |
| | | return shape |
| | | }, |
| | | |
| | | isInLatLngs: function (l, latlngs) { |
| | | if (!latlngs) return false |
| | | let i, k, len |
| | | let part = [] |
| | | const w = this._clickTolerance() |
| | | this._projectLatlngs(latlngs, part, this._pxBounds) |
| | | part = part[0] |
| | | const p = this._map.latLngToLayerPoint(l) |
| | | |
| | | if (!this._pxBounds.contains(p)) { |
| | | return false |
| | | } |
| | | for (i = 1, len = part.length, k = 0; i < len; k = i++) { |
| | | if (L.LineUtil.pointToSegmentDistance(p, part[k], part[i]) <= w) { |
| | | return true |
| | | } |
| | | } |
| | | return false |
| | | } |
| | | |
| | | } |
| | | |
| | | const PolygonMixin = { |
| | | |
| | | getEditorClass: function (tools) { |
| | | return (tools && tools.options.polygonEditorClass) ? tools.options.polygonEditorClass : L.Editable.PolygonEditor |
| | | }, |
| | | |
| | | shapeAt: function (latlng, latlngs) { |
| | | // We can have those cases: |
| | | // - latlngs are just a flat array of latlngs, use this |
| | | // - latlngs is an array of arrays of latlngs, this is a simple polygon (maybe with holes), use the first |
| | | // - latlngs is an array of arrays of arrays, this is a multi, loop over |
| | | let shape = null |
| | | latlngs = latlngs || this._latlngs |
| | | if (!latlngs.length) return shape |
| | | else if (isFlat(latlngs) && this.isInLatLngs(latlng, latlngs)) shape = latlngs |
| | | else if (isFlat(latlngs[0]) && this.isInLatLngs(latlng, latlngs[0])) shape = latlngs |
| | | else for (let i = 0; i < latlngs.length; i++) if (this.isInLatLngs(latlng, latlngs[i][0])) return latlngs[i] |
| | | return shape |
| | | }, |
| | | |
| | | isInLatLngs: function (l, latlngs) { |
| | | let inside = false |
| | | let l1 |
| | | let l2 |
| | | let j |
| | | let k |
| | | let len2 |
| | | for (j = 0, len2 = latlngs.length, k = len2 - 1; j < len2; k = j++) { |
| | | l1 = latlngs[j] |
| | | l2 = latlngs[k] |
| | | |
| | | if (((l1.lat > l.lat) !== (l2.lat > l.lat)) && |
| | | (l.lng < (l2.lng - l1.lng) * (l.lat - l1.lat) / (l2.lat - l1.lat) + l1.lng)) { |
| | | inside = !inside |
| | | } |
| | | } |
| | | |
| | | return inside |
| | | }, |
| | | |
| | | parentShape: function(shape, latlngs) { |
| | | latlngs = latlngs || this._latlngs |
| | | if (!latlngs) return |
| | | let idx = L.Util.indexOf(latlngs, shape) |
| | | if (idx !== -1) return latlngs |
| | | for (let i = 0; i < latlngs.length; i++) { |
| | | idx = L.Util.indexOf(latlngs[i], shape) |
| | | if (idx !== -1) return latlngs[i] |
| | | } |
| | | } |
| | | |
| | | inside = !inside |
| | | } |
| | | } |
| | | |
| | | let MarkerMixin = { |
| | | return inside |
| | | }, |
| | | |
| | | getEditorClass: function(tools) { |
| | | return (tools && tools.options.markerEditorClass) ? tools.options.markerEditorClass : L.Editable.MarkerEditor |
| | | } |
| | | |
| | | parentShape: function (shape, latlngs) { |
| | | latlngs = latlngs || this._latlngs |
| | | if (!latlngs) return |
| | | let idx = L.Util.indexOf(latlngs, shape) |
| | | if (idx !== -1) return latlngs |
| | | for (let i = 0; i < latlngs.length; i++) { |
| | | idx = L.Util.indexOf(latlngs[i], shape) |
| | | if (idx !== -1) return latlngs[i] |
| | | } |
| | | } |
| | | |
| | | let RectangleMixin = { |
| | | } |
| | | |
| | | getEditorClass: function(tools) { |
| | | return (tools && tools.options.rectangleEditorClass) ? tools.options.rectangleEditorClass : L.Editable.RectangleEditor |
| | | } |
| | | const MarkerMixin = { |
| | | |
| | | } |
| | | getEditorClass: function (tools) { |
| | | return (tools && tools.options.markerEditorClass) ? tools.options.markerEditorClass : L.Editable.MarkerEditor |
| | | } |
| | | |
| | | let CircleMixin = { |
| | | } |
| | | |
| | | getEditorClass: function(tools) { |
| | | return (tools && tools.options.circleEditorClass) ? tools.options.circleEditorClass : L.Editable.CircleEditor |
| | | } |
| | | const RectangleMixin = { |
| | | |
| | | } |
| | | getEditorClass: function (tools) { |
| | | return (tools && tools.options.rectangleEditorClass) ? tools.options.rectangleEditorClass : L.Editable.RectangleEditor |
| | | } |
| | | |
| | | let keepEditable = function() { |
| | | // Make sure you can remove/readd an editable layer. |
| | | this.on('add', this._onEditableAdd) |
| | | } |
| | | } |
| | | |
| | | let isFlat = L.LineUtil.isFlat || L.LineUtil._flat || L.Polyline._flat // <=> 1.1 compat. |
| | | const CircleMixin = { |
| | | |
| | | if (L.Polyline) { |
| | | L.Polyline.include(EditableMixin) |
| | | L.Polyline.include(PolylineMixin) |
| | | L.Polyline.addInitHook(keepEditable) |
| | | } |
| | | if (L.Polygon) { |
| | | L.Polygon.include(EditableMixin) |
| | | L.Polygon.include(PolygonMixin) |
| | | } |
| | | if (L.Marker) { |
| | | L.Marker.include(EditableMixin) |
| | | L.Marker.include(MarkerMixin) |
| | | L.Marker.addInitHook(keepEditable) |
| | | } |
| | | if (L.Rectangle) { |
| | | L.Rectangle.include(EditableMixin) |
| | | L.Rectangle.include(RectangleMixin) |
| | | } |
| | | if (L.Circle) { |
| | | L.Circle.include(EditableMixin) |
| | | L.Circle.include(CircleMixin) |
| | | } |
| | | getEditorClass: function (tools) { |
| | | return (tools && tools.options.circleEditorClass) ? tools.options.circleEditorClass : L.Editable.CircleEditor |
| | | } |
| | | |
| | | L.LatLng.prototype.update = function(latlng) { |
| | | latlng = L.latLng(latlng) |
| | | this.lat = latlng.lat |
| | | this.lng = latlng.lng |
| | | } |
| | | }, window)) |
| | | } |
| | | |
| | | const keepEditable = function () { |
| | | // Make sure you can remove/readd an editable layer. |
| | | this.on('add', this._onEditableAdd) |
| | | } |
| | | |
| | | const isFlat = L.LineUtil.isFlat || L.LineUtil._flat || L.Polyline._flat // <=> 1.1 compat. |
| | | |
| | | if (L.Polyline) { |
| | | L.Polyline.include(EditableMixin) |
| | | L.Polyline.include(PolylineMixin) |
| | | L.Polyline.addInitHook(keepEditable) |
| | | } |
| | | if (L.Polygon) { |
| | | L.Polygon.include(EditableMixin) |
| | | L.Polygon.include(PolygonMixin) |
| | | } |
| | | if (L.Marker) { |
| | | L.Marker.include(EditableMixin) |
| | | L.Marker.include(MarkerMixin) |
| | | L.Marker.addInitHook(keepEditable) |
| | | } |
| | | if (L.Rectangle) { |
| | | L.Rectangle.include(EditableMixin) |
| | | L.Rectangle.include(RectangleMixin) |
| | | } |
| | | if (L.Circle) { |
| | | L.Circle.include(EditableMixin) |
| | | L.Circle.include(CircleMixin) |
| | | } |
| | | |
| | | L.LatLng.prototype.update = function (latlng) { |
| | | latlng = L.latLng(latlng) |
| | | this.lat = latlng.lat |
| | | this.lng = latlng.lng |
| | | } |
| | | }, window)) |
| | | } |
| | | |
| | | export default { |
| | | init |
| | | init |
| | | } |