1

高德地图上绘制飞机标记,相信很多人都使用marker来实现的,但如果这个数据较多的话, 地图就会有些卡,严重影响用户使用,那么怎样才能在大数据的情况下不卡呢?

  1. 如果需求较少的话,可以使用MassMarks麻点层来解决这个问题,但其可扩展性较弱
  2. 选择CustomLayer来实现

这里我们主要讲的是CustomLayer来实现,相对以前的多个marker实现,那么使用CustomLayer就好多了。。

下面先看下效果吧
图片描述

使用customLayer主要的工作还是在于绘制canvas

行看一个简单的示例(绘制飞机和添加事件)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="jquery.min.js"></script>
</head>
<body>
    
    <canvas id="canvas"></canvas>
    
    <script>
    var canvas = document.getElementById('canvas');
    canvas.width = 500;
    canvas.height = 500;
    var ctx = canvas.getContext('2d');
    // 背景
    ctx.fillStyle = '#999';
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // 飞机
    var coord = [200, 200],
        planeSize = 26,
        planeHalf = planeSize/2;

    var x = coord[0],
        y = coord[1],
        angle = 2*Math.PI*(-60/360);

    // 蓝色线
    ctx.save();
    ctx.moveTo(x, y);
    ctx.lineTo(x-200, y);
    ctx.lineWidth = 1;
    ctx.strokeStyle = 'blue';
    ctx.stroke();
    ctx.restore();

    // 黑色飞机
    ctx.save();
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.lineTo(x+2, y+3);
    ctx.lineTo(x+2, y+12);
    ctx.lineTo(x+13, y+20);
    ctx.lineTo(x+2, y+16);
    ctx.lineTo(x+2, y+23);
    ctx.lineTo(x+5, y+26);
    ctx.lineTo(x, y+25);
    ctx.lineTo(x-5, y+26);
    ctx.lineTo(x-2, y+23);
    ctx.lineTo(x-2, y+16);
    ctx.lineTo(x-13, y+20);
    ctx.lineTo(x-2, y+12);
    ctx.lineTo(x-2, y+3);
    ctx.fillStyle = '#000';
    ctx.fill();
    ctx.closePath();
    ctx.restore();

    // 绿色飞机
    ctx.save();
    ctx.translate(0, -planeHalf);
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.lineTo(x+2, y+3);
    ctx.lineTo(x+2, y+12);
    ctx.lineTo(x+13, y+20);
    ctx.lineTo(x+2, y+16);
    ctx.lineTo(x+2, y+23);
    ctx.lineTo(x+5, y+26);
    ctx.lineTo(x, y+25);
    ctx.lineTo(x-5, y+26);
    ctx.lineTo(x-2, y+23);
    ctx.lineTo(x-2, y+16);
    ctx.lineTo(x-13, y+20);
    ctx.lineTo(x-2, y+12);
    ctx.lineTo(x-2, y+3);
    ctx.fillStyle = '#690';
    ctx.fill();
    ctx.closePath();
    ctx.restore();

    // 白色飞机
    ctx.save();
    ctx.translate(x, y);
    ctx.rotate(angle);
    ctx.translate(-x, -y);
    ctx.beginPath();
    ctx.translate(0, -planeHalf);
    ctx.moveTo(x, y);
    ctx.lineTo(x+2, y+3);
    ctx.lineTo(x+2, y+12);
    ctx.lineTo(x+13, y+20);
    ctx.lineTo(x+2, y+16);
    ctx.lineTo(x+2, y+23);
    ctx.lineTo(x+5, y+26);
    ctx.lineTo(x, y+25);
    ctx.lineTo(x-5, y+26);
    ctx.lineTo(x-2, y+23);
    ctx.lineTo(x-2, y+16);
    ctx.lineTo(x-13, y+20);
    ctx.lineTo(x-2, y+12);
    ctx.lineTo(x-2, y+3);
    ctx.fillStyle = '#fff';
    ctx.fill();
    ctx.closePath();
    ctx.restore();

    // 红色飞机
    ctx.save();
    ctx.translate(x, y);
    ctx.rotate(angle);
    ctx.translate(-x, -y);
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.lineTo(x+2, y+3);
    ctx.lineTo(x+2, y+12);
    ctx.lineTo(x+13, y+20);
    ctx.lineTo(x+2, y+16);
    ctx.lineTo(x+2, y+23);
    ctx.lineTo(x+5, y+26);
    ctx.lineTo(x, y+25);
    ctx.lineTo(x-5, y+26);
    ctx.lineTo(x-2, y+23);
    ctx.lineTo(x-2, y+16);
    ctx.lineTo(x-13, y+20);
    ctx.lineTo(x-2, y+12);
    ctx.lineTo(x-2, y+3);
    ctx.fillStyle = '#f00';
    ctx.fill();
    ctx.closePath();
    ctx.restore();

    // 点击点在飞机矩形范围内[ Math.round(Math.sqrt(planeHalf*planeHalf*2))==18 ] ( 推荐 )
    var modulesPoints = {};
    modulesPoints['plane'] = {
        xMin: x-18,
        xMax: x+18,
        yMin: y-18,
        yMax: y+18
    };
    canvas.addEventListener('click', function(e) {
        var clientX = e.clientX,
            clientY = e.clientY,
            planeRect = modulesPoints['plane'];
        if(planeRect.xMin<clientX && clientX<planeRect.xMax && planeRect.yMin<clientY && clientY<planeRect.yMax) {
            console.log(+new Date);
        }
    });

    // 点击点只在飞机内
    // var modulesPoints = {};
    // for(var i=0; i<canvas.width; i++) {
    //     for(var j=0; j<canvas.height; j++) {
    //         if(ctx.isPointInPath(i, j)) {
    //             if(!modulesPoints['plane']) {
    //                 modulesPoints['plane'] = {};
    //             }
    //             modulesPoints['plane']['x_'+i] = i;
    //             modulesPoints['plane']['y_'+j] = j;
    //         }
    //     }
    // }
    // canvas.addEventListener('click', function(e) {
    //     if(modulesPoints['plane']['x_'+e.clientX] && modulesPoints['plane']['y_'+e.clientY]) {
    //         console.log(+new Date);
    //     }
    // });
    </script>
