谷歌地图距离近似

Ada*_*mes 7 google-maps distance google-maps-api-3

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

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

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

  • 有没有办法计算沿路线当前位置的距离?
  • 点数有限制吗?
  • 有没有具体的方法来避免这种情况?

要使用图像扩展我的示例:

示例场景

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

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

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

提前致谢.

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

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

Ste*_* Mc 4

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

\n\n

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

\n\n

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

\n\n

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

\n\n

希望这可以帮助

\n\n

编辑: \n我假设您可以使用 google api 提取折线坐标。我还没有在 api 版本 3 中这样做,但它应该很简单。此外,折线坐标应该非常接近,您不需要插入中间点 - 只需获取最近的折线坐标(无需进行方位角和距离计算)。

\n\n

Edit2 - 带代码

\n\n

我尝试过将一些代码放在一起,但可能没有时间在您的时间限制内完成它(我有工作)。你应该能够明白要点。坐标转换代码取自可移动类型网站,基本的谷歌地图内容取自谷歌的示例之一。基本上,它通过鼠标单击绘制一条折线,将每次鼠标单击的纬度/经度放入表字段中,将坐标转换为 OSGB,然后转换为 OS 网格(请参见此处)。第一次单击后,它会计算每个后续点之间的距离。希望这能让你上路。

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

地图.js:

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