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

    四.实现效果

       

  • 相关阅读:
    php jquery+ajax写批量删除
    thinkphp 验证码
    thinkphp 框架中的一部分方法解析
    ThinkPHP框架的增删改
    ThinkPHP 框架模型
    ThinkPHP框架之空控制器
    江苏省盐城市响水县运河镇开源路商业街商铺及套间招商招租出售
    苏州鼎耀文化传媒有限公司
    怎么给罗技K480 增加Home、End键
    C#实现APK自动打包
  • 原文地址:https://www.cnblogs.com/tracine0513/p/11792557.html
Copyright © 2020-2023  润新知