根据路径方向更改谷歌地图标记方向

Posted

技术标签:

【中文标题】根据路径方向更改谷歌地图标记方向【英文标题】:Change Google map marker orientation according path direction 【发布时间】:2014-06-02 16:43:54 【问题描述】:

我想知道是否可以根据地图上绘制的路径更改标记方向。这是一个例子:

如您所见,标记是一辆汽车(带有前保险杠和尾灯)。我想将汽车定位在路径前进的方向上(在此示例中,将汽车定位在右侧约 45 度)。

我正在使用DirectionsService 来绘制路径,并且我有一个随机点数。有时只有 1 点,有时 10 点。我在绘制路径之前添加了标记。这是我绘制路径的方式:

// Intialize the Path Array
var path = new google.maps.MVCArray();

// Intialise the Direction Service
var service = new google.maps.DirectionsService();

// Set the Path Stroke Color
var poly = new google.maps.Polyline( map: gmap, strokeColor: '#dd0000' ); // #4986E7

// Draw the path for this vehicle
for (var i = 0; i < pathPoints.length; i++) 
    if ((i + 1) < pathPoints.length) 
        var src = pathPoints[i];
        var des = pathPoints[i + 1];
        path.push(src);
        poly.setPath(path);
        service.route(
            origin: src,
            destination: des,
            travelMode: google.maps.DirectionsTravelMode.DRIVING
        , function (result, status) 
            if (status == google.maps.DirectionsStatus.OK) 
                for (var i = 0, len = result.routes[0].overview_path.length; i < len; i++)
                    path.push(result.routes[0].overview_path[i]);
                
            
        );
    

有可能吗?

【问题讨论】:

【参考方案1】:

一种选择是将您的图标转换为 SVG,然后使用 Symbol rotation 属性将其与道路对齐(您可以对 PNG 图像执行相同的操作,方法是制作大量旋转图标的副本度数或二度,或者通过制作允许您任意旋转PNG图标的自定义图标)

marker.setPosition(p);
var heading = google.maps.geometry.spherical.computeHeading(lastPosn,p);
icon.rotation = heading;
marker.setIcon(icon);

proof of concept fiddle

代码 sn-p:

var map;
var directionDisplay;
var directionsService;
var stepDisplay;
var markerArray = [];
var position;
var marker = null;
var polyline = null;
var poly2 = null;
var speed = 0.000005,
  wait = 1;
var infowindow = null;
var timerHandle = null;

function createMarker(latlng, label, html) 
  var contentString = '<b>' + label + '</b><br>' + html;
  var marker = new google.maps.Marker(
    position: latlng,
    map: map,
    title: label,
    zIndex: Math.round(latlng.lat() * -100000) << 5
  );
  marker.myname = label;
  google.maps.event.addListener(marker, 'click', function() 
    infowindow.setContent(contentString);
    infowindow.open(map, marker);
  );
  return marker;


function initialize() 
  infowindow = new google.maps.InfoWindow(
    size: new google.maps.Size(150, 50)
  );
  // Instantiate a directions service.
  directionsService = new google.maps.DirectionsService();

  // Create a map and center it on Manhattan.
  var myOptions = 
    zoom: 13,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  ;
  map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

  address = 'new york';
  geocoder = new google.maps.Geocoder();
  geocoder.geocode(
    'address': address
  , function(results, status) 
    map.setCenter(results[0].geometry.location);
  );

  // Create a renderer for directions and bind it to the map.
  var rendererOptions = 
    map: map
  ;
  directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);

  // Instantiate an info window to hold step text.
  stepDisplay = new google.maps.InfoWindow();

  polyline = new google.maps.Polyline(
    path: [],
    strokeColor: '#FF0000',
    strokeWeight: 3
  );
  poly2 = new google.maps.Polyline(
    path: [],
    strokeColor: '#FF0000',
    strokeWeight: 3
  );


var steps = [];

