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 ...
随机推荐
- 微信小程序开发资源整理
有兴趣学习微信小程序开发的可以关注简书专题 微信小程序开发 由于微信已经开发文档和开发工具了,所以下面的内容用处不大了. 具体参考:http://mp.weixin.qq.com/wiki/ 这篇文章 ...
- springboot web开发【转】【补】
pom.xml引入webjars的官网 https://www.webjars.org/ https://www.thymeleaf.org/doc/tutorials/3.0/usingthymel ...
- docker安装 2016-11-06 19:14 299人阅读 评论(31) 收藏
Docker支持运行在以下CentOS版本: CentOS 7.X 安装在二进制兼容的EL7版本如 Scientific Linux也是可能成功的,但是Docker 没有测试过并且不官方支持. 此文带 ...
- 条件变量用例--解锁与signal的顺序问题
我们知道,当调用signal/broadcast唤醒等待条件变量的其他线程时,既可以在加锁的情况下调用signal/broadcast,也可以在解锁的情况下调用. 那么,到底哪种情况更好呢?man手册 ...
- MaxCompute 费用暴涨之新增SQL分区裁剪失败
现象:因业务需求新增了SQL任务,这SQL扫描的表为分区表,且SQL条件里表只指定了一个分区,按指定的分区来看数据量并不大,但是SQL的费用非常高.费用比预想的结果相差几倍甚至10倍以上. 若只知道总 ...
- @codeforces - 718E@ Matvey's Birthday
目录 @description@ @solution@ @accepted code@ @detail@ @description@ 给定一个长度为 n 的字符串 s,保证只包含前 8 个小写字母 ' ...
- 2019-2-3-VisualStudio-扩展开发-添加输出窗口
title author date CreateTime categories VisualStudio 扩展开发 添加输出窗口 lindexi 2019-02-03 11:41:40 +0800 2 ...
- Java练习 SDUT-2174_回文时间
回文时间 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description HH 每天都会熬夜写代码,然后很晚才睡觉,但是每天早晨六点多必 ...
- day5-python之递归与二分法
一.递归的定义 递归调用是函数嵌套调用的一种特殊形式,函数在调用时,直接或间接调用了自身,就是递归调用 二.递归分为两个阶段:递推,回溯 age(5) = age(4) + 2 age(4) = ag ...
- [***]HZOI20190714 T2熟练剖分
这题真的神仙,蒟弱表示看题解看不懂……std看了几个小时大概看懂,还有一些细节的东西没有思考. 最难受的是题解和std好像并不是同一个人写的……数组状态不一样……看了好久才看出来f也是前缀和. F[i ...