最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Drive time polygons with Google Maps API - Stack Overflow

programmeradmin2浏览0评论

I'm looking for a pure (or as-pure-as-possible) Google Maps JavaScript API V3 solution to add drive-time polygon functionality to my Google Maps app.

Basically the workflow is that the user clicks a Marker or point on the map, and a polygon is generated around the Marker/point indicating the area where you can drive within 5 minutes in any direction.

I'm not sure if it is even possible to do this as a pure Google Maps API solution, because it would require too many calls to the Directions Service with random routes and then any that don't fall within the time threshold are thrown away. Not sure if this is feasible or what the best way to engineer this would be. It might be necessary to use some backend processing for this, but I want to rule out out all other options first, but any examples of backend solutions are wele.

Any help, suggestions, or examples are greatly appreciated. The following are some examples I have e across, but they either aren't a pure Google Maps API solution and/or aren't using drive-times:

  1. Google Maps Utility Library example - draws 1, 2, and 3 minutes drive-time polygons around the point you click on, but it uses Esri's sample Geoprocessing services to run the calculations and generate the polygon geometry, so that's not going to work for my requirements.

  2. 30 mile directions - it's a pure Google Maps API solution using the Directions API and draws a nice polygon, but it doesn't take drive-times into the calculation, just distance from the point to 30 miles out.

  3. Mapnificent - seems to be a pure Google Maps API solution, and they have an API, but they are dealing with public transit times pulled from public transit data, not drive times. So the areas that "open up" as you increase the time indicate where from the point you can get on public transit within the specified time. I want this, but with driving times instead of transit times.

I'm looking for a pure (or as-pure-as-possible) Google Maps JavaScript API V3 solution to add drive-time polygon functionality to my Google Maps app.

Basically the workflow is that the user clicks a Marker or point on the map, and a polygon is generated around the Marker/point indicating the area where you can drive within 5 minutes in any direction.

I'm not sure if it is even possible to do this as a pure Google Maps API solution, because it would require too many calls to the Directions Service with random routes and then any that don't fall within the time threshold are thrown away. Not sure if this is feasible or what the best way to engineer this would be. It might be necessary to use some backend processing for this, but I want to rule out out all other options first, but any examples of backend solutions are wele.

Any help, suggestions, or examples are greatly appreciated. The following are some examples I have e across, but they either aren't a pure Google Maps API solution and/or aren't using drive-times:

  1. Google Maps Utility Library example - draws 1, 2, and 3 minutes drive-time polygons around the point you click on, but it uses Esri's sample Geoprocessing services to run the calculations and generate the polygon geometry, so that's not going to work for my requirements.

  2. 30 mile directions - it's a pure Google Maps API solution using the Directions API and draws a nice polygon, but it doesn't take drive-times into the calculation, just distance from the point to 30 miles out.

  3. Mapnificent - seems to be a pure Google Maps API solution, and they have an API, but they are dealing with public transit times pulled from public transit data, not drive times. So the areas that "open up" as you increase the time indicate where from the point you can get on public transit within the specified time. I want this, but with driving times instead of transit times.

Share Improve this question edited Sep 7, 2012 at 16:31 Gady asked Sep 6, 2012 at 17:29 GadyGady 4,9958 gold badges42 silver badges48 bronze badges 6
  • 2 What you call "30 mile directions" is my mashup, though it's old and written with the V2 API. You can use the same principle as that mashup uses, namely calculate a circle around the start point and use its points as destinations, then for each route cut it off when it reaches the desired driving time. I don't have the time to create a full example, but if you give it a try and run into problems just post a specific question and I'll be happy to help, (and I'm sure others will too). :) – Marcelo Commented Sep 7, 2012 at 8:23
  • @Marcelo Thanks for pointing me in the right direction, your mashup is definitely impressive and I have already begun to do what you describe. So just to clarify, you are cutting off the route when it reaches a certain distance? If that's the case, I can probably use a lot of what you did and instead of grabbing the distance, grab the drive-time. I just worry about performance and the amount of calls to the Directions Service, but I'll give it a shot. I'll migrate it to v3 also. Thanks again! – Gady Commented Sep 7, 2012 at 16:11
  • 1 yes, that's exactly what the function shortenAndShow() does. Since the 30 mile destination circle has a straight line radius, it is unlikely to be reached because roads have curves, so you reach the 30 miles before you reach the edge of the blue circle. Note that performance will depend on the interval, (in degrees), along the circle, at which you set a possible destination point. An attempt every 1 degree will take 10 times more requests than an attempt every 10 degrees (default is 30 degrees). Good luck and (+1) for a well researched question. – Marcelo Commented Sep 7, 2012 at 16:21
  • Since there were no other answers, I have posted an answer based on these ments. :-) – Marcelo Commented Sep 8, 2012 at 9:47
  • @Gady did you found a proper answer? Anything close enough to 99.co's feature – lessless Commented Oct 9, 2015 at 7:03
 |  Show 1 more ment

