    import * as T from '@turf/turf'
    /**
     * @description: leaflet内使用mapboxgl插件，copy from https://github.com/mapbox/mapbox-gl-leaflet
     * 初始版本:0.0.16
     * 改写后支持接入zondy mapboxgl内核库
     * @param {*} L
     * @param {*} mapboxgl
     * @return {*}
     */
    export default function leafletMapbox(L, mapboxgl) {
      L.MapboxGL = L.Layer.extend({
        options: {
          updateInterval: 32,
          // How much to extend the overlay view (relative to map size)
          // e.g. 0.1 would be 10% of map view in each direction
          // 内边距设置为1，在旋转时可以遮住当前屏幕范围
          padding: 1,
          // whether or not to register the mouse and keyboard
          // events on the mapbox overlay
          interactive: false,
          // set the tilepane as the default pane to draw gl tiles
          pane: 'tilePane',
          // 裁剪区域
          clippingArea: null,
          // 级数偏移
          zoomOffset: 0
        },

        initialize(options) {
          L.setOptions(this, options)

          if (options.accessToken) {
            mapboxgl.accessToken = options.accessToken
          }

          // setup throttling the update event when panning
          this._throttledUpdate = L.Util.throttle(
            this._update,
            this.options.updateInterval,
            this
          )
        },

        onAdd(map) {
          if (!this._container) {
            this._initContainer()
          }

          const paneName = this.getPaneName()
          map.getPane(paneName).appendChild(this._container)

          this._initGL()

          this._offset = this._calcRotateOffset()

          // work around https://github.com/mapbox/mapbox-gl-leaflet/issues/47
          if (map.options.zoomAnimation) {
            L.DomEvent.on(
              map._proxy,
              L.DomUtil.TRANSITION_END,
              this._transitionEnd,
              this
            )
          }

          map._addZoomLimit(this)
        },

        onRemove(map) {
          if (this._map._proxy && this._map.options.zoomAnimation) {
            L.DomEvent.off(
              this._map._proxy,
              L.DomUtil.TRANSITION_END,
              this._transitionEnd,
              this
            )
          }
          const paneName = this.getPaneName()
          map.getPane(paneName).removeChild(this._container)
        },

        getEvents() {
          return {
            move: this._throttledUpdate, // sensibly throttle updating while panning
            zoomanim: this._animateZoom, // applys the zoom animation to the <canvas>
            zoom: this._pinchZoom, // animate every zoom event for smoother pinch-zooming
            zoomstart: this._zoomStart, // flag starting a zoom to disable panning
            zoomend: this._zoomEnd,
            resize: this._resize
          }
        },
        setClippingArea(data) {
          this.options.clippingArea = data
          this.clippingAreaBounds = null
          this._update()
        },
        getClippingArea() {
          return this.options.clippingArea
        },
        getMapboxMap() {
          return this._glMap
        },

        getCanvas() {
          return this._glMap.getCanvas()
        },

        getSize() {
          return this._map.getSize().multiplyBy(1 + this.options.padding * 2)
        },

        getBounds() {
          const halfSize = this.getSize().multiplyBy(0.5)
          const center = this._map.latLngToContainerPoint(this._map.getCenter())
          return L.latLngBounds(
            this._map.containerPointToLatLng(center.subtract(halfSize)),
            this._map.containerPointToLatLng(center.add(halfSize))
          )
        },

        getContainer() {
          return this._container
        },

        // returns the pane name set in options if it is a valid pane, defaults to tilePane
        getPaneName() {
          return this._map.getPane(this.options.pane)
            ? this.options.pane
            : 'tilePane'
        },

        _initContainer() {
          const container = (this._container = L.DomUtil.create(
            'div',
            'leaflet-gl-layer'
          ))

          const size = this.getSize()

          container.style.width = `${size.x}px`
          container.style.height = `${size.y}px`

          const topLeft = this._calcRotateOffset()

          L.DomUtil.setPosition(container, topLeft)
        },

        _initGL() {
          const center = this._map.getCenter()
          const crs = this._map.options.crs
          this._offsetZoom = 0
          let projCRS
          const crsOptions = crs.options

          if (crsOptions) {
            const mapboxTileSize = crsOptions.mapboxTileSize || 256
            const projCRSOptions = {
              resolutions: crsOptions.resolutions,
              origin: crsOptions.origin,
              tileSize: mapboxTileSize
            }
            if (mapboxgl.Proj && mapboxgl.Proj.CRS) {
              projCRS = new mapboxgl.Proj.CRS(
                crs.code,
                crs.def || crs.code,
                projCRSOptions
              )
              // projCRS getZoomOffset计算投影坐标系的偏移(针对超出投影系范围进行偏移处理)
              // 计算当前使用的size,因为本质上是mapbox在渲染，并没有依赖于leaflet的网格图层，我们需要知道对应leaflet层级的瓦片是否正确，如果瓦片大小是512，需要偏移下mapbox的zoom层级
              // Math.floor(mapboxTileSize/256 -1 )
              this._offsetZoom =
                projCRS.getZoomOffset() -
                Math.floor(mapboxTileSize / 256 - 1) -
                this.options.zoomOffset
            } else {
              if (crsOptions.bounds) {
                const max = crsOptions.bounds.max
                const min = crsOptions.bounds.min
                projCRSOptions.bounds = [min.x, min.y, max.x, max.y]
              }
              projCRS = new mapboxgl.CRS(crs.code, projCRSOptions)
            }
          } else {
            projCRS = crs.code
          }

          const options = L.extend({}, this.options, {
            container: this._container,
            center: [center.lng, center.lat],
            zoom: this._map.getZoom() - 1,
            attributionControl: false,
            crs: projCRS,
            minZoom: 0,
            renderWorldCopies: false
          })

          if (!this._glMap) this._glMap = new mapboxgl.Map(options)
          else {
            this._glMap.setCenter(options.center)
            this._glMap.setZoom(options.zoom)
          }
          // allow GL base map to pan beyond min/max latitudes
          this._glMap.transform.latRange = null
          this._transformGL(this._glMap)

          if (this._glMap._canvas.canvas) {
            // older versions of mapbox-gl surfaced the canvas differently
            this._glMap._actualCanvas = this._glMap._canvas.canvas
          } else {
            this._glMap._actualCanvas = this._glMap._canvas
          }

          // treat child <canvas> element like L.ImageOverlay
          const canvas = this._glMap._actualCanvas
          L.DomUtil.addClass(canvas, 'leaflet-image-layer')
          L.DomUtil.addClass(canvas, 'leaflet-zoom-animated')
          if (this.options.interactive) {
            L.DomUtil.addClass(canvas, 'leaflet-interactive')
          }
          if (this.options.className) {
            L.DomUtil.addClass(canvas, this.options.className)
          }
        },
        /**
         * @description: 计算旋转实际的偏移值
         * @return {*}
         */
        _calcRotateOffset() {
          // 需要注意的是，mapboxgl容器实际上是以中心点作为和leaflet覆盖层关联的点
          // 也就是说，mapboxgl容器和leaflet容器中心点是一致的
          // 旋转时如果还是根据左上角点计算，则在旋转后会出现偏移，因此以中心点作为偏移的基准点
          const offset = this._map.getSize().multiplyBy(this.options.padding)
          const viewHalf = this._map.getSize()._divideBy(2)
          const topLeft = this._map
            ._latLngToNewLayerPoint(
              this._map.getCenter(),
              this._map.getZoom(),
              this._map.getCenter()
            )
            .subtract(viewHalf)
            .subtract(offset)
          return topLeft
        },
        _update(e) {
          if (!this._map) {
            return
          }
          // update the offset so we can correct for it later when we zoom
          this._offset = this._calcRotateOffset()

          if (this._zooming) {
            return
          }

          const gl = this._glMap
          const size = this.getSize()

          const container = this._container

          this._transformGL(gl)

          const x_round = Math.round(size.x)
          const y_round = Math.round(size.y)

          if (
            Math.round(gl.transform.width) !== x_round ||
            Math.round(gl.transform.height) !== y_round
          ) {
            container.style.width = `${x_round}px`
            container.style.height = `${y_round}px`

            gl.transform.width = x_round
            gl.transform.height = y_round

            if (gl._resize !== null && gl._resize !== undefined) {
              gl._resize()
            } else {
              gl.resize()
            }
          } else {
            // older versions of mapbox-gl surfaced update publicly
            if (gl._update !== null && gl._update !== undefined) {
              gl._update()
            } else {
              gl.update()
            }
          }
        },

        _transformGL(gl) {
          const center = this._clampCenter(this._map.getCenter())
          // gl.setView([center.lat, center.lng], this._map.getZoom() - 1, 0);
          // calling setView directly causes sync issues because it uses requestAnimFrame
          const tr = gl.transform
          tr.center = mapboxgl.LngLat.convert([center.lng, center.lat])
          // 设置不进行自动重算zoom center
          tr.resetZoomScale = false

          // transfrom zoom等级为整数，否则会出现瓦片和实际位置偏移
          // offsetZoom定义为mapboxgl内部层级偏移值，非0情况下表示，由于此投影坐标系超出投影系范围，故进行偏移
          tr.zoom = Math.floor(this._map.getZoom() - this._offsetZoom - 1)
          const currentCenter = tr.center
          const centerOffset = [0, 0]
          if (
            Math.abs(currentCenter.lng - center.lng) > 10e-8 ||
            Math.abs(currentCenter.lat - center.lat) > 10e-8
          ) {
            const p1 = tr.coordinatePoint(tr.locationCoordinate(currentCenter))
            const p2 = tr.coordinatePoint(tr.locationCoordinate(center))
            centerOffset[0] = p1.x - p2.x
            centerOffset[1] = p1.y - p2.y
          }

          // 偏移container 需要对照mapboxgl内核库偏移
          const container = this._container
          const topLeft = this._calcRotateOffset().add(centerOffset)
          // 矢量瓦片裁剪处理程序
          const polygon = this.options.clippingArea
          if (polygon) {
            // 计算裁剪区缓存四至
            if (!this.clippingAreaBounds) {
              const [x1, y1, x2, y2] = T.bbox(T.polygon(polygon))
              this.clippingAreaBounds = L.latLngBounds(
                L.latLng(y1, x1),
                L.latLng(y2, x2)
              )
            }
            // 先进行四至边界判断
            if (this.clippingAreaBounds.intersects(this._map.getBounds())) {
              const clipPxs = polygon.map((coords) => {
                return coords.map((v) => {
                  const point = this._map.latLngToContainerPoint(
                    L.latLng(v[1], v[0])
                  )
                  return [point.x, point.y]
                })
              })
              const polygon1 = T.polygon(clipPxs)
              const size = this._map.getSize()
              const polygon2 = T.polygon([
                [
                  [0, 0],
                  [size.x, 0],
                  [size.x, size.y],
                  [0, size.y],
                  [0, 0]
                ]
              ])

              const intersection = T.intersect(polygon1, polygon2)
              if (intersection) {
                const coords = T.coordAll(intersection)
                if (coords && coords.length > 3) {
                  let clipStr = ''
                  coords.forEach((coord) => {
                    clipStr += `${coord[0]}px ${coord[1]}px,`
                  })
                  clipStr = clipStr.substring(0, clipStr.length - 1)
                  container.style.clipPath = `polygon(${clipStr})`
                }
              }
            } else {
              container.style.clipPath = 'none'
            }
          } else {
            container.style.clipPath = 'none'
          }

          L.DomUtil.setPosition(container, topLeft)
        },
        _clampCenter(center) {
          if (!center) return center
          const lng = center.lng
          const lat = Math.min(Math.max(center.lat, -90), 90)
          return L.latLng(lat, lng)
        },
        // update the map constantly during a pinch zoom
        _pinchZoom(e) {
          this._glMap.jumpTo({
            zoom: Math.floor(this._map.getZoom() - this._offsetZoom - 1),
            center: this._clampCenter(this._map.getCenter())
          })
        },

        // borrowed from L.ImageOverlay
        // https://github.com/Leaflet/Leaflet/blob/master/src/layer/ImageOverlay.js#L139-L144
        _animateZoom(e) {
          const scale = this._map.getZoomScale(e.zoom)
          const padding = this._map
            .getSize()
            .multiplyBy(this.options.padding * scale)
          const viewHalf = this.getSize()._divideBy(2)
          // corrections for padding (scaled), adapted from
          // https://github.com/Leaflet/Leaflet/blob/master/src/map/Map.js#L1490-L1508
          const topLeft = this._map
            .project(e.center, e.zoom)
            ._subtract(viewHalf)
            ._add(this._map._getMapPanePos().add(padding))
            ._round()
          const offset = this._map
            .project(this._map.getBounds().getNorthWest(), e.zoom)
            ._subtract(topLeft)

          L.DomUtil.setTransform(
            this._glMap._actualCanvas,
            offset.subtract(this._offset),
            scale
          )
        },

        _zoomStart(e) {
          this._zooming = true
          // 处理动画效果异常问题
          if (this._container) {
            this._container.style.visibility = 'hidden'
          }
          this._offset = this._calcRotateOffset()
        },

        _zoomEnd() {
          const scale = this._map.getZoomScale(this._map.getZoom())

          L.DomUtil.setTransform(
            this._glMap._actualCanvas,
            // https://github.com/mapbox/mapbox-gl-leaflet/pull/130
            null,
            scale
          )

          this._zooming = false
          this._update()
          // 处理动画效果异常问题
          if (this._container) {
            this._container.style.visibility = 'visible'
          }
        },

        _transitionEnd(e) {
          L.Util.requestAnimFrame(function () {
            const zoom = this._map.getZoom()
            const center = this._map.getCenter()
            const offset = this._map.latLngToContainerPoint(
              this._map.getBounds().getNorthWest()
            )

            // reset the scale and offset
            L.DomUtil.setTransform(this._glMap._actualCanvas, offset, 1)

            // enable panning once the gl map is ready again
            this._glMap.once(
              'moveend',
              L.Util.bind(function () {
                this._zoomEnd()
              }, this)
            )

            // update the map position
            this._glMap.jumpTo({
              center: this._clampCenter(center),
              zoom: Math.floor(zoom - this._offsetZoom - 1)
            })
          }, this)
        },

        _resize(e) {
          this._transitionEnd(e)
        }
      })

      L.mapboxGL = function (options) {
        return new L.MapboxGL(options)
      }
    }