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 ...
随机推荐
- django2.0基础
一.安装与项目的创建 1.安装 pip install django 2.查看版本 python -m django --version 3.创建项目 django-admin startprojec ...
- <> 是不等号的意思
<> 是不等号的意思,也有的语言可以写作:# 或者 != 1.=表示 等于: 2.<> 表示不等于:(注释:在 SQL 的一些版本中,该操作符可被写成 !=): 3.> ...
- TP3.2的URL重写省略index.php问题
1. 在tp3框架的配置文件里,明确指定了路由的格式,这个配置位于thinkPHP文件夹下的conf文件夹里的convention.php中,修改以下字段 'URL_MODEL' => 2, # ...
- python 编码和解码
- Java练习 SDUT-3339_计算长方形的周长和面积(类和对象)
计算长方形的周长和面积(类和对象) Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 设计一个长方形类Rect,计算长方形 ...
- java对象转化为json字符串并传到前台
package cc.util; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import ...
- 使用HSV色彩空间遮罩绿色区域
HSV 颜色空间 导入资源 In []: import matplotlib.pyplot as plt import matplotlib.image as mpimg import numpy ...
- Project Euler Problem 23-Non-abundant sums
直接暴力搞就行,优化的地方应该还是计算因子和那里,优化方法在这里:http://www.cnblogs.com/guoyongheng/p/7780345.html 这题真坑,能被写成两个相同盈数之和 ...
- uva 10566 Crossed Ladders (二分)
http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&p ...
- phpmyadmin设置自动登录和取消自动登录
1首先在phpmyadmin安装目录下找到config.sample.inc.php复制一份文件名改为config.inc.php 2打开config.inc.php 找到 $cfg['Serve ...