openlayers/overlay/mapv/MapvBaseLayer.js Source
/**
 * modify to zondy mapv
 * @author 潘卓然 ParnDeedlit
 */

import {
    baiduMapLayer,
    DataSet
} from "mapv";
// import ol from 'ol';

var BaseLayer = baiduMapLayer ? baiduMapLayer.__proto__ : Function;

/**
 * @class ol.zondy.MapvBaseLayer
 * @classdesc MapV的核心渲染图层,这里是直接集成的baiduMapLayer,原因在于mapv的对外导出exports的就是baiduMapLayer
 * @param map - {Object} 传入的openlayer的地图对象
 * @param dataSet - {MapvDataSet} 传入的mapv的属性。 <br>
 * @param options - {MapvOption} 可选参数。<br>
 * @param leafletLayer - {Object} 传入的leaflet的实际渲染图层。<br>
 * 
 * @example
options = {
    zIndex: 1, // 层级
    size: 5, // 大小值
    unit: 'px', // 'px': 以像素为单位绘制,默认值。'm': 以米制为单位绘制,会跟随地图比例放大缩小
    mixBlendMode: 'normal', // 不同图层之间的叠加模式,参考[https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode](https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode)
    fillStyle: 'rgba(200, 200, 50, 1)', // 填充颜色
    strokeStyle: 'rgba(0, 0, 255, 1)', // 描边颜色
    lineWidth: 4, // 描边宽度
    globalAlpha: 1, // 透明度
    globalCompositeOperation: 'lighter', // 颜色叠加方式
    coordType: 'bd09ll', // 可选百度墨卡托坐标类型bd09mc和百度经纬度坐标类型bd09ll(默认)
    shadowColor: 'rgba(255, 255, 255, 1)', // 投影颜色
    shadowBlur: 35,  // 投影模糊级数
    updateCallback: function (time) { // 重绘回调函数,如果是时间动画、返回当前帧的时间
    },
    shadowOffsetX: 0,
    shadowOffsetY: 0,
    context: '2d', // 可选2d和webgl,webgl目前只支持画simple模式的点和线
    lineCap: 'butt',
    lineJoin: 'miter',
    miterLimit: 10,
    methods: { // 一些事件回调函数
        click: function (item) { // 点击事件,返回对应点击元素的对象值
            console.log(item);
        },
        mousemove: function(item) { // 鼠标移动事件,对应鼠标经过的元素对象值
            console.log(item);
        }
    },
    animation: {
        type: 'time', // 按时间展示动画
        stepsRange: { // 动画时间范围,time字段中值
            start: 0,
            end: 100
        },
        trails: 10, // 时间动画的拖尾大小
        duration: 5, // 单个动画的时间,单位秒
    }
}
 */
export class MapvBaseLayer extends BaseLayer {

    constructor(map, dataSet, options, leafletLayer, offset, pixelRatio) {
        super(map, dataSet, options);

        if (!BaseLayer) {
            return
        };

        this.map = map; //此处的map是外面传入的leaflet的map对象
        this.dataSet = dataSet;
        this.offset = offset;
        this.pixelRatio = pixelRatio;

        var self = this;
        var data = null;
        this.options = options || {};

        self.init(options);
        self.argCheck(options);

        this.initDevicePixelRatio();

        this.canvasLayer = leafletLayer;

        this.clickEvent = this.clickEvent.bind(this);
        this.mousemoveEvent = this.mousemoveEvent.bind(this);   

        this.bindEvent();
    }

    isEnabledTime() {
        var animationOptions = this.options.animation;
        return animationOptions && !(animationOptions.enabled === false);
    }