1 Answer 1

Reset to default 4

What you call "30 mile directions" is my mashup, though it's old and written with the V2 API. You can use the same principle as that mashup uses, namely calculate a circle around the start point and use its points as destinations, then for each route cut it off when it reaches the desired driving time.

the original link is dead (code snippet below)

  • link to a live copy (still v2)
  • link to v3 port

In that example, the function shortenAndShow() cuts off the portion of the route that goes beyond 30 miles. Since the 30 mile destination circle has a straight line radius, it is unlikely to be reached because roads have curves, so you reach the 30 miles before you reach the edge of the blue circle. You can do the same, but cutting the route off based on drive time instead of distance.

Performance will depend on the interval, (in degrees), along the circle, at which you set a possible destination point. An attempt every 1 degree will take 10 times more requests than an attempt every 10 degrees (default is 30 degrees).

code snippet:

var map;
var container;
var zoom = 9;
var centerPoint = new google.maps.LatLng(35.149534, -90.04898);
var dirService = new google.maps.DirectionsService();
var centerMarker;
var circleMarkers = Array();
var circlePoints = Array();
var drivePolyPoints = Array();
var searchPolygon, drivePolygon;
var distToDrive = 30; // miles
var pointInterval = 30;
var searchPoints = [];
var polyline;
var polylines = [];
var redIcon8 = "https://maps.gstatic./intl/en_us/mapfiles/markers2/measle.png";

function initialize() {
  map = new google.maps.Map(
    document.getElementById("map_canvas"), {
      center: centerPoint,
      zoom: 9,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    });
  google.maps.event.addListener(map, "click", mapClick);
}
google.maps.event.addDomListener(window, "load", initialize);

function mapClick(evt) {
  // map.clearOverlays();
  circleMarkers = Array();
  if (!centerMarker) {
    centerMarker = new google.maps.Marker({
      position: evt.latLng,
      map: map
    });
  } else {
    centerMarker.setMap(null);
    centerMarker.setPosition(evt.latLng);
  }
  centerMarker.setMap(map);
  searchPoints = getCirclePoints(evt.latLng, distToDrive);
  drivePolyPoints = Array();
  getDirections();
}

function getCirclePoints(center, radius) {
  var bounds = new google.maps.LatLngBounds();
  var circlePoints = Array();
  var searchPoints = Array();
  with(Math) {
    var rLat = (radius / 3963.189) * (180 / PI); // miles
    var rLng = rLat / cos(center.lat() * (PI / 180));
    for (var a = 0; a < 361; a++) {
      var aRad = a * (PI / 180);
      var x = center.lng() + (rLng * cos(aRad));
      var y = center.lat() + (rLat * sin(aRad));
      var point = new google.maps.LatLng(parseFloat(y), parseFloat(x), true);
      bounds.extend(point);
      circlePoints.push(point);
      if (a % pointInterval == 0) {
        searchPoints.push(point);
      }
    }
  }
  searchPolygon = new google.maps.Polygon({
    paths: circlePoints,
    strokeColor: '#0000ff',
    strokeWeight: 1,
    strokeOpacity: 1,
    fillColor: '#0000ff',
    fillOpacity: 0.2
  });
  searchPolygon.setMap(map);
  map.fitBounds(bounds);
  return searchPoints;
}

