google directions: how to get transit_details?

1.7k views Asked by At

EDIT: this problem is solved thanks to the help of geocodezip who pointed me the right way in the comments...

problem: i want transit-directions from google as plain text and i need the transit_details. i tried adding all variations of stuff like (which does not work):

steps[i].transit.arrival_stop.name

solution: most transit routes have the first and last steps walking. in this case, .transit does not exist and produces the error.

i fixed the code below accordingly (which now works).

<!DOCTYPE html>
<html>

<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
  <title></title>
  <script type="text/javascript" src="http://maps.google.com/maps/api/js?"></script>
</head>

<body style="font-family: Arial; font-size: 12px; color:#FFFFFF;" bgcolor="#202020">
  <div id="panel" style="width: 300px; float: left;"></div>
  <div id="map" style="width: 300px; height: 300px;"></div>
  <script type="text/javascript">

  calcRoute();

  function calcRoute() {
    var directionsService = new google.maps.DirectionsService();
    var directionsDisplay = new google.maps.DirectionsRenderer();

    var map = new google.maps.Map(document.getElementById('map'), {
      zoom: 7,
      mapTypeId: google.maps.MapTypeId.ROADMAP,
    });

    directionsDisplay.setMap(map);

    var request = {
      origin: 'Potsdamer Platz, 10785 Berlin',
      destination: 'Falckensteinstraße, Berlin',
      travelMode: google.maps.DirectionsTravelMode.TRANSIT,
    };

    directionsService.route(request, function(response, status) {
      if (status == google.maps.DirectionsStatus.OK) {
        directionsDisplay.setDirections(response);
        writeDirectionsSteps(directionsDisplay.directions.routes[0].legs[0].steps);
      }
      else {
        console.error('DirectionsStatus is ' + status);
      }
    });
  }

  function writeDirectionsSteps(steps) {
    var directions = document.getElementById('panel');
    directions.innerHTML = '';
    for (var i = 0; i < steps.length; i++) {
      directions.innerHTML += '<br/><br/>' + steps[i].instructions + '<br/>' + steps[i].distance.text;
      if (typeof steps[i].transit !== "undefined") {
        directions.innerHTML += '<br/>' + steps[i].transit.arrival_stop.name;
      }
    }
  }

  </script>
</body>
</html>

(code based on: Google Maps Api v3: How to change the Default waypoint markers in the Directions (set)Panel?)

any help is appreciated a lot!

1

There are 1 answers

0
geocodezip On BEST ANSWER

You need to code defensively. The .transit property will not be there in all the steps, only those that involve the actual transit itself (i.e. not on the walk to and from the station).

function writeDirectionsSteps(steps) {
  var directions = document.getElementById('panel');
  directions.innerHTML = '';
  for (var i = 0; i < steps.length; i++) {
    directions.innerHTML += '<br/><br/>' + steps[i].instructions + '<br/>' + steps[i].distance.text;
    // if .transit property exists
    if (!!steps[i].transit) {
      directions.innerHTML += '<br/>' + steps[i].transit.arrival_stop.name;
    }
  }
}

proof of concept fiddle

code snippet:

var directionsDisplay;
var directionsService = new google.maps.DirectionsService();
var map;
var routeBounds = false;
var overlayWidth = 200; // Width of the overlay DIV
var leftMargin = 30; // Grace margin to avoid too close fits on the edge of the overlay
var rightMargin = 80; // Grace margin to avoid too close fits on the right and leave space for the controls

overlayWidth += leftMargin;

var start = new google.maps.LatLng(3.148173, 101.7148792);
var end = new google.maps.LatLng(3.1347725, 101.6893408);

function initialize() {

  var btn1 = document.getElementById('calcRoute');
  btn1.addEventListener('click', calcRoute);

  var btn2 = document.getElementById('offsetMap');
  btn2.addEventListener('click', offsetMap);

  var btn3 = document.getElementById('fitAndOffsetMap');
  btn3.addEventListener('click', fitAndOffsetMap);

  var btn4 = document.getElementById('fitMap');
  btn4.addEventListener('click', fitMap);

  directionsDisplay = new google.maps.DirectionsRenderer({
    draggable: true
  });

  var mapOptions = {
    zoom: 13,
    mapTypeId: google.maps.MapTypeId.ROADMAP,
    center: start,
    panControlOptions: {
      position: google.maps.ControlPosition.TOP_RIGHT
    },
    zoomControlOptions: {
      position: google.maps.ControlPosition.TOP_RIGHT
    }
  };

  map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
  directionsDisplay.setMap(map);
}

