Google Maps API v3 - 沿着折线拖曳标记 [英] Google Maps API v3 - Draggable Marker Along a Polyline
问题描述
我正在尝试创建一个局限于多段线的可拖动标记。我已阅读此文章(将Google地图V3标记限制为拖动到折线),但我不想创建标记可以移动的点。有没有其他方法可以做到这一点,而无需为标记创建点数组?如果任何人都可以指出我正确的方向,非常感谢。
据我所知,您必须将多段线加载到数组中。似乎没有办法解决这个问题。我不确定api如何捕捉道路,但我假设它基于这个概念(将点加载到数组中)。
我找到了一个较旧的maps v2库,它根据鼠标移动事件更新标记,在缩放结束时加载行数据。我更新了代码以使用api v3,并用拖动事件替换了鼠标事件。
要使用此库,请像这样初始化:
var snapToRoute = new SnapToRoute(map_instance,initial_marker,polyline);
图书馆可以在这里找到: SnapToRoute
**更新** 示例小提琴
这是我的修改版本:
pre $函数SnapToRoute(map,marker,polyline){
this.routePixels_ = [];
this.normalProj_ = map.getProjection();
this.map_ = map;
this.marker_ = marker;
this.polyline_ =折线;
this.init_();
SnapToRoute.prototype.init_ = function(){
this.loadLineData_();
this.loadMapListener_();
};
SnapToRoute.prototype.updateTargets = function(marker,polyline){
this.marker_ = marker || this.marker_;
this.polyline_ =折线|| this.polyline_;
this.loadLineData_();
};
SnapToRoute.prototype.loadMapListener_ = function(){
var me = this;
google.maps.event.addListener(me.marker_,dragend,function(evt){
me.updateMarkerLocation_(evt.latLng);
});
google.maps.event.addListener(me.marker_,drag,function(evt){
me.updateMarkerLocation_(evt.latLng);
});
google.maps.event.addListener(me.map_,zoomend,function(evt){
me.loadLineData_();
});
};
SnapToRoute.prototype.loadLineData_ = function(){
var zoom = this.map_.getZoom();
this.routePixels_ = [];
var path = this.polyline_.getPath();
for(var i = 0; i< path.getLength(); i ++){
var Px = this.normalProj_.fromLatLngToPoint(path.getAt(i));
this.routePixels_.push(Px);
}
};
SnapToRoute.prototype.updateMarkerLocation_ = function(mouseLatLng){
var markerLatLng = this.getClosestLatLng(mouseLatLng);
this.marker_.setPosition(markerLatLng);
};
SnapToRoute.prototype.getClosestLatLng = function(latlng){
var r = this.distanceToLines_(latlng);
返回this.normalProj_.fromPointToLatLng(新的google.maps.Point(r.x,r.y));
};
SnapToRoute.prototype.getDistAlongRoute = function(latlng){
if(typeof(opt_latlng)==='undefined'){
latlng = this.marker_.getLatLng() ;
}
var r = this.distanceToLines_(latlng);
返回this.getDistToLine_(r.i,r.to);
};
SnapToRoute.prototype.distanceToLines_ = function(mouseLatLng){
var zoom = this.map_.getZoom();
var mousePx = this.normalProj_.fromLatLngToPoint(mouseLatLng);
var routePixels_ = this.routePixels_;
返回this.getClosestPointOnLines_(mousePx,routePixels_);
};
SnapToRoute.prototype.getDistToLine_ =函数(line,to){
var routeOverlay = this.polyline_;
var d = 0; (n = 1; n
}
d + = google.maps.geometry.spherical.computeDistanceBetween(routeOverlay.getAt(line - 1),routeOverlay.getAt(line))* to;
return d;
};
SnapToRoute.prototype.getClosestPointOnLines_ = function(pXy,aXys){
var minDist;
var to;
var from;
var x;
var y;
var i;
var dist;如果(aXys.length> 1){
for(var n = 1; n< aXys.length; n ++){
if(aXys [n]),
。 x!== aXys [n - 1] .x){
var a =(aXys [n] .y - aXys [n - 1] .y)/(aXys [n] .x - aXys [n - 1] .x);
var b = aXys [n] .y - a * aXys [n] .x;
dist = Math.abs(a * pXy.x + b - pXy.y)/ Math.sqrt(a * a + 1);
} else {
dist = Math.abs(pXy.x - aXys [n] .x);
}
var rl2 = Math.pow(aXys [n] .y - aXys [n - 1] .y,2)+ Math.pow(aXys [n] .x - aXys [n-1] .x,2);
var ln2 = Math.pow(aXys [n] .y - pXy.y,2)+ Math.pow(aXys [n] .x - pXy.x,2);
var lnm12 = Math.pow(aXys [n - 1] .y - pXy.y,2)+ Math.pow(aXys [n - 1] .x - pXy.x,2);
var dist2 = Math.pow(dist,2);
var calcrl2 = ln2 - dist2 + lnm12 - dist2; (calcrl2> r12){
dist = Math.sqrt(Math.min(ln2,lnm12)); ((minDist == null)||(minDist> dist)){
to = Math.sqrt(lnm12-dist2)/ Math.sqrt(
)
RL2);
from = Math.sqrt(ln2 - dist2)/ Math.sqrt(r12);
minDist = dist;
i = n;
}
}
if(to> 1){
to = 1;
}
if(from> 1){
to = 0;
from = 1;
}
var dx = aXys [i - 1] .x - aXys [i] .x;
var dy = aXys [i - 1] .y - aXys [i] .y;
x = aXys [i - 1] .x - (dx * to);
y = aXys [i - 1] .y - (dy * to);
}
return {'x':x,'y':y,'i':我,'to':to,'from':from};
};
代码段:
var geocoder; var directionsDisplay; var directionsService = new google.maps.DirectionsService(); var map; var polyline = new google.maps.Polyline({path:[],strokeColor:'#FF0000' ,strokeWeight:3}); var marker; function initialize(){directionsDisplay = new google.maps.DirectionsRenderer(); map = new google.maps.Map(document.getElementById(map_canvas),{center:new google.maps.LatLng(37.4419,-122.1419),zoom:13,mapTypeId:google.maps.MapTypeId.ROADMAP}); calcRoute(New York,NY,Baltimore,MD); directionsDisplay.setMap(map);} google.maps.event.addDomListener(window,load,initialize);函数calcRoute(start,end){var request = {origin:start,destination:end,travelMode:google.maps .TravelMode.DRIVING}; directionsService.route(request,function(response,status){if(status == google.maps.DirectionsStatus.OK){// directionsDisplay.setDirections(response); renderRoute(response);}});} function renderRoute(response ){var bounds = new google.maps.LatLngBounds(); var route = response.routes [0]; var summaryPanel = document.getElementById(directions_panel); var detailsPanel = document.getElementById(direction_details); var path = response.routes [0] .overview_path; var legs = response.routes [0] .legs;对于(i = 0; i r12){dist = Math.sqrt(Math.min(In2,1nm12)); }((minDist == null)||(minDist> dist)){to = Math.sqrt(lnm12-dist2)/ Math.sqrt(r12);从= Math.sqrt(ln2-dist2)/ Math.sqrt(r12); minDist = dist; i = n; }} if(to> 1){to = 1; } if(from> 1){to = 0; from = 1; } var dx = aXys [i - 1] .x - aXys [i] .x; var dy = aXys [i - 1] .y - aXys [i] .y; x = aXys [i-1] .x - (dx * to); y = aXys [i-1] .y - (dy * to); } return {'x':x,'y':y,'i':i,'to':to,'from':from};};
html,body,#map_canvas {height:100%;宽度:100%; margin:0px; < script src =https:// //maps.googleapis.com/maps/api/js\"></script><div id =map_canvasstyle =border:2px solid#3872ac;>< / div>
I am trying to create a draggable marker that is confined to a polyline. I have read this post (Confine dragging of Google Maps V3 Marker to Polyline), but I do not want to create the points that the marker can move along. Are there other ways to do this without having to create a points array for the marker? If anyone can point me in the right direction, it is much appreciated.
From what I understand, you have to load the polyline points into an array. It seems that there is no way around this. I am not sure how the the directions api snaps to roads, but I am assuming that it is based on this concept (loading points into an array).
I have found an older maps v2 library that updates the marker based on mouse movement events, which loads the line data on zoom end. I have updated the code to work with api v3 and replaced the mouse events with drag events.
To use this library, initialize like this:
var snapToRoute = new SnapToRoute(map_instance, initial_marker, polyline);
The library can be found here: SnapToRoute
** Update ** example fiddle
Here is my modified version:
function SnapToRoute(map, marker, polyline) {
this.routePixels_ = [];
this.normalProj_ = map.getProjection();
this.map_ = map;
this.marker_ = marker;
this.polyline_ = polyline;
this.init_();
}
SnapToRoute.prototype.init_ = function () {
this.loadLineData_();
this.loadMapListener_();
};
SnapToRoute.prototype.updateTargets = function (marker, polyline) {
this.marker_ = marker || this.marker_;
this.polyline_ = polyline || this.polyline_;
this.loadLineData_();
};
SnapToRoute.prototype.loadMapListener_ = function () {
var me = this;
google.maps.event.addListener(me.marker_, "dragend", function (evt) {
me.updateMarkerLocation_(evt.latLng);
});
google.maps.event.addListener(me.marker_, "drag", function (evt) {
me.updateMarkerLocation_(evt.latLng);
});
google.maps.event.addListener(me.map_, "zoomend", function (evt) {
me.loadLineData_();
});
};
SnapToRoute.prototype.loadLineData_ = function () {
var zoom = this.map_.getZoom();
this.routePixels_ = [];
var path = this.polyline_.getPath();
for (var i = 0; i < path.getLength() ; i++) {
var Px = this.normalProj_.fromLatLngToPoint(path.getAt(i));
this.routePixels_.push(Px);
}
};
SnapToRoute.prototype.updateMarkerLocation_ = function (mouseLatLng) {
var markerLatLng = this.getClosestLatLng(mouseLatLng);
this.marker_.setPosition(markerLatLng);
};
SnapToRoute.prototype.getClosestLatLng = function (latlng) {
var r = this.distanceToLines_(latlng);
return this.normalProj_.fromPointToLatLng(new google.maps.Point(r.x, r.y));
};
SnapToRoute.prototype.getDistAlongRoute = function (latlng) {
if (typeof (opt_latlng) === 'undefined') {
latlng = this.marker_.getLatLng();
}
var r = this.distanceToLines_(latlng);
return this.getDistToLine_(r.i, r.to);
};
SnapToRoute.prototype.distanceToLines_ = function (mouseLatLng) {
var zoom = this.map_.getZoom();
var mousePx = this.normalProj_.fromLatLngToPoint(mouseLatLng);
var routePixels_ = this.routePixels_;
return this.getClosestPointOnLines_(mousePx, routePixels_);
};
SnapToRoute.prototype.getDistToLine_ = function (line, to) {
var routeOverlay = this.polyline_;
var d = 0;
for (var n = 1; n < line; n++) {
d += google.maps.geometry.spherical.computeDistanceBetween(routeOverlay.getAt(n - 1), routeOverlay.getAt(n));
}
d += google.maps.geometry.spherical.computeDistanceBetween(routeOverlay.getAt(line - 1), routeOverlay.getAt(line)) * to;
return d;
};
SnapToRoute.prototype.getClosestPointOnLines_ = function (pXy, aXys) {
var minDist;
var to;
var from;
var x;
var y;
var i;
var dist;
if (aXys.length > 1) {
for (var n = 1; n < aXys.length ; n++) {
if (aXys[n].x !== aXys[n - 1].x) {
var a = (aXys[n].y - aXys[n - 1].y) / (aXys[n].x - aXys[n - 1].x);
var b = aXys[n].y - a * aXys[n].x;
dist = Math.abs(a * pXy.x + b - pXy.y) / Math.sqrt(a * a + 1);
} else {
dist = Math.abs(pXy.x - aXys[n].x);
}
var rl2 = Math.pow(aXys[n].y - aXys[n - 1].y, 2) + Math.pow(aXys[n].x - aXys[n - 1].x, 2);
var ln2 = Math.pow(aXys[n].y - pXy.y, 2) + Math.pow(aXys[n].x - pXy.x, 2);
var lnm12 = Math.pow(aXys[n - 1].y - pXy.y, 2) + Math.pow(aXys[n - 1].x - pXy.x, 2);
var dist2 = Math.pow(dist, 2);
var calcrl2 = ln2 - dist2 + lnm12 - dist2;
if (calcrl2 > rl2) {
dist = Math.sqrt(Math.min(ln2, lnm12));
}
if ((minDist == null) || (minDist > dist)) {
to = Math.sqrt(lnm12 - dist2) / Math.sqrt(rl2);
from = Math.sqrt(ln2 - dist2) / Math.sqrt(rl2);
minDist = dist;
i = n;
}
}
if (to > 1) {
to = 1;
}
if (from > 1) {
to = 0;
from = 1;
}
var dx = aXys[i - 1].x - aXys[i].x;
var dy = aXys[i - 1].y - aXys[i].y;
x = aXys[i - 1].x - (dx * to);
y = aXys[i - 1].y - (dy * to);
}
return { 'x': x, 'y': y, 'i': i, 'to': to, 'from': from };
};
code snippet:
var geocoder;
var directionsDisplay;
var directionsService = new google.maps.DirectionsService();
var map;
var polyline = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
var marker;
function initialize() {
directionsDisplay = new google.maps.DirectionsRenderer();
map = new google.maps.Map(
document.getElementById("map_canvas"), {
center: new google.maps.LatLng(37.4419, -122.1419),
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
calcRoute("New York, NY", "Baltimore, MD");
directionsDisplay.setMap(map);
}
google.maps.event.addDomListener(window, "load", initialize);
function calcRoute(start, end) {
var request = {
origin: start,
destination: end,
travelMode: google.maps.TravelMode.DRIVING
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
// directionsDisplay.setDirections(response);
renderRoute(response);
}
});
}
function renderRoute(response) {
var bounds = new google.maps.LatLngBounds();
var route = response.routes[0];
var summaryPanel = document.getElementById("directions_panel");
var detailsPanel = document.getElementById("direction_details");
var path = response.routes[0].overview_path;
var legs = response.routes[0].legs;
for (i = 0; i < legs.length; i++) {
if (i == 0) {
marker = new google.maps.Marker({
position: legs[i].start_location,
draggable: true,
map: map
});
}
var steps = legs[i].steps;
for (j = 0; j < steps.length; j++) {
var nextSegment = steps[j].path;
for (k = 0; k < nextSegment.length; k++) {
polyline.getPath().push(nextSegment[k]);
bounds.extend(nextSegment[k]);
}
}
}
polyline.setMap(map);
map.fitBounds(bounds);
var snapToRoute = new SnapToRoute(map, marker, polyline);
}
function SnapToRoute(map, marker, polyline) {
this.routePixels_ = [];
this.normalProj_ = map.getProjection();
this.map_ = map;
this.marker_ = marker;
this.editable_ = Boolean(false);
this.polyline_ = polyline;
this.init_();
}
SnapToRoute.prototype.init_ = function() {
this.loadLineData_();
this.loadMapListener_();
};
SnapToRoute.prototype.updateTargets = function(marker, polyline) {
this.marker_ = marker || this.marker_;
this.polyline_ = polyline || this.polyline_;
this.loadLineData_();
};
SnapToRoute.prototype.loadMapListener_ = function() {
var me = this;
google.maps.event.addListener(me.marker_, "dragend", function(evt) {
me.updateMarkerLocation_(evt.latLng);
});
google.maps.event.addListener(me.marker_, "drag", function(evt) {
me.updateMarkerLocation_(evt.latLng);
});
google.maps.event.addListener(me.map_, "zoomend", function(evt) {
me.loadLineData_();
});
};
SnapToRoute.prototype.loadLineData_ = function() {
var zoom = this.map_.getZoom();
this.routePixels_ = [];
var path = this.polyline_.getPath();
for (var i = 0; i < path.getLength(); i++) {
var Px = this.normalProj_.fromLatLngToPoint(path.getAt(i));
this.routePixels_.push(Px);
}
};
SnapToRoute.prototype.updateMarkerLocation_ = function(mouseLatLng) {
var markerLatLng = this.getClosestLatLng(mouseLatLng);
this.marker_.setPosition(markerLatLng);
};
SnapToRoute.prototype.getClosestLatLng = function(latlng) {
var r = this.distanceToLines_(latlng);
return this.normalProj_.fromPointToLatLng(new google.maps.Point(r.x, r.y));
};
SnapToRoute.prototype.getDistAlongRoute = function(latlng) {
if (typeof(opt_latlng) === 'undefined') {
latlng = this.marker_.getLatLng();
}
var r = this.distanceToLines_(latlng);
return this.getDistToLine_(r.i, r.to);
};
SnapToRoute.prototype.distanceToLines_ = function(mouseLatLng) {
var zoom = this.map_.getZoom();
var mousePx = this.normalProj_.fromLatLngToPoint(mouseLatLng);
var routePixels_ = this.routePixels_;
return this.getClosestPointOnLines_(mousePx, routePixels_);
};
SnapToRoute.prototype.getDistToLine_ = function(line, to) {
var routeOverlay = this.polyline_;
var d = 0;
for (var n = 1; n < line; n++) {
d += google.maps.geometry.spherical.computeDistanceBetween(routeOverlay.getAt(n - 1), routeOverlay.getAt(n));
}
d += google.maps.geometry.spherical.computeDistanceBetween(routeOverlay.getAt(line - 1), routeOverlay.getAt(line)) * to;
return d;
};
SnapToRoute.prototype.getClosestPointOnLines_ = function(pXy, aXys) {
var minDist;
var to;
var from;
var x;
var y;
var i;
var dist;
if (aXys.length > 1) {
for (var n = 1; n < aXys.length; n++) {
if (aXys[n].x !== aXys[n - 1].x) {
var a = (aXys[n].y - aXys[n - 1].y) / (aXys[n].x - aXys[n - 1].x);
var b = aXys[n].y - a * aXys[n].x;
dist = Math.abs(a * pXy.x + b - pXy.y) / Math.sqrt(a * a + 1);
} else {
dist = Math.abs(pXy.x - aXys[n].x);
}
var rl2 = Math.pow(aXys[n].y - aXys[n - 1].y, 2) + Math.pow(aXys[n].x - aXys[n - 1].x, 2);
var ln2 = Math.pow(aXys[n].y - pXy.y, 2) + Math.pow(aXys[n].x - pXy.x, 2);
var lnm12 = Math.pow(aXys[n - 1].y - pXy.y, 2) + Math.pow(aXys[n - 1].x - pXy.x, 2);
var dist2 = Math.pow(dist, 2);
var calcrl2 = ln2 - dist2 + lnm12 - dist2;
if (calcrl2 > rl2) {
dist = Math.sqrt(Math.min(ln2, lnm12));
}
if ((minDist == null) || (minDist > dist)) {
to = Math.sqrt(lnm12 - dist2) / Math.sqrt(rl2);
from = Math.sqrt(ln2 - dist2) / Math.sqrt(rl2);
minDist = dist;
i = n;
}
}
if (to > 1) {
to = 1;
}
if (from > 1) {
to = 0;
from = 1;
}
var dx = aXys[i - 1].x - aXys[i].x;
var dy = aXys[i - 1].y - aXys[i].y;
x = aXys[i - 1].x - (dx * to);
y = aXys[i - 1].y - (dy * to);
}
return {
'x': x,
'y': y,
'i': i,
'to': to,
'from': from
};
};
html,
body,
#map_canvas {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js"></script>
<div id="map_canvas" style="border: 2px solid #3872ac;"></div>
这篇关于Google Maps API v3 - 沿着折线拖曳标记的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!