function calcRoute() 

  if (timerHandle) 
    clearTimeout(timerHandle);
  
  if (marker) 
    marker.setMap(null);
  
  polyline.setMap(null);
  poly2.setMap(null);
  directionsDisplay.setMap(null);
  polyline = new google.maps.Polyline(
    path: [],
    strokeColor: '#FF0000',
    strokeWeight: 3
  );
  poly2 = new google.maps.Polyline(
    path: [],
    strokeColor: '#FF0000',
    strokeWeight: 3
  );
  // Create a renderer for directions and bind it to the map.
  var rendererOptions = 
    map: map
  ;
  directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);

  var start = document.getElementById("start").value;
  var end = document.getElementById("end").value;
  var travelMode = google.maps.DirectionsTravelMode.DRIVING;

  var request = 
    origin: start,
    destination: end,
    travelMode: travelMode
  ;

  // Route the directions and pass the response to a
  // function to create markers for each step.
  directionsService.route(request, function(response, status) 
    if (status == google.maps.DirectionsStatus.OK) 
      directionsDisplay.setDirections(response);

      var bounds = new google.maps.LatLngBounds();
      var route = response.routes[0];
      startLocation = new Object();
      endLocation = new Object();

      // For each route, display summary information.
      var path = response.routes[0].overview_path;
      var legs = response.routes[0].legs;
      for (i = 0; i < legs.length; i++) 
        if (i === 0) 
          startLocation.latlng = legs[i].start_location;
          startLocation.address = legs[i].start_address;
          //   marker = createMarker(legs[i].start_location, "start", legs[i].start_address, "green");
        
        endLocation.latlng = legs[i].end_location;
        endLocation.address = legs[i].end_address;
        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);
      map.setZoom(18);
      startAnimation();
    
  );




var step = 50; // 5; // metres
var tick = 100; // milliseconds
var eol;
var k = 0;
var stepnum = 0;
var speed = "";
var lastVertex = 1;

//=============== animation functions ======================
function updatePoly(d) 
  // Spawn a new polyline every 20 vertices, because updating a 100-vertex poly is too slow
  if (poly2.getPath().getLength() > 20) 
    poly2 = new google.maps.Polyline([polyline.getPath().getAt(lastVertex - 1)]);
    // map.addOverlay(poly2)
  

  if (polyline.GetIndexAtDistance(d) < lastVertex + 2) 
    if (poly2.getPath().getLength() > 1) 
      poly2.getPath().removeAt(poly2.getPath().getLength() - 1);
    
    poly2.getPath().insertAt(poly2.getPath().getLength(), polyline.GetPointAtDistance(d));
   else 
    poly2.getPath().insertAt(poly2.getPath().getLength(), endLocation.latlng);
  


function animate(d) 
  if (d > eol) 
    map.panTo(endLocation.latlng);
    marker.setPosition(endLocation.latlng);
    return;
  
  var p = polyline.GetPointAtDistance(d);
  map.panTo(p);
  var lastPosn = marker.getPosition();
  marker.setPosition(p);
  var heading = google.maps.geometry.spherical.computeHeading(lastPosn, p);
  icon.rotation = heading;
  marker.setIcon(icon);
  updatePoly(d);
  timerHandle = setTimeout("animate(" + (d + step) + ")", tick);


function startAnimation() 
  eol = polyline.Distance();
  map.setCenter(polyline.getPath().getAt(0));
  marker = new google.maps.Marker(
    position: polyline.getPath().getAt(0),
    map: map,
    icon: icon
  );

  poly2 = new google.maps.Polyline(
    path: [polyline.getPath().getAt(0)],
    strokeColor: "#0000FF",
    strokeWeight: 10
  );
  // map.addOverlay(poly2);
  setTimeout("animate(50)", 2000); // Allow time for the initial map display

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

//=============== ~animation funcitons =====================

var car = "M17.402,0H5.643C2.526,0,0,3.467,0,6.584v34.804c0,3.116,2.526,5.644,5.643,5.644h11.759c3.116,0,5.644-2.527,5.644-5.644 V6.584C23.044,3.467,20.518,0,17.402,0z M22.057,14.188v11.665l-2.729,0.351v-4.806L22.057,14.188z M20.625,10.773 c-1.016,3.9-2.219,8.51-2.219,8.51H4.638l-2.222-8.51C2.417,10.773,11.3,7.755,20.625,10.773z M3.748,21.713v4.492l-2.73-0.349 V14.502L3.748,21.713z M1.018,37.938V27.579l2.73,0.343v8.196L1.018,37.938z M2.575,40.882l2.218-3.336h13.771l2.219,3.336H2.575z M19.328,35.805v-7.872l2.729-0.355v10.048L19.328,35.805z";
var icon = 
  path: car,
  scale: .7,
  strokeColor: 'white',
  strokeWeight: .10,
  fillOpacity: 1,
  fillColor: '#404040',
  offset: '5%',
  // rotation: parseInt(heading[i]),
  anchor: new google.maps.Point(10, 25) // orig 10,50 back of car, 10,0 front of car, 10,25 center of car
