2015-08-04 10 views
1

Я пытаюсь создать перетаскиваемый маркер, ограниченный полилинией. Я прочитал this post (Confine dragging of Google Maps V3 Marker to Polyline), но я не хочу создавать точки, которые маркер может перемещать. Существуют ли другие способы сделать это без создания массива точек для маркера? Если кто-то может указать мне в правильном направлении, это очень ценится.API Карт Google v3 - Перетаскиваемый маркер вдоль полилинии

+1

Я нашел библиотеку snaptoroute, которая использует движение мыши, но она основана на v2 API. Я изменил его для работы с перетаскиваемыми маркерами и обновил его для работы с v3. После завершения я обновлю этот вопрос с помощью рабочего кода. – kryptonkal

ответ

4

Из чего я понимаю, вам нужно загрузить точки полилинии в массив. Кажется, что нет никакого способа обойти это. Я не уверен, как направления api привязаны к дорогам, но я предполагаю, что он основан на этой концепции (загрузка точек в массив).

Я нашел библиотеку старых версий v2, которая обновляет маркер на основе событий перемещения мыши, который загружает данные линии в конец зума. Я обновил код для работы с api v3 и заменил события мыши событиями перетаскивания.

Чтобы использовать эту библиотеку, инициализировать так:

var snapToRoute = new SnapToRoute(map_instance, initial_marker, polyline); 

Библиотеку можно найти здесь: SnapToRoute

** Обновление ** example fiddle

Вот моя модифицированная версия:

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

фрагмент кода:

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>

+0

Спасибо geocodezip за добавление фрагмента кода. Я уверен, что это будет полезно для кого-то другого. – kryptonkal