Comment ajouter des marqueurs sur Google Maps polylignes en fonction de la distance le long de la ligne?

StackOverflow https://stackoverflow.com/questions/2698112

Question

Je suis en train de créer une carte Google où l'utilisateur peut tracer la route qu'il a marché / RAN / et de voir à bicyclette combien de temps il a couru. La classe GPolyline avec elle la méthode de la getLength() est très utile à cet égard (au moins pour les API Google Maps V2), mais je voulais ajouter des marqueurs basés sur la distance, par exemple un marqueur pour 1 km, 5 km, 10 km, etc., mais il semble qu'il n'y a aucun moyen évident de trouver un point sur une polyligne fonction de la distance le long de la ligne, il est. Toutes les suggestions?

Était-ce utile?

La solution

répondu à un problème similaire quelques mois sur la façon d'aborder ce sur le côté serveur dans SQL server 2008, je suis le même algorithme portage à l'aide de JavaScript Google Maps API v2 .

Pour les besoins de cet exemple, nous allons utiliser un simple de 4 points polyligne, avec une longueur totale de 8.800 mètres CIRCA. L'extrait ci-dessous définira cette polyligne et le rendre sur la carte:

var map = new GMap2(document.getElementById('map_canvas'));

var points = [
   new GLatLng(47.656, -122.360),
   new GLatLng(47.656, -122.343),
   new GLatLng(47.690, -122.310),
   new GLatLng(47.690, -122.270)
];

var polyline = new GPolyline(points, '#f00', 6);

map.setCenter(new GLatLng(47.676, -122.343), 12);
map.addOverlay(polyline);

Maintenant, avant l'approche de l'algorithme actuel, nous aurons besoin d'une fonction qui renvoie le point de destination lorsque donné un point de départ, un point final, et la distance à Voyage le long de cette ligne, Heureusement, il y a quelques implémentations pratiques JavaScript par Chris Veness Calculer la distance, roulement et plus entre les points Latitude / Longitude .

En particulier, je l'ai adapté les deux méthodes suivantes de la source ci-dessus pour travailler avec la classe GLatLng de Google:

Ils ont été utilisés pour étendre la classe GLatLng de Google avec une méthode moveTowards(), qui, lorsqu'il est donné un autre point et une distance en mètres, il retournera un autre GLatLng le long de cette ligne lorsque la distance est parcourue à partir du point d'origine vers le point passé en paramètre.

GLatLng.prototype.moveTowards = function(point, distance) {   
   var lat1 = this.lat().toRad();
   var lon1 = this.lng().toRad();
   var lat2 = point.lat().toRad();
   var lon2 = point.lng().toRad();         
   var dLon = (point.lng() - this.lng()).toRad();

   // Find the bearing from this point to the next.
   var brng = Math.atan2(Math.sin(dLon) * Math.cos(lat2),
                         Math.cos(lat1) * Math.sin(lat2) -
                         Math.sin(lat1) * Math.cos(lat2) * 
                         Math.cos(dLon));

   var angDist = distance / 6371000;  // Earth's radius.

   // Calculate the destination point, given the source and bearing.
   lat2 = Math.asin(Math.sin(lat1) * Math.cos(angDist) + 
                    Math.cos(lat1) * Math.sin(angDist) * 
                    Math.cos(brng));

   lon2 = lon1 + Math.atan2(Math.sin(brng) * Math.sin(angDist) *
                            Math.cos(lat1), 
                            Math.cos(angDist) - Math.sin(lat1) *
                            Math.sin(lat2));

   if (isNaN(lat2) || isNaN(lon2)) return null;

   return new GLatLng(lat2.toDeg(), lon2.toDeg());
}

Avoir cette méthode, nous pouvons maintenant aborder le problème comme suit:

  1. Itérer à travers chaque point de la trajectoire.
  2. Trouver la distance entre le point courant dans l'itération au point suivant.
  3. Si la distance au point 2 est plus la distance nous avons besoin de Voyage sur le chemin:

    ... alors le point de destination se situe entre ce point et le suivant. Il suffit d'appliquer la méthode moveTowards() au point courant, en passant le point suivant et la distance à Voyage. Retour le résultat et briser l'itération.

    Else:

    ... le point de destination est en outre dans le trajet à partir du point suivant de l'itération. Nous devons soustraire la distance entre ce point et le point suivant de la distance totale à Voyage le long du chemin. Continuez à travers l'itération avec la distance modifiée.

