Source: easyMap.js

/**
 * 二次封装地图对象
 */

import * as maptalks from "./libs/maptalks"
import Tool from './ext/tool/index'
import Geom from './ext/geom/index'
import * as layers from './ext/layer'
import EasyDrawTool from './ext/draw/EasyDrawTool'
import * as controls from './ext/control'
import InfoWindow from './ext/InfoWindow'
import 'xgplayer'
import FlvJsPlayer from 'htxgplayer-flv.js'

class EasyMap {
    /**
     * EasyMap地图类
     * @constructor
     * @param {String} container - DOM元素id
     * @param {Object} options - 配置项
     * @param {EasyTileLayer} options.defaultLayer - 默认基础图层
     * @param {Array} options.center - 地图中心位置,默认为 [114.515013, 38.041251]
     * @param {Array} options.extent - 地图范围
     * @param {Boolean} options.zoom - 地图初始缩放级别,默认为7
     * @param {Boolean} options.seamlessZoom - 是否使用无缝缩放模式,默认为false
     * @param {number|undefined} options.minZoom - 地图最小缩放级别
     * @param {number|undefined} options.maxZoom - 地图最大缩放级别
     * @param {boolean|undefined} options.zoomControl - 是否显示地图缩放控件,默认为 false
     * @param {boolean|undefined} options.scaleControl - 是否显示比例尺控件,默认为 false
     * @param {boolean|undefined} options.overviewControl - 是否显示鹰眼控件,默认为 false
     */
    constructor(container, options = {}) {
        this.curIndex = 0
        this.map = null
        // 默认图层
        this._layers = [new layers.EasyVectorLayer('default_vector')]
        // 基础图层
        if (options.defaultLayer !== undefined) {
            this._baseLayer = options.defaultLayer
        } else {
            this._baseLayer = new layers.EasyTileLayer('base', {
                urlTemplate: 'http://webrd{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
                subdomains: ['01', '02', '03', '04']
            })
        }
        this.container = container
        // 默认配置对象
        const defaultOption = {
            center: [114.515013, 38.041251], // 石家庄
            zoom: 7,
            layers: [this._layers[0].layer],
            seamlessZoom: false,
            zoomControl: false,
            scaleControl: false,
            overviewControl: false,
            baseLayer: this._baseLayer.layer,
            attribution: false
        };
        //合并配置
        let option = {}
        Object.assign(option, defaultOption, options)
        this._initMap(option)
    }

    /**
      * 初始化地图
      * @param {Object} option - 地图初始化配置项
      */
    _initMap(option) {
        this.map = new maptalks.Map(this.container, option)
        this._baseLayer.map = this
        this._layers.forEach(e => {
            e.map = this
        })
    }

    /**
     * 获取空间参考
     * @returns {*} 空间参考
     */
    getSpatialReference() {
        return this.map.getSpatialReference()
    }

    /**
     * 设置空间参考
     * @param {Object} spatialReference 要设置的空间参考
     * @returns {EasyMap}
     */
    setSpatialReference(spatialReference) {
        this.map.setSpatialReference(spatialReference)
        return this
    }

    /**
     * 获取地图的当前缩放等级
     * @return Integer 缩放等级
     */
    getZoom() {
        return this.map.getZoom()
    }

    /**
     * 设置地图的缩放等级
     * @param zoom
     * @param options
     * @return {EasyMap}
     */
    setZoom(zoom, options = void 0) {
        this.map.setZoom(zoom, options)
        return this
    }

    /**
     * 放大地图,缩放等级+1
     * @return {EasyMap}
     */
    zoomIn() {
        this.map.zoomIn()
        return this
    }

    /**
     * 缩小地图,缩放等级-1
     * @return {EasyMap}
     */
    zoomOut() {
        this.map.zoomOut()
        return this
    }

    /**
     * 获取地图的中心点
     * @param {String} type 返回的坐标类型,默认为Number数组
     * @returns {maptalks.Coordinate|Array<Number>} 坐标点
     */
    getCenter(type = null) {
        const coordinate = this.map.getCenter()
        if (type === 'coordinate') {
            return coordinate
        } else {
            return maptalks.Coordinate.toNumberArrays(coordinate).map(n => n.toFixed(6))
        }
    }

    /**
     * 设置地图的中心点
     * @param center Coordinate 中心点
     * @return {EasyMap}
     */
    setCenter(center) {
        this.map.setCenter(center)
        return this
    }