    /**
     * @function ol.zondy.MapvBaseLayer.prototype.clickEvent
     * @description 百度mapv原本的事件只有clickEvent和mousemoveEvent
     * @param e - {Object}  点击事件对象 {latlng, layerPoint, containerPoint, originalEvent}
     * @example 
     * //mapv.map.BaseLayer.clickEvent
     * clickEvent(pixel, e) {
     *    var dataItem = this.isPointInPath(this.getContext(), pixel);
     *    if (dataItem) {
     *       this.options.methods.click(dataItem, e);
     *    } else {
     *       this.options.methods.click(null, e);
     *    }
     *  }
     */
    clickEvent(e) {
        var offset = this.map.getPixelFromCoordinate([0, 0]); //this.map.getPixelOrigin()
        var devicePixelRatio = this.devicePixelRatio || 1;
        var canvasPoint = e.layerPoint;
        var pixel = {
            x: (canvasPoint[0] - this.offset[0]) / devicePixelRatio,
            y: (canvasPoint[1] - this.offset[1]) / devicePixelRatio
        }
        //super.clickEvent(pixel, e);
    }

    /**
     * @function ol.zondy.MapvBaseLayer.prototype.mousemoveEvent
     * @description 百度mapv原本的事件只有clickEvent和mousemoveEvent
     * @param e - {Object}  点击事件对象 {latlng, layerPoint, containerPoint, originalEvent}
     * @example 
     * //mapv.map.BaseLayer.mousemoveEvent
     * mousemoveEvent(pixel, e) {
     *   var dataItem = this.isPointInPath(this.getContext(), pixel);
     *   if (dataItem) {
     *       this.options.methods.mousemove(dataItem, e);
     *   } else {
     *       this.options.methods.mousemove(null, e);
     *   }
     * }
     */
    mousemoveEvent(e) {
        if (!e) {
            return;
        }
        var offset = this.map.getPixelFromCoordinate([0, 0]); //this.map.getPixelOrigin()
        var devicePixelRatio = this.devicePixelRatio || 1;
        var canvasPoint = e.layerPoint;
        var pixel = {
            x: (canvasPoint[0] - offset[0]) / devicePixelRatio,
            y: (canvasPoint[1] - offset[1]) / devicePixelRatio
        }
        //super.mousemoveEvent(pixel, e);
    }

    addAnimatorEvent() {
        this.map.on('movestart', this.animatorMovestartEvent.bind(this));
        this.map.on('moveend', this.animatorMoveendEvent.bind(this));
    }

    removeAnimatorEvent() {
        this.map.off('movestart', this.animatorMovestartEvent.bind(this));
        this.map.off('moveend', this.animatorMoveendEvent.bind(this));
    }

    animatorMovestartEvent() {
        var animationOptions = this.options.animation;
        if (this.isEnabledTime() && this.animator) {
            this.steps.step = animationOptions.stepsRange.start;
            this.animator.stop();
        }
    }

    animatorMoveendEvent() {
        if (this.isEnabledTime() && this.animator) {
            this.animator.start();
        }
    }

    bindEvent(e) {
        var map = this.map;
        this.addAnimatorEvent();

        if (this.options.methods) {
            if (this.options.methods.click) {
                map.on('click', this.clickEvent);
            }
            if (this.options.methods.mousemove) {
                map.on('mousemove', this.mousemoveEvent);
            }
        }
    }

    unbindEvent(e) {
        var map = this.map;
        this.removeAnimatorEvent();
        if (this.options.methods) {
            if (this.options.methods.click) {
                map.removeListener('click', this.clickEvent);
            }
            if (this.options.methods.mousemove) {
                map.removeListener('mousemove', this.mousemoveEvent);
            }
        }
    }

    /**
     * @function ol.zondy.MapvBaseLayer.prototype.initDevicePixelRatio
     * @description window.devicePixelRatio是设备上物理像素和设备独立像素(device-independent pixels (dips))的比例。
     * 公式表示就是:window.devicePixelRatio = 物理像素 / dips,该函数主要应用与移动设备
     */
    initDevicePixelRatio() {
        this.devicePixelRatio = window.devicePixelRatio || 1;
    }

    getContext() {
        return this.canvasLayer.getCanvas().getContext(this.context);
    }

