Arcgis api for javascript学习笔记(3.2版本) - 匀速行驶轨迹动画效果

一.前言

  有这样一个需求:已知某条线上的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();
        });

四.实现效果

   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章