一.前言
有这样一个需求:已知某条线上的n个点的经纬度数组 [[116.2968, 39.90245],[116.297443, 39.902454],[116.297454, 39.90312],[116.296295, 39.903133],[116.296258, 39.902454],[116.296794, 39.902446]] ,实现物体运行轨迹。
如果这些点中两个距离很近,那么我们可以用一个定时器在地图上每次重新画一个点,这样肉眼看到这个点上的运动效果。
但是如果这些点钟两个点距离比较远,那么这个轨迹运动效果就是一跳一跳那种,没有那种连贯性。
二.实现思路
既然两个点A,B因为距离比较远,导致绘制完A点后再绘制B会出现那种A点一下跳到B点的感觉,那么我们可以在A点B点两点之间再平均选择n个点,在绘制完A点之后,再逐个绘制这n个点,最后再绘制B点,这样就可以达到那种平滑运动的效果。
不过在实现的过程中,要考虑一下几个问题:
问题1.两个点之间的距离有的长有的短,那么在两个点之间到底选取多少个点比较合适;
问题2.如果点是一个图标,如一个车辆得图标,那么车辆图标应该与轨迹线平行,并且车头应该朝向运动的方向;
问题3.尽量采用WGS84进行相关计算,因为屏幕坐标点计算相关后会导致一定得误差;
解决方案:给物体设定一个运行速度 _speed(千米/秒),假设定时器 _seed (毫秒/次),那么定时器每次运行的距离为:_avg_distance = _speed * _seed / 1000 (千米/次)
再通过算法计算AB两点间的距离为 _distance(千米),这样就可以判断再AB两点之间定时器要执行多少次也就是要选取多少个点了
然后计算AB两点得方向角,这个方向角也是选取的N个点得方向角,最后从A点开始,根据A点的经纬度坐标、方向角、_avg_distance 逐个计算这n个点的经纬度坐标
接下来就可以绘制这些点了,并且是那种平滑得运动效果
三.实现代码
//WGS84与 web墨卡托相互转换
define({
// 核心公式
// 平面坐标x = 经度*20037508.34/108
// 平面坐标y = log(tan((90+纬度)*PI/360))/(PI/360)*20037508.34/180
// 经度= 平面坐标x/20037508.34*180
// 纬度= 180/(PI*(2*atan(exp(平面坐标y/20037508.34*180*PI/180))-PI/2)
longlat2WebMercator: function (longitude, latitude) {
var x = longitude * 20037508.34 / 180;
// Math.log:求对数函数
var y = Math.log(Math.tan((90 + latitude) * Math.PI / 360)) / (Math.PI / 180);
y = y * 20037508.34 / 180;
return { "x": x, "y": y };
},
webMercator2LongLat: function (x, y) {
var longitude = x / 20037508.34 * 180;
var latitude = y / 20037508.34 * 180;
latitude = 180 / Math.PI * (2 * Math.atan(Math.exp(latitude * Math.PI / 180)) - Math.PI / 2);
return {
"longitude": longitude,
"latitude": latitude
};
}
});
//测量计算模块
define(["extras/Coordinates"], function (Coordinates) {
return {
lengthByMercator: function (pt1, pt2) {
//测距(单位:米)
var a_pow = Math.pow((pt1.x - pt2.x), 2);
var b_pow = Math.pow((pt1.y - pt2.y), 2);
var c_pow = a_pow + b_pow;
var length = Math.sqrt(c_pow);
return length;
},
areaByMercator: function (pt1, pt2, pt3) {
//测面(单位:平方米)
return ((pt1.x * pt2.y - pt2.x * pt1.y) + (pt2.x * pt3.y - pt3.x * pt2.y) + (pt3.x * pt1.y - pt1.x * pt3.y)) / 2;
},
angleByLongLat: function (longitude1, latitude1, longitude2, latitude2) {
var ptTemp1 = Coordinates.longlat2WebMercator(longitude1, latitude1);
var ptTemp2 = Coordinates.longlat2WebMercator(longitude2, latitude2);
var x = ptTemp2.x - ptTemp1.x;
var y = ptTemp2.y - ptTemp1.y;
var angle = Math.atan2(y, x);
angle = (angle - Math.PI / 2) / Math.PI * 180;
return angle;
},
angleByMercator: function (pt1, pt2) {
var x = pt2.x - pt1.x;
var y = pt2.y - pt1.y;
var angle = Math.atan2(y, x);
angle = (angle - Math.PI / 2) / Math.PI * 180;
return angle;
},
EARTH_RADIUS: 6378.137, //地球赤道半径(单位:km)
EARTH_ARC: 111.199, //地球每度的弧长(单位:km)
_rad: function (val) {
//转化为弧度(rad)
return val * Math.PI / 180.0;;
},
distanceByLongLat: function (longitude1, latitude1, longitude2, latitude2) {
//求两经纬度距离(单位:km)
var r1 = this._rad(latitude1);
var r2 = this._rad(longitude1);
var a = this._rad(latitude2);
var b = this._rad(longitude2);
var s = Math.acos(
Math.cos(r1) * Math.cos(a) * Math.cos(r2 - b)
+ Math.sin(r1) * Math.sin(a)
) * this.EARTH_RADIUS;
return s;
},
azimuthByLongLat: function (longitude1, latitude1, longitude2, latitude2) {
//求两经纬度方向角角度(单位:°)
var azimuth = 0;
//console.info("------------------------------------");
if (longitude2 === longitude1 && latitude2 > latitude1) {
azimuth = 0;
}
else if (longitude2 === longitude1 && latitude2 < latitude1) {
azimuth = 180;
}
else if (latitude2 === latitude1 && longitude2 < longitude1) {
azimuth = 270;
}
else if (latitude2 === latitude1 && longitude2 > longitude1) {
azimuth = 360;
}
else {
var radLongitude1 = this._rad(longitude1);
var radLongitude2 = this._rad(longitude2);
var radLatitude1 = this._rad(latitude1);
var radLatitude2 = this._rad(latitude2);
azimuth = Math.sin(radLatitude1) * Math.sin(radLatitude2) + Math.cos(radLatitude1) * Math.cos(radLatitude2) * Math.cos(radLongitude2 - radLongitude1);
azimuth = Math.sqrt(1 - azimuth * azimuth);
azimuth = Math.cos(radLatitude2) * Math.sin(radLongitude2 - radLongitude1) / azimuth;
azimuth = Math.asin(azimuth) * 180 / Math.PI;
if (latitude2 < latitude1) {
//console.info("三四象限");
azimuth = 180 - azimuth;
}
else if (latitude2 > latitude1 && longitude2 < longitude1) {
//console.info("第二象限");
azimuth = 360 + azimuth;
}
// else {
// console.info("第一象限");
// }
}
//console.info(azimuth);
return azimuth;
},
getNextPoint: function (longitude1, latitude1, distance, azimuth) {
//distance表示两点间得距离(单位:km)
azimuth = this._rad(azimuth);
// 将距离转换成经度的计算公式
var lon = longitude1 + (distance * Math.sin(azimuth)) / (this.EARTH_ARC * Math.cos(this._rad(latitude1)));
// 将距离转换成纬度的计算公式
var lat = latitude1 + (distance * Math.cos(azimuth)) / this.EARTH_ARC;
return { "longitude": lon, "latitude": lat };
}
}
});
define([
"dojo/_base/declare",
'esri/Color',
'esri/graphic',
"esri/geometry/Point",
'esri/geometry/Polyline',
'esri/symbols/SimpleLineSymbol',
'esri/symbols/PictureMarkerSymbol',
'esri/layers/GraphicsLayer',
"extras/MeatureTool"
], function (declare, Color, Graphic, Point, Polyline, SimpleLineSymbol, PictureMarkerSymbol, GraphicsLayer, MeatureTool) {
return declare([GraphicsLayer], {
_img: "",
_pts: [],
_ptIndex: 0,
_ptGraphic: null, //图形要素
_seed: 100, //多长时间执行一次,(单位:毫秒/次)
_speed: 10, //物体运行速度(千米/秒)
_timer: null, //定时器
_running: true, //定时器运行状态
initial: function (options) {
var _this = this;
_this._img = options.img;
_this._speed = options.speed || _this._speed;
_this._pts.length = 0;
_this._ptIndex = 0;
_this._timer = null;
_this._running = true;
//定义线符号
var lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255, 0, 0]), 2);
var lineGeometry = new Polyline({ "paths": options.paths });
var lineGraphic = new Graphic(lineGeometry, lineSymbol);
_this.add(lineGraphic);
_this._ptGraphic = new Graphic();
_this.add(this._ptGraphic);
var pathLastIndex = options.paths[0].length - 1;
for (var i = 0; i < pathLastIndex; i++) {
var longitude1 = options.paths[0][i][0];
var latitude1 = options.paths[0][i][1];
var longitude2 = options.paths[0][i + 1][0];
var latitude2 = options.paths[0][i + 1][1];
//两点之间的图标倾斜角度
var angle = MeatureTool.angleByLongLat(longitude1, latitude1, longitude2, latitude2);
//计算两点之间的方向角(单位:度)
var azimuth = MeatureTool.azimuthByLongLat(longitude1, latitude1, longitude2, latitude2);
//console.info(azimuth);
//将起点添加到数组中
_this._pts.push({ "longitude": longitude1, "latitude": latitude1, "angle": angle });
//计算两点间的距离(单位:千米)
var distance = MeatureTool.distanceByLongLat(longitude1, latitude1, longitude2, latitude2);
//定时器平均每次能运行的距离(单位:千米/次)
var avg_distance = (_this._speed * _this._seed) / 1000;
//如果两点间得距离小于定时器每次运行的距离,则不用在两个经纬度点之间选取分割点
if (distance <= avg_distance) {
continue;
}
//计算两点间,定时器需要执行的次数
var times = distance / avg_distance;
for (var j = 1; j < times; j++) {
var curr_distance = avg_distance * j
var pt = MeatureTool.getNextPoint(longitude1, latitude1, curr_distance, azimuth);
pt.angle = angle;
_this._pts.push(pt);
}
}
var ptLast = {
"longitude": options.paths[0][pathLastIndex][0],
"latitude": options.paths[0][pathLastIndex][1],
"angle": _this._pts[_this._pts.length - 1].angle
};
_this._pts.push(ptLast);
_this._ptDraw();
},
//运行动画效果
run: function () {
var _this = this;
_this._timer = setInterval(function () {
if (_this._running) {
if (_this._ptIndex >= _this._pts.length) {
clearInterval(_this._timer);
}
if (_this._ptIndex <= _this._pts.length - 1) {
_this._ptDraw();
}
}
}, _this._seed);
},
toggle: function () {
var _this = this;
_this._running = !_this._running;
},
_ptDraw: function () {
var _this = this;
var pt = _this._pts[_this._ptIndex];
var ptGeometry = new Point({
"longitude": pt.longitude,
"latitude": pt.latitude
});
var ptSymbol = new PictureMarkerSymbol({
"url": _this._img,
"height": 32,
"width": 18,
"type": "esriPMS",
"angle": pt.angle,
});
_this._ptGraphic.setGeometry(ptGeometry);
_this._ptGraphic.setSymbol(ptSymbol);
_this._ptIndex++;
}
});
});
require(["esri/map", "arcgis_js_v320_api_ex/MovingLayer", "dojo/domReady!"], function (Map, MovingLayer) {
var map = new Map("viewDiv", {
"basemap": "streets",
"scale": 50000,
"center": [116.29, 39.90],
});
var paths = [[
[116.2968, 39.90245],
[116.297443, 39.902454],
[116.297454, 39.90312],
[116.296295, 39.903133],
[116.296258, 39.902454],
[116.296794, 39.902446]
]];
var movingLayer = new MovingLayer();
map.addLayer(movingLayer);
movingLayer.initial({
"img": "/static/img/car.png",
"paths": paths,
"speed": 0.011
});
movingLayer.run();
});
四.实现效果