    /**
     * 获取地图的最大范围
     * @return Extent 范围,[xmin,ymin,xmax,ymax]
     */
    getMaxExtent() {
        return this.map.getMaxExtent()
    }

    /**
     * 设置地图的最大范围
     * @param extent Extent 范围,[xmin,ymin,xmax,ymax]
     * @return {EasyMap}
     */
    setMaxExtent(extent) {
        this.map.setMaxExtent(extent)
        return this
    }

    /**
     * 添加图层
     * @param {EasyLayer} layer 要添加的图层
     * @return {EasyMap}
     */
    addLayer(layer) {
        this.map.addLayer(layer.layer)
        this._layers.push(layer)
        return this
    }

    /**
     * 根据图层id获取图层
     * @param id String 图层id
     * @return {EasyLayer|undefined} 获取到的图层
     */
    getLayer(id) {
        let layer = undefined
        this._layers.forEach(e => {
            if (e.id === id) {
                layer = e
            }
        })
        return layer
    }

    /**
     * 获取基础图层
     * @returns {*}
     */
    getBaseLayer() {
        return this._baseLayer
    }

    /**
     * 获取所有图层
     * @returns {[EasyLayer]} 获取到的图层
     */
    getLayers(filter = null) {
        if (filter === null) {
            filter = function() {
                return true
            }
        }
        const layers = []
        this._layers.forEach(e => {
            if (filter(e)) {
                layers.push(e)
            }
        })
        return layers
    }

    /**
     * 移除指定图层
     * @param {EasyLayer} layer
     * @return {EasyMap}
     */
    removeLayer(layer) {
        this.map.removeLayer(layer.layer)
        this._layers.splice(this._layers.findIndex(e => e.id === layer.id), 1)
        return this
    }

    /**
     * 事件绑定
     * @param {String} eventsOn 要注册的事件类型
     * @param {*} handler 要调用的处理函数
     * @param {*} context 处理程序的上下文
     */
    on(eventsOn, handler, context = null) {
        this.map.on(eventsOn, handler, context)
    }

    /**
     * 事件绑定,别名
     * @param {String} eventsOn 要注册的事件类型
     * @param {*} handler 要调用的处理函数
     * @param {*} context 处理程序的上下文
     */
    addMapEventListener(eventsOn, handler, context = null) {
        this.map.addEventListener(eventsOn, handler, context)
    }

    /**
     * 单次事件绑定,调用一次后移除
     * @param {String} eventTypes 要注册的事件类型
     * @param {*} handler 要调用的处理函数
     * @param {*} context 处理程序的上下文
     */
    once(eventTypes, handler, context = null) {
        this.map.once(eventTypes, handler, context)
    }

    /**
     * 单次事件绑定,调用一次后移除,别名
     * @param {String} eventTypes 要注册的事件类型
     * @param {*} handler 要调用的处理函数
     * @param {*} context 处理程序的上下文
     */
    addMapEventListenerOnce(eventTypes, handler, context = null) {
        this.map.once(eventTypes, handler, context)
    }

    /**
     * 事件移除
     * @param {String} eventsOff 要移除的事件类型
     * @param {*} handler 要调用的处理函数
     * @param {*} context 处理程序的上下文
     */
    un(eventsOff, handler, context = null) {
        this.map.off(eventsOff, handler, context)
    }

    /**
     * 事件移除,别名
     * @param {String} eventsOff 要移除的事件类型
     * @param {*} handler 要调用的处理函数
     * @param {*} context 处理程序的上下文
     */
    removeMapEventListener(eventsOff, handler, context = null) {
        this.map.removeEventListener(eventsOff, handler, context)
    }

    /**
     * 触发事件
     * @param eventType 要触发的事件类型
     * @param param 传给handler的参数
     */
    fire(eventType, param) {
        this.map.fire(eventType, param)
    }

    /**
     *清空指定图层上的内容,如果不传递图层id则清空所有图层上的内容
     * @param id 图层id
     */
    clean(id) {
        if (!id) {
            let layers = this.map.getLayers()
            for (let i = 0; i < layers.length; i++) {
                layers[i].clear()
            }
        }else{
            let layer = this.map.getLayer(id)
            if (layer){
                layer.clear()
            }
        }
    }