function getDirections() {
  if (!searchPoints.length) {
    return;
  }
  var to = searchPoints.shift();
  var request = {
    origin: centerMarker.getPosition(),
    destination: to,
    travelMode: google.maps.TravelMode.DRIVING
  };
  dirService.route(request, function(result, status) {
    if (status == google.maps.DirectionsStatus.OK) {
      var distance = parseInt(result.routes[0].legs[0].distance.value / 1609);
      var duration = parseFloat(result.routes[0].legs[0].duration.value / 3600).toFixed(2);
      var path = result.routes[0].overview_path;
      var legs = result.routes[0].legs;
      if (polyline && polyline.setPath) {
        polyline.setPath([]);
      } else {
        polyline = new google.maps.Polyline({
          path: [],
          // map: map,
          strokeColor: "#FF0000",
          strokeOpacity: 1
        });
      }
      for (i = 0; i < legs.length; i++) {
        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);
      shortenAndShow(polyline);
      getDirections();
    } else {
      console.log("Directions request failed, status=" + status + " [from:" + request.origin + " to:" + request.destination + "]");
      getDirections();
    }
  });
}

function shortenAndShow(polyline) {
  var distToDriveM = distToDrive * 1609;
  var dist = 0;
  var cutoffIndex = 0;
  var copyPoints = Array();
  for (var n = 0; n < polyline.getPath().getLength() - 1; n++) {
    dist += google.maps.geometry.spherical.puteDistanceBetween(polyline.getPath().getAt(n), polyline.getPath().getAt(n + 1));
    //GLog.write(dist + ' - ' + distToDriveM);
    if (dist < distToDriveM) {
      copyPoints.push(polyline.getPath().getAt(n));
    } else {
      break;
    }
  }
  var lastPoint = copyPoints[copyPoints.length - 1];
  var newLine = new google.maps.Polyline({
    path: copyPoints,
    strokeColor: '#ff0000',
    strokeWeight: 2,
    strokeOpacity: 1
  });
  newLine.setMap(map);
  polylines.push(newLine);
  drivePolyPoints.push(lastPoint);
  addBorderMarker(lastPoint, dist)
  if (drivePolyPoints.length > 3) {
    if (drivePolygon) {
      drivePolygon.setMap(null);
    }
    drivePolygon = new google.maps.Polygon({
      paths: drivePolyPoints,
      strokeColor: '#00ff00',
      strokeWeight: 1,
      strokeOpacity: 1,
      fillColor: '#00ff00',
      fillOpacity: 0.4
    });
    drivePolygon.setMap(map);
  }
}

function addBorderMarker(pt, d) {
  var str = pt.lat().toFixed(6) + ',' + pt.lng().toFixed(6) + ' - Driving Distance: ' + (d / 1609).toFixed(2) + ' miles';
  var marker = new google.maps.Marker({
    position: pt,
    icon: redIcon8,
    title: str
  });
  circleMarkers.push(marker);
  marker.setMap(map);
}

function clearOverlays() {
  for (var i = 0; i < circleMarkers.length; i++) {
    circleMarkers[i].setMap(null);
  }
  circleMarkers = [];
  for (var i = 0; i < circlePoints.length; i++) {
    circlePoints[i].setMap(null);
  }
  circlePoints = [];
  for (var i = 0; i < polylines.length; i++) {
    polylines[i].setMap(null);
  }
  polylines = [];
  if (searchPolygon && searchPolygon.setMap) searchPolygon.setMap(null);
  if (drivePolygon && drivePolygon.setMap) drivePolygon.setMap(null);
  if (centerMarker && centerMarker.setMap) centerMarker.setMap(null);
}
html,
body,
#map_canvas {
  height: 100%;
  width: 100%;
  margin: 0px;
  padding: 0px
}
<a href="#" onclick="clearOverlays();">Clear</a>&nbsp;|&nbsp;
<a href="#" onclick="pointInterval=30;clearOverlays();">interval 30 (default)</a>&nbsp;|&nbsp;
<a href="#" onclick="pointInterval=20;clearOverlays();">interval 20</a>&nbsp;|&nbsp;

<script src="https://maps.googleapis./maps/api/js?libraries=geometry"></script>
<div id="map_canvas"></div>

发布评论

评论列表(0)

  1. 暂无评论