谷歌地图距离近似

时间:2011-12-14 22:06:38

标签: google-maps google-maps-api-3 distance

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

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

如果它是一条直线,这本来就很简单。

  • 有没有办法计算沿路线当前位置的距离?
  • 点数有限制吗?
  • 是否有特定的方法可以避免这种情况?

使用图片扩展我的示例:

Example scenario

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

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

我对谷歌地图API相对较新,所以任何想法都会有所帮助。其他类似的问题已在SO上提出,但从我所看到的情况来看,没有人回答或要求我提供尽可能多的细节。

提前致谢。

更新:花了很多时间尝试解决这个问题我失败了。这就是我所知道的:

  • 我应该使用PolyLine创建路径(我可以这样做,我有一个lat / lng列表)
  • 有一个名为epoly.js的JS扩展,但这与V3不兼容
  • 使用spherical.interpolate不会工作,因为它不遵循路径。

5 个答案:

答案 0 :(得分:4)

我过去常常做过很多这样的东西作为制图师。您的折线由一系列点(纬度/长坐标)组成。在每个连续的点之间,你计算距离,在你走的过程中将它加起来,直到达到所需的距离。

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

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

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

希望这有帮助

修改 我假设您可以使用google api提取折线坐标。我没有在api的第3版中做到这一点,但它应该是直截了当的。此外,折线坐标应该非常接近,您不需要插入中间点 - 只需抓取最近的折线坐标(节省您必须进行方位和距离计算)。

Edit2 - 使用代码

我已经把一些代码放在一起,但可能没有时间在你的时间限制内完成它(我确实有工作)。你应该能够获得这个数字。坐标转换代码从可移动类型的网站和谷歌的一个示例中提取基本谷歌地图。基本上,它通过鼠标单击绘制折线,将每个鼠标单击的纬度/长度放在表格字段中,将坐标转换为OSGB,然后转换为OS网格(请参阅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 }
        ;
        }
    }

} ();

答案 1 :(得分:2)

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

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

  • 从您的折线开始(为简单起见,我们假设您只有一条路径,这是一系列LatLng)
  • 如果您想估计这个人的位置,请根据时间确定沿路径的百分比(例如,早上8点他们是50%)
  • 现在,对于路径中的每个LatLng,通过添加LatLng之间的距离来计算它沿路径总长度的小数距离(您可以使用computeLength作为路径,并使用computeDistanceBetween作为每个LatLng)
  • 一旦你得到一个> 50%的分数(在这种情况下),你知道这个人在这个LatLng和前一个之间。然后,如果您愿意,您可以确切地计算出确切位置的距离,或者只是跳过这一步,如果它是一个相当短的段并将它们的标记放在其中一个LatLng上。
  • 以上是一般概念,但当然您应该通过为每个路径预先计算每个LatLng的百分比距离并将其存储在单独的对象中进行优化,并跟踪路径中的最后一个索引,以便您不要下次计算他们的距离时,从头开始,等等。

希望这有帮助。

答案 2 :(得分:2)

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

让我们看一个例子:

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

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

中间点(整个路线的一半)将位于最大step的某个地方,长度接近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,但这不是必需的,它只是一个很好的方法来显示折线。无论如何,您可以使用Interactive Polyline Encoder Utility粘贴此编码折线(将\\替换为\后)以查看它。

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

在您的应用中,您将使用几何图书馆中的Encoding Methods(您需要load explicitly)将此折线解码为整个LatLng点阵列。然后,您可以在每两个相邻点之间使用computeDistanceBetween来确定哪一个是该折线中距离start_location超过20,000的第一个LatLng点(X)。然后,您将该点加上前一个点(X)并在Y和{{1}}之间进行直线插值。此时,您可以依靠这两个点之间的直线来合理地逼近路线的形状。

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

答案 3 :(得分:1)

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

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

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

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

使用速度和时间范围计算估计的行程距离。

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

从最后一个已知位置画一条虚线到猜测位置。

祝你好运!


PS。

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

使用geometry spherical命名空间函数“computeLength”

计算点之间的长度

答案 4 :(得分:1)

此网站:http://www.gmap-pedometer.com/可能会引起关注,因为它允许用户绘制路线,并沿路线添加英里或公里标记,因此它必须进行与您需要的计算类似的计算。