派生自 wuyushui/SewerAndRainNetwork

YANGDL
2021-01-05 1820aef3fb5c926664de1d4d484f64a5c9ba7099
优化逻辑
9个文件已修改
9811 ■■■■ 已修改文件
src/components/plugin/CanvasMarkers.js 744 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/plugin/CustomPopup.js 126 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/plugin/Editable.js 3845 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/plugin/MagicMarker.js 78 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/plugin/PathDashFlow.js 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/plugin/PathDrag.js 262 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/plugin/cluster-layer/leaflet.markercluster-src.js 4503 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/plugin/wmts_plugins.js 172 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/tools.js 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/plugin/CanvasMarkers.js
@@ -1,386 +1,386 @@
'use strict'
import Rbush from 'rbush'
function init(L) {
    var CanvasIconLayer = (L.Layer ? L.Layer : L.Class).extend({
function init (L) {
  var CanvasIconLayer = (L.Layer ? L.Layer : L.Class).extend({
        // Add event listeners to initialized section.
        initialize: function(options) {
            L.setOptions(this, options)
            this._onClickListeners = []
            this._onHoverListeners = []
        },
    // Add event listeners to initialized section.
    initialize: function (options) {
      L.setOptions(this, options)
      this._onClickListeners = []
      this._onHoverListeners = []
    },
        setOptions: function(options) {
            L.setOptions(this, options)
            return this.redraw()
        },
        redraw: function() {
            this._redraw(true)
        },
    setOptions: function (options) {
      L.setOptions(this, options)
      return this.redraw()
    },
    redraw: function () {
      this._redraw(true)
    },
        // Multiple layers at a time for rBush performance
        addMarkers: function(markers) {
            var self = this
            var tmpMark = []
            var tmpLatLng = []
    // Multiple layers at a time for rBush performance
    addMarkers: function (markers) {
      var self = this
      var tmpMark = []
      var tmpLatLng = []
            markers.forEach(function(marker) {
                if (!((marker.options.pane === 'markerPane') && marker.options.icon)) {
                    console.error('Layer isn\'t a marker')
                    return
                }
                var latlng = marker.getLatLng()
                var isDisplaying = self._map.getBounds().contains(latlng)
                var s = self._addMarker(marker, latlng, isDisplaying)
                // Only add to Point Lookup if we are on map
                if (isDisplaying === true) tmpMark.push(s[0])
                tmpLatLng.push(s[1])
            })
            self._markers.load(tmpMark)
            self._latlngMarkers.load(tmpLatLng)
        },
        // Adds single layer at a time. Less efficient for rBush
        addMarker: function(marker) {
            var self = this
            var latlng = marker.getLatLng()
            var isDisplaying = self._map.getBounds().contains(latlng)
            var dat = self._addMarker(marker, latlng, isDisplaying)
            // Only add to Point Lookup if we are on map
            if (isDisplaying === true) self._markers.insert(dat[0])
            self._latlngMarkers.insert(dat[1])
        },
        addLayer: function(layer) {
            if ((layer.options.pane === 'markerPane') && layer.options.icon) this.addMarker(layer)
            else console.error('Layer isn\'t a marker')
        },
        addLayers: function(layers) {
            this.addMarkers(layers)
        },
        removeLayer: function(layer) {
            this.removeMarker(layer, true)
        },
        removeMarker: function(marker, redraw) {
            var self = this
            // If we are removed point
            if (marker['minX']) marker = marker.data
            var latlng = marker.getLatLng()
            var isDisplaying = self._map.getBounds().contains(latlng)
            var markerData = {
                minX: latlng.lng,
                minY: latlng.lat,
                maxX: latlng.lng,
                maxY: latlng.lat,
                data: marker
            }
            self._latlngMarkers.remove(markerData, function(a, b) {
                return a.data._leaflet_id === b.data._leaflet_id
            })
            self._latlngMarkers.total--
            self._latlngMarkers.dirty++
            if (isDisplaying === true && redraw === true) {
                self._redraw(true)
            }
        },
        onAdd: function(map) {
            this._map = map
            if (!this._canvas) this._initCanvas()
            if (this.options.pane) this.getPane().appendChild(this._canvas)
            else map._panes.overlayPane.appendChild(this._canvas)
            map.on('moveend', this._reset, this)
            map.on('resize', this._reset, this)
            map.on('click', this._executeListeners, this)
            map.on('mousemove', this._executeListeners, this)
            map.on('zoomstart', this._canvasHide, this)
            map.on('zoomend', this._canvasShow, this)
        },
        onRemove: function(map) {
            if (this.options.pane) this.getPane().removeChild(this._canvas)
            else map.getPanes().overlayPane.removeChild(this._canvas)
            map.off('click', this._executeListeners, this)
            map.off('mousemove', this._executeListeners, this)
            map.off('moveend', this._reset, this)
            map.off('resize', this._reset, this)
            map.off('zoomstart', this._canvasHide, this)
            map.off('zoomend', this._canvasShow, this)
        },
        addTo: function(map) {
            map.addLayer(this)
            return this
        },
        clearLayers: function() {
            this._latlngMarkers = null
            this._markers = null
            this._redraw(true)
        },
        _canvasHide: function() {
            this._canvas.style.visibility = 'hidden'
        },
        _canvasShow: function() {
            this._canvas.style.visibility = 'visible'
        },
        _addMarker: function(marker, latlng, isDisplaying) {
            var self = this
            // Needed for pop-up & tooltip to work.
            marker._map = self._map
            // _markers contains Points of markers currently displaying on map
            if (!self._markers) self._markers = new Rbush()
            // _latlngMarkers contains Lat\Long coordinates of all markers in layer.
            if (!self._latlngMarkers) {
                self._latlngMarkers = new Rbush()
                self._latlngMarkers.dirty = 0
                self._latlngMarkers.total = 0
            }
            L.Util.stamp(marker)
            var pointPos = self._map.latLngToContainerPoint(latlng)
            var iconSize = marker.options.icon.options.iconSize
            var adjX = iconSize[0] / 2
            var adjY = iconSize[1] / 2
            var ret = [({
                minX: (pointPos.x - adjX),
                minY: (pointPos.y - adjY),
                maxX: (pointPos.x + adjX),
                maxY: (pointPos.y + adjY),
                data: marker
            }), ({
                minX: latlng.lng,
                minY: latlng.lat,
                maxX: latlng.lng,
                maxY: latlng.lat,
                data: marker
            })]
            self._latlngMarkers.dirty++
            self._latlngMarkers.total++
            // Only draw if we are on map
            if (isDisplaying === true) self._drawMarker(marker, pointPos)
            return ret
        },
        _drawMarker: function(marker, pointPos) {
            var self = this
            if (!this._imageLookup) this._imageLookup = {}
            if (!pointPos) {
                pointPos = self._map.latLngToContainerPoint(marker.getLatLng())
            }
            var iconUrl = marker.options.icon.options.iconUrl
            if (marker.canvas_img) {
                self._drawImage(marker, pointPos)
            } else {
                if (self._imageLookup[iconUrl]) {
                    marker.canvas_img = self._imageLookup[iconUrl][0]
                    if (self._imageLookup[iconUrl][1] === false) {
                        self._imageLookup[iconUrl][2].push([marker, pointPos])
                    } else {
                        self._drawImage(marker, pointPos)
                    }
                } else {
                    var i = new Image()
                    i.src = iconUrl
                    marker.canvas_img = i
                    // Image,isLoaded,marker\pointPos ref
                    self._imageLookup[iconUrl] = [i, false, [[marker, pointPos]]]
                    i.onload = function() {
                        self._imageLookup[iconUrl][1] = true
                        self._imageLookup[iconUrl][2].forEach(function(e) {
                            self._drawImage(e[0], e[1])
                        })
                    }
                }
            }
        },
        _drawImage: function(marker, pointPos) {
            var options = marker.options.icon.options
            this._context.drawImage(
                marker.canvas_img,
                pointPos.x - options.iconAnchor[0],
                pointPos.y - options.iconAnchor[1],
                options.iconSize[0],
                options.iconSize[1]
            )
        },
        _reset: function() {
            var topLeft = this._map.containerPointToLayerPoint([0, 0])
            L.DomUtil.setPosition(this._canvas, topLeft)
            var size = this._map.getSize()
            this._canvas.width = size.x
            this._canvas.height = size.y
            this._redraw()
        },
        _redraw: function(clear) {
            var self = this
            if (clear) this._context.clearRect(0, 0, this._canvas.width, this._canvas.height)
            if (!this._map || !this._latlngMarkers) return
            var tmp = []
            // If we are 10% individual inserts\removals, reconstruct lookup for efficiency
            if (self._latlngMarkers.dirty / self._latlngMarkers.total >= 0.1) {
                self._latlngMarkers.all().forEach(function(e) {
                    tmp.push(e)
                })
                self._latlngMarkers.clear()
                self._latlngMarkers.load(tmp)
                self._latlngMarkers.dirty = 0
                tmp = []
            }
            var mapBounds = self._map.getBounds()
            // Only re-draw what we are showing on the map.
            var mapBoxCoords = {
                minX: mapBounds.getWest(),
                minY: mapBounds.getSouth(),
                maxX: mapBounds.getEast(),
                maxY: mapBounds.getNorth()
            }
            self._latlngMarkers.search(mapBoxCoords).forEach(function(e) {
                // Readjust Point Map
                var pointPos = self._map.latLngToContainerPoint(e.data.getLatLng())
                var iconSize = e.data.options.icon.options.iconSize
                var adjX = iconSize[0] / 2
                var adjY = iconSize[1] / 2
                var newCoords = {
                    minX: (pointPos.x - adjX),
                    minY: (pointPos.y - adjY),
                    maxX: (pointPos.x + adjX),
                    maxY: (pointPos.y + adjY),
                    data: e.data
                }
                tmp.push(newCoords)
                // Redraw points
                self._drawMarker(e.data, pointPos)
            })
            // Clear rBush & Bulk Load for performance
            this._markers.clear()
            this._markers.load(tmp)
        },
        _initCanvas: function() {
            this._canvas = L.DomUtil.create('canvas', 'leaflet-canvas-icon-layer leaflet-layer')
            var originProp = L.DomUtil.testProp(['transformOrigin', 'WebkitTransformOrigin', 'msTransformOrigin'])
            this._canvas.style[originProp] = '50% 50%'
            var size = this._map.getSize()
            this._canvas.width = size.x
            this._canvas.height = size.y
            this._context = this._canvas.getContext('2d')
            var animated = this._map.options.zoomAnimation && L.Browser.any3d
            L.DomUtil.addClass(this._canvas, 'leaflet-zoom-' + (animated ? 'animated' : 'hide'))
        },
        addOnClickListener: function(listener) {
            this._onClickListeners.push(listener)
        },
        addOnHoverListener: function(listener) {
            this._onHoverListeners.push(listener)
        },
        _executeListeners: function(event) {
            if (!this._markers) return
            var me = this
            var x = event.containerPoint.x
            var y = event.containerPoint.y
            if (me._openToolTip) {
                me._openToolTip.closeTooltip()
                delete me._openToolTip
            }
            var ret = this._markers.search({ minX: x, minY: y, maxX: x, maxY: y })
            if (ret && ret.length > 0) {
                me._map._container.style.cursor = 'pointer'
                if (event.type === 'click') {
                    var hasPopup = ret[0].data.getPopup()
                    if (hasPopup) ret[0].data.openPopup()
                    me._onClickListeners.forEach(function(listener) { listener(event, ret) })
                }
                if (event.type === 'mousemove') {
                    var hasTooltip = ret[0].data.getTooltip()
                    if (hasTooltip) {
                        me._openToolTip = ret[0].data
                        ret[0].data.openTooltip()
                    }
                    me._onHoverListeners.forEach(function(listener) { listener(event, ret) })
                }
            } else {
                me._map._container.style.cursor = ''
            }
      markers.forEach(function (marker) {
        if (!((marker.options.pane === 'markerPane') && marker.options.icon)) {
          console.error('Layer isn\'t a marker')
          return
        }
    })
    L.canvasIconLayer = function(options) {
        return new CanvasIconLayer(options)
        var latlng = marker.getLatLng()
        var isDisplaying = self._map.getBounds().contains(latlng)
        var s = self._addMarker(marker, latlng, isDisplaying)
        // Only add to Point Lookup if we are on map
        if (isDisplaying === true) tmpMark.push(s[0])
        tmpLatLng.push(s[1])
      })
      self._markers.load(tmpMark)
      self._latlngMarkers.load(tmpLatLng)
    },
    // Adds single layer at a time. Less efficient for rBush
    addMarker: function (marker) {
      var self = this
      var latlng = marker.getLatLng()
      var isDisplaying = self._map.getBounds().contains(latlng)
      var dat = self._addMarker(marker, latlng, isDisplaying)
      // Only add to Point Lookup if we are on map
      if (isDisplaying === true) self._markers.insert(dat[0])
      self._latlngMarkers.insert(dat[1])
    },
    addLayer: function (layer) {
      if ((layer.options.pane === 'markerPane') && layer.options.icon) this.addMarker(layer)
      else console.error('Layer isn\'t a marker')
    },
    addLayers: function (layers) {
      this.addMarkers(layers)
    },
    removeLayer: function (layer) {
      this.removeMarker(layer, true)
    },
    removeMarker: function (marker, redraw) {
      var self = this
      // If we are removed point
      if (marker.minX) marker = marker.data
      var latlng = marker.getLatLng()
      var isDisplaying = self._map.getBounds().contains(latlng)
      var markerData = {
        minX: latlng.lng,
        minY: latlng.lat,
        maxX: latlng.lng,
        maxY: latlng.lat,
        data: marker
      }
      self._latlngMarkers.remove(markerData, function (a, b) {
        return a.data._leaflet_id === b.data._leaflet_id
      })
      self._latlngMarkers.total--
      self._latlngMarkers.dirty++
      if (isDisplaying === true && redraw === true) {
        self._redraw(true)
      }
    },
    onAdd: function (map) {
      this._map = map
      if (!this._canvas) this._initCanvas()
      if (this.options.pane) this.getPane().appendChild(this._canvas)
      else map._panes.overlayPane.appendChild(this._canvas)
      map.on('moveend', this._reset, this)
      map.on('resize', this._reset, this)
      map.on('click', this._executeListeners, this)
      map.on('mousemove', this._executeListeners, this)
      map.on('zoomstart', this._canvasHide, this)
      map.on('zoomend', this._canvasShow, this)
    },
    onRemove: function (map) {
      if (this.options.pane) this.getPane().removeChild(this._canvas)
      else map.getPanes().overlayPane.removeChild(this._canvas)
      map.off('click', this._executeListeners, this)
      map.off('mousemove', this._executeListeners, this)
      map.off('moveend', this._reset, this)
      map.off('resize', this._reset, this)
      map.off('zoomstart', this._canvasHide, this)
      map.off('zoomend', this._canvasShow, this)
    },
    addTo: function (map) {
      map.addLayer(this)
      return this
    },
    clearLayers: function () {
      this._latlngMarkers = null
      this._markers = null
      this._redraw(true)
    },
    _canvasHide: function () {
      this._canvas.style.visibility = 'hidden'
    },
    _canvasShow: function () {
      this._canvas.style.visibility = 'visible'
    },
    _addMarker: function (marker, latlng, isDisplaying) {
      var self = this
      // Needed for pop-up & tooltip to work.
      marker._map = self._map
      // _markers contains Points of markers currently displaying on map
      if (!self._markers) self._markers = new Rbush()
      // _latlngMarkers contains Lat\Long coordinates of all markers in layer.
      if (!self._latlngMarkers) {
        self._latlngMarkers = new Rbush()
        self._latlngMarkers.dirty = 0
        self._latlngMarkers.total = 0
      }
      L.Util.stamp(marker)
      var pointPos = self._map.latLngToContainerPoint(latlng)
      var iconSize = marker.options.icon.options.iconSize
      var adjX = iconSize[0] / 2
      var adjY = iconSize[1] / 2
      var ret = [({
        minX: (pointPos.x - adjX),
        minY: (pointPos.y - adjY),
        maxX: (pointPos.x + adjX),
        maxY: (pointPos.y + adjY),
        data: marker
      }), ({
        minX: latlng.lng,
        minY: latlng.lat,
        maxX: latlng.lng,
        maxY: latlng.lat,
        data: marker
      })]
      self._latlngMarkers.dirty++
      self._latlngMarkers.total++
      // Only draw if we are on map
      if (isDisplaying === true) self._drawMarker(marker, pointPos)
      return ret
    },
    _drawMarker: function (marker, pointPos) {
      var self = this
      if (!this._imageLookup) this._imageLookup = {}
      if (!pointPos) {
        pointPos = self._map.latLngToContainerPoint(marker.getLatLng())
      }
      var iconUrl = marker.options.icon.options.iconUrl
      if (marker.canvas_img) {
        self._drawImage(marker, pointPos)
      } else {
        if (self._imageLookup[iconUrl]) {
          marker.canvas_img = self._imageLookup[iconUrl][0]
          if (self._imageLookup[iconUrl][1] === false) {
            self._imageLookup[iconUrl][2].push([marker, pointPos])
          } else {
            self._drawImage(marker, pointPos)
          }
        } else {
          var i = new Image()
          i.src = iconUrl
          marker.canvas_img = i
          // Image,isLoaded,marker\pointPos ref
          self._imageLookup[iconUrl] = [i, false, [[marker, pointPos]]]
          i.onload = function () {
            self._imageLookup[iconUrl][1] = true
            self._imageLookup[iconUrl][2].forEach(function (e) {
              self._drawImage(e[0], e[1])
            })
          }
        }
      }
    },
    _drawImage: function (marker, pointPos) {
      var options = marker.options.icon.options
      this._context.drawImage(
        marker.canvas_img,
        pointPos.x - options.iconAnchor[0],
        pointPos.y - options.iconAnchor[1],
        options.iconSize[0],
        options.iconSize[1]
      )
    },
    _reset: function () {
      var topLeft = this._map.containerPointToLayerPoint([0, 0])
      L.DomUtil.setPosition(this._canvas, topLeft)
      var size = this._map.getSize()
      this._canvas.width = size.x
      this._canvas.height = size.y
      this._redraw()
    },
    _redraw: function (clear) {
      var self = this
      if (clear) this._context.clearRect(0, 0, this._canvas.width, this._canvas.height)
      if (!this._map || !this._latlngMarkers) return
      var tmp = []
      // If we are 10% individual inserts\removals, reconstruct lookup for efficiency
      if (self._latlngMarkers.dirty / self._latlngMarkers.total >= 0.1) {
        self._latlngMarkers.all().forEach(function (e) {
          tmp.push(e)
        })
        self._latlngMarkers.clear()
        self._latlngMarkers.load(tmp)
        self._latlngMarkers.dirty = 0
        tmp = []
      }
      var mapBounds = self._map.getBounds()
      // Only re-draw what we are showing on the map.
      var mapBoxCoords = {
        minX: mapBounds.getWest(),
        minY: mapBounds.getSouth(),
        maxX: mapBounds.getEast(),
        maxY: mapBounds.getNorth()
      }
      self._latlngMarkers.search(mapBoxCoords).forEach(function (e) {
        // Readjust Point Map
        var pointPos = self._map.latLngToContainerPoint(e.data.getLatLng())
        var iconSize = e.data.options.icon.options.iconSize
        var adjX = iconSize[0] / 2
        var adjY = iconSize[1] / 2
        var newCoords = {
          minX: (pointPos.x - adjX),
          minY: (pointPos.y - adjY),
          maxX: (pointPos.x + adjX),
          maxY: (pointPos.y + adjY),
          data: e.data
        }
        tmp.push(newCoords)
        // Redraw points
        self._drawMarker(e.data, pointPos)
      })
      // Clear rBush & Bulk Load for performance
      this._markers.clear()
      this._markers.load(tmp)
    },
    _initCanvas: function () {
      this._canvas = L.DomUtil.create('canvas', 'leaflet-canvas-icon-layer leaflet-layer')
      var originProp = L.DomUtil.testProp(['transformOrigin', 'WebkitTransformOrigin', 'msTransformOrigin'])
      this._canvas.style[originProp] = '50% 50%'
      var size = this._map.getSize()
      this._canvas.width = size.x
      this._canvas.height = size.y
      this._context = this._canvas.getContext('2d')
      var animated = this._map.options.zoomAnimation && L.Browser.any3d
      L.DomUtil.addClass(this._canvas, 'leaflet-zoom-' + (animated ? 'animated' : 'hide'))
    },
    addOnClickListener: function (listener) {
      this._onClickListeners.push(listener)
    },
    addOnHoverListener: function (listener) {
      this._onHoverListeners.push(listener)
    },
    _executeListeners: function (event) {
      if (!this._markers) return
      var me = this
      var x = event.containerPoint.x
      var y = event.containerPoint.y
      if (me._openToolTip) {
        me._openToolTip.closeTooltip()
        delete me._openToolTip
      }
      var ret = this._markers.search({ minX: x, minY: y, maxX: x, maxY: y })
      if (ret && ret.length > 0) {
        me._map._container.style.cursor = 'pointer'
        if (event.type === 'click') {
          var hasPopup = ret[0].data.getPopup()
          if (hasPopup) ret[0].data.openPopup()
          me._onClickListeners.forEach(function (listener) { listener(event, ret) })
        }
        if (event.type === 'mousemove') {
          var hasTooltip = ret[0].data.getTooltip()
          if (hasTooltip) {
            me._openToolTip = ret[0].data
            ret[0].data.openTooltip()
          }
          me._onHoverListeners.forEach(function (listener) { listener(event, ret) })
        }
      } else {
        me._map._container.style.cursor = ''
      }
    }
  })
  L.canvasIconLayer = function (options) {
    return new CanvasIconLayer(options)
  }
}
export default {
    init
  init
}
src/components/plugin/CustomPopup.js
@@ -1,74 +1,74 @@
'use strict'
const init = (L) => {
    L.CustomPopup = L.Popup.extend({
        _initLayout: () => {
            // 此处生成的容器,Leaflet会根据其类名自动适配Transform,匹配样式,所以如果要自定义的话,该部分样式要自己重写,我这里只解决了自适应容器的问题,以下采用原生容器,所以后面我会加上样式覆盖。
            let prefix = 'leaflet-popup'
            let container = (this._container = L.DomUtil.create(
                    'div',
                    prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-animated'
                ))
            let wrapper = container
            this._contentNode = L.DomUtil.create('div', prefix + '-content', wrapper)
            L.DomEvent.disableClickPropagation(wrapper)
                .disableScrollPropagation(this._contentNode)
                .on(wrapper, 'contextmenu', L.DomEvent.stopPropagation)
            this._tipContainer = L.DomUtil.create(
                'div',
                prefix + '-tip-container',
                container
            )
            this._tip = L.DomUtil.create('div', prefix + '-tip', this._tipContainer)
        },
        // 位置更新
        _updatePosition: () => {
            if (!this._map) {
                return
            }
            let pos = this._map.latLngToLayerPoint(this._latlng)
            let offset = L.point(this.options.offset)
            let anchor = [0, 0]
            if (this._zoomAnimated) {
                setPosition(this._container, pos.add(anchor))
            } else {
                offset = offset.add(pos).add(anchor)
            }
            let bottom = (this._containerBottom = -offset.y)
            let left = (this._containerLeft =
  L.CustomPopup = L.Popup.extend({
    _initLayout: () => {
      // 此处生成的容器,Leaflet会根据其类名自动适配Transform,匹配样式,所以如果要自定义的话,该部分样式要自己重写,我这里只解决了自适应容器的问题,以下采用原生容器,所以后面我会加上样式覆盖。
      const prefix = 'leaflet-popup'
      const container = (this._container = L.DomUtil.create(
        'div',
        prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-animated'
      ))
      const wrapper = container
      this._contentNode = L.DomUtil.create('div', prefix + '-content', wrapper)
      L.DomEvent.disableClickPropagation(wrapper)
        .disableScrollPropagation(this._contentNode)
        .on(wrapper, 'contextmenu', L.DomEvent.stopPropagation)
      this._tipContainer = L.DomUtil.create(
        'div',
        prefix + '-tip-container',
        container
      )
      this._tip = L.DomUtil.create('div', prefix + '-tip', this._tipContainer)
    },
    // 位置更新
    _updatePosition: () => {
      if (!this._map) {
        return
      }
      const pos = this._map.latLngToLayerPoint(this._latlng)
      let offset = L.point(this.options.offset)
      const anchor = [0, 0]
      if (this._zoomAnimated) {
        setPosition(this._container, pos.add(anchor))
      } else {
        offset = offset.add(pos).add(anchor)
      }
      const bottom = (this._containerBottom = -offset.y)
      const left = (this._containerLeft =
                    -Math.round(this._containerWidth / 2) + offset.x)
            // bottom position the popup in case the height of the popup changes (images loading etc)
            this._container.style.bottom = bottom + 'px'
            this._container.style.left = left + 'px'
        },
        // 重写层级变化触发更新
        _animateZoom: (e) => {
            let pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center)
            let anchor = [0, 0]
            setPosition(this._container, pos.add(anchor))
        }
    })
      // bottom position the popup in case the height of the popup changes (images loading etc)
      this._container.style.bottom = bottom + 'px'
      this._container.style.left = left + 'px'
    },
    // 重写层级变化触发更新
    _animateZoom: (e) => {
      const pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center)
      const anchor = [0, 0]
      setPosition(this._container, pos.add(anchor))
    }
  })
// 重写setTransform,由于不再固定宽度,所以增加translateX(-50%)水平居中
    const setTransform = (el, offset, scale) => {
        let pos = offset || new L.Point(0, 0)
        el.style[L.DomUtil.TRANSFORM] =
  // 重写setTransform,由于不再固定宽度,所以增加translateX(-50%)水平居中
  const setTransform = (el, offset, scale) => {
    const pos = offset || new L.Point(0, 0)
    el.style[L.DomUtil.TRANSFORM] =
            (L.Browser.ie3d
                ? 'translate(' + pos.x + 'px,' + pos.y + 'px,0) translateX(-50%)'
                : 'translate3d(' + pos.x + 'px,' + pos.y + 'px,0) translateX(-50%)') +
              ? 'translate(' + pos.x + 'px,' + pos.y + 'px,0) translateX(-50%)'
              : 'translate3d(' + pos.x + 'px,' + pos.y + 'px,0) translateX(-50%)') +
            (scale ? ' scale(' + scale + ')' : '')
  }
  // 因为重写了setTransform,所以setPosition也要重新指向方法
  const setPosition = (el, point) => {
    el._leaflet_pos = point
    if (L.Browser.any3d) {
      setTransform(el, point)
    } else {
      el.style.left = point.x + 'px'
      el.style.top = point.y + 'px'
    }
// 因为重写了setTransform,所以setPosition也要重新指向方法
    const setPosition = (el, point) => {
        el._leaflet_pos = point
        if (L.Browser.any3d) {
            setTransform(el, point)
        } else {
            el.style.left = point.x + 'px'
            el.style.top = point.y + 'px'
        }
    }
  }
}
export default {
    init
  init
}
src/components/plugin/Editable.js
@@ -1,1963 +1,1962 @@
'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
}
src/components/plugin/MagicMarker.js
@@ -1,49 +1,49 @@
const init = (L) => {
    (function(window) {
        console.log(window)
        let setOptions = function(obj, options) {
            for (let i in options) {
                obj[i] = options[i]
            }
            return obj
  (function (window) {
    console.log(window)
    const setOptions = function (obj, options) {
      for (const i in options) {
        obj[i] = options[i]
      }
      return obj
    }
    L.Icon.Magic = function (options) {
      let opts
      if (options.iconUrl) {
        opts = {
          html: "<div class='magicDiv'><div class='magictime " + options.magic + "'><img id='migic' src='" + options.iconUrl + "'/></div></div>"
          // className: 'magicDiv',
        }
        L.Icon.Magic = function(options) {
            let opts
            if (options.iconUrl) {
                 opts = {
                    html: "<div class='magicDiv'><div class='magictime " + options.magic + "'><img id='migic' src='" + options.iconUrl + "'/></div></div>"
                    // className: 'magicDiv',
                }
            } else {
                 opts = {
                    html: "<div class='magicDiv'><div class='magictime " + options.magic + "'>" + options.html + '</div></div>'
                    // className: 'magicDiv',
                }
            }
            delete options.html
            let magicIconOpts = setOptions(opts, options)
            // console.log(magicIconOpts)
            let magicIcon = L.divIcon(magicIconOpts)
            return magicIcon
      } else {
        opts = {
          html: "<div class='magicDiv'><div class='magictime " + options.magic + "'>" + options.html + '</div></div>'
          // className: 'magicDiv',
        }
      }
      delete options.html
      const magicIconOpts = setOptions(opts, options)
      // console.log(magicIconOpts)
      const magicIcon = L.divIcon(magicIconOpts)
      return magicIcon
    }
        L.icon.magic = function(options) {
            return new L.Icon.Magic(options)
        }
    L.icon.magic = function (options) {
      return new L.Icon.Magic(options)
    }
        L.Marker.Magic = L.Marker.extend({
            initialize: function(latlng, options) {
                options.icon = L.icon.magic(options)
                L.Marker.prototype.initialize.call(this, latlng, options)
            }
        })
    L.Marker.Magic = L.Marker.extend({
      initialize: function (latlng, options) {
        options.icon = L.icon.magic(options)
        L.Marker.prototype.initialize.call(this, latlng, options)
      }
    })
        L.marker.magic = function(latlng, options) {
            return new L.Marker.Magic(latlng, options)
        }
    })(window)
    L.marker.magic = function (latlng, options) {
      return new L.Marker.Magic(latlng, options)
    }
  })(window)
}
export default {
    init
  init
}
src/components/plugin/PathDashFlow.js
@@ -1,43 +1,43 @@
// @class PolyLine
import * as L from 'leaflet'
let DashFlow = () => {
    L.Path.mergeOptions({
        // @option dashSpeed: Number
        // The speed of the dash array, in pixels per second
        dashSpeed: 0
    })
const DashFlow = () => {
  L.Path.mergeOptions({
    // @option dashSpeed: Number
    // The speed of the dash array, in pixels per second
    dashSpeed: 0
  })
    var _originalBeforeAdd = L.Path.prototype.beforeAdd
  var _originalBeforeAdd = L.Path.prototype.beforeAdd
    L.Path.include({
  L.Path.include({
        beforeAdd: function(map) {
            _originalBeforeAdd.bind(this)(map)
    beforeAdd: function (map) {
      _originalBeforeAdd.bind(this)(map)
            if (this.options.dashSpeed) {
                this._lastDashFrame = performance.now()
                this._dashFrame = L.Util.requestAnimFrame(this._onDashFrame.bind(this))
            }
        },
      if (this.options.dashSpeed) {
        this._lastDashFrame = performance.now()
        this._dashFrame = L.Util.requestAnimFrame(this._onDashFrame.bind(this))
      }
    },
        _onDashFrame: function() {
            if (!this._renderer) {
                return
            }
    _onDashFrame: function () {
      if (!this._renderer) {
        return
      }
            var now = performance.now()
            var dashOffsetDelta = (now - this._lastDashFrame) * this.options.dashSpeed / 1000
      var now = performance.now()
      var dashOffsetDelta = (now - this._lastDashFrame) * this.options.dashSpeed / 1000
            this.options.dashOffset = Number(this.options.dashOffset || 0) + dashOffsetDelta
            this._renderer._updateStyle(this)
      this.options.dashOffset = Number(this.options.dashOffset || 0) + dashOffsetDelta
      this._renderer._updateStyle(this)
            this._lastDashFrame = performance.now()
      this._lastDashFrame = performance.now()
            this._dashFrame = L.Util.requestAnimFrame(this._onDashFrame.bind(this))
        }
      this._dashFrame = L.Util.requestAnimFrame(this._onDashFrame.bind(this))
    }
    })
  })
}
export default { DashFlow }
src/components/plugin/PathDrag.js
@@ -1,143 +1,143 @@
'use strict'
const init = (L) => {
    /* A Draggable that does not update the element position
  /* A Draggable that does not update the element position
    and takes care of only bubbling to targetted path in Canvas mode. */
    L.PathDraggable = L.Draggable.extend({
  L.PathDraggable = L.Draggable.extend({
        initialize: function(path) {
            this._path = path
            this._canvas = (path._map.getRenderer(path) instanceof L.Canvas)
            let element = this._canvas ? this._path._map.getRenderer(this._path)._container : this._path._path
            L.Draggable.prototype.initialize.call(this, element, element, true)
        },
    initialize: function (path) {
      this._path = path
      this._canvas = (path._map.getRenderer(path) instanceof L.Canvas)
      const element = this._canvas ? this._path._map.getRenderer(this._path)._container : this._path._path
      L.Draggable.prototype.initialize.call(this, element, element, true)
    },
        _updatePosition: function() {
            let e = { originalEvent: this._lastEvent }
            this.fire('drag', e)
        },
    _updatePosition: function () {
      const e = { originalEvent: this._lastEvent }
      this.fire('drag', e)
    },
        _onDown: function(e) {
            let first = e.touches ? e.touches[0] : e
            this._startPoint = new L.Point(first.clientX, first.clientY)
            if (this._canvas && !this._path._containsPoint(this._path._map.mouseEventToLayerPoint(first))) {
                return
            }
            L.Draggable.prototype._onDown.call(this, e)
    _onDown: function (e) {
      const first = e.touches ? e.touches[0] : e
      this._startPoint = new L.Point(first.clientX, first.clientY)
      if (this._canvas && !this._path._containsPoint(this._path._map.mouseEventToLayerPoint(first))) {
        return
      }
      L.Draggable.prototype._onDown.call(this, e)
    }
  })
  L.Handler.PathDrag = L.Handler.extend({
    initialize: function (path) {
      this._path = path
    },
    getEvents: function () {
      return {
        dragstart: this._onDragStart,
        drag: this._onDrag,
        dragend: this._onDragEnd
      }
    },
    addHooks: function () {
      if (!this._draggable) {
        this._draggable = new L.PathDraggable(this._path)
      }
      this._draggable.on(this.getEvents(), this).enable()
      L.DomUtil.addClass(this._draggable._element, 'leaflet-path-draggable')
    },
    removeHooks: function () {
      this._draggable.off(this.getEvents(), this).disable()
      L.DomUtil.removeClass(this._draggable._element, 'leaflet-path-draggable')
    },
    moved: function () {
      return this._draggable && this._draggable._moved
    },
    _onDragStart: function () {
      this._startPoint = this._draggable._startPoint
      this._path
        .closePopup()
        .fire('movestart')
        .fire('dragstart')
    },
    _onDrag: function (e) {
      const path = this._path
      const event = (e.originalEvent.touches && e.originalEvent.touches.length === 1 ? e.originalEvent.touches[0] : e.originalEvent)
      const newPoint = L.point(event.clientX, event.clientY)
      const latlng = path._map.layerPointToLatLng(newPoint)
      this._offset = newPoint.subtract(this._startPoint)
      this._startPoint = newPoint
      this._path.eachLatLng(this.updateLatLng, this)
      path.redraw()
      e.latlng = latlng
      e.offset = this._offset
      path.fire('drag', e)
      e.latlng = this._path.getCenter ? this._path.getCenter() : this._path.getLatLng()
      path.fire('move', e)
    },
    _onDragEnd: function (e) {
      if (this._path._bounds) this.resetBounds()
      this._path.fire('moveend')
        .fire('dragend', e)
    },
    latLngToLayerPoint: function (latlng) {
      // Same as map.latLngToLayerPoint, but without the round().
      const projectedPoint = this._path._map.project(L.latLng(latlng))
      return projectedPoint._subtract(this._path._map.getPixelOrigin())
    },
    updateLatLng: function (latlng) {
      const oldPoint = this.latLngToLayerPoint(latlng)
      oldPoint._add(this._offset)
      const newLatLng = this._path._map.layerPointToLatLng(oldPoint)
      latlng.lat = newLatLng.lat
      latlng.lng = newLatLng.lng
    },
    resetBounds: function () {
      this._path._bounds = new L.LatLngBounds()
      this._path.eachLatLng(function (latlng) {
        this._bounds.extend(latlng)
      })
    }
  })
  L.Path.include({
    eachLatLng: function (callback, context) {
      context = context || this
      const loop = function (latlngs) {
        for (let i = 0; i < latlngs.length; i++) {
          if (L.Util.isArray(latlngs[i])) loop(latlngs[i])
          else callback.call(context, latlngs[i])
        }
      }
      loop(this.getLatLngs ? this.getLatLngs() : [this.getLatLng()])
    }
    })
  })
    L.Handler.PathDrag = L.Handler.extend({
        initialize: function(path) {
            this._path = path
        },
        getEvents: function() {
            return {
                dragstart: this._onDragStart,
                drag: this._onDrag,
                dragend: this._onDragEnd
            }
        },
        addHooks: function() {
            if (!this._draggable) {
                this._draggable = new L.PathDraggable(this._path)
            }
            this._draggable.on(this.getEvents(), this).enable()
            L.DomUtil.addClass(this._draggable._element, 'leaflet-path-draggable')
        },
        removeHooks: function() {
            this._draggable.off(this.getEvents(), this).disable()
            L.DomUtil.removeClass(this._draggable._element, 'leaflet-path-draggable')
        },
        moved: function() {
            return this._draggable && this._draggable._moved
        },
        _onDragStart: function() {
            this._startPoint = this._draggable._startPoint
            this._path
                .closePopup()
                .fire('movestart')
                .fire('dragstart')
        },
        _onDrag: function(e) {
            let path = this._path
            let event = (e.originalEvent.touches && e.originalEvent.touches.length === 1 ? e.originalEvent.touches[0] : e.originalEvent)
            let newPoint = L.point(event.clientX, event.clientY)
            let latlng = path._map.layerPointToLatLng(newPoint)
            this._offset = newPoint.subtract(this._startPoint)
            this._startPoint = newPoint
            this._path.eachLatLng(this.updateLatLng, this)
            path.redraw()
            e.latlng = latlng
            e.offset = this._offset
            path.fire('drag', e)
            e.latlng = this._path.getCenter ? this._path.getCenter() : this._path.getLatLng()
            path.fire('move', e)
        },
        _onDragEnd: function(e) {
            if (this._path._bounds) this.resetBounds()
            this._path.fire('moveend')
                .fire('dragend', e)
        },
        latLngToLayerPoint: function(latlng) {
            // Same as map.latLngToLayerPoint, but without the round().
            let projectedPoint = this._path._map.project(L.latLng(latlng))
            return projectedPoint._subtract(this._path._map.getPixelOrigin())
        },
        updateLatLng: function(latlng) {
            let oldPoint = this.latLngToLayerPoint(latlng)
            oldPoint._add(this._offset)
            let newLatLng = this._path._map.layerPointToLatLng(oldPoint)
            latlng.lat = newLatLng.lat
            latlng.lng = newLatLng.lng
        },
        resetBounds: function() {
            this._path._bounds = new L.LatLngBounds()
            this._path.eachLatLng(function(latlng) {
                this._bounds.extend(latlng)
            })
        }
    })
    L.Path.include({
        eachLatLng: function(callback, context) {
            context = context || this
            let loop = function(latlngs) {
                for (let i = 0; i < latlngs.length; i++) {
                    if (L.Util.isArray(latlngs[i])) loop(latlngs[i])
                    else callback.call(context, latlngs[i])
                }
            }
            loop(this.getLatLngs ? this.getLatLngs() : [this.getLatLng()])
        }
    })
    L.Path.addInitHook(function() {
        this.dragging = new L.Handler.PathDrag(this)
        if (this.options.draggable) {
            this.once('add', function() {
                this.dragging.enable()
            })
        }
    })
  L.Path.addInitHook(function () {
    this.dragging = new L.Handler.PathDrag(this)
    if (this.options.draggable) {
      this.once('add', function () {
        this.dragging.enable()
      })
    }
  })
}
export default {
    init
  init
}
src/components/plugin/cluster-layer/leaflet.markercluster-src.js
Diff too large
src/components/plugin/wmts_plugins.js
@@ -1,96 +1,96 @@
/* eslint-disable no-prototype-builtins */
'use strict'
const init = (L) => {
    L.TileLayer.WMTS = L.TileLayer.extend({
        defaultWmtsParams: {
            service: 'WMTS',
            request: 'GetTile',
            version: '1.0.0',
            layer: '',
            style: '',
            tilematrixset: '',
            format: 'image/jpeg'
        },
        initialize: function (url, options) { // (String, Object)
            this._url = url;
            var lOptions= {};
            var cOptions = Object.keys(options);
            cOptions.forEach(element=>{
               lOptions[element.toLowerCase()]=options[element];
            });
            var wmtsParams = L.extend({}, this.defaultWmtsParams);
            var tileSize = lOptions.tileSize || this.options.tileSize;
            if (lOptions.detectRetina && L.Browser.retina) {
                wmtsParams.width = wmtsParams.height = tileSize * 2;
            } else {
                wmtsParams.width = wmtsParams.height = tileSize;
            }
            for (var i in lOptions) {
                // all keys that are in defaultWmtsParams options go to WMTS params
                if (wmtsParams.hasOwnProperty(i) && i!="matrixIds") {
                    wmtsParams[i] = lOptions[i];
                }
            }
            this.wmtsParams = wmtsParams;
            this.matrixIds = options.matrixIds||this.getDefaultMatrix();
            L.setOptions(this, options);
        },
        onAdd: function (map) {
            this._crs = this.options.crs || map.options.crs;
            L.TileLayer.prototype.onAdd.call(this, map);
        },
        getTileUrl: function (coords) { // (Point, Number) -> String
            var tileSize = this.options.tileSize;
            var nwPoint = coords.multiplyBy(tileSize);
            nwPoint.x+=1;
            nwPoint.y-=1;
            var sePoint = nwPoint.add(new L.Point(tileSize, tileSize));
            var zoom = this._tileZoom;
            var nw = this._crs.project(this._map.unproject(nwPoint, zoom));
            var se = this._crs.project(this._map.unproject(sePoint, zoom));
            var tilewidth = se.x-nw.x;
            var ident = this.matrixIds[zoom].identifier;
            var tilematrix = this.wmtsParams.tilematrixset + ":" + ident;
            var X0 = this.matrixIds[zoom].topLeftCorner.lng;
            var Y0 = this.matrixIds[zoom].topLeftCorner.lat;
            var tilecol=Math.floor((nw.x-X0)/tilewidth);
            var tilerow=-Math.floor((nw.y-Y0)/tilewidth);
            var url = L.Util.template(this._url, {s: this._getSubdomain(coords)});
            return url + L.Util.getParamString(this.wmtsParams, url) + "&tilematrix=" + tilematrix + "&tilerow=" + tilerow +"&tilecol=" + tilecol;
        },
        setParams: function (params, noRedraw) {
            L.extend(this.wmtsParams, params);
            if (!noRedraw) {
                this.redraw();
            }
            return this;
        },
        getDefaultMatrix : function () {
            /**
             * the matrix3857 represents the projection
  L.TileLayer.WMTS = L.TileLayer.extend({
    defaultWmtsParams: {
      service: 'WMTS',
      request: 'GetTile',
      version: '1.0.0',
      layer: '',
      style: '',
      tilematrixset: '',
      format: 'image/jpeg'
    },
    initialize: function (url, options) { // (String, Object)
      this._url = url
      var lOptions = {}
      var cOptions = Object.keys(options)
      cOptions.forEach(element => {
        lOptions[element.toLowerCase()] = options[element]
      })
      var wmtsParams = L.extend({}, this.defaultWmtsParams)
      var tileSize = lOptions.tileSize || this.options.tileSize
      if (lOptions.detectRetina && L.Browser.retina) {
        wmtsParams.width = wmtsParams.height = tileSize * 2
      } else {
        wmtsParams.width = wmtsParams.height = tileSize
      }
      for (var i in lOptions) {
        // all keys that are in defaultWmtsParams options go to WMTS params
        if (wmtsParams.hasOwnProperty(i) && i != 'matrixIds') {
          wmtsParams[i] = lOptions[i]
        }
      }
      this.wmtsParams = wmtsParams
      this.matrixIds = options.matrixIds || this.getDefaultMatrix()
      L.setOptions(this, options)
    },
    onAdd: function (map) {
      this._crs = this.options.crs || map.options.crs
      L.TileLayer.prototype.onAdd.call(this, map)
    },
    getTileUrl: function (coords) { // (Point, Number) -> String
      var tileSize = this.options.tileSize
      var nwPoint = coords.multiplyBy(tileSize)
      nwPoint.x += 1
      nwPoint.y -= 1
      var sePoint = nwPoint.add(new L.Point(tileSize, tileSize))
      var zoom = this._tileZoom
      var nw = this._crs.project(this._map.unproject(nwPoint, zoom))
      var se = this._crs.project(this._map.unproject(sePoint, zoom))
      var tilewidth = se.x - nw.x
      var ident = this.matrixIds[zoom].identifier
      var tilematrix = this.wmtsParams.tilematrixset + ':' + ident
      var X0 = this.matrixIds[zoom].topLeftCorner.lng
      var Y0 = this.matrixIds[zoom].topLeftCorner.lat
      var tilecol = Math.floor((nw.x - X0) / tilewidth)
      var tilerow = -Math.floor((nw.y - Y0) / tilewidth)
      var url = L.Util.template(this._url, { s: this._getSubdomain(coords) })
      return url + L.Util.getParamString(this.wmtsParams, url) + '&tilematrix=' + tilematrix + '&tilerow=' + tilerow + '&tilecol=' + tilecol
    },
    setParams: function (params, noRedraw) {
      L.extend(this.wmtsParams, params)
      if (!noRedraw) {
        this.redraw()
      }
      return this
    },
    getDefaultMatrix: function () {
      /**
             * the matrix3857 represents the projection
             * for in the IGN WMTS for the google coordinates.
             */
            var matrixIds3857 = new Array(22);
            for (var i= 0; i<22; i++) {
                matrixIds3857[i]= {
                    identifier    : "" + i,
                    topLeftCorner : new L.LatLng(20037508.3428,-20037508.3428)
                };
            }
            return matrixIds3857;
      var matrixIds3857 = new Array(22)
      for (var i = 0; i < 22; i++) {
        matrixIds3857[i] = {
          identifier: '' + i,
          topLeftCorner: new L.LatLng(20037508.3428, -20037508.3428)
        }
    });
    L.tileLayer.wmts = function (url, options) {
        return new L.TileLayer.WMTS(url, options);
    };
      }
      return matrixIds3857
    }
  })
  L.tileLayer.wmts = function (url, options) {
    return new L.TileLayer.WMTS(url, options)
  }
}
export default {
    init
  init
}
src/utils/tools.js
@@ -1,9 +1,9 @@
import * as $CONST from './constant'
import { Message } from 'element-ui'
// import { notify } from '@nutui/nutui'
export const _ = require('lodash')
const notify = window.vm.$notify
/**
 * 集合转换为JSON
 * @param obj collection数据
@@ -993,23 +993,38 @@
}
export function success (msg = $CONST.MSG_SYS_SUCCESS) {
  notify.success(msg)
  Message({
    message: msg,
    type: 'success'
  })
}
export function fail (msg = $CONST.MSG_SYS_FAIL) {
  notify.warn(msg)
  Message({
    message: msg,
    type: 'error'
  })
}
export function error (msg = $CONST.MSG_SYS_ERR) {
  notify.danger(msg)
  Message({
    message: msg,
    type: 'error'
  })
}
export function warning (msg = $CONST.MSG_SYS_WARNING) {
  notify.warn(msg)
  Message({
    message: msg,
    type: 'warning'
  })
}
export function info (msg = $CONST.MSG_SYS_CANCELED) {
  notify.primary(msg)
  Message({
    message: msg,
    type: 'info'
  })
}
/**