我正在寻找位于路线边界内的地方。我想按照距路线的距离顺序返回结果。
我在我的附近搜索请求中尝试了rankby=distance
,但它不起作用,因为它需要一个位置和半径,而我有一个LatLngBounds对象和一条路线。这甚至可能吗?
这是我的脚本沿着边界内的路线返回的地方。
它以随机顺序返回结果...
注意:目标是使用边界而不是位置。
答案 0 :(得分:7)
在我们考虑按路线距离排序结果之前,我们必须有一些基本功能来计算距离。我们立即陷入困境,因为地球当然是一个球体,球体上的距离比平面上的距离更复杂。
然而,在相对较小的距离(例如Tamiami到迈阿密),我们可以将距离看作是在飞机上,并取得合理的结果。如果我们能够接受这种近似,我们只需要一种方法来确定点和线段之间的最小距离。我没有重新发明轮子,而是改编了this SO answer的一些代码:
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)); }
既然我们所有使用距离(当前)都是排序,我们不必采取额外的步骤来取平方根,所以我们可以简单地使用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 );
}
请注意,我小心地命名此函数以指示此计算笛卡尔距离,这可能会导致长路线或靠近极点的路线出现问题。
现在我们有了这个功能,我们所要做的就是用距离注释每个结果,这样我们就可以对它进行排序,然后实际执行排序:
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; } );
为了调试可视化目的,我添加了一些代码来绘制从路径上最近点到每个位置的线:
var distLine = new google.maps.Polyline({
path: [place.closestPointOnPath, place.geometry.location],
strokeColor: '#ff0000',
strokeOpacity: 1.0,
strokeWeight: 2
});
distLine.setMap( map );
最后,为了额外的功劳,我使用了hasrsine公式(改编自this SO answer)
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;
}
所以现在我们可以在结果列表中显示真实的地理距离。
注意:因为我们计算飞机上的距离,对于长路线或靠近极点的路线,完全有可能订单与基于由半径确定的地理距离的(正确)顺序不匹配式。要解决此问题,需要推导出一种算法来确定球体上的点/线段距离,这是另一天的任务。
您可以在此处查看完整的解决方案:http://jsfiddle.net/UqLTE/4/
答案 1 :(得分:1)
如果您使用的是Place Search Request方法,则可以使用bounds参数而不是location和radius。
否则,您可以使用Google Maps API方法,将框的中心作为圆的中心,将框的对角线的一半作为圆的半径。这将为您提供包围盒子的圆圈内的所有位置。然后,您可以扫描整个集合,删除圆圈内但框外的任何位置。
将rankby = distance添加到方法调用将按照距指定位置的距离(例如框的中心)的顺序对结果进行排序。如果需要按照距离路径的距离排序,则需要计算距离路线的距离。最简单的方法是使用矢量投影计算路线中每个线段的垂线。如果投影点偏离线段的一端,则距离是该线末端的终点。沿向量的距离符号将告诉您要测试哪一端。到最近线段的距离是到路线的最短距离。
Google Maps API V3中最接近的方法是:DistanceMatrixService,用于查找多个源位置和目标位置之间的距离。还有一些其他方法可能对计算的某些部分有所帮助,但我看不到任何能找到路线/折线和地点/点之间距离的方法。
答案 2 :(得分:0)
这是一个想法。您必须从路径起点获取每个地点的每个距离,并将该值作为列表的属性添加,然后根据属性的值对列表进行排序。
以下是如何根据列表的属性值对列表进行排序的示例。
的 HTML 强>
<ul id="place-list">
<li data-id="4">Tokyo 4</li>
<li data-id="0">Paris 0</li>
<li data-id="5">Frankfurt 5</li>
<li data-id="2">London 2</li>
<li data-id="1">greece 1</li>
<li data-id="3">Munich 3</li>
</ul>
<button id="asc">ASC</button>
<button id="desc">DESC</button>
<强>的jQuery 强>
var sortArray = function (items, inverse) {
var inverse = inverse || false;
var sortedArray = items.map(function () {
return {
id: $(this).data("id"),
element: $(this)[0].outerHTML
};
});
var appendTo = items.parent();
items.remove();
sortedArray.sort(function (a, b) {
return a.id > b.id ? (inverse ? -1 : 1) : (inverse ? 1 : -1);
});
sortedArray.each(function () {
$(appendTo).append(this.element);
});
}
$("#asc").click(function () {
sortArray($("#place-list").find("li"));
});
$("#desc").click(function () {
sortArray($("#place-list").find("li"), true);
});