13

I've created a Google Map and have drawn a polyline on it. I've then added a marker to the start of the polyine (same coords as the starting coords of the polyline).

What I'd like to be able to do, is grab and drag the marker but have it "stick" to the polyline such that you can only drag it along the polyline and not away or to the side of it.

Is it possible to confine a draggable marker to a path in GM V3? If not, can anyone think how this might be done? There's the possibility of snapping the marker to the nearest point on the path when the user drops it, but I'd prefer a smoother "drag along the path" effect.

Happy to have ArcGis suggestions too. Have not provided code as this is more of an in theory question.

Let me know if I need to explain further.

Thanks in advance

4

2 回答 2

13

好的,所以我设法解决了这个问题。它并不完全优雅,我相信它可以改进,但这是我提出的一般概念:

我从 GPX 文件创建了一个 latlng 点数组,但它们仅每 20 秒左右记录一次点。这对我的目的来说粒度不够,所以我所做的是在 gpx 记录的每对点之间用大约 10 个点(在一条直线上)填充点数组:

$.each(array_of_points_to_pad, function(key, pt) {
    var current_point = pt;//The current point
    var next_point = array_of_points_to_pad[key + 1];//The point immediately after the current point

    //Check that we're not on the last point 
    if (typeof next_point !== 'undefined') {
        //Get a 10th of the difference in latitude between current and next points
        var lat_incr = (next_point.lat() - current_point.lat()) / 10;

        //Get a 10th of the difference in longitude between current and next points
        var lng_incr = (next_point.lng() - current_point.lng()) / 10;

        //Add the current point to a new padded_points array
        padded_points.push(current_point);

        //Now add 10 additional points at lat_incr & lng_incr intervals between current and next points (in the new padded_points array)
        for (var i = 1; i <= 10; i++) {
            var new_pt = new google.maps.LatLng(current_point.lat() + (i * lat_incr), current_point.lng() + (i * lng_incr));
            padded_points.push(new_pt);
        }
    }
});

现在我有了更精细的点数组,我用它来绘制折线。填充的折线看起来与没有填充的折线没有什么不同,因为所有附加点都位于现有点之间的线性“as-the-crow-flies”线上。

var line = new google.maps.Polyline({
    path: polyline_path_points_padded,
    strokeColor: '#ff0000',
    strokeOpacity: 1.0,
    strokeWeight: 2
});
line.setMap(map);

现在我在行首添加一个可拖动的标记:

var latLng = new google.maps.LatLng(startlat,startlng);
var marker = new google.maps.Marker({
  position: latLng,
  map: map,
  draggable:true
});

剩下要做的就是控制这个标记的拖动和拖动事件:

google.maps.event.addDomListener(marker,'dragend',function(e){
    marker.setPosition(find_closest_point_on_path(e.latLng,padded_points));
});

google.maps.event.addDomListener(marker,'drag',function(e){
    marker.setPosition(find_closest_point_on_path(e.latLng,padded_points));
});

在这里,我们只是在拖动和放下标记时将标记的 latLng 发送到函数 find_closest_point_on_path()。我们将填充的点数组作为搜索路径发送。

该函数如下所示:

function find_closest_point_on_path(marker_pt,path_pts){
    distances = new Array();
    distance_keys = new Array();
    $.each(path_pts,function(key, path_pt){
        var R = 6371; // km
        var dLat = (path_pt.lat()-marker_pt.lat()).toRad();
        var dLon = (path_pt.lng()-marker_pt.lng()).toRad();
        var lat1 = marker_pt.lat().toRad();
        var lat2 = path_pt.lat().toRad();

        var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
                Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2); 
        var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
        var d = R * c;
        //Store the key of the point on the path that matches this distance
        distance_keys[d] = key; 

    });
            //Return the latLng pt on the path for the second closest point 
    return path_pts[distance_keys[_.min(distances)]+1];
}

这个函数的作用(借助度数到弧度函数)是找到标记位置和线上所有点之间的距离。然后它找到离标记最近的点,并返回紧随其后的下一个最近点的坐标。这样,当您拖放标记时,它会“捕捉”到下一个点(而不是卡在一个点上)。

下面的工作JS小提琴:

http://jsfiddle.net/Z5GwW/4/

没有跨浏览器测试。在 Chrome 最新版本中工作。

于 2012-05-23T01:38:34.880 回答
0

感谢您提供此解决方案。

为了让它在没有依赖关系的情况下工作,我不得不修改几行:

首先,检查 toRad() 函数是否存在:

if (typeof(Number.prototype.toRad) === "undefined") {
	  Number.prototype.toRad = function() {
	    return this * Math.PI / 180;
	  }
	}

而且,删除_。通过替换返回码的依赖:

return path_pts[distance_keys[ Math.min(...distances) ]+1];

最后,在 distancekeys[] 之前包含 distances[]

distances[key] = d;
distance_keys[d] = key; 

于 2017-02-03T09:25:05.560 回答