function offsetMap() {

  if (routeBounds !== false) {

    // Clear listener defined in directions results
    google.maps.event.clearListeners(map, 'idle');

    // Top right corner
    var topRightCorner = new google.maps.LatLng(map.getBounds().getNorthEast().lat(), map.getBounds().getNorthEast().lng());

    // Top right point
    var topRightPoint = fromLatLngToPoint(topRightCorner).x;

    // Get pixel position of leftmost and rightmost points
    var leftCoords = routeBounds.getSouthWest();
    var leftMost = fromLatLngToPoint(leftCoords).x;
    var rightMost = fromLatLngToPoint(routeBounds.getNorthEast()).x;

    // Calculate left and right offsets
    var leftOffset = (overlayWidth - leftMost);
    var rightOffset = ((topRightPoint - rightMargin) - rightMost);

    // Only if left offset is needed
    if (leftOffset >= 0) {

      if (leftOffset < rightOffset) {

        var mapOffset = Math.round((rightOffset - leftOffset) / 2);

        // Pan the map by the offset calculated on the x axis
        map.panBy(-mapOffset, 0);

        // Get the new left point after pan
        var newLeftPoint = fromLatLngToPoint(leftCoords).x;

        if (newLeftPoint <= overlayWidth) {

          // Leftmost point is still under the overlay
          // Offset map again
          offsetMap();
        }

      } else {

        // Cannot offset map at this zoom level otherwise both leftmost and rightmost points will not fit
        // Zoom out and offset map again
        map.setZoom(map.getZoom() - 1);
        offsetMap();
      }
    }
  }
}

function fromLatLngToPoint(latLng) {

  var scale = Math.pow(2, map.getZoom());
  var nw = new google.maps.LatLng(map.getBounds().getNorthEast().lat(), map.getBounds().getSouthWest().lng());
  var worldCoordinateNW = map.getProjection().fromLatLngToPoint(nw);
  var worldCoordinate = map.getProjection().fromLatLngToPoint(latLng);

  return new google.maps.Point(Math.floor((worldCoordinate.x - worldCoordinateNW.x) * scale), Math.floor((worldCoordinate.y - worldCoordinateNW.y) * scale));
}

function calcRoute() {

  var request = {
    origin: start,
    destination: end,
    travelMode: google.maps.DirectionsTravelMode.TRANSIT,
    transitOptions: {
      departureTime: new Date("01/03/2017 9:00 AM EST")
    }

  };

  directionsService.route(request, function(response, status) {

    if (status == google.maps.DirectionsStatus.OK) {

      directionsDisplay.setDirections(response);

      // Define route bounds for use in offsetMap function
      routeBounds = response.routes[0].bounds;

      // Write directions steps
      writeDirectionsSteps(response.routes[0].legs[0].steps);

      // Wait for map to be idle before calling offsetMap function
      google.maps.event.addListener(map, 'idle', function() {

        // Offset map
        offsetMap();
      });

      // Listen for directions changes to update bounds and reapply offset
      google.maps.event.addListener(directionsDisplay, 'directions_changed', function() {

        // Get the updated route directions response
        var updatedResponse = directionsDisplay.getDirections();

        // Update route bounds
        routeBounds = updatedResponse.routes[0].bounds;

        // Fit updated bounds
        map.fitBounds(routeBounds);

        // Write directions steps
        writeDirectionsSteps(updatedResponse.routes[0].legs[0].steps);

        // Offset map
        offsetMap();
      });
    }
  });
}

function writeDirectionsSteps(steps) {
  var directions = document.getElementById('overlayContent');
  directions.innerHTML = '';
  for (var i = 0; i < steps.length; i++) {
    directions.innerHTML += '<br/><br/>' + steps[i].instructions + '<br/>' + steps[i].distance.text;
    // if .transit property exists
    if (!!steps[i].transit) {
      directions.innerHTML += '<br/>station name:' + steps[i].transit.arrival_stop.name;
    }
  }
}

function fitMap() {

  if (routeBounds !== false) {

    map.fitBounds(routeBounds);
  }
}

function fitAndOffsetMap() {

  if (routeBounds !== false) {

    map.fitBounds(routeBounds);
    offsetMap();
  }
}

initialize();
body {
  margin: 0;
  padding: 0;
  font-family: Arial;
}
#map-canvas {
  height: 450px;
}
#overlay {
  position: absolute;
  width: 200px;
  height: 450px;
  background: black;
  opacity: .8;
  top: 0;
  left: 0;
  overflow: auto;
}
#overlayContent {
  color: white;
  padding: 10px 20px;
}
#overlayContent p {
  font-size: 12px;
  margin: 6px 0;
}
#overlayContent small {
  display: block;
  text-align: right;
  font-style: italic;
}
small {
  font-size: 9px;
}
i {
  color: lightblue;
}
h1 {
  font-size: 20px;
}
h5 {
  font-size: 12px;
}
button {
  margin: 20px 0 0 20px;
}
<script src="https://maps.googleapis.com/maps/api/js?libraries=geometry"></script>
<div id="map-canvas"></div>
<div id="overlay">
  <div id="overlayContent">
    <h1>DIV OVERLAY</h1>

    <h5>Routes should not be drawn below this element.</h5>

    <h5>Click the <i>Calc route</i> button to draw the directions route.</h5>

    <h5><i>Map offset</i> will be applied automatically.</h5>

    <h5><i>Drag the route</i> to see how it is applied.</h5>

    <h5>Click the <i>Offset map</i> button to reapply the offset.</h5>

    <h5>Click the <i>Fit only</i> button to only fit route bounds.</h5>

    <h5>Click the <i>Fit and offset map</i> button to fit to route bounds and reapply offset.</h5>

  </div>
</div>
<button id="calcRoute">Calc route</button>
<button id="offsetMap">Offset map</button>
<button id="fitMap">Fit only</button>
<button id="fitAndOffsetMap">Fit and offset map</button>