Arcgis api for javascript学习笔记(3.2版本) - 匀速行驶轨迹动画效果
一.前言
有这样一个需求:已知某条线上的n个点的经纬度数组 ,实现物体运行轨迹。
如果这些点中两个距离很近,那么我们可以用一个定时器在地图上每次重新画一个点,这样肉眼看到这个点上的运动效果,如下图代码:
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 ptGraphic = new Graphic();
map.add(this.ptGraphic);
var index = 0;
setInterval(function() {
index++;
var ptGeometry = new Point({
longitude: paths[index].longitude,
latitude: paths[index].latitude
});
var ptSymbol = new PictureMarkerSymbol({
url: 'car.png',
height: 32,
width: 18,
type: 'esriPMS'
});
ptGraphic.setGeometry(ptGeometry);
ptGraphic.setSymbol(ptSymbol);
}, 1000);
但是如果这些点钟两个点距离比较远,那么这个轨迹运动效果就是一跳一跳那种,没有连贯性。
二.实现思路
既然两个点A,B因为距离比较远,导致绘制完A点后再绘制B会出现那种A点一下跳到B点的感觉,那么我们可以在A点B点两点之间再选取多个点,这些点的距离我们肉眼再屏幕上无法感觉到。然后从A点逐个绘制这些点,这样我们肉眼就不会看到一下子跳到下一个点得感觉,肉眼上观察就是那种平滑运动的效果。
不过在实现的过程中,要考虑一下几个问题:
问题1.两个点之间的距离有的长有的短,那么在两个点之间到底选取多少个点比较合适;
问题2.如果点是一个图标,如一个车辆得图标,那么车辆图标应该与轨迹线平行,并且车头应该朝向运动的方向(也就是车辆行驶过程中转弯的效果);
问题3.尽量采用WGS84进行相关计算,因为屏幕坐标点计算相关后会导致一定得误差;
解决方案:
问题3:可以通过算法实现 WGS84 与 web 墨卡托之间的相互转换,详见:Coordinates.js;
问题2:在实例化 PictureMarkerSymbol 对象时,有一个"angle"属性,这个就表示图片的偏移角度。这个偏移角度可以通过AB两点得经纬度计算得到,详见:MeatureTool.js;
问题1:给物体设定两个参数 运行速度 _speed(千米/秒)、定时器执行间隔 _seed (毫秒/次);
那么定时器每次运行的距离为:avg_distance = _speed * _seed / 1000 (千米/次);
再通过 MeatureTool.js 中提供的 distanceByLongLat 函数算法计算AB两点间的距离为 distance(千米);
这样我们要在AB两个点中间选取点得个数就等价于定时器在AB两个点之间执行的次数: times(单位:次) = distance / avg_distance
然后通过 MeatureTool.js 中提供的 getNextPoint 函数算法逐步计算这些点的经纬度坐标
三.实现代码
Coordinates.js
/* WGS84与web墨卡托之间的相互转换 */
define({
/*
* 经纬度转屏幕坐标
* 平面坐标x = 经度*20037508.34/108
* 平面坐标y = log(tan((90+纬度)*PI/360))/(PI/360)*20037508.34/180
*/
longlat2WebMercator: function (longitude, latitude) {
var x = longitude * 20037508.34 / 180;
var y = Math.log(Math.tan((90 + latitude) * Math.PI / 360)) / (Math.PI / 180);
y = y * 20037508.34 / 180;
return { "x": x, "y": y };
},
/*
* 屏幕坐标转经纬度
* 经度 = 平面坐标x/20037508.34*180
* 纬度 = 180/(PI*(2*atan(exp(平面坐标y/20037508.34*180*PI/180))-PI/2)
*/
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
};
}
});
Coordinates.js
MeatureTool.js
/* 测量工具 */
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;
},
/* 测量两个屏幕点之间的倾斜角 */
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;
},
/* 测量两个经纬点之间的倾斜角 */
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;
},
EARTH_RADIUS: 6378.137, //地球赤道半径(单位:km)
EARTH_ARC: 111.199, //地球每度的弧长(单位:km)
_rad: function (val) {
//转化为弧度(rad)
return val * Math.PI / 180.0;;
},
/* 测量两经纬度距离(单位:km) */
distanceByLongLat: function (longitude1, latitude1, longitude2, latitude2) {
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;
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 };
}
}
});
MeatureTool.js
MovingLayer.js
define([
"dojo/_base/declare",
'esri/Color',
'esri/graphic',
"esri/geometry/Point",
'esri/geometry/Polyline',
"esri/geometry/webMercatorUtils",
'esri/symbols/SimpleLineSymbol',
'esri/symbols/PictureMarkerSymbol',
'esri/layers/GraphicsLayer',
"extras/MeatureTool",
"extras/Coordinates"
], function (declare, Color, Graphic, Point, Polyline, webMercatorUtils, SimpleLineSymbol, PictureMarkerSymbol, GraphicsLayer, MeatureTool, Coordinates) {
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; //定义线符号
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);
},
_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++;
},
toggle: function () {
var _this = this;
_this._running = !_this._running;
}
});
});
MovingLayer.js
页面代码
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();
});
四.实现效果
Arcgis api for javascript学习笔记(3.2版本) - 匀速行驶轨迹动画效果的更多相关文章
- Arcgis api for javascript学习笔记(4.5版本)-三维地图的飞行效果
其实就只是用到了 view.goTo() 函数,再利用 window.setInterval() 函数(定时器)定时执行goTo().代码如下: <!DOCTYPE html> < ...
- Arcgis api for javascript学习笔记(4.5版本) - 获取FeatureLayer中的graphics集合
在Arcgis api for javascript 3.x 版本中,我们可以直接通过某个FeatureLayer对象中的graphics属性获取要素集合. graphics属性 但是在4.x版本中, ...
- Arcgis api for javascript学习笔记(4.5版本) - 本地部署及代理配置
在开发过程中,由于api的文件比较多,没必要每个项目都将api加入到解决方案中.况且在VS中如果将api加入解决方案,在编写css或js代码时,由于智能提示需要扫描脚本等文件,会导致VS很卡.所以个人 ...
- Arcgis api for javascript学习笔记(3.2X版本)-初步尝试
Arcgis api for javascript(3.22版本)官方地址 :https://developers.arcgis.com/javascript/3/ 1. 根据官方示例实现一个简单地图 ...
- Arcgis api for javascript学习笔记(4.6版本) - 二维MapView中的FeatureLayer显示标注
4.6版本api的FeatureLayer中有提供 labelsVisible 和 labelingInfo 两个属性,设置这两个属性可以实现显示将属性中某个字段作为标注.但是这两个属性只针对三维Sc ...
- Arcgis api for javascript学习笔记(4.5版本) - 点击多边形(Polygon)并高亮显示
在现在的 arcgis_js_v45_api 版本中并没有直接提供点击Polygon对象高亮显示.需要实现如下几个步骤: 1.点击地图时,获取Polygon的Graphic对象: 2.对获取到的Gra ...
- Arcgis api for javascript学习笔记(4.5版本)-三维地图实现弹窗功能
1. 对于Graphic对象,在初始化Graphic对象时设置popupTemplate属性,即可实现点击Graphic时显示弹窗. <!DOCTYPE html> <html> ...
- Arcgis api for javascript学习笔记(4.5版本)-三维地图并叠加天地图标注
1.三维地图实现 在官网的demo中就有三维地图的实现,如下图所示 <!DOCTYPE html> <html> <head> <meta charset=& ...
- Arcgis api for javascript学习笔记(3.2X版本)-Map图层叠加以及基本操作
1. 不设置默认底图,第一个图层作为底图,然后叠加另外一个图层 先添加图层1,第一个图层1作为默认底图,然后在图层1上叠加图层2,并设置图层2的透明度为50%. <!DOCTYPE html&g ...
随机推荐
- Linux中的库
一.基本概念 1.1.什么是库 在 windows 平台和 linux 平台下都大量存在着库. 本质上来说库是一种可执行的二进制代码(但不可以独立执行),可以被操作系统载入内存执行. 由 ...
- framework7日期插件使用
1.引入框架文件 <link rel="stylesheet" href="framework7.ios.min.css"> <link re ...
- Effective C++: 04设计与声明
18:让接口容易被正确使用,不易被误用 1:理想上,如果客户企图使用某个接口而却没有获得他所预期的行为,这个代码不该通过编译:如果代码通过了编译,它的作为就该是客户所想要的. 2:许多客户端的错误可以 ...
- 不撞南墙不回头———深度优先搜索(DFS)Oil Deposits
Oil Deposits Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Tota ...
- python内置函数and匿名函数
一.内置函数 什什么是内置函数? 就是python给你提供的. 拿来直接⽤用的函数, 比如print., input等等. 截⽌止 到python版本3.6.2 python⼀一共提供了了68个内置函 ...
- P4930「FJ2014集训」采药人的路径
题目:P4930「FJ2014集训」采药人的路径 思路: 这篇不算题解,是让自己复习的,什么都没说清楚. 很久没有写点分治了,以前为了赶课件学的太急,板子都没打对就照着题解写题,导致学得很不扎实. 这 ...
- W600 一块新的 KiCad PCB
W600 一块新的 KiCad PCB 打算做以下功能. Type-C USB. 使用 KiCad 画板. 加入串口芯片,方便调试. 使用 PCB 天线.
- Java练习 SDUT-2761_编码
编码 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 给你一个由大写字母组成的组成的字符串,你可以用如下规则对其进行编码 ...
- PHP 手机短信验证码 laravel 实现流程
https://blog.csdn.net/uknow0904/article/details/80336941 本人在自己博客(Laravel)的注册部分 使用手机号注册,需要发送短信验证码. 使用 ...
- oracle 用TRUNCATE替代DELETE
当删除表中的记录时,在通常情况下, 回滚段(rollback segments ) 用来存放可以被恢复的信息. 如果你没有COMMIT事务, ORACLE会将数据恢复到删除之前的状态(准确地说是恢复到 ...