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();
        });

四.實現效果

   

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