Google Maps API - 在定义的点中沿着折线保持可拖动标记

时间:2017-09-18 18:16:46

标签: javascript google-maps

我试图将标记点保留在预定义的点(纬度/经度)中,在示例中的数组“polypath”中,因此标记沿着折线移动到最近的存在点,所以我可以使用选定的点到另一个计算。

下面的代码显示了用户可以将标记保留在折线中的任何位置的当前行为:

var gmap;
var  currentMarker = new google.maps.Marker({
            position: new google.maps.LatLng(54.13512, -117.0114),
            draggable: true
        });
var snapToRoute = null;
var polypath = new Array(
        new google.maps.LatLng(54.13512, -117.0114),
        new google.maps.LatLng(54.13353, -117.01141),
        new google.maps.LatLng(54.1332, -117.01159),
        new google.maps.LatLng(54.13241, -117.01157),
        new google.maps.LatLng(54.13217, -117.01142),
        new google.maps.LatLng(54.12725, -117.01143),
        new google.maps.LatLng(54.12185, -117.00125),
        new google.maps.LatLng(54.1188, -116.9955),
        new google.maps.LatLng(54.11603, -116.99473),
        new google.maps.LatLng(54.11517, -116.99281),
        new google.maps.LatLng(54.10235, -116.97771),
        new google.maps.LatLng(54.10244, -116.96093),
        new google.maps.LatLng(54.10197, -116.96067),
        new google.maps.LatLng(54.10158, -116.96075),
        new google.maps.LatLng(54.09808, -116.95913),
        new google.maps.LatLng(54.09725, -116.95668),
        new google.maps.LatLng(54.09669, -116.95598),
        new google.maps.LatLng(54.08378, -116.9471),
        new google.maps.LatLng(54.08218, -116.94696),
        new google.maps.LatLng(54.07374, -116.93832),
        new google.maps.LatLng(54.07319, -116.93571),
        new google.maps.LatLng(54.07185, -116.93527));
        
    var polyline = new google.maps.Polyline({
				path: polypath,
				strokeColor: "red",
				strokeWeight: 2,
				strokeOpacity: 1
			});

function initialize() {
    gmap = new google.maps.Map(
    document.getElementById("map_canvas"), {
        center: new google.maps.LatLng(54.10244, -116.96093),
        zoom: 12
    });
    
    
     currentMarker.setMap(gmap);
    polyline.setMap(gmap);
   
    
   snapToRoute = new SnapToRoute(gmap, currentMarker, polyline);
}

google.maps.event.addDomListener(window, "load", initialize);

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
    };
};
html, body, #map_canvas {
    height: 100%;
    width: 100%;
    margin: 0px;
    padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js?sensor=false&libraries=geometry,places&ext=.js"></script>
<div id="map_canvas" style="border: 2px solid #3872ac;"></div>

1 个答案:

答案 0 :(得分:3)

删除将点插值到顶点之间的最近点的代码。

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

更改它以返回其中一个顶点(i是下一个顶点,i-1是前一个顶点):

x = aXys[i].x; 
y = aXys[i].y; 

proof of concept fiddle

代码段

var gmap;
var currentMarker = new google.maps.Marker({
  position: new google.maps.LatLng(54.13512, -117.0114),
  draggable: true
});
var snapToRoute = null;
var polypath = new Array(
  new google.maps.LatLng(54.13512, -117.0114),
  new google.maps.LatLng(54.13353, -117.01141),
  new google.maps.LatLng(54.1332, -117.01159),
  new google.maps.LatLng(54.13241, -117.01157),
  new google.maps.LatLng(54.13217, -117.01142),
  new google.maps.LatLng(54.12725, -117.01143),
  new google.maps.LatLng(54.12185, -117.00125),
  new google.maps.LatLng(54.1188, -116.9955),
  new google.maps.LatLng(54.11603, -116.99473),
  new google.maps.LatLng(54.11517, -116.99281),
  new google.maps.LatLng(54.10235, -116.97771),
  new google.maps.LatLng(54.10244, -116.96093),
  new google.maps.LatLng(54.10197, -116.96067),
  new google.maps.LatLng(54.10158, -116.96075),
  new google.maps.LatLng(54.09808, -116.95913),
  new google.maps.LatLng(54.09725, -116.95668),
  new google.maps.LatLng(54.09669, -116.95598),
  new google.maps.LatLng(54.08378, -116.9471),
  new google.maps.LatLng(54.08218, -116.94696),
  new google.maps.LatLng(54.07374, -116.93832),
  new google.maps.LatLng(54.07319, -116.93571),
  new google.maps.LatLng(54.07185, -116.93527));

var polyline = new google.maps.Polyline({
  path: polypath,
  strokeColor: "red",
  strokeWeight: 2,
  strokeOpacity: 1
});

function initialize() {
  gmap = new google.maps.Map(
    document.getElementById("map_canvas"), {
      center: new google.maps.LatLng(54.10244, -116.96093),
      zoom: 12
    });


  currentMarker.setMap(gmap);
  polyline.setMap(gmap);


  snapToRoute = new SnapToRoute(gmap, currentMarker, polyline);
}

google.maps.event.addDomListener(window, "load", initialize);

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;
    }
    x = aXys[i].x; // aXys[i - 1].x - (dx * to);
    y = aXys[i].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?sensor=false&libraries=geometry,places&ext=.js"></script>
<div id="map_canvas" style="border: 2px solid #3872ac;"></div>