首页 文章

谷歌 Map 距离近似

提问于
浏览
7

我已经开始创建一个网站,用户可以被有效地跟踪(他们知道他们正在被跟踪) . 用户将走一条特定的路线(更准确地说是英国曼彻斯特),其中有10个检查站 . 检查点是 Map 上的静态位置 . 使用Google Maps API我知道我可以在 Map 上绘制一个位置,即检查点 . 我还存储用户到达所述检查点的时间 . 考虑到检查点之间的距离,我可以使用基本数学计算它们的平均速度 .

现在我想做的是根据他们的速度绘制他们的估计位置 . 我遇到的困难是从当前位置 along the route 绘制一个新的位置x英里/米(任何单位) .

如果它是一条直线,这将是简单的 .

  • 有没有办法计算沿路线当前位置的距离?

  • 点数有限制吗?

  • 是否有特定的方法可以避免这种情况?

To expand my example with an image:

Example scenario

想象一下,用户在上午07:00到达第一位置标记,并且估计他们将在上午09:00到达第二位置标记 . 现在(例如)的时间是上午08:00,意味着(估计)用户应该在标记之间的大约一半 . 然后我会计算他们走过的距离(再次,估计)并在 Map 上绘制距离第一位标记“距离”的位置 .

希望我已经清楚地解释了这个场景,让人们理解 .

我对Google Maps API比较陌生,所以任何想法都会有所帮助 . 其他类似的问题已在SO上提出,但从我所看到的情况来看,没有人回答或要求我提供尽可能多的细节 .

提前致谢 .

UPDATE: 花了很多时间试图解决这个问题我失败了 . 这就是我所知道的:

  • 我应该使用PolyLine创建路径(我可以这样做,我有一个lat / lng列表)

  • 有一个名为epoly.js的JS扩展,但这与V3不兼容

  • 使用spherical.interpolate不会工作,因为它不遵循路径 .

