components/layer/radarLayer.js

/*``
 * @Author: tjc
 * @Date:   2017-04-20 17:03:05
 * @Last Modified by:   hydata
 * @Last Modified time: 2018-05-10 17:12:02
 * @Email: 304861063@qq.com
 * @File Path: F:\work\hyMap\src\components\layer\buildingLayer.js
 * @File Name: clustersLayer.js
 * @Descript:雷达图,利用three进行绘制
 */
import * as THREE from 'three';
import mapboxgl from 'mapbox-gl';
import baseLayer from './baseLayer';

/**
 * 雷达层
 * @extends baseLayer
 */
class RadarLayer extends baseLayer {
    /**
     * 初始化
     * @param {*} options
     */
    constructor(options) {

        super(options);
        this.options = options;
        this.threebox = options.threebox;
        this.play = false;
        this.init();


    }

    /**
     * 添加雷达图
     */
    init() {

        if (!this.play) {

            this.addRadarToMap(this.options);
            this.play = true;

        }

    }

    /**
     * 添加雷达图
     * @param center 中心点坐标
     * @param radius 半径
     * @param color  渲染颜色
     */
    addRadarToMap({center, radius, color} = {}) {

        this.radius = radius;
        radius = radius * 1000;

        // //创建组
        // this.group = new THREE.Group();

        //创建圆形区域并添加至地图
        let circleGeometry = new THREE.CircleBufferGeometry(radius, 72);
        circleGeometry.addAttribute('position', circleGeometry.attributes.position);
        let cMat = this.createCircleMaterial({
            color: color,
            radius: radius
        });
        this.circleMesh = new THREE.Mesh(circleGeometry, cMat);
        this.threebox.addAtCoordinate(this.circleMesh, center, {
            scaleToLatitude: true,
            preScale: 1
        });

        //创建扇形区域并添加至地图
        let sectorGeometry = new THREE.CircleBufferGeometry(radius, 72, 2 * Math.PI / 3, Math.PI / 3);
        sectorGeometry.addAttribute('position', sectorGeometry.attributes.position);
        let sMat = this.createSectorMaterial({
            color: color,
            radius: radius
        });
        this.sectorMesh = new THREE.Mesh(sectorGeometry, sMat);
        this.threebox.addAtCoordinate(this.sectorMesh, [center[0], center[1], 5], {
            scaleToLatitude: true,
            preScale: 1
        });


        //循环转动扇形
        const run = () => {

            this.sectorMesh.rotateZ(-Math.PI / 60);
            this.threebox.update();
            this.animation = window.requestAnimationFrame(run);

        };
        run();

        // this.addSliderSymbol(raderAttr);


    }

    /**
     * 生成圆形材质
     * @param opt
     * @returns {RawShaderMaterial}
     */
    createCircleMaterial = opt => {

        opt = opt || {};
        let ret = {
            uniforms: {
                color: {type: 'c', value: new THREE.Color(opt.color !== null ? opt.color : '#ff0000')},
                radius: {type: 'f', value: (typeof opt.radius === 'number' ? opt.radius : 3000.000)}
            },
            vertexShader: [
                'precision mediump float;',
                'precision mediump int;',
                'uniform mat4 modelViewMatrix;',
                'uniform mat4 projectionMatrix;',

                'attribute vec3 position;',
                'varying vec3 vPosition;',
                'void main()	{',
                //'vPosition = position.xyz + vec3(1000, 0,0);',
                'vPosition = position;',
                'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',

                '}'
            ].join('\n'),
            fragmentShader: [
                'precision mediump float;',
                'precision mediump int;',

                'uniform vec3 color;',
                'uniform float radius;',

                'varying vec3 vPosition;',

                'void main(){',
                'vec4 vcolor = vec4( color,1);',
                'float opacity=sqrt(vPosition.x*vPosition.x+vPosition.y*vPosition.y)/radius/1.5;',//透明度沿半径方向衰减
                'vcolor.a = opacity*opacity*opacity ;',
                'gl_FragColor = vcolor;',
                '}'
            ].join('\n'),
            transparent: true,
            side: THREE.DoubleSide
        };
        return new THREE.RawShaderMaterial(ret);

    };