Vous avez sans doute remarqué que nous pouvons facilement mettre en œuvre ce qui précède récursive, au lieu de itérativement. Alors faisons-le:

function moveAlongPath(points, distance, index) {
   index = index || 0;  // Set index to 0 by default.

   if (index < points.length) {
      // There is still at least one point further from this point.

      // Construct a GPolyline to use its getLength() method.
      var polyline = new GPolyline([points[index], points[index + 1]]);

      // Get the distance from this point to the next point in the polyline.
      var distanceToNextPoint = polyline.getLength();

      if (distance <= distanceToNextPoint) {
         // distanceToNextPoint is within this point and the next. 
         // Return the destination point with moveTowards().
         return points[index].moveTowards(points[index + 1], distance);
      }
      else {
         // The destination is further from the next point. Subtract
         // distanceToNextPoint from distance and continue recursively.
         return moveAlongPath(points,
                              distance - distanceToNextPoint,
                              index + 1);
      }
   }
   else {
      // There are no further points. The distance exceeds the length  
      // of the full path. Return null.
      return null;
   }  
}

Avec la méthode ci-dessus, si nous définissons un tableau de points de GLatLng, et nous invoquons notre fonction moveAlongPath() avec ce tableau de points et une distance de 2500 mètres, il retournera un GLatLng sur ce chemin à 2,5 km de la première Point.

var points = [
   new GLatLng(47.656, -122.360),
   new GLatLng(47.656, -122.343),
   new GLatLng(47.690, -122.310),
   new GLatLng(47.690, -122.270)
];

var destinationPointOnPath = moveAlongPath(points, 2500);

// destinationPointOnPath will be a GLatLng on the path 
// at 2.5km from the start.

Par conséquent, tout ce que nous devons faire est d'appeler moveAlongPath() pour chaque point de contrôle dont nous avons besoin sur le chemin. Si vous avez besoin de trois marqueurs à 1 km, 5 km et 10 km, vous pouvez simplement faire:

map.addOverlay(new GMarker(moveAlongPath(points, 1000)));
map.addOverlay(new GMarker(moveAlongPath(points, 5000)));
map.addOverlay(new GMarker(moveAlongPath(points, 10000)));

Notez cependant que moveAlongPath() peut revenir null si nous demandons un point de contrôle supplémentaire de la longueur totale du chemin, il sera plus sage de vérifier la valeur de retour avant de passer à new GMarker().

Nous pouvons mettre cela ensemble pour la mise en œuvre complète. Dans cet exemple, nous laissons tomber un marqueur 1.000 mètres le long du chemin de 8,8 km définis précédemment:

<!DOCTYPE html>
<html> 
<head> 
   <meta http-equiv="content-type" content="text/html; charset=UTF-8"/> 
   <title>Google Maps - Moving point along a path</title> 
   <script src="http://maps.google.com/maps?file=api&v=2&sensor=false"
           type="text/javascript"></script> 