</body>
</html>

效果:
图片描述

基本原理就是上面那样的,用过canvas的同学应该都能看懂,当然也可以先了解下canvas基础canvas API

接下来就请严读完整代码喽~~~
注释也都很清淅。。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
    *{margin: 0;}
    </style>
    <script src="js/jquery.min.js"></script>
</head>
<body>
    <div id="zh_map"></div>
    
    <script type="text/javascript" src="http://webapi.amap.com/maps?v=1.3&key=b705b0ffe322148bbf5c1febdf47fe95&plugin=AMap.Geocoder"></script>
    <script type="text/javascript" src="js/socketio.js"></script>
    <script type="text/javascript" src="js/wgs2mars.min.js"></script>
    <script type="text/javascript" src="js/protobuf/protobuf.js"></script>

    <script>
    // 初始化
    var initSet = (function set() {
        document.getElementById('zh_map').style.cssText = 'height: '+window.innerHeight+'px;';
        return set;
    })();
    window.onresize = function() {
        initSet();
    }
    // 变量
    var planeMarkers = {},
        planeLayer = null,
        planeSize = 26,
        planePoints = {},
        selectedPlaneAnum = null;
    // 打印log
    function log() {
        var args = Array.prototype.slice.call(arguments);
        if(args.length == 1) {
            console.log(args[0]);
        } else {
            console.log('%c'+args[0], 'color: '+(args[1]?args[1]:'#333'));
        }
    }
    // 地图
    var map = new AMap.Map('zh_map', {
        zoom: 10,
        center: [102.926672, 25.09155],
        doubleClickZoom: false,
        mapStyle: 'blue_night',
        features: ['bg', 'point']
    });

    // 数据结构
    var locProto,
        pathProto;
        protobuf.load('js/protobuf/flightloc.json', function(err, root) {
            locProto = root.lookupType("Loc.FlightLoc");
            pathProto = root.lookupType("Loc.FlightPath");
        });

    // 请求appid、token
    function reqToken() {
        $.ajax({
            type: 'get',
            url: 'http://***/***/get_client_token',
            success: function(res) {
                res = JSON.parse(res);
                if(res.status) {
                    planeFn(res.appid, res.token);
                }
            }
        });
    }
    reqToken();

    function planeFn(appid, token) {
        socket = null;
        // 可视范围
        var curBounds = map.getBounds();
        var clientBounds = [curBounds.northeast.lat,curBounds.southwest.lng,curBounds.southwest.lat,curBounds.northeast.lng]; // 左上右下

        // 连接socket
        socket = io('https://***.***.com/***', {path: "/**/socket.io", transports: ['websocket'], reconnection: true});
        // 连接
        socket.on('connect', function() {
            log('connect success.', 'green');
        });
        // 验证
        socket.on('validate', function(ack) {
            log('validate...', 'blue');
            ack(appid, token, '{}');
            socket.emit('sub', clientBounds, -1, '');
        });
        // 验证成功
        socket.on('validateSuccess', function() {
            log('socket validate success.', 'green');
        });
        // 验证失败
        socket.on('validateFailed', function() {
            log('socket validate failed.', 'red');
            reqToken();
        });
        socket.on('disconnect', function() {
            log('socket disconnect.', 'red');
            reqToken();
        });
        // 心跳
        socket.on('heartbeat', function(ack) {
            ack();
        });
        // 监听adsb数据
        socket.on('~', function(buffer) {
            if(locProto) {
                var item = locProto.decode(new Uint8Array(buffer)), // 数据解码
                    Anum = item.Anum,         // 飞机编号
                    Lon = item.Lon,         // 经度
                    Lat = item.Lat,         // 纬度
                    Alt = item.Alt,         // 高度
                    Ang = item.Ang,         // 角度
                    Spd = item.Spd;         // 速度
                var coord = transformFromWGSToGCJ(Lon, Lat); // WGS(World Geodetic System) 世界大地坐标系 转 高德地图GCJ(国测局坐标)使用的火星坐标系
                    Lon = coord.lng;
                    Lat = coord.lat;
                planeMarkers[Anum] = item;
            } else {
                log('监控飞机位置失败~');
            }
        });
    }

    // 重新订阅
    function resub(socket) {
        // 可视范围
        var curBounds = map.getBounds();
        var clientBounds = [curBounds.northeast.lat,curBounds.southwest.lng,curBounds.southwest.lat,curBounds.northeast.lng]; // 左上右下
        // 订阅
        socket.emit("sub", clientBounds, -1, '');
    }

    // 拖动结束
    map.on('dragend', function() {
        if(socket) resub(socket);
    });

    // 缩放结束
    map.on('zoomend', function() {
        if(socket) resub(socket);
    });

    // 绘制
    function drawPlane(anum) {
        if(planeLayer) {
            map.remove(planeLayer);
            planeLayer = null
            planePoints = {};
        }
        map.plugin(['AMap.CustomLayer'], function() {
            var canvas = document.createElement('canvas');
                canvas.id = 'plane_layer';
                canvasWth = map.getSize().width;
                canvasHgt = map.getSize().height;
                canvas.width = canvasWth;
                canvas.height = canvasHgt;
            planeLayer = new AMap.CustomLayer(canvas, {
                zIndex: 110
            });
            planeLayer.render = function() {
                var ctx = canvas.getContext('2d');
                    ctx.clearRect(0, 0, canvasWth, canvasHgt);
                $.each(planeMarkers, function(k, v) {
                    var Anum = v.Anum,         // 飞机编号
                        Fnum = v.Fnum,         // 航班号
                        Org = v.Org,         // 出发地
                        Dst = v.Dst,         // 目的地
                        Squawk = v.Squawk,   // 应答码
                        Lon = v.Lon,          // 经度
                        Lat = v.Lat,          // 纬度
                        Spd = v.Spd,         // 速度
                        Ang = v.Ang,         // 角度
                        Alt = v.Alt;          // 高度
                    var coord = transformFromWGSToGCJ(Lon, Lat);
                        Lon = coord.lng;
                        Lat = coord.lat;
                    var containerCoord = map.lngLatToContainer([coord.lng, coord.lat]),
                        x = containerCoord.x-planeSize/2,
                        y = containerCoord.y,
                        angle = 2*Math.PI*(Ang/360);

                    // 绘制飞机
                    ctx.save();
                    ctx.translate(x, y);
                    ctx.rotate(angle);
                    ctx.translate(-x, -y);
                    ctx.beginPath();
                    ctx.translate(0, -planeHalf);
                    ctx.moveTo(x, y);
                    ctx.lineTo(x+2, y+3);
                    ctx.lineTo(x+2, y+12);
                    ctx.lineTo(x+13, y+20);
                    ctx.lineTo(x+2, y+16);
                    ctx.lineTo(x+2, y+23);
                    ctx.lineTo(x+5, y+26);
                    ctx.lineTo(x, y+25);
                    ctx.lineTo(x-5, y+26);
                    ctx.lineTo(x-2, y+23);
                    ctx.lineTo(x-2, y+16);
                    ctx.lineTo(x-13, y+20);
                    ctx.lineTo(x-2, y+12);
                    ctx.lineTo(x-2, y+3);
                    ctx.fillStyle = (selectedPlaneAnum===Anum?'#FF5F3A':'#1282F7');
                    ctx.fill();
                    ctx.closePath();
                    ctx.restore();

                    // 保存坐标点
                    planePoints[Anum] = {
                        xMin: x-18,
                        xMax: x+18,
                        yMin: y-18,
                        yMax: y+18
                    };

                    // 绘制矩形
                    x += planeHalf;
                    y -= planeHalf;
                    ctx.fillStyle = 'rgba(0,0,0,0.5)';
                    ctx.fillRect(x, y, 100, 150);
                    ctx.restore();

                    // 绘制文字
                    x += 10;
                    ctx.fillStyle = '#fff';
                    ctx.fillText(Anum, x, y+20);
                    ctx.fillText(Fnum, x, y+40);
                    ctx.fillText(Org+'-'+Dst, x, y+60);
                    ctx.fillText(Squawk||'--', x, y+80);
                    ctx.fillText(Ang, x, y+100);
                    ctx.fillText(Spd.toFixed(2), x, y+120);
                    ctx.fillText(Alt.toFixed(2), x, y+140);
                });
            }
            planeLayer.setMap(map);
        });
    }

    // 1秒钟绘制一次
    setInterval(function() {
        drawPlane(selectedPlaneAnum);
    }, 1000);

    // 事件
    $('#zh_map').on('click', 'canvas',function(e) {
        var clientX = e.clientX,
            clientY = e.clientY;
        $.each(planePoints, function(k, v) {
            if(v.xMin<clientX && clientX<v.xMax && v.yMin<clientY && clientY<v.yMax) {
                selectedPlaneAnum = k;
            }
        });
        drawPlane(selectedPlaneAnum);
    });
    </script>
</body>
</html>

写到最后,欢迎关注作者:http://fenxianglu.cn/


anchovy
1.9k 声望89 粉丝