    /**
     * 生成扇形材质
     * @param opt
     * @returns {RawShaderMaterial}
     */
    createSectorMaterial = opt => {

        opt = opt || {};
        let ret = {
            uniforms: {
                color: {type: 'c', value: new THREE.Color(opt.color !== null ? opt.color : '#ff0000')},
                radius: {type: 'f', value: (typeof opt.radius === 'number' ? parseFloat(opt.radius) : 3000.000)}
            },
            vertexShader: [
                'precision mediump float;',
                'precision mediump int;',
                'uniform mat4 modelViewMatrix;',
                'uniform mat4 projectionMatrix;',

                'attribute vec3 position;',
                'varying vec3 vPosition;',
                'void main()	{',
                'vPosition = position;',
                'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
                '}'
            ].join('\n'),
            fragmentShader: [
                'precision mediump float;',
                'precision mediump int;',

                'uniform vec3 color;',
                'uniform float radius;',

                'varying vec3 vPosition;',

                'void main(){',
                'vec4 vcolor = vec4( color,1);',
                'float angel=atan(abs(vPosition.y)/(2.00000*radius-abs(vPosition.x)));',
                'float opacity= angel/(3.1415/4.00000);',//透明度随角度递增而增加
                'vcolor.a =opacity ;',
                'gl_FragColor = vcolor;',
                '}'
            ].join('\n'),
            transparent: true,
            side: THREE.DoubleSide
        };
        return new THREE.RawShaderMaterial(ret);

    };


    /**
     * 改变雷达半径
     * @param position
     * @param newRadius
     */
    setRadarOptions(position, newRadius) {

        this.threebox.moveToCoordinate(this.circleMesh, position, {
            preScale: newRadius / this.radius
        });
        let sectorCoordinate = [position[0], position[1], 5];
        this.threebox.moveToCoordinate(this.sectorMesh, sectorCoordinate,
            {
                preScale: newRadius / this.radius
            });

    }

    /**
     *@在雷达图上添加滑动和关闭按钮
     *@author lc
     *@date 2018-04-13 10:28:58
     */
    addSliderSymbol({position, radius} = {}) {

        //图标信息
        let marker =
            {
                'properties': {
                    'message': 'Foo',
                    'iconSize': [50, 80]
                },
                'geometry': {
                    'type': 'Point',
                    'coordinates': position
                }
            };
        //获取图标容器
        let el = document.createElement('mapbox-gl-slider');
        el.style.width = marker.properties.iconSize[0] + 'px';
        el.style.height = marker.properties.iconSize[1] + 'px';
        el.style.fontSize = 18 + 'px';
        el.style.color = '#ffffff';
        //显示雷达半径
        let html = '';
        html += '<div style="width:100%;height:30%;">' + radius + 'm</div>';
        el.innerHTML = html;

        //对图标添加拖拽监听,放大缩小雷达
        el.addEventListener('mousedown', (event) => {

            let mouseMove = true;
            let distance = radius;
            event.stopPropagation();
            this.map.on('mousemove', (ev) => {

                if (mouseMove) {

                    distance = 6378137 * 2 * Math.PI * Math.cos(position[1] / 180 * Math.PI) * (ev.lngLat.lng - position[0]) / 360;
                    if (distance > radius[0] && distance < radius[1]) {

                        //设置图标位置
                        slider.setLngLat([ev.lngLat.lng, position[1]]);
                        //更新显示雷达半径
                        html = '';
                        if (distance > 3000) {

                            html += '<div>' + (distance / 1000).toFixed(2) + 'km</div>';

                        }
                        else {

                            html += '<div>' + distance.toFixed(0) + 'm</div>';

                        }
                        el.innerHTML = html;
                        //缩放雷达覆盖范围
                        // this.threebox.moveToCoordinate(this.circleGroup, position, {preScale: distance / radius});
                        // let sectorCoordinate = [position[0], position[1], 5];
                        // this.threebox.moveToCoordinate(this.sectorMesh, sectorCoordinate, {preScale: distance / radius});

                    }

                }

            });
            /*  el.onmouseup = () => {
                  updateMarkerLayer(doQuerys(raderAttr.position, distance / 1000, this.layerGeojson), "testPolice");
                  mouseMove = false;
              };*/
            this.map.on('mouseup', () => {

                mouseMove = false;

            });

        });

        // 将图标放置至地图上
        let markerPosition = [marker.geometry.coordinates[0] + radius / (111000 * Math.cos(marker.geometry.coordinates[1] / 180 * Math.PI)), marker.geometry.coordinates[1]];
        let slider = new mapboxgl.Marker(el)
            .setLngLat(markerPosition)
            .addTo(this.map);

    }

    /**
     * 清空
     */
    clear() {

        cancelAnimationFrame(this.animation);
        this.threebox.remove(this.circleMesh.parent);
        this.threebox.remove(this.sectorMesh.parent);
        this.threebox.update();
        this.play = false;

    }

    /**
     * 显示雷达层
     */
    showRadarLayer() {

        this.init(this.options);

    }
}

export default RadarLayer;