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

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

如果它是一条直线,这将很简单。

  • 有没有办法计算沿路线当前位置的距离?
  • 点数有限制吗?
  • 是否有应该避免的具体方法?

  • 用图片扩展我的例子:

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

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

    我对谷歌 map API 比较陌生,所以任何想法都会有所帮助。其他类似的问题也被问到过,但据我所知,没有人得到回答或要求提供尽可能多的细节。

    提前致谢。

    更新: 花了很多时间试图解决这个问题后,我惨遭失败。这是我所知道的:
  • 我应该使用 PolyLine 创建路径(我可以这样做,我有一个 lat/lng 列表)
  • 有一个名为 epoly.js 的 JS 扩展,但这与 V3
  • 不兼容
  • 使用spheric.interpolate 不起作用,因为它不遵循路径。
  • 最佳答案

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

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

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

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

    希望这可以帮助

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

    Edit2 - 使用代码

    我已经尝试将一些代码放在一起,但可能没有时间在您的时限内完成它(我确实有工作)。你应该能够得到jist。坐标转换代码是从移动式网站和谷歌示例之一中的基本谷歌 map 中提取的。基本上它通过鼠标点击绘制一条折线,将每次鼠标点击的纬度/经度放入表格字段,将坐标转换为 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 }
            ;
            }
        }
    
    } ();
    

    关于google-maps - 谷歌 map 距离近似值,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/8512372/

    10-12 01:46
    查看更多