;

/*********************************************************************\
*                                                                     *
* epolys.js                                          by Mike Williams *
* updated to API v3                                  by Larry Ross    *
*                                                                     *
* A Google Maps API Extension                                         *
*                                                                     *
* Adds various Methods to google.maps.Polygon and google.maps.Polyline *
*                                                                     *
* .Contains(latlng) returns true is the poly contains the specified   *
*                   GLatLng                                           *
*                                                                     *
* .Area()           returns the approximate area of a poly that is    *
*                   not self-intersecting                             *
*                                                                     *
* .Distance()       returns the length of the poly path               *
*                                                                     *
* .Bounds()         returns a GLatLngBounds that bounds the poly      *
*                                                                     *
* .GetPointAtDistance() returns a GLatLng at the specified distance   *
*                   along the path.                                   *
*                   The distance is specified in metres               *
*                   Reurns null if the path is shorter than that      *
*                                                                     *
* .GetPointsAtDistance() returns an array of GLatLngs at the          *
*                   specified interval along the path.                *
*                   The distance is specified in metres               *
*                                                                     *
* .GetIndexAtDistance() returns the vertex number at the specified    *
*                   distance along the path.                          *
*                   The distance is specified in metres               *
*                   Returns null if the path is shorter than that      *
*                                                                     *
* .Bearing(v1?,v2?) returns the bearing between two vertices          *
*                   if v1 is null, returns bearing from first to last *
*                   if v2 is null, returns bearing from v1 to next    *
*                                                                     *
*                                                                     *
***********************************************************************
*                                                                     *
*   This javascript is provided by Mike Williams                      *
*   Blackpool Community Church Javascript Team                        *
*   http://www.blackpoolchurch.org/                                   *
*   http://econym.org.uk/gmap/                                        *
*                                                                     *
*   This work is licenced under a Creative Commons Licence            *
*   http://creativecommons.org/licenses/by/2.0/uk/                    *
*                                                                     *
***********************************************************************
*                                                                     *
* Version 1.1       6-Jun-2007                                        *
* Version 1.2       1-Jul-2007 - fix: Bounds was omitting vertex zero *
*                                add: Bearing                         *
* Version 1.3       28-Nov-2008  add: GetPointsAtDistance()           *
* Version 1.4       12-Jan-2009  fix: GetPointsAtDistance()           *
* Version 3.0       11-Aug-2010  update to v3                         *
*                                                                     *
\*********************************************************************/

// === first support methods that don't (yet) exist in v3
google.maps.LatLng.prototype.distanceFrom = function(newLatLng) 
  var EarthRadiusMeters = 6378137.0; // meters
  var lat1 = this.lat();
  var lon1 = this.lng();
  var lat2 = newLatLng.lat();
  var lon2 = newLatLng.lng();
  var dLat = (lat2 - lat1) * Math.PI / 180;
  var dLon = (lon2 - lon1) * Math.PI / 180;
  var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  var d = EarthRadiusMeters * c;
  return d;


google.maps.LatLng.prototype.latRadians = function() 
  return this.lat() * Math.PI / 180;


google.maps.LatLng.prototype.lngRadians = function() 
  return this.lng() * Math.PI / 180;


// === A method which returns the length of a path in metres ===
google.maps.Polygon.prototype.Distance = function() 
  var dist = 0;
  for (var i = 1; i < this.getPath().getLength(); i++) 
    dist += this.getPath().getAt(i).distanceFrom(this.getPath().getAt(i - 1));
  
  return dist;


// === A method which returns a GLatLng of a point a given distance along the path ===
// === Returns null if the path is shorter than the specified distance ===
google.maps.Polygon.prototype.GetPointAtDistance = function(metres) 
  // some awkward special cases
  if (metres == 0) return this.getPath().getAt(0);
  if (metres < 0) return null;
  if (this.getPath().getLength() < 2) return null;
  var dist = 0;
  var olddist = 0;
  for (var i = 1;
    (i < this.getPath().getLength() && dist < metres); i++) 
    olddist = dist;
    dist += this.getPath().getAt(i).distanceFrom(this.getPath().getAt(i - 1));
  
  if (dist < metres) 
    return null;
  
  var p1 = this.getPath().getAt(i - 2);
  var p2 = this.getPath().getAt(i - 1);
  var m = (metres - olddist) / (dist - olddist);
  return new google.maps.LatLng(p1.lat() + (p2.lat() - p1.lat()) * m, p1.lng() + (p2.lng() - p1.lng()) * m);