    /**
     * 测距
     */
    measureLine() {
        let distanceTool = new maptalks.DistanceTool({
            'symbol': {
                'lineColor': '#34495e',
                'lineWidth': 2
            },
            'vertexSymbol': {
                'markerType': 'ellipse',
                'markerFill': '#1bbc9b',
                'markerLineColor': '#000',
                'markerLineWidth': 3,
                'markerWidth': 10,
                'markerHeight': 10
            },

            'labelOptions': {
                'textSymbol': {
                    'textFaceName': 'monospace',
                    'textFill': '#fff',
                    'textLineSpacing': 1,
                    'textHorizontalAlignment': 'right',
                    'textDx': 15,
                    'markerLineColor': '#b4b3b3',
                    'markerFill': '#000'
                },
                'boxStyle': {
                    'padding': [6, 2],
                    'symbol': {
                        'markerType': 'square',
                        'markerFill': '#000',
                        'markerFillOpacity': 0.9,
                        'markerLineColor': '#b4b3b3'
                    }
                }
            },
            'clearButtonSymbol': [{
                'markerType': 'square',
                'markerFill': '#000',
                'markerLineColor': '#b4b3b3',
                'markerLineWidth': 2,
                'markerWidth': 15,
                'markerHeight': 15,
                'markerDx': 20
            }, {
                'markerType': 'x',
                'markerWidth': 10,
                'markerHeight': 10,
                'markerLineColor': '#fff',
                'markerDx': 20
            }],
            'language': 'zh-CN'
        }).addTo(this.map);
    }

    /**
     * 测面积
     */
    measureArea(func) {
        let areaTool = new maptalks.AreaTool({
            'symbol': {
                'lineColor': '#1bbc9b',
                'lineWidth': 2,
                'polygonFill': '#fff',
                'polygonOpacity': 0.3
            },
            'vertexSymbol': {
                'markerType': 'ellipse',
                'markerFill': '#34495e',
                'markerLineColor': '#1bbc9b',
                'markerLineWidth': 3,
                'markerWidth': 10,
                'markerHeight': 10
            },
            'labelOptions': {
                'textSymbol': {
                    'textFaceName': 'monospace',
                    'textFill': '#fff',
                    'textLineSpacing': 1,
                    'textHorizontalAlignment': 'right',
                    'textDx': 15
                },
                'boxStyle': {
                    'padding': [6, 2],
                    'symbol': {
                        'markerType': 'square',
                        'markerFill': '#000',
                        'markerFillOpacity': 0.9,
                        'markerLineColor': '#b4b3b3'
                    }
                }
            },
            'clearButtonSymbol': [{
                'markerType': 'square',
                'markerFill': '#000',
                'markerLineColor': '#b4b3b3',
                'markerLineWidth': 2,
                'markerWidth': 15,
                'markerHeight': 15,
                'markerDx': 22
            }, {
                'markerType': 'x',
                'markerWidth': 10,
                'markerHeight': 10,
                'markerLineColor': '#fff',
                'markerDx': 22
            }],
            language: 'zh-CN'
        }).addTo(this.map);
        func(areaTool.getLastMeasure())
    }

    /**
     * 获取两个坐标点间的距离
     * @param {Array} coord1 坐标点1
     * @param {Array} coord2 坐标点2
     * @returns {null|number} 距离/单位:米
     */
    getLength(coord1, coord2) {
        return this.map.computeLength(coord1, coord2)
    }

    /**
     * 获取地理区域的面积
     * @param {Array} coords 区域坐标数组
     * @returns {null|number} 面积/单位:平方米
     */
    getArea(coords) {
        return new maptalks.Polygon([coords]).getArea()
    }

    /**
     * 所有交互启动/停止
     * @param{boolean} onOrOff true:启动交互 false:禁用交互
     * @param{String} type 交互类型:draggable,zoomable,scrollWheelZoom,touchZoom,doubleClickZoom
     */
    interaction(onOrOff,type) {
        if (!type){
            this.map.config('draggable', onOrOff);
            this.map.config('zoomable', onOrOff);
            this.map.config('scrollWheelZoom', onOrOff);
            this.map.config('touchZoom', onOrOff);
            this.map.config('doubleClickZoom', onOrOff);
        }else{
            this.map.config(type, onOrOff);
        }
    }

    /**
     * 显示图层控件
     */
    showMapControl(){
        this.showZoomControl()
        this.showScaleControl()
        this.showOverviewControl()
    }

    /**
     * 隐藏图层控件
     */
    hideMapControl(){
        this.closeZoomControl()
        this.closeScaleControl()
        this.closeOverviewControl()
    }