    _canvasUpdate(time) {
        if (!this.canvasLayer || this.canvasLayer.disposeFlag) {
            return;
        }

        var self = this;
        var map = this.map;

        var animationOptions = self.options.animation;

        var context = this.getContext();

        if (self.isEnabledTime()) {
            if (time === undefined) {
                this.clear(context);
                return;
            }
            if (this.context == '2d') {
                context.save();
                context.globalCompositeOperation = 'destination-out';
                context.fillStyle = 'rgba(0, 0, 0, .1)';
                context.fillRect(0, 0, context.canvas.width, context.canvas.height);
                context.restore();
            }
        } else {
            this.clear(context);
        }

        if (this.context == '2d') {
            for (var key in self.options) {
                context[key] = self.options[key];
            }
        } else {
            context.clear(context.COLOR_BUFFER_BIT);
        }

        /* if (self.options.minZoom && map.getZoom() < self.options.minZoom || self.options.maxZoom && map.getZoom() > self.options.maxZoom) {
            return;
        } */

        /* var scale = 1;
        if (this.context != '2d') {
            scale = this.canvasLayer.devicePixelRatio;
        } */

        var extent = map.getView().calculateExtent();
        var lefttop = map.getPixelFromCoordinate([extent[0], extent[3]]);

        self._mapCenter = map.getView().getCenter();
        self._mapCenterPx = map.getPixelFromCoordinate(self._mapCenter);
        
        self._reselutions = map.getView().getResolution();
        self._rotation = -map.getView().getRotation();

        var scaleRatio = self.pixelRatio || 1;
        var dataGetOptions = {
            // 将经纬度转换成屏幕上的点
            transferCoordinate: function (coordinate) {
                // get center from the map (projected)
                /* var point = map.latLngToContainerPoint(new ol.LatLng(coordinate[1], coordinate[0]));
                return [point.x - offset.x, point.y - offset.y]; */

                var x = (coordinate[0] - self._mapCenter[0]) / self._reselutions,
                    y = (self._mapCenter[1] - coordinate[1]) / self._reselutions;
                var scaledP = [x + self._mapCenterPx[0], y + self._mapCenterPx[1]];
                scaledP = scale(scaledP, self._mapCenterPx, 1);
                return [(scaledP[0] + self.offset[0]) * scaleRatio, 
                (scaledP[1] + self.offset[1]) * scaleRatio];
            }
        }

        function scale(pixelP, center, scaleRatio) {
            var x = (pixelP[0] - center[0]) * scaleRatio + center[0];
            var y = (pixelP[1] - center[1]) * scaleRatio + center[1];
            return [x, y];
        }

        if (time !== undefined) {
            dataGetOptions.filter = function (item) {
                var trails = animationOptions.trails || 10;
                if (time && item.time > (time - trails) && item.time < time) {
                    return true;
                } else {
                    return false;
                }
            }
        }

        var data = self.dataSet.get(dataGetOptions);

        this.processData(data);

        if (self.options.unit == 'm' && self.options.size) {
            //self.options._size = self.options.size / zoomUnit;
            self.options._size = self.options.size;
        } else {
            self.options._size = self.options.size;
        }

        var originpoint = map.getPixelFromCoordinate([0, 0]);
        var pixel = {
            x: originpoint[0] - lefttop[0],
            y: originpoint[1] - lefttop[1]
        };

        this.drawContext(context, new DataSet(data), self.options, pixel);
        self.options.updateCallback && self.options.updateCallback(time);
    }

    init(options) {

        var self = this;

        self.options = options;

        this.initDataRange(options);

        this.context = self.options.context || '2d';

        if (self.options.zIndex) {
            this.canvasLayer && this.canvasLayer.setZIndex(self.options.zIndex);
        }

        this.initAnimator();
    }

    setOffset(offset) {
        this.offset = offset;
    }

    updateData(data, options) {
        var _data = data;
        if (_data && _data.get) {
            _data = _data.get();
        }
        if (_data != undefined) {
            this.dataSet.set(_data);
        }

        super.update({
            options: options
        });

    }

    addData(data, options) {
        var _data = data;
        if (data && data.get) {
            _data = data.get();
        }
        this.dataSet.add(_data);
        this.update({
            options: options
        });
    }

    draw() {
        this.canvasLayer.draw();
    }

    //该函数从mapv/canvas/clear中提取
    clear(context) {
        context = context || this.getContext();
        context && context.clearRect && context.clearRect(0, 0, context.canvas.width, context.canvas.height);
    }

}