Google Maps API V3 - 最接近边界内的路线

Cyb*_*kie 3 javascript geolocation google-maps-api-3

我正在寻找位于路线边界内的地方.我想按照距路线的距离顺序返回结果.

我尝试rankby=distance了我的附近搜索请求,但它不起作用,因为它需要一个位置和半径,而我有一个LatLngBounds对象和一个路由.这甚至可能吗?

这是我的脚本沿着边界内的路线返回的位置.

http://jsfiddle.net/FMpBU/

它以随机顺序返回结果...

注意:目标是使用边界而不是位置.

Eth*_*own 7

在我们考虑按路线距离排序结果之前,我们必须有一些基本功能来计算距离.我们立即陷入困境,因为地球当然是一个球体,球体上的距离比平面上的距离更复杂.

然而,在相对较小的距离(例如Tamiami到迈阿密),我们可以将距离看作是在飞机上,并取得合理的结果.如果我们能够接受这种近似,我们只需要一种方法来确定点和线段之间的最小距离.我没有重新发明轮子,而是根据这个SO答案修改了一些代码:

    function sqr(x) { return x * x }
    function dist2(v, w) { return sqr(v.x - w.x) + sqr(v.y - w.y) }
    function distToSegment2(p, v, w) {
      return dist2(getClosestPoint(p,v,w));
    }
    function getClosestPoint( p, v, w ) {
      var l2 = dist2(v, w);
      if (l2 === 0) return v;   // line is actually a point; just return one ofthe two points
      var t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
      // point is closest to v, return v
      if (t < 0) return v;
      // point is closest to w, return w
      if (t > 1) return w;
      // point is closets to some midpoint, return that
      return { x: v.x + t * (w.x - v.x), y: v.y + t * (w.y - v.y) };
    }
    function distToSegment(p, v, w) { return Math.sqrt(distToSegmentSquared(p, v, w)); }
Run Code Online (Sandbox Code Playgroud)

现在我们所有使用距离(当前)都是排序,我们不必采取额外的步骤来取平方根,所以我们可以简单地使用dist2(距离平方)函数来节省一点额外的计算.

Google路线查询的结果包含一个名为的数组overview_path,其中包含用于在地图上实际绘制路径的所有线段.我们将使用这些段来确定最近的点:

    function closestPointOnPath_Cartesian( place, path, cb ) {
        var min = Number.MAX_VALUE;
        var closestPoint = null;
        for( var i=0; i<path.length-1; i++ ) {
            var v = { x: path[i].lng(), y: path[i].lat() };
            var w = { x: path[i+1].lng(), y: path[i+1].lat() };
            var p1 = { x: place.geometry.location.lng(), 
                        y: place.geometry.location.lat() };
            var p2 = getClosestPoint( p1, v, w );
            var d2 = dist2( p1, p2 );
            if( d2 < min ) {
                min = d2;
                closestPoint = new google.maps.LatLng( p2.y, p2.x );
            }
        }
        cb( closestPoint, min );
    }
Run Code Online (Sandbox Code Playgroud)

请注意,我小心地命名此函数以指示此计算笛卡尔距离,这可能导致长路线或靠近极点的路线出现问题.

现在我们有了这个函数,我们所要做的就是用距离注释每个结果,这样我们就可以对它进行排序,然后实际执行排序:

for( var i=0; i<results.length; i++ ) {
        closestPointOnPath_Cartesian( results[i], 
            result.routes[0].overview_path, 
            function( closestPoint, coordDist2 ){
                results[i].closestPointOnPath = closestPoint;
                results[i].coordDist2 = coordDist2;
                results[i].geoDistKm = geoDistanceKm( results[i].geometry.location, closestPoint );
        });
    }
    // sort results by relative distance (coordDist2)
    results.sort( function(a,b) { return a.coordDist2 - b.coordDist2; } );
Run Code Online (Sandbox Code Playgroud)

为了调试可视化目的,我添加了一些代码来绘制从路径上最近点到每个位置的线:

       var distLine = new google.maps.Polyline({
               path: [place.closestPointOnPath, place.geometry.location],
               strokeColor: '#ff0000',
               strokeOpacity: 1.0,
               strokeWeight: 2
           });
           distLine.setMap( map );
Run Code Online (Sandbox Code Playgroud)

最后,为了额外的功劳,我使用了hasrsine公式(改编自这个SO答案)

    Number.prototype.toRad = function() {
       return this * Math.PI / 180;
    }
    // geographic distance courtesy the haversine formula
    function geoDistanceKm(p1,p2) {
        var R = 6371; // km 
        var x1 = p2.lat()-p1.lat();
        var dLat = x1.toRad();  
        var x2 = p2.lng()-p1.lng();
        var dLon = x2.toRad();  
        var a = Math.sin(dLat/2) * Math.sin(dLat/2) + 
            Math.cos(p1.lat().toRad()) * Math.cos(p2.lat().toRad()) * 
            Math.sin(dLon/2) * Math.sin(dLon/2);  
        var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
        return R * c;
    }
Run Code Online (Sandbox Code Playgroud)

所以现在我们可以在结果列表中显示真实的地理距离.

注意:因为我们计算飞机上的距离,对于长路线或靠近极点的路线,完全有可能订单与基于由半正公式确定的地理距离的(正确)顺序不匹配.要解决此问题,需要推导出一种算法来确定球体上的点/线段距离,这是另一天的任务.

你可以在这里看到完整的解决方案:http://jsfiddle.net/UqLTE/4/