    /**
     * 显示缩放控件
     */
    showZoomControl() {
        if (this.map.zoomControl){
            if(!this.map.zoomControl.isVisible()){
                this.map.zoomControl.show()
            }
        }else{
            let zoom = new maptalks.control.Zoom({
                'position'  : 'top-left',
                'slider'    : true,
                'zoomLevel' : true
            });
            zoom.addTo(this.map)
            this.map.zoomControl = zoom
        }
    }

    /**
     * 关闭缩放控件
     */
    closeZoomControl() {
        if (this.map.zoomControl){
            this.map.zoomControl.hide()
        }
    }

    /**
     * 设置缩放控件
     * @param{Object} options
     */
    setZoomControl(options) {
        console.log(this.map);
        if (options){
            if (this.map.zoomControl){
                let option = {}
                let defaultOption = this.map.zoomControl.options
                Object.assign(option, defaultOption, options);
                this.map.zoomControl.remove()//?必须先移除原来的控件,再设置,否则'position'属性不起作用
                let zoom = new maptalks.control.Zoom(option);
                zoom.addTo(this.map)
                this.map.zoomControl = zoom
            }
        }
    }

    /**
     * 显示比例尺控件
     */
    showScaleControl() {
        if (this.map.scaleControl){
            if(!this.map.scaleControl.isVisible()){
                this.map.scaleControl.show()
            }
        }else{
            let metric = new maptalks.control.Scale({
                'position'  : 'bottom-left',
                'maxWidth': 150,
                'metric': true,
                'imperial': false
            });
            metric.addTo(this.map)
            this.map.scaleControl = metric
        }
    }

    /**
     * 关闭比例尺控件
     */
    closeScaleControl() {
        if (this.map.scaleControl){
            this.map.scaleControl.hide()
        }
    }

    /**
     * 设置比例尺控件
     */
    setScaleControl(options) {
        if (options){
            if (this.map.scaleControl){
                let option = {}
                let defaultOption = this.map.scaleControl.options
                Object.assign(option, defaultOption, options);
                this.map.scaleControl.remove()//?必须先移除原来的控件,再设置,否则'position'属性不起作用
                let metric = new maptalks.control.Scale(option);
                metric.addTo(this.map)
                this.map.scaleControl = metric
            }
        }
    }

    /**
     * 显示鹰眼控件
     */
    showOverviewControl() {
        // this.map.overviewControl.show()
        if (this.map.overviewControl){
            if(!this.map.overviewControl.isVisible()){
                this.map.overviewControl.show()
            }
        }else{
            let overview = new maptalks.control.Overview();
            overview.addTo(this.map)
            this.map.overviewControl = overview
        }
    }

    /**
     * 关闭鹰眼控件
     */
    closeOverviewControl() {
        if (this.map.overviewControl){
            this.map.overviewControl.hide()
        }
    }