// === A method which returns an array of GLatLngs of points a given interval along the path ===
google.maps.Polygon.prototype.GetPointsAtDistance = function(metres) 
  var next = metres;
  var points = [];
  // some awkward special cases
  if (metres <= 0) return points;
  var dist = 0;
  var olddist = 0;
  for (var i = 1;
    (i < this.getPath().getLength()); i++) 
    olddist = dist;
    dist += this.getPath().getAt(i).distanceFrom(this.getPath().getAt(i - 1));
    while (dist > next) 
      var p1 = this.getPath().getAt(i - 1);
      var p2 = this.getPath().getAt(i);
      var m = (next - olddist) / (dist - olddist);
      points.push(new google.maps.LatLng(p1.lat() + (p2.lat() - p1.lat()) * m, p1.lng() + (p2.lng() - p1.lng()) * m));
      next += metres;
    
  
  return points;


// === A method which returns the Vertex number at a given distance along the path ===
// === Returns null if the path is shorter than the specified distance ===
google.maps.Polygon.prototype.GetIndexAtDistance = function(metres) 
    // some awkward special cases
    if (metres == 0) return this.getPath().getAt(0);
    if (metres < 0) return null;
    var dist = 0;
    var olddist = 0;
    for (var i = 1;
      (i < this.getPath().getLength() && dist < metres); i++) 
      olddist = dist;
      dist += this.getPath().getAt(i).distanceFrom(this.getPath().getAt(i - 1));
    
    if (dist < metres) 
      return null;
    
    return i;
  
  // === Copy all the above functions to GPolyline ===
google.maps.Polyline.prototype.Distance = google.maps.Polygon.prototype.Distance;
google.maps.Polyline.prototype.GetPointAtDistance = google.maps.Polygon.prototype.GetPointAtDistance;
google.maps.Polyline.prototype.GetPointsAtDistance = google.maps.Polygon.prototype.GetPointsAtDistance;
google.maps.Polyline.prototype.GetIndexAtDistance = google.maps.Polygon.prototype.GetIndexAtDistance;
html 
  height: 100%;

body 
  height: 100%;
  margin: 0px;
  font-family: Helvetica, Arial;
<script src="https://maps.googleapis.com/maps/api/js?libraries=geometry&key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<div id="tools">start:
  <input type="text" name="start" id="start" value="union square, NY" />end:
  <input type="text" name="end" id="end" value="times square, NY" />
  <input type="submit" onclick="calcRoute();" />
</div>
<div id="map_canvas" style="width:100%;height:90%;"></div>

【讨论】:

【参考方案2】:

最简单的解决方案(用于谷歌地图):

 var rotationAngle = google.maps.geometry.spherical.computeHeading(oldLatlng, currentlatlng);

 $('img[src="path_to_marker_icon_image"]').css(
  transform: "rotate(" + rotationAngle + "deg)"
 );

【讨论】:

【参考方案3】:

借助 'GMSMapView' Delegates 方法,您可以轻松使用它。

var lastMapBearing: CLLocationDirection?
var lastUserBearing: CLLocationDirection?

func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) 
    lastMapBearing = position.bearing
    driverMarker?.rotation = (lastUserBearing ?? 0) - lastMapBearing!


func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) 
    lastUserBearing = newHeading.trueHeading
    driverMarker?.rotation = lastUserBearing! - (lastMapBearing ?? 0)

确保您可以分配委托 itSelf。

【讨论】:

以上是关于根据路径方向更改谷歌地图标记方向的主要内容,如果未能解决你的问题,请参考以下文章

旋转谷歌地图以显示我的实时方向

谷歌地图 API 方向

谷歌方向 API 行程时间与实时谷歌地图不同

谷歌地图:在移动应用程序中嵌入多个位置和方向

在我的 Android 应用程序上全面实施谷歌地图方向服务

根据谷歌地图的行车路线