</head> 
<body onunload="GUnload()"> 
   <div id="map_canvas" style="width: 500px; height: 300px;"></div>

   <script type="text/javascript"> 

   Number.prototype.toRad = function() {
      return this * Math.PI / 180;
   }

   Number.prototype.toDeg = function() {
      return this * 180 / Math.PI;
   }

   GLatLng.prototype.moveTowards = function(point, distance) {   
      var lat1 = this.lat().toRad();
      var lon1 = this.lng().toRad();
      var lat2 = point.lat().toRad();
      var lon2 = point.lng().toRad();         
      var dLon = (point.lng() - this.lng()).toRad();

      // Find the bearing from this point to the next.
      var brng = Math.atan2(Math.sin(dLon) * Math.cos(lat2),
                            Math.cos(lat1) * Math.sin(lat2) -
                            Math.sin(lat1) * Math.cos(lat2) * 
                            Math.cos(dLon));

      var angDist = distance / 6371000;  // Earth's radius.

      // Calculate the destination point, given the source and bearing.
      lat2 = Math.asin(Math.sin(lat1) * Math.cos(angDist) + 
                       Math.cos(lat1) * Math.sin(angDist) * 
                       Math.cos(brng));

      lon2 = lon1 + Math.atan2(Math.sin(brng) * Math.sin(angDist) *
                               Math.cos(lat1), 
                               Math.cos(angDist) - Math.sin(lat1) *
                               Math.sin(lat2));

      if (isNaN(lat2) || isNaN(lon2)) return null;

      return new GLatLng(lat2.toDeg(), lon2.toDeg());
   }

   function moveAlongPath(points, distance, index) {        
      index = index || 0;  // Set index to 0 by default.

      if (index < points.length) {
         // There is still at least one point further from this point.

         // Construct a GPolyline to use the getLength() method.
         var polyline = new GPolyline([points[index], points[index + 1]]);

         // Get the distance from this point to the next point in the polyline.
         var distanceToNextPoint = polyline.getLength();

         if (distance <= distanceToNextPoint) {
            // distanceToNextPoint is within this point and the next. 
            // Return the destination point with moveTowards().
            return points[index].moveTowards(points[index + 1], distance);
         }
         else {
            // The destination is further from the next point. Subtract
            // distanceToNextPoint from distance and continue recursively.
            return moveAlongPath(points,
                                 distance - distanceToNextPoint,
                                 index + 1);
         }
      }
      else {
         // There are no further points. The distance exceeds the length  
         // of the full path. Return null.
         return null;
      }  
   }

   var map = new GMap2(document.getElementById('map_canvas'));

   var points = [
      new GLatLng(47.656, -122.360),
      new GLatLng(47.656, -122.343),
      new GLatLng(47.690, -122.310),
      new GLatLng(47.690, -122.270)
   ];

   var polyline = new GPolyline(points, '#f00', 6);

   var nextMarkerAt = 0;     // Counter for the marker checkpoints.
   var nextPoint = null;     // The point where to place the next marker.

   map.setCenter(new GLatLng(47.676, -122.343), 12);

   // Draw the path on the map.
   map.addOverlay(polyline);

   // Draw the checkpoint markers every 1000 meters.
   while (true) {
      // Call moveAlongPath which will return the GLatLng with the next
      // marker on the path.
      nextPoint = moveAlongPath(points, nextMarkerAt);

      if (nextPoint) {
         // Draw the marker on the map.
         map.addOverlay(new GMarker(nextPoint));

         // Add +1000 meters for the next checkpoint.
         nextMarkerAt += 1000;    
      }
      else {
         // moveAlongPath returned null, so there are no more check points.
         break;
      }            
   }
   </script>
</body> 
</html>

Capture d'écran de l'exemple ci-dessus, montrant un marqueur 1.000 mètres:

Google Maps - D&eacute;placer le point le long d

Autres conseils

  

J'ai trouvé pourquoi j'ai eu la   inexactitude. En fait, en V3 de GMap,   nous n'avons pas la fonction « getLength »   plus que le retour de la longueur en kilomètres   ou mètres de la polyligne.

Voici les prototypes pour la fonction requise - Espérons que cela aide plus loin:

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

google.maps.LatLng.prototype.distanceFrom = function(newLatLng) {
    //var R = 6371; // km (change this constant to get miles)
    var R = 6378100; // 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 = R * c;
    return d;
}

la source

Peut-être la meilleure approche serait de calculer où ces points sont.

Comme un algorithme de base vous pouvez itérer sur tous les points de la polyligne et calculer la distance cumulée - si le segment suivant vous met sur la distance, vous pouvez interpoler le point où la distance est atteinte - puis ajoutez simplement point d'intérêt à votre carte pour cela.