    /**
     * 设置鹰眼控件
     */
    setOverviewControl(options) {
        if (options){
            if (this.map.overviewControl){
                let option = {}
                let defaultOption = this.map.overviewControl.options
                Object.assign(option, defaultOption, options);
                this.map.overviewControl.remove()//?必须先移除原来的控件,再设置,否则'position'属性不起作用
                let overview = new maptalks.control.Overview(option);
                overview.addTo(this.map)
                this.map.overviewControl = overview
            }
        }
    }
    /**
     * 创建热力图层
     * @param{String} id 图层id
     * @param{Array} data
     * @param{Object} options
     * @returns {EasyHeatmapLayer} 热力图层
     */
    createHeatMap(id,data,options){
        let heatmapLayer = new layers.EasyHeatmapLayer(id,data,options)
        heatmapLayer.addTo(this)
        return heatmapLayer
    }
    /**
     * 创建聚合图层
     * @param{String} id
     * @param{Array} data
     * @param{Object} options
     * @returns {EasyClusterLayer} 聚合图层
     */
    createClusterLayer(id,data,options){
        let clusterLayer = new layers.EasyClusterLayer(id,data,options)
        clusterLayer.addTo(this)
        return clusterLayer
    }
    /**
     * 显示百度地图
     */
    setBdBaseLayer(){
        this.map.removeLayer('skymarker')
        this.map.setBaseLayer(new maptalks.TileLayer("base",{
            urlTemplate:'http://online{s}.map.bdimg.com/onlinelabel/?qt=tile&x={x}&y={y}&z={z}&styles=pl&scaler=1&p=1',
            subdomains:[0,1,2,3,4,5,6,7,8,9],
        }))
        this.map.config("spatialReference",{ projection : 'baidu' })
    }
    /**
     * 显示高德地图
     */
    setGdBaseLayer(){
        this.map.removeLayer('skymarker')
        this.map.setBaseLayer(new maptalks.TileLayer("base",{
            urlTemplate: 'http://webrd{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
            subdomains: ['01', '02', '03', '04']
        }))
        this.map.config("spatialReference",{ projection : 'EPSG:3857' })
    }
    /**
     * 显示天地图
     */
    setSkyBaseLayer(){
        this.map.removeLayer('skymarker')
        this.map.setBaseLayer(new maptalks.TileLayer("base",{
            crossOrigin: "Anonymous",
            urlTemplate:'http://t{s}.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=de0dc270a51aaca3dd4e64d4f8c81ff6',
            subdomains:[1, 2, 3, 4],
        }))
        this.map.addLayer(new maptalks.TileLayer("skymarker",{
            crossOrigin: "Anonymous",
            urlTemplate:'http://t{s}.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=de0dc270a51aaca3dd4e64d4f8c81ff6',
            subdomains:[1, 2, 3, 4],
        }))
        this.map.config("spatialReference",{ projection : 'EPSG:3857' })
    }
    /**
     * 绘制省会边界
     * @param {Array}  arr 省会坐标
     * @param {Object} option 填充选项
     * @param {String} option.lineColor 边样式
     * @param {String} option.lineWidth 边宽度
     * @param {String} option.polygonFill 填充颜色
     * @param {String} option.polygonOpacity 透明度 0-1
     */
    showBorder(arr,option){
        this.provinces = arr
        let options = {};
        let defaultOption = {
            'lineColor' : '#34495e',
            'lineWidth' : 1,
            'polygonFill' : 'rgb(0,0,0)',
            'polygonOpacity' : 0.0
          }
        Object.assign(options, defaultOption, option);
        for(let i=0;i<arr.length;i++){
            console.log(JSON.parse(arr[i].points));
            let polygon = new maptalks.MultiPolygon(
                JSON.parse(arr[i].points)
              , {
                visible : true,
                editable : true,
                cursor : 'pointer',
                shadowBlur : 0,
                shadowColor : 'black',
                draggable : false,
                dragShadow : false, // display a shadow during dragging
                drawOnAxis : null,  // force dragging stick on a axis, can be: x, y
                symbol: options
              });
              new maptalks.VectorLayer('vector'+i, polygon).addTo(this.map);
        }
    }
    /**
     * 设置某个省市的填充颜色,边框宽度,透明度等样式
     * @param {*} name 省市的名称,或者代码
     * @param {Object} option 样式选项
     * @param {String} option.lineColor 边样式
     * @param {String} option.lineWidth 边宽度
     * @param {String} option.polygonFill 填充颜色
     * @param {String} option.polygonOpacity 透明度 0-1
     */
    setBorder(name,option){
        this.map.config('pitch',45)
        let options = {};
        let defaultOption = {
            'lineColor' : '#34495e',
            'lineWidth' : 1,
            'polygonFill' : 'rgb(0,0,0)',
            'polygonOpacity' : 0.0
          }
        Object.assign(options, defaultOption, option);

        for(let i=0;i<this.provinces.length;i++){
            if(this.provinces[i].name === name
                || this.provinces[i].code === name){
                let layer = this.map.getLayer('vector'+i)
                let geos = layer.getGeometries()
                for(let x=0;x<geos.length;x++){
                    let geo = geos[x]
                    geo.setSymbol(options)
                }

                break
            }
        }
    }

    /**
     * 切换地图风格
     * @param {Number} type 0:科技灰 1:科技蓝 2:黑夜
     */
    changeMapFilter(type){
        if(type === 0){
            let baseLayer = this.map.getBaseLayer()
            baseLayer.config('cssFilter','grayscale(100%)')//灰色
        }else if(type === 1){
            let baseLayer = this.map.getBaseLayer()
            baseLayer.config('cssFilter','sepia(100%) invert(90%)')//蓝色
        }else if(type === 2){
            let baseLayer = this.map.getBaseLayer()
            baseLayer.config('cssFilter','invert(100%)')//黑色
        }
    }

}
export {
    maptalks, EasyMap, Tool, layers, EasyDrawTool, Geom, controls, InfoWindow, FlvJsPlayer
}