Google Maps API v3 - 沿折线的可拖动标记 [英] Google Maps API v3 - Draggable Marker Along a Polyline

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

问题描述

我正在尝试创建一个仅限于折线的可拖动标记.我已经阅读了这篇文章(限制拖动 Google Maps V3 Marker 到折线),但我不想创建标记可以移动的点.是否有其他方法可以做到这一点而不必为标记创建点数组?如果有人能指出我正确的方向,我将不胜感激.

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.

推荐答案

据我所知,您必须将折线点加载到数组中.似乎没有办法解决这个问题.我不确定方向 api 如何捕捉到道路,但我假设它基于这个概念(将点加载到数组中).

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).

我发现了一个较旧的地图 v2 库,它根据鼠标移动事件更新标记,在缩放结束时加载线数据.我已经更新了代码以使用 api v3,并用拖动事件替换了鼠标事件.

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

可以在此处找到该库:SnapToRoute

** 更新 ** 示例小提琴

这是我修改后的版本:

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

小提琴示例

代码片段:

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天全站免登陆