Skip to content

Latest commit

 

History

History
175 lines (138 loc) · 7.4 KB

07-Display-turn-by-turn.md

File metadata and controls

175 lines (138 loc) · 7.4 KB

Display The Turn-by-turn Directions

If we look closely at the legs array that we get back, each leg has an array of maneuvers that one has to follow to get from point A to point B. Each of the maneuvers contains a lot of information about what the user should do at a given place on the route. W're going to concentrate on a few of the pieces of information, the type, instruction, verbal_post_transition_instruction the begin_shape_index and the end_shape_index. These are all the information that the GPS device we use when driving around tells us where to go, so we're going to give our user the same information, but in text form.

As a first step, we're going to store the directions we get from the API on our app object so we can refer to them later. In our queryMobility method's success callback, let's attach the data.trip object to app.trip:

  success: function(data, status, req){
    app.trip = data.trip;
    var coords = polyline.decode(data.trip.legs[0].shape);
    callback(null, coords);
  }

Now we can get to the directions in any of the other methods called after this point.

Let's add the directions list to the same area below the search bars as our results lists go. But instead of rendering them into the same DOM area as the results list, we'll create another list element so we can keep any styling separated.

In index.html let's add a <ul> for our directions at the bottom of our sidebar div:

  <!DOCTYPE html>
  <html>
    <head>
      <title>API Workshop</title>
      <link rel="stylesheet" href="https://openlayers.org/en/v3.19.1/css/ol.css" type="text/css">
      <script src="https://openlayers.org/en/v3.19.1/build/ol.js"></script>
      <script src="https://code.jquery.com/jquery-3.1.1.min.js"
        crossorigin="anonymous"></script>
      <script src="./util.js"></script>
      <script src="./decode.js"></script>
      <link rel="stylesheet" href="./app.css" type="text/css">
    </head>
    <body>
      <div id="map" class="map"></div>

      <div id="sidebar" class="sidebar">
        <div id="search-bar-container" class="directions-mode">
          <span id="search-from" class="search-bar">
            <input id="search-from-input" class="search-input" type="text"></input>
            <button id="clear-from-search" class="search-button">X</button>
          </span>
          <span id="search-to" class="search-bar">
            <input id="search-to-input" class="search-input" type="text"></input>
            <button id="clear-to-search" class="search-button">X</button>
          </span>
        </div>
        <ul id="results-list" class="results-list hidden"></ul>

        <!-- add directions list here to hold our turn-by-turn directions -->
        <ul id="directions-list" class="directions-list styled-scrollbar hidden"></ul>

      </div>
      <script src="./app.js"></script>
    </body>
  </html>

Let's create a renderDirectionsList method similar to our renderResultsList method, where we build out the DOM elements that make up our directions list and add them to the page. This method gets a little verbose so take a look at the comments below for some explanation on what we're doing:

  renderDirectionsList: function(err){
    // grab references to the sidebar and directions list DOM elements so we can manipulate them
    var sidebar = $('#sidebar');
    var directionsList = $('#directions-list');

    // empty the directions list in case it already had information inside it
    directionsList.empty();
    
    // make sure that we actually have data on the trip object in app
    if(app.trip && app.trip.legs){
      
      // use the array.map method to iterate through all maneuvers 
      // in the first leg and add the results to an array called directions
      var directions = app.trip.legs[0].maneuvers.map(function(man){
        
        // create our list item element
        var li = $('<li class="directions-list-item"></li>');
        
        // create a container for the instructions to go in
        var instructionContainer = $('<div class="directions-list-instruction-container"></div>');

        // create the actual instruction element with our text from the maneuver
        var instruction = $('<div class="directions-list-item-direction">' + man.instruction + '</div>');

        // append the instruction into the instruction container
        instructionContainer.append(instruction);

        // if our maneuver has the verbal post transition instruction let's add that as a little subtext
        if(dir.hasOwnProperty('verbal_post_transition_instruction')){
          var then = $('<div class="directions-then">Then ' + man.verbal_post_transition_instruction + '</div>')
          instructionContainer.append(then)
        }

        // ready to add the instruction container to the list item
        li.append(instructionContainer);

        // return the list item to become part of the directions array of DOM elements
        return li;
      })

      // add the directions array of elements to the directions list
      directionsList.append(directions);

      // show the list
      directionsList.removeClass('hidden');

      // add the expanded class to sidebar, it just makes it almost full-page-height
      sidebar.addClass('sidebar-expanded');
    }else{
      // if we don't have any directions hide the list and remove the expanded class
      directionsList.addClass('hidden');
      sidebar.removeClass('sidebar-expanded');
    }
  }

WHEW! that was a lot, but we got it all in there and we now just need to call it somewhere to display our directions in the sidebar. Let's add that call to the displayRoute method so we show the directions after we draw the route:

  displayRoute: function(err, coords){
    console.log('should be working')
    if(err){
      console.log(err);
    }else{
      var route = {
        type: 'Feature',
        geometry: {
          type: 'LineString',
          coordinates: coords
        }
      }

      app.routeLayer.setSource( new ol.source.Vector({
        features: (new ol.format.GeoJSON({featureProjection: mapProjection})).readFeatures(route)
      }))

      map.getView().fit(
        app.routeLayer.getSource().getExtent(),
        map.getSize()
      )      

      app.renderDirectionsList();
    }
  }

Awesome, now we get directions to show up, but arrgh, they never go away!

Let's add a couple lines to our clearList function to get rid of the directions when the user clears either the starting or destination location from the inputs:

  clearList: function(e){
    app.options = [];
    app.trip = {};
    app.renderResultsList();
    app.renderDirectionsList();
  }

We can set app.trip to an empty object and then call app.renderDirectionsList() to hide the list (remember what we did in the if statement when app.trip has no legs).

Ugh, now the route stays on the map as well, let's clean that up by adding one more line to set the source to null which makes our route disappear:

  clearList: function(e){
    app.options = [];
    app.trip = {};
    app.renderResultsList();
    app.renderDirectionsList();
    app.routeLayer.setSource(null);
  }

Alright, we're getting close to an app with some functionality, we just need to pretty it up a little bit and add some more interaction. In lesson 8 we'll add icons based on the maneuver type to the list then later we'll even add them to the map.