Je l'ai utilisé la méthode Martin Zeitler travailler avec Google Map V3 et son beau travail.

 function init() {
       var mapOptions = {
            zoom: 15,
            center: new google.maps.LatLng(-6.208437004433984, 106.84543132781982),
            suppressInfoWindows: true,
                     };

        // Get all html elements for map
        var mapElement = document.getElementById('map1');

        // Create the Google Map using elements
        map = new google.maps.Map(mapElement, mapOptions);

        var nextMarkerAt = 0;     // Counter for the marker checkpoints.
        var nextPoint = null;     // The point where to place the next marker.


        while (true) {

            var routePoints = [ new google.maps.LatLng(47.656, -122.360),
                                new google.maps.LatLng(47.656, -122.343),
                                new google.maps.LatLng(47.690, -122.310),
                                new google.maps.LatLng(47.690, -122.270)];

                nextPoint = moveAlongPath(routePoints, nextMarkerAt);

            if (nextPoint) {
              //Adding marker from localhost
                MarkerIcon = "http://192.168.1.1/star.png";
                var marker = new google.maps.Marker
                    ({position: nextPoint,
                        map: map,
                        icon: MarkerIcon
                    });
                // Add +1000 meters for the next checkpoint.
                nextMarkerAt +=1000;

            }
            else {
                // moveAlongPath returned null, so there are no more check points.
                break;
            }
        }
 }


   Number.prototype.toRad = function () {
        return this * Math.PI / 180;
    }

    Number.prototype.toDeg = function () {
        return this * 180 / Math.PI;
    }

    function moveAlongPath(point, distance, index) {
        index = index || 0;  // Set index to 0 by default.

        var routePoints = [];

        for (var i = 0; i < point.length; i++) {
            routePoints.push(point[i]);
        }

        if (index < routePoints.length) {
            // There is still at least one point further from this point.

            // Construct a GPolyline to use the getLength() method.
            var polyline = new google.maps.Polyline({
                path: [routePoints[index], routePoints[index + 1]],
                strokeColor: '#FF0000',
                strokeOpacity: 0.8,
                strokeWeight: 2,
                fillColor: '#FF0000',
                fillOpacity: 0.35
            });

            // Get the distance from this point to the next point in the polyline.
            var distanceToNextPoint = polyline.Distance();

            if (distance <= distanceToNextPoint) {
                // distanceToNextPoint is within this point and the next.
                // Return the destination point with moveTowards().
                return moveTowards(routePoints, distance,index);
            }
            else {
                // The destination is further from the next point. Subtract
                // distanceToNextPoint from distance and continue recursively.
                return moveAlongPath(routePoints,
                    distance - distanceToNextPoint,
                    index + 1);
            }
        }
        else {
            // There are no further points. The distance exceeds the length
            // of the full path. Return null.
            return null;
        }
    }

    function moveTowards(point, distance,index) {

        var lat1 = point[index].lat.toRad();
        var lon1 = point[index].lng.toRad();
        var lat2 = point[index+1].lat.toRad();
        var lon2 = point[index+1].lng.toRad();
        var dLon = (point[index + 1].lng - point[index].lng).toRad();

        // Find the bearing from this point to the next.
        var brng = Math.atan2(Math.sin(dLon) * Math.cos(lat2),
            Math.cos(lat1) * Math.sin(lat2) -
            Math.sin(lat1) * Math.cos(lat2) *
            Math.cos(dLon));

        var angDist = distance / 6371000;  // Earth's radius.

        // Calculate the destination point, given the source and bearing.
        lat2 = Math.asin(Math.sin(lat1) * Math.cos(angDist) +
            Math.cos(lat1) * Math.sin(angDist) *
            Math.cos(brng));

        lon2 = lon1 + Math.atan2(Math.sin(brng) * Math.sin(angDist) *
            Math.cos(lat1),
            Math.cos(angDist) - Math.sin(lat1) *
            Math.sin(lat2));

        if (isNaN(lat2) || isNaN(lon2)) return null;



        return new google.maps.LatLng(lat2.toDeg(), lon2.toDeg());
    }

    google.maps.Polyline.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;
    }

    google.maps.LatLng.prototype.distanceFrom = function (newLatLng) {
        //var R = 6371; // km (change this constant to get miles)
        var R = 6378100; // 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 = R * c;
        return d;
    }

Je voulais au port réponse de Daniel Vassalo pour iOS, mais il n'a pas été travaillé correctement et certains marqueurs ont été mal placé jusqu'à ce que J'ai changé

var dLon = (point.lng() - this.lng()).toRad();

à

var dLon = point.lng().toRad() - this.lng().toRad();

Donc, si quelqu'un ayant un mal à comprendre pourquoi sont les marqueurs sont mal placés, essayez cela et peut-être vous aidera.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top