<template lang="pug">
div
  div(v-if="!hasBmView", ref="view", style="width: 100%; height: 100%")
  slot
</template>

<script>
import Vue from 'vue'
import 'ol/ol.css'
import Map from 'ol/Map'
import View from 'ol/View'
import config from './mapConfig'
import VectorLayer from 'ol/layer/Vector'
import VectorSource from 'ol/source/Vector'
import Cluster from 'ol/source/Cluster'
import { getFeatureOfMaxZIndex } from '../base/util'
import { GeometryType } from '../base/GeometryType'
import  ZoomSlider  from '../controls/ZoomSlider'
import  FullScreen  from '../controls/FullScreen'
import  Loading  from '../controls/Loading'
import {defaults as defaultControls} from 'ol/control'
import {defaults as defaultInteractions, DragRotateAndZoom} from 'ol/interaction'
import  Overview  from '../controls/Overview'
import  CanvasScaleLine  from '../controls/CanvasScaleLine'
import {Tile as TileLayer} from 'ol/layer.js'
import TileJSON from 'ol/source/TileJSON.js'
import { transform } from 'ol/proj'
const _panFactor = 0.0005
export default {
  name: 'sl-map',
  props: {
    mapType: {
      type: String
    },
    center: {
      type: Array,
      required: true
    },
    zoom: {
      type: Number,
      default: 2
    },
    minZoom: {
      type: Number,
      default: 0
    },
    maxZoom: {
      type: Number,
      default: 28
    },
    extent: {
      type: Array,
      default: function () {
        return []
      }
    },
    projection: {
      type: String,
      default: 'EPSG:3857'
    },
    rotation: {
      type: Number,
      default: 0
    }
  },
  data () {
    return {
      hasBmView: false,
      olmap: null
    }
  },
  watch: {
   mapType: function(type) {
      var layers = this.getAllLayers()
      layers.forEach((layer) => {
        let flag = layer.get('isBaseMap')
        if(flag) {
           this.olmap.removeLayer(layer)
        }
      })
      if(config.layers(type)){
        this.olmap.addLayer(config.layers(type))
      }  
   },
   // 监听地图中心点---
   center (val) {
      const { olmap, center } = this
      olmap.getView().setCenter(center)
    },
    // 监听地图缩放级别----
    zoom (val) {
      const { olmap } = this
      olmap.getView().setZoom(val)
    },
    // 监听地图最新缩放级别
    minZoom (val) {
      const { olmap } = this
      olmap.getView().setMinZoom(val)
    },
    // 监听地图最大缩放级别
    maxZoom (val) {
      const { olmap } = this
      olmap.getView().setMaxZoom(val)
    },
    // 监听地图范围变化-----
    extent(mapExtent) {
      this.olmap.getView().fit(mapExtent)
    }
  },
  methods: {
    initMap() {
      let $el = this.$refs.view // 地图布局，地图是用容器渲染-------
      for (let $node of this.$slots.default || []) {
        if ($node.componentOptions && $node.componentOptions.tag === 'sl-view') {
          this.hasBmView = true
          $el = $node.elm
        }
      }
      const { zoom, center, minZoom, maxZoom, projection, mapType} = this
      var { extent} = this
      if (extent && extent.length === 0) {
        extent = undefined
      }
      var view = new View({//view的坐标系和中心点都可以设置
        projection,
        center,
        zoom,
        minZoom,
        maxZoom,
        extent
      })
      const map = new Map({
        target: $el,
        keyboardEventTarget:document,
        view,
        controls: defaultControls({attribution:false, zoom: false}),
        interactions: defaultInteractions().extend([
          new DragRotateAndZoom()
        ])
      })
      
      // 传递 范围后，中心点，级别定位地图失效，根据范围来缩放地图----
      if (extent) {
        map.getView().fit(extent, map.getSize())
      }
      let layer = null
      if(config.layers(mapType)){//mapconfig中对象config中包含地图地址
         layer = config.layers(mapType)
      }
      if (layer) {
        map.addLayer(layer)
      }
      //测试区域高亮显示
      //中间站
        // var stops=[
        //     [12909554.6597,4933234.84552],   //14
        //     [12909824.6852,4931594.7854],    //43
        //     [12910026.8837,4930523.89946],   //63
        //     [12910870.563,4929357.26511]     //85
        // ];
      // 
      // //底图
      // var base = new TileLayer({
      //   source: new TileJSON({
      //     url: 'https://api.tiles.mapbox.com/v3/mapbox.world-light.json?secure',
      //     crossOrigin: 'anonymous'
      //   })
      // });
      // //测试控制范围的图层
    
      // var overlay = new TileLayer({
      //   extent: ol.proj.fromLonLat([121.26220782190506, 31.146874950477105,
      //            121.35463553612284, 31.22450427254761],'EPSG:3857'),
      //   source: new TileJSON({
      //     url: 'https://map.geoq.cn/ArcGIS/rest/services/ChinaOnlineCommunity/MapServer',
      //     crossOrigin: 'anonymous'
      //   })
      // });
      // if (overlay) {
      //   map.addLayer(overlay)
      // }
      // if (base) {
      //   map.addLayer(base)
      // }
      // 添加鹰眼控件----
      var ov = new Overview(
        {	layers: [layer],
          minZoom,
          maxZoom,
          zoom,
          center,
          projection
        })
      map.addControl(ov)
      // 添加比例尺控件
      var scaleLineControl = new CanvasScaleLine()
		  map.addControl(scaleLineControl)
      map.addControl(new ZoomSlider())
      map.addControl(new FullScreen())
      this.olmap = map
      Vue.prototype.$map = map
      this.$emit('ready', {//子组件传递方法给父组件
        map
      })
      // 地图移动结束事件
      this.olmap.on('moveend', ev => {
        let zoom = this.olmap.getView().getZoom()
        let center = this.olmap.getView().getCenter()
        let extent = this.olmap.getView().calculateExtent(this.olmap.getSize())
        this.$emit('zoom', { zoom, center, extent })
       // console.log("移动地图范围改变了")
      })
      // 地图开始移动事件
      this.olmap.on('movestart', ev => {
       
      })
      // 鼠标拖拽事件
      this.olmap.on('pointerdrag', ev => {
        let loc = ev.coordinate
        this.$emit('mousedrag', { lon: loc[0], lat: loc[1] })
      })
      // 鼠标移动事件
      this.olmap.on('pointermove', ev => {
        let loc = ev.coordinate
        let featurelist = []
        let obj = null
        this.forEachFeatureAtPixel(ev.pixel, (fea, layer) => {
          if (layer) {
            let zIndex = layer && layer.getZIndex()
            featurelist.push({index: zIndex, feature: fea})
          }
        })
        let vector = getFeatureOfMaxZIndex(featurelist)
        let feature = null 
        if (vector) {
          let flag = vector.get('features')
          if (flag) { // 表示该要素是聚合要素-------
               // 获取该聚合要素类含有的要素的个数----
              var size = vector.get('features').length
              if (size === 1) { 
                  feature = vector.get('features')[0]
              } else {
                  feature = null
              }
          } else { 
               feature = vector  
          }  
        } 
        if (feature) {
            obj = {
              lon: loc[0],
              lat: loc[1],
              feature,
              attributes: feature.get('attributes'),
            }
        } else {
            obj = {
              lon: loc[0],
              lat: loc[1]
            }
        }
        obj.dragging = ev.dragging
        this.$emit('mousemove', obj)
      })
      // 鼠标单击事件
      this.olmap.on('click', ev => {
        let loc = ev.coordinate
        let featurelist = []
        let center = []
        let obj = null
        this.forEachFeatureAtPixel(ev.pixel, (fea, layer) => {
          if (layer) {
            let zIndex = layer && layer.getZIndex()
            featurelist.push({index: zIndex, feature: fea})
          }
        })
        let vector = getFeatureOfMaxZIndex(featurelist)
        let feature = null
        if (vector) {
          let flag = vector.get('features')
          if (flag) { // 表示该要素是聚合要素-------
               // 获取该聚合要素类含有的要素的个数----
              var size = vector.get('features').length
              if (size === 1) { 
                  feature = vector.get('features')[0]
              } else {
                  feature = null
              }
          } else { 
               feature = vector  
          }  
        } 
        // 判断要素是否拾取到-------
        if (feature) {
            let type = feature.getGeometry().getType()
            if (type === GeometryType.POINT) {
              center = feature.getGeometry().getCoordinates()
            }
            if (type === GeometryType.POLYGON) {
              center = feature.getGeometry().getInteriorPoint().getCoordinates()
            }
            if (type === GeometryType.MULTIPOLYGON) {
              const bounds = feature.getGeometry().getExtent()
              center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2]
            }
            if (type === GeometryType.POLYLINE) {
              center = feature.getGeometry().getLastCoordinate()
            }
            obj = {
              lon: loc[0],
              lat: loc[1],
              feature,
              attributes: feature.get('attributes'),
              type: type,
              center: center
            }
        } else {
          obj = {
            lon: loc[0],
            lat: loc[1]
          }
        }
        this.$emit('click', obj)
      })
      // 鼠标单次单击事件
      this.olmap.on('singleclick', ev => {
        let loc = ev.coordinate
        this.$emit('singleclick', { lon: loc[0], lat: loc[1] })
      })
      // 鼠标双击事件
      this.olmap.on('dblclick', ev => {
        let loc = ev.coordinate
        this.$emit('dblclick', { lon: loc[0], lat: loc[1] })
      })
      // 地图渲染事件----渲染前
      this.olmap.on('precompose', ev => {
        this.$emit('precompose')
        // console.log('地图渲染前------地图渲染事件')
      })
      // 地图渲染事件----渲染后
      this.olmap.on('postcompose', ev => {
        this.$emit('postcompose', ev)
        // console.log('地图渲染后------地图渲染事件')
      })
      // 地图事件，渲染后----
      this.olmap.on('postrender', ev => {
        this.$emit('postrender')
        // console.log('地图渲染后------地图事件')
      })
      // 地图事件，渲染后----
      this.olmap.on('change:layerGroup', ev => {
        // console.log('地图事件---图层发生变化----')
      })
      // // 地图事件，渲染后----
      // this.olmap.on('change:view', ev => {
      //   console.log('地图视图发生改变-----')
      //   // console.log(ev)
      // })
      // 地图缩放事件
      this.olmap.getView().on('change:resolution', ev => {
        let zoom = this.olmap.getView().getZoom()
        let center = this.olmap.getView().getCenter()
        let extent = this.olmap.getView().calculateExtent(this.olmap.getSize())
        this.$emit('zoom', { zoom, center, extent })
        // console.log('地图缩放级别改变导致地图范围改变了')
        // console.log(extent + '范围')
        // console.log(center + '中心点')
      })
    },
    // 定位缩放地图-----
    centerAndZoom (x, y, level) {
      this.centerAt(x, y)
      this.setLevel(level)
    },
    // 定位地图-----
    centerAt (x, y) {
      this.olmap.getView().setCenter([x, y])
    },
    // 根据偏差定位地图-----
    centerAtWithOffset (x, y, offX, offY) {
      var _screenP = this.toScreen(x, y)
      _screenP.x = _screenP.x + offX
      _screenP.y = _screenP.y + offY
      var _mapP = this.toMap(_screenP.x, _screenP.y)
      this.centerAt(_mapP.x, _mapP.y)
    },
    // 设置地图缩放级别---
    setLevel (level) {
      this.olmap.getView().setZoom(level)
    },
    // 向左移动地图-----
    moveToLeft () {
      var view = this.olmap.getView()
      var mapCenter = view.getCenter()
      // 让地图中心的x值增加，即可使得地图向左移动，增加的值根据效果可自由设定
      mapCenter[0] += _panFactor
      view.setCenter(mapCenter)
      this.olmap.render()
    },
    // 向右移动地图-----
    moveToRight () {
      var view = this.olmap.getView()
      var mapCenter = view.getCenter()
      // 让地图中心的x值减少，即可使得地图向右移动，减少的值根据效果可自由设定
      mapCenter[0] -= _panFactor
      view.setCenter(mapCenter)
      this.olmap.render()
    },
    // 向上移动地图
    moveToUp () {
      var view = this.olmap.getView()
      var mapCenter = view.getCenter()
      // 让地图中心的y值减少，即可使得地图向上移动，减少的值根据效果可自由设定
      mapCenter[1] -= _panFactor
      view.setCenter(mapCenter)
      this.olmap.render()
    },
    // 向下移动地图
    moveToDown () {
      var view = this.olmap.getView()
      var mapCenter = view.getCenter()
      // 让地图中心的y值增加，即可使得地图向下移动，增加的值根据效果可自由设定
      mapCenter[1] += _panFactor
      view.setCenter(mapCenter)
      this.olmap.render()
    },
    // 获取当前地图的中心点------
    getCurrCenter () {
      return this.olmap.getView().getCenter()
    },
    // 地图级别
    getLevel () {
      return this.olmap.getView().getZoom()
    },
    // 设置地图范围
    setExtent (extent) {
      this.olmap.getView().fit([extent.minX, extent.minY, extent.maxX, extent.maxY], this.olmap.getSize())
    },
    // 获取地图当前范围------
    getCurrentExtent () {
      var extent = this.olmap.getView().calculateExtent(this.olmap.getSize())
      if (!extent) {
        return null
      }
      return {
        minX: extent[0],
        minY: extent[1],
        maxX: extent[2],
        maxY: extent[3]
      }
    },
    // 根据事件获取事件发生的位置----
    getEventCoordinate (event) {
      return this.olmap.getEventCoordinate(event)
    },
    // 判断经纬度代表的点是否在当前可视范围内---
    isInCurrentExtent (x, y) {

    },
    // 将像素点转化为经纬度
    toMap (x, y) {
      var pixel = [x, y]
      var coordinate = this.olmap.getCoordinateFromPixel(pixel)
      if (!coordinate) {
        return null
      }
      return {
        x: coordinate[0],
        y: coordinate[1]
      }
    },
    // 将经纬度点转化为像素点
    toScreen (x, y) {
      var coordinate = [x, y]
      var pixel = this.olmap.getPixelFromCoordinate(coordinate)
      if (!pixel) {
        return null
      }
      return {
        x: pixel[0],
        y: pixel[1]
      }
    },
    // 获取地图的视图对象
    getView () {
      return this.olmap.getView()
    },
    // 获取地图的所有图层对象--
    getAllLayers () {
      return this.olmap.getLayers().getArray()
    },
    // 获取添加到地图的所有图层的id-----
    getAllLayersID () {
      var layerIdArr = []
      var layers = this.getAllLayers()
      layers.forEach(function (_layer) {
        var id = _layer.get('id')
        layerIdArr.push(id)
      })
      return layerIdArr
    },
    // 根据图层id获取相应的图层对象----
    getLayer (layerId) {
      var layers = this.getAllLayers()
      var layer = null
      layers.forEach(function (_layer) {
        var id = _layer.get('id')
        if (id === layerId) {
          layer = _layer
        }
      })
      return layer
    },
    // 根据图层id判断相应图层是否已经添加地图中---
    isLayerExist (layerid) {
      return this.getLayer(layerid) == null
    },
    // 地图放大----
    zoomIn () {
      this.setLevel(this.getLevel() + 1)
    },
    // 地图缩小-----
    zoomOut () {
      this.setLevel(this.getLevel() - 1 >= 0 ? (this.getLevel() - 1) : 0)
    },
    // 根据配置文件添加图层
    addLayer (config) {
      var {
        id,
        name,
        isCluster = false,
        clusterDistance = 20,
        isAddToMap = true,
        z_index = 3
      } = config
      let layer = null
      if (this.getLayer(id)) {
        layer = this.getLayer(id)
      } else {
        var source = new VectorSource()
        if (isCluster) {
          source = new Cluster({
            source: new VectorSource(),
            distance: clusterDistance
          })
        }
        layer = new VectorLayer({
          source,
          id,
          name
        })
        if (isAddToMap) {
            this.olmap.addLayer(layer)
        }
      }
      layer.setZIndex(z_index)
      return layer
    },
    // 根据图层id删除地图-------
    removeLayer (id) {
      this.olmap.removeLayer(this.getLayer(id))
    },
    // 添加控件
    addControl (control) {
      this.olmap.addControl(control)
    },
    // 添加交互组件
    addInteraction (interaction, isActive = false) {
      this.olmap.addInteraction(interaction)
      interaction.setActive(isActive)
    },
    // 添加可视化组件------
    removeControl (control) {
      this.olmap.removeControl(control)
    },
    // 删除交互组件----
    removeInteraction (interaction) {
      this.olmap.removeInteraction(interaction)
    },
    getInteractions () {
      return this.olmap.getInteractions()
    },
    getControls () {
      return this.olmap.getControls()
    },
    // 获取所有的覆盖物----
    getOverlays () {
      return this.olmap.getOverlays()
    },
    // 添加覆盖物----
    addOverlay (overlay) {
      this.olmap.addOverlay(overlay)
    },
    // 删除覆盖物----
    removeOverlay (overlayer) {
      this.olmap.removeOverlay(overlayer)
    },
    // 根据id获取覆盖物-----
    getOverlayById (id) {
      return this.olmap.getOverlayById(id)
    },
    // 获取呢当前地图的视口----
    getViewport () {
      return this.olmap.getViewport()
    },
    // 根据像素点查询要素-----
    forEachFeatureAtPixel (pixel, callback) {
      this.olmap.forEachFeatureAtPixel(pixel, function (feature, layer) {
        callback(feature, layer)
      })
    },
    // 获取地图容器------
    getTargetElement () {
      return this.olmap.getTargetElement()
    },
    // 获取地图容器大小
    getSize () {
      return this.olmap.getSize()
    },
    // 判断该像素点是否能拾取要素对象----
    hasFeatureAtPixel (pix) {
      return this.olmap.hasFeatureAtPixel(pix)
    },
    // 清除id对应的图层的所有要素对象----
    clearLayerFeatures (id) {
      this.getLayer(id).getSource().clear()
    },
    // 重新渲染地图------
    render() {
      this.olmap.render()
    }
  },
  mounted () {
    this.initMap()
  }, 
  destroyed () {
    console.log("destroy map")
    this.olmap = null
  }
}
</script>
