Google Maps API v3 - 沿着折线拖曳标记 [英] Google Maps API v3 - Draggable Marker Along a Polyline

查看:79
本文介绍了Google Maps API v3 - 沿着折线拖曳标记的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个局限于多段线的可拖动标记。我已阅读此文章(将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(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;如果(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://


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 };
};

example fiddle

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