在高德地图上绘制飞机标记,相信很多人都使用marker来实现的,但如果这个数据较多的话, 地图就会有些卡,严重影响用户使用,那么怎样才能在大数据的情况下不卡呢?
- 如果需求较少的话,可以使用MassMarks麻点层来解决这个问题,但其可扩展性较弱
- 选择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/
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。