5 回答

  • 2

    作为一名制图师,我曾经在过去的生活中做过很多这样的事情 . 您的折线由一系列点(纬度/长坐标)组成 . 在每个连续点之间计算距离,随着时间的推移将其加起来,直到达到所需的距离 .

    真正的技巧是计算两个纬度/长点之间的距离,这两个点是球面坐标(即曲面上的点) . 由于您处理的距离相当小,因此可以将纬度/经度坐标转换为局部 Map 网格系统(平面) . 然后两点之间的距离是直线向前直角毕达哥拉斯(平方和的总和) . Movable Type网站在这个here上有很多好的(javascript)代码 .

    第二种方法是进行球面距离计算 - 不是很漂亮,但你可以看到它here

    就个人而言,我会将坐标转换为本地网格系统,在英国应该是OSGB . 它是最少扭曲的方法 .

    希望这可以帮助

    Edit: 我在api的第3版中完成了这个,但它应该是直截了当的 . 此外,折线坐标应该非常接近,您不需要插入中间点 - 只需 grab 最近的折线坐标(节省您必须进行方位和距离计算) .

    Edit2 - With Code

    我有时间在你的时限内完成它(我确实有工作) . 你应该能够获得这个数字 . 坐标转换代码从可移动类型的网站和谷歌的一个示例中提取基本谷歌 Map . 基本上它用鼠标点击绘制折线,将每个鼠标点击的lat / long放在表字段中,将坐标转换为OSGB,然后转换为OS Grid(参见here) . 首次点击后,然后计算每个后续点之间的距离 . 希望这能让你上路 .

    <!DOCTYPE html>
    <html>
      <head>
        <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
        <style type="text/css">
          html { height: 100% }
          body { height: 100%; margin: 0; padding: 0 }
          #map_canvas { height: 100% }
        </style>
        <script type="text/javascript"
          src="http://maps.googleapis.com/maps/api/js?sensor=false">
        </script>
    
          <script src="Map.js" type="text/javascript"></script>
      </head>
      <body onload="initialize()" style="width:100%;height:100%">
      <div style="margin-right:auto;margin-left:auto;margin-top:100px;width:900px;">
        <div id="map_canvas" style="width:600px; height:500px;float:left;"></div>
          <div style="float:right;">
      <table>
      <tr>
        <td align="right">Latitude:</td>
        <td><input id="txtLatitude" maxlength="11" type="text" class="inputField"/></td>
      </tr>
      <tr>
        <td align="right">Longitude:</td>
        <td><input id="txtLongitude" maxlength="11" type="text" class="inputField"/></td>
      </tr>
    
      <tr>
        <td align="right">Eastings:</td>
        <td><input id="txtEast" maxlength="11" type="text" class="inputField"/></td>
      </tr>
      <tr>
        <td align="right">Northings:</td>
        <td><input id="txtNorth" maxlength="11" type="text" class="inputField"/></td>
      </tr>
    
       <tr>
        <td align="right">Distance:</td>
        <td><input id="txtDistance" maxlength="11" type="text" class="inputField"/></td>
      </tr>
    
      <tr>
        <td colspan=2 align="right">
    
        </td>
      </tr>
    </table>
    </div>
      </div>
    
    
    
      </body>
    </html>
    

    Map.js:

    function initialize() {
    
        var myOptions = {
            center: new google.maps.LatLng(53.43057, -2.14727),
            zoom: 18,
            mapTypeId: google.maps.MapTypeId.ROADMAP
        };
        var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
        var tempIcon = new google.maps.MarkerImage(
        "http://labs.google.com/ridefinder/images/mm_20_green.png",
        new google.maps.Size(12, 20),
        new google.maps.Size(6, 20)
        );
        var newShadow = new google.maps.MarkerImage(
        "http://labs.google.com/ridefinder/images/mm_20_shadow.png",
        new google.maps.Size(22, 20),
        new google.maps.Point(13, 13)
        );
    
        var tempMarker = new google.maps.Marker();
        tempMarker.setOptions({
            icon: tempIcon,
            shadow: newShadow,
            draggable: true
        });
        var latlngs = new google.maps.MVCArray();
        var displayPath = new google.maps.Polyline({
            map: map,
            strokeColor: "#FF0000",
            strokeOpacity: 1.0,
            strokeWeight: 2,
            path: latlngs
        });
        var lastEast;
        var lastNorth;
        function showTempMarker(e) {
            //Pythagorean distance calculates the length of the  hypotenuse (the sloping side)
            //of a right angle triangle. Plain (cartesian) coordinates are all right angle triangles.
            //The length of the hypotenuse is always the distance between two coordinates.
            //One side of the triangle is the difference in east coordinate and the other is
            //the difference in north coordinates
            function pythagorasDistance(E, N) {
                if (lastEast) {
                    if (lastEast) {
                        //difference in east coordinates. We don't know what direction we are going so
                        //it could be a negative number - so just take the absolute value (ie - get rid of any minus sign)
                        var EastDistance = Math.abs(E - lastEast);
                        //difference in north coordinates
                        var NorthDistance = Math.abs(N - lastNorth);
                        //take the power
                        var EastPower = Math.pow(EastDistance, 2);
                        var NorthPower = Math.pow(NorthDistance, 2);
                        //add them together and take the square root
                        var pythagorasDistance = Math.sqrt(EastPower + NorthPower );
                        //round the answer to get rid of ridiculous decimal places (we're not measuring to the neares millimetre)
                        var result = Math.floor(pythagorasDistance);
    
                        document.getElementById('txtDistance').value = result;
                    }
                }
    
            }
    
            function calcCatesian(degLat, degLng) {
                var OSGBLL = LL.convertWGS84toOSGB36(new LatLon(degLat, degLng));
                var EN = LL.LatLongToOSGrid(OSGBLL);
    
                document.getElementById('txtEast').value = EN.east;
                document.getElementById('txtNorth').value = EN.north;
                pythagorasDistance(EN.east, EN.north);
                lastEast = EN.east;
                lastNorth = EN.north;
    
            }
    
            tempMarker.setPosition(e.latLng);
            var lat = e.latLng.lat();
            var lng = e.latLng.lng();
            document.getElementById('txtLatitude').value = lat;
            document.getElementById('txtLongitude').value = lng;
            calcCatesian(lat, lng);
    
            google.maps.event.addListener(tempMarker, "drag", function() {
                document.getElementById('txtLatitude').value = tempMarker.getPosition().lat();
                document.getElementById('txtLongitude').value = tempMarker.getPosition().lng();
                calcCatesian(lat, lng);
            });
            tempMarker.setMap(map);
    
            var newLocation = new google.maps.LatLng(lat, lng);
            latlngs.push(newLocation);
            displayPath.setPath(latlngs);
    
        }
    
        google.maps.event.addListener(map, "click", showTempMarker);
    }
    
    // ---- the following are duplicated from LatLong.html ---- //
    
    /*
     * construct a LatLon object: arguments in numeric degrees & metres
     *
     * note all LatLong methods expect & return numeric degrees (for lat/long & for bearings)
     */
    function LatLon(lat, lon, height) {
        if (arguments.length < 3)
            height = 0;
        this.lat = lat;
        this.lon = lon;
        this.height = height;
    }
    
    function setPrototypes() {
    
        /*
         * represent point {lat, lon} in standard representation
         */
        LatLon.prototype.toString = function() {
            return this.lat.toLat() + ', ' + this.lon.toLon();
        }
        // extend String object with method for parsing degrees or lat/long values to numeric degrees
        //
        // this is very flexible on formats, allowing signed decimal degrees, or deg-min-sec suffixed by
        // compass direction (NSEW). A variety of separators are accepted (eg 3º 37' 09"W) or fixed-width
        // format without separators (eg 0033709W). Seconds and minutes may be omitted. (Minimal validation
        // is done).
    
        String.prototype.parseDeg = function() {
            if (!isNaN(this))
                return Number(this);                 // signed decimal degrees without NSEW
    
            var degLL = this.replace(/^-/, '').replace(/[NSEW]/i, '');  // strip off any sign or compass dir'n
            var dms = degLL.split(/[^0-9.]+/);                     // split out separate d/m/s
            for (var i in dms)
                if (dms[i] == '')
                    dms.splice(i, 1);
            // remove empty elements (see note below)
            switch (dms.length) {                                  // convert to decimal degrees...
                case 3:
                    // interpret 3-part result as d/m/s
                    var deg = dms[0] / 1 + dms[1] / 60 + dms[2] / 3600;
                    break;
                case 2:
                    // interpret 2-part result as d/m
                    var deg = dms[0] / 1 + dms[1] / 60;
                    break;
                case 1:
                    // decimal or non-separated dddmmss
                    if (/[NS]/i.test(this))
                        degLL = '0' + degLL;       // - normalise N/S to 3-digit degrees
                    var deg = dms[0].slice(0, 3) / 1 + dms[0].slice(3, 5) / 60 + dms[0].slice(5) / 3600;
                    break;
                default:
                    return NaN;
            }
            if (/^-/.test(this) || /[WS]/i.test(this))
                deg = -deg; // take '-', west and south as -ve
            return deg;
        }
        // note: whitespace at start/end will split() into empty elements (except in IE)
    
        // extend Number object with methods for converting degrees/radians
    
        Number.prototype.toRad = function() {  // convert degrees to radians
            return this * Math.PI / 180;
        }
        Number.prototype.toDeg = function() {  // convert radians to degrees (signed)
            return this * 180 / Math.PI;
        }
        // extend Number object with methods for presenting bearings & lat/longs
    
        Number.prototype.toDMS = function(dp) {  // convert numeric degrees to deg/min/sec
            if (arguments.length < 1)
                dp = 0;      // if no decimal places argument, round to int seconds
            var d = Math.abs(this);  // (unsigned result ready for appending compass dir'n)
            var deg = Math.floor(d);
            var min = Math.floor((d - deg) * 60);
            var sec = ((d - deg - min / 60) * 3600).toFixed(dp);
            // fix any nonsensical rounding-up
            if (sec == 60) {
                sec = (0).toFixed(dp);
                min++;
            }
            if (min == 60) {
                min = 0;
                deg++;
            }
            if (deg == 360)
                deg = 0;
            // add leading zeros if required
            if (deg < 100)
                deg = '0' + deg;
            if (deg < 10)
                deg = '0' + deg;
            if (min < 10)
                min = '0' + min;
            if (sec < 10)
                sec = '0' + sec;
            return deg + '\u00B0' + min + '\u2032' + sec + '\u2033';
        }
        Number.prototype.toLat = function(dp) {  // convert numeric degrees to deg/min/sec latitude
            return this.toDMS(dp).slice(1) + (this < 0 ? 'S' : 'N');  // knock off initial '0' for lat!
        }
        Number.prototype.toLon = function(dp) {  // convert numeric degrees to deg/min/sec longitude
            return this.toDMS(dp) + (this > 0 ? 'E' : 'W');
        }
        /*
         * extend Number object with methods for converting degrees/radians
         */
        Number.prototype.toRad = function() {  // convert degrees to radians
            return this * Math.PI / 180;
        }
        Number.prototype.toDeg = function() {  // convert radians to degrees (signed)
            return this * 180 / Math.PI;
        }
        /*
         * pad a number with sufficient leading zeros to make it w chars wide
         */
        Number.prototype.padLZ = function(w) {
            var n = this.toString();
            for (var i = 0; i < w - n.length; i++)
                n = '0' + n;
            return n;
        }
    };
    
    setPrototypes();
    
    LL = function() {
    
        // ellipse parameters
        var e = {
            WGS84: {
                a: 6378137,
                b: 6356752.3142,
                f: 1 / 298.257223563
            },
            Airy1830: {
                a: 6377563.396,
                b: 6356256.910,
                f: 1 / 299.3249646
            }
        };
    
        // helmert transform parameters
        var h = {
            WGS84toOSGB36: {
                tx: -446.448,
                ty: 125.157,
                tz: -542.060,   // m
                rx: -0.1502,
                ry: -0.2470,
                rz: -0.8421,  // sec
                s: 20.4894
            },                               // ppm
            OSGB36toWGS84: {
                tx: 446.448,
                ty: -125.157,
                tz: 542.060,
                rx: 0.1502,
                ry: 0.2470,
                rz: 0.8421,
                s: -20.4894
            }
        };
    
        return {
    
            convertOSGB36toWGS84: function(p1) {
                var p2 = this.convert(p1, e.Airy1830, h.OSGB36toWGS84, e.WGS84);
                return p2;
            },
            convertWGS84toOSGB36: function(p1) {
                var p2 = this.convert(p1, e.WGS84, h.WGS84toOSGB36, e.Airy1830);
                return p2;
            },
            convert: function(p1, e1, t, e2) {
                // -- convert polar to cartesian coordinates (using ellipse 1)
    
                p1.lat = p1.lat.toRad();
                p1.lon = p1.lon.toRad();
    
                var a = e1.a, b = e1.b;
    
                var sinPhi = Math.sin(p1.lat), cosPhi = Math.cos(p1.lat);
                var sinLambda = Math.sin(p1.lon), cosLambda = Math.cos(p1.lon);
                var H = p1.height;
    
                var eSq = (a * a - b * b) / (a * a);
                var nu = a / Math.sqrt(1 - eSq * sinPhi * sinPhi);
    
                var x1 = (nu + H) * cosPhi * cosLambda;
                var y1 = (nu + H) * cosPhi * sinLambda;
                var z1 = ((1 - eSq) * nu + H) * sinPhi;
    
                // -- apply helmert transform using appropriate params
    
                var tx = t.tx, ty = t.ty, tz = t.tz;
                var rx = t.rx / 3600 * Math.PI / 180;  // normalise seconds to radians
                var ry = t.ry / 3600 * Math.PI / 180;
                var rz = t.rz / 3600 * Math.PI / 180;
                var s1 = t.s / 1e6 + 1;              // normalise ppm to (s+1)
    
                // apply transform
                var x2 = tx + x1 * s1 - y1 * rz + z1 * ry;
                var y2 = ty + x1 * rz + y1 * s1 - z1 * rx;
                var z2 = tz - x1 * ry + y1 * rx + z1 * s1;
    
                // -- convert cartesian to polar coordinates (using ellipse 2)
    
                a = e2.a, b = e2.b;
                var precision = 4 / a;  // results accurate to around 4 metres
    
                eSq = (a * a - b * b) / (a * a);
                var p = Math.sqrt(x2 * x2 + y2 * y2);
                var phi = Math.atan2(z2, p * (1 - eSq)), phiP = 2 * Math.PI;
                while (Math.abs(phi - phiP) > precision) {
                    nu = a / Math.sqrt(1 - eSq * Math.sin(phi) * Math.sin(phi));
                    phiP = phi;
                    phi = Math.atan2(z2 + eSq * nu * Math.sin(phi), p);
                }
                var lambda = Math.atan2(y2, x2);
                H = p / Math.cos(phi) - nu;
    
                return new LatLon(phi.toDeg(), lambda.toDeg(), H);
            },
            /*
            * convert numeric grid reference (in metres) to standard-form grid ref
            */
            gridrefNumToLet: function(e, n, digits) {
                // get the 100km-grid indices
                var e100k = Math.floor(e / 100000), n100k = Math.floor(n / 100000);
    
                if (e100k < 0 || e100k > 6 || n100k < 0 || n100k > 12)
                    return '';
    
                // translate those into numeric equivalents of the grid letters
                var l1 = (19 - n100k) - (19 - n100k) % 5 + Math.floor((e100k + 10) / 5);
                var l2 = (19 - n100k) * 5 % 25 + e100k % 5;
    
                // compensate for skipped 'I' and build grid letter-pairs
                if (l1 > 7)
                    l1++;
                if (l2 > 7)
                    l2++;
                var letPair = String.fromCharCode(l1 + 'A'.charCodeAt(0), l2 + 'A'.charCodeAt(0));
    
                // strip 100km-grid indices from easting & northing, and reduce precision
                e = Math.floor((e % 100000) / Math.pow(10, 5 - digits / 2));
                n = Math.floor((n % 100000) / Math.pow(10, 5 - digits / 2));
    
                var gridRef = letPair + e.padLZ(digits / 2) + n.padLZ(digits / 2);
    
                return gridRef;
            },
            LatLongToOSGrid: function(p) {
                var lat = p.lat.toRad(), lon = p.lon.toRad();
    
                var a = 6377563.396, b = 6356256.910;          // Airy 1830 major & minor semi-axes
                var F0 = 0.9996012717;                         // NatGrid scale factor on central meridian
                var lat0 = (49).toRad(), lon0 = (-2).toRad();  // NatGrid true origin
                var N0 = -100000, E0 = 400000;                 // northing & easting of true origin, metres
                var e2 = 1 - (b * b) / (a * a);                      // eccentricity squared
                var n = (a - b) / (a + b), n2 = n * n, n3 = n * n * n;
    
                var cosLat = Math.cos(lat), sinLat = Math.sin(lat);
                var nu = a * F0 / Math.sqrt(1 - e2 * sinLat * sinLat);              // transverse radius of curvature
                var rho = a * F0 * (1 - e2) / Math.pow(1 - e2 * sinLat * sinLat, 1.5);  // meridional radius of curvature
                var eta2 = nu / rho - 1;
    
                var Ma = (1 + n + (5 / 4) * n2 + (5 / 4) * n3) * (lat - lat0);
                var Mb = (3 * n + 3 * n * n + (21 / 8) * n3) * Math.sin(lat - lat0) * Math.cos(lat + lat0);
                var Mc = ((15 / 8) * n2 + (15 / 8) * n3) * Math.sin(2 * (lat - lat0)) * Math.cos(2 * (lat + lat0));
                var Md = (35 / 24) * n3 * Math.sin(3 * (lat - lat0)) * Math.cos(3 * (lat + lat0));
                var M = b * F0 * (Ma - Mb + Mc - Md);              // meridional arc
    
                var cos3lat = cosLat * cosLat * cosLat;
                var cos5lat = cos3lat * cosLat * cosLat;
                var tan2lat = Math.tan(lat) * Math.tan(lat);
                var tan4lat = tan2lat * tan2lat;
    
                var I = M + N0;
                var II = (nu / 2) * sinLat * cosLat;
                var III = (nu / 24) * sinLat * cos3lat * (5 - tan2lat + 9 * eta2);
                var IIIA = (nu / 720) * sinLat * cos5lat * (61 - 58 * tan2lat + tan4lat);
                var IV = nu * cosLat;
                var V = (nu / 6) * cos3lat * (nu / rho - tan2lat);
                var VI = (nu / 120) * cos5lat * (5 - 18 * tan2lat + tan4lat + 14 * eta2 - 58 * tan2lat * eta2);
    
                var dLon = lon - lon0;
                var dLon2 = dLon * dLon, dLon3 = dLon2 * dLon, dLon4 = dLon3 * dLon, dLon5 = dLon4 * dLon, dLon6 = dLon5 * dLon;
    
                var N = I + II * dLon2 + III * dLon4 + IIIA * dLon6;
                var E = E0 + IV * dLon + V * dLon3 + VI * dLon5;
    
                E = Math.floor(E * 100) / 100;
                N = Math.floor(N * 100) / 100;
    
                //return this.gridrefNumToLet(E, N, 8);
                return { east: E, north: N }
            ;
            }
        }
    
    } ();
    
  • 4

    我认为你正在寻找类似于this函数的东西,它在给定的行上返回一个特定百分比的点 . 不幸的是,我可能值得一看 .

    与此同时,这是一个快速的黑客概念,可以为您提供足够的细节,满足您的需求:

    • 从您的折线开始(为简单起见,假设您只有一条路径,那么是一系列LatLngs)

    • 当你想估计这个人的位置时,按照时间确定路径的百分比(例如,早上8点它们是50%)

    • 现在,对于路径中的每个LatLng,通过添加LatLng之间的距离来计算它沿路径总长度的小数距离(您可以使用computeLength作为路径,使用computeDistanceBetween作为每个LatLng)

    • 一旦你得到> 50%的分数(在这种情况下),你知道这个人在这个LatLng和前一个之间 . 然后,如果您愿意,您可以确切地计算出确切位置的距离,或者只是跳过这一步,如果它是一个非常短的段并将它们的标记放在这些LatLng中的一个上 .

    • 以上是一般概念,但当然你应该通过为每个路径预先计算每个LatLng的百分比距离并将其存储在一个单独的对象中进行优化,并跟踪路径中的最后一个索引,这样就不会下次计算他们的距离时从头开始,等等 .

    希望这可以帮助 .

  • 2

    我认为你已经得到了答案,除了我没有看到任何人明确提到的一个小细节:你需要使用 steps 中编码的 polyline 到达你将在两点之间进行插值的点 . 足够接近,使它们之间的直线很好地接近路线的形状 .

    我们来看一个例子:

    从马德里到托莱多的行车路线:

    http://maps.googleapis.com/maps/api/directions/json?origin=Toledo&destination=Madrid&region=es&sensor=false

    中间点(整个路线的中间点)将位于最近的长度为近50公里的某个地方:

    {
       "distance" : {
          "text" : "49.7 km",
          "value" : 49697
       },
       "duration" : {
          "text" : "26 mins",
          "value" : 1570
       },
       "end_location" : {
          "lat" : 40.26681000000001,
          "lng" : -3.888580
       },
       "html_instructions" : "Continue onto \u003cb\u003eAP-41\u003c/b\u003e\u003cdiv style=\"font-size:0.9em\"\u003eToll road\u003c/div\u003e",
       "polyline" : {
          "points" : "kdtrFj`~VEkA[oLEy@Cs@KeAM_BOgBy@eGs@iFWgBS{AQ{AQcBMuAM}BKcBGiCM_EGaFKgEKeDOqC[yFWwCIi@Is@i@_EaAsFy@aEeAcEsAqEuAeE]w@k@qAeAcCm@sA}@cBgB{CaBgCiEyFuB}BsBuBaDwCa@]_CsBmD_Di@g@aAaAs@s@{@aAU[q@w@s@{@wE{Ge@s@_@o@a@q@o@kAmAaCaAqBeAcCw@kBy@yBw@_Cg@aB}AkEoBgFyDwIcBwDa@iAcBwCgAcBsAiBy@uAeCiDqBeCaB_BuA_BiDeC{@o@u@k@cD{B}@g@y@_@k@]cD_BkD{AwBu@cA]eAYsD_AuE_AqBY{Du@_BW_AQm@K_AOiAWuAa@qA_@mA_@aBi@MGOGMGy@[OI_Bw@uBkAkDsBaAg@oEiCg@YeEcC}As@m@WqCmAmEeBuAe@mDeAqCq@qCm@iB]eBY}BYeCYi@C{@IgBMwCMmAEmAC{A@mB?wBFsBBiBHeAJcBNgBNcBRcC\\qCd@sBb@eAXeBd@uBn@{Bp@uAd@}B~@gD|AmCrA{@j@}Az@kDvB{AbAqA|@}AnAaAx@aAv@}AtAaA`AwClD{HzImH~IiF|F{@~@o@v@qAhAsAhAqA`AyAbA{A~@m@Xw@h@gCnAiBz@uAt@wAh@}@XwCz@qBd@qCf@gBXkBTw@FaCTmDPsADwDJgCFoFXwDXoDb@gCd@wB`@gCh@_D~@qC~@gC~@wChAmDxAaC|@sCbAgEzAuGbBaB`@cFdAo@NoAXiC^cD^oDXmEToBJkABA?Q@_@@yDBwBAoB?wBEm@A_CKO?_EUcD[eEe@uAQq@I]GqDs@e@Ii@K_@IOEgAWk@MsBi@mBg@WIc@MkEwA{Am@yB}@yDcB_CgAsAs@eB}@aBaAiD{ByCqBkA}@mA}@uAiAwCcCyAoAmEiE{@aAgAyA{@cAmAuAaBsBkAyAgBcCwAoBwAwByCyEyBmD{BsDgCaEuA{Co@eAuC_Fs@iA{@iAo@_A{A}BkGmHmAwAeBaBcBeBcHsGmCkCyCyCm@m@m@m@_A_AkDaDkCiCkDgD}@y@iE_FcC}CkBkCw@gAs@cAcC{D_BmCe@}@}AuCsAkCiCqFkAgCe@kAgAeCw@mBuAaDWg@g@iAEE{AqCq@kA_@k@oCwDuAeBoAqAUQ_@YMOm@k@e@g@_@]u@m@k@a@i@_@YOSOe@[k@_@w@c@a@Ok@WyAo@y@[eBm@}Ac@{Bk@kASwBS}AMyBO}BGuGJsAJmCRuDn@iCn@}C`AwBx@kB|@{BfAmBfAkCdBaCzA_BpA_BlAuAnAeCdCuD`EgBzBgClDyBrD{AtCy@bB_@b@Wl@c@`AWr@KVSd@MXIPGPSd@MZQb@MZ_@bAm@dBQd@[`A_@jAGRIVGPGVIVSt@k@xBe@jBKd@U`As@nDKb@Q`AgAtHADM~ACNK|@SpBQhBKnBKxACv@OhDO~EGdFAhA@|CC~B?rA@vB@hB@dD@vBAbCCvCEzBGpBEpAEpAKrBI~@Ej@Et@WxCa@vDYrBq@bEQfAUnAy@vD}BtJUx@K^wBfGwCdHqBxD_B`CsBbDwCnEgCrCuCzCyBpBiCzBmBvAaC|AuAv@eAj@OHuAp@}@^oBz@eExAgBb@uFpAaC`@mDb@iCRmADaDFy@B}E?aEQaJcAuB]uA[uBc@kDu@eD{@{Cs@iDu@wBe@eEo@{BQwDYiEMkBEaA?aA?aB?_B@aBDsBJwDT{Ed@o@JcALi@JcBVuBb@sA\\eAV{Ct@aA\\cBh@_Bh@mAb@sCpAwDhB_CpA}BvAg@\\mAr@aBjAqBzAgBxAeBzAoBlB_C~BgAhAUV[`@uCjD_BvBQVGDw@fAiAdBeAdBuC`Fe@|@wCbGU^]r@]r@oAvCeApCQZKXo@vBu@|B}@zCoAjEg@vBc@~AOt@k@~Bu@jD}@tDc@zAW`AWv@Ux@[bAw@xBuAhD{@jByCvFcClDwBvCkCrCgCdCsBzAgBnAkBjAkBbAmAj@gAf@mDjAsBl@sBf@uBb@oBXaBLiBNaADgABuABkBCgEUuCU}Ck@_Cg@kCu@yB{@{BaAqBaA}@i@kAq@OIe@[g@_@a@WmAaAeAy@iAeA}@_AmAsAu@w@{@gA_@e@o@cAk@_Ay@sAYm@_@m@_@u@]q@u@cBi@eA[y@Se@g@iAYs@_@oAMi@[aAa@uA_@wAS}@a@cB]wAWqAI]CKgAyDu@yCo@eCgAmDu@cCmAoDmBwEgAaCa@_AcByCqDwGiBkCw@iAgBaCkAoAiC{CkBiBuAsAoBcBeEaD}BaBs@c@gCyAKEoBgAuAk@eBy@oAe@uCcAgBo@mD_AkCk@kAUsASgAQeAIm@ImCW_E[_FWwCSkBMuAM[E{@IGAmBUmCc@}@QcAUsA_@cAWgBi@w@UwAk@a@MmAi@eAe@yBiAk@[SMKEw@g@k@_@w@e@aC_Bc@]yBgBeCmB}BmB}BsB_BoAw@o@s@g@oDiCuA{@_BcAgAq@uBsAaAc@{@_@y@_@sAm@yD}AeDgAsDiAeCeAaCy@iCgAiBcAeAc@c@OyE{A{Ag@y@YaBm@{Aq@gAm@i@][YMMYWaGwGi@y@{A{B_A{Aw@iAs@iA_A}AcAaBsAiBeBkBoAiAaBsA{AcAoAq@iB}@qBu@wBk@cBa@qAW}@I}CSwEBiDVcBR_BXiCr@gBp@wBbAkAp@qA|@y@l@wCjC{@~@gArAmCzDiAnBm@tAu@jBq@pBmAvDwAnFw@bCELq@tBw@pBgAdCS\\qCnF_@f@yBtC{AhBqAvAkBhB{ArAyAhAg@Ze@Z{BrAiBz@SHy@^OFs@X_AZ_Bd@WJaDr@}B\\eBPoBNq@F_@@iC@gACu@Ai@Ey@IQC_B[}Bo@_@Ks@S"
       },
       "start_location" : {
          "lat" : 39.92150,
          "lng" : -3.927260
       },
       "travel_mode" : "DRIVING"
    },
    

    我担心这条折线太长(2856个字符)到display it directly in the Static Maps API,但是's not necessary, it'只是在这里显示折线的好方法 . 无论如何,您可以使用Interactive Polyline Encoder Utility粘贴此编码折线(在用 \ 替换 \\ 之后)来查看它 .

    现在,让我们想象你需要在这条路线中找到正好20公里的点 . 从这一步开始 . 也就是说, start_locationend_location 之间的点距离 start_location ,距上述折线定义的路线为20,000米 .

    在您的应用程序中,您将使用几何库中的Encoding Methods(您需要load explicitly)将此折线解码为整个LatLng点阵列 . 然后,您可以使用每两个相邻点之间的computeDistanceBetween来确定哪一个是该折线中的第一个LatLng点( Y ),该点距离 start_location 超过20,000 . 然后你取这个点加上前一个点( X )并在 XY 之间进行直线插值 . 此时,您可以指望这两个点之间的直线是路线形状的合理近似值 .

    请注意,这是一个相当详细的计算,可能会变得太昂贵 . 如果由于折线的大尺寸而遇到性能问题,可以通过删除部分点来简化它 . 巧妙地进行这种简化可能同样昂贵,所以我会保持简单;)

  • 1

    我会说这是可行的 . :-)这是我想象它的方式,但我还没有测试过它 .

    首先根据用户应该采用的“猜测路线”定义PolyLine . 将其存储在js中的局部变量中 . 有很多分数会很方便,使得估计点更好 .

    然后设置一个间隔(window.setInterval)来检查用户位置的更新,比如说每30秒 . 如果位置比间隔更新 - 显示已知位置并从上一个已知位置绘制实线,则创建一行已知数据 . (的setpath)

    如果没有新数据,请使用最新的几个已知点进行简单的速度计算 .

    使用速度和时间帧计算估计的行程距离 .

    使用计算的距离,加载您的估计路线对象并在“猜测路线”中逐点“行走”,直到伪步距离几乎等于您的估计值 . 然后返回到达正确距离的点 .

    从最后一个已知位置画一条虚线到猜到的位置 .

    祝好运!


    PS .

    PolyLine是由许多路径和航点组成的线对象

    使用geometry spherical命名空间函数"computeLength"计算点之间的长度

  • 1

    该网站:http://www.gmap-pedometer.com/可能是有意义的,因为它允许用户绘制路线,并添加英里或公里标记沿着路线,所以它必须做一个你需要的计算 .

相关问题