Google Directions API, In Class App

Contents

Indices and tables

Exercise Overview

For this week, the goal is to create an app that will fetch driving directions from the Google Directions API and print the directions to the screen.

The app should have the following functionality:

  • Prompt the user for a start location

  • Prompt the user for a destination location

  • Query the Google Directions API for directions from the start to the destination location

  • Print the travel directions obtained from the Google Directions API as a list of formatted strings

    • Include the instructions, distance and duration

    • Include the maneuver but replace it with an appropriate unicode arrow!

      • A good reference for unicode arrows is here, though note that not all of them will render correctly.

This will require using the input() function, the str.format() function, the json package, and the Requests package to handle the HTTP request interchange with the Google Directions API endpoint.

You should setup a project in PyCharm in order to make debugging your app as simple as possible.

Google Directions API Intro

The Google Directions API web service accepts HTTP requests for directions between a start and an endpoint. The full developer documentation that describes everything is possible with this service, is here. However, it is not necessary to read the full API documentation to use the service. The parts necessary to complete this app will be described here.

The Directions API is accessed using the HTTP protocol at the following URL:

The “json” string at the end tells Google to return the results as a JSON encoded string.

Parameters added to the HTTP request are used to send the API additional information. Some parameters are optional.

Note

Parameters are added to the HTTP request as key/value pairs, where each key/value pair is seperated from the next using the & character. For example:

payment=VISA&amount=100&currency=canadian

The parameters of interest are the following:

  • origin : [REQUIRED] The address, textual latitude/longitude value, or place ID from which you wish to calculate directions.
  • destination : [REQUIRED] The address, textual latitude/longitude value, or place ID to which you wish to calculate directions.
  • travel_mode : [OPTIONAL] Specifies the mode of transport to use when calculating directions. Valid values driving, walking, bicycling and transit. Defaults to driving.
  • units : [OPTIONAL] Specifies the unit system to use when displaying results. Valid values are metric and imperial. Defaults to metric.
  • key : [REQUIRED] Your application’s API key. There is a small charge for each request to the API ($0.005 USD at the time of writing) and the key tells Google who to charge. To avoid malicious use of the key, it is not included in these directions but will be given out in class.

There are many additional parameters (such as waypoints) described in the developer documentation, however, we will not make use of these for this exercise.

An example request using the above parameters might look like the following:

The API will return a JSON encoded response containing the directions as well as other interesting ancillary information.

The status key within the Directions response object contains the status of the request, and may contain debugging information to help you track down why the Directions service failed. The full list of possible values is given here

Of note concerning the direction information is that Google breaks them up into 3 containers of information

  • Routes Array: A sequence of instructions from the origin to the destination. There is always a routes array even if the service returns no results (in which case the routes array is empty). There can be multiple routes to a destination. Routes consist of nested legs and steps.

  • Legs Array : A route may have one or more legs (segments between waypoints) depending on how many waypoints were specified. A route with no waypoints has just one leg.

  • Steps Array: A step is the most atomic unit of a direction’s route. Each element in the steps array is a specific, single instruction to follow on the journey. There are 4 fields in each step of note:

    • distance: Contains the distance covered by this step until the next step.
    • duration: Contains the typical time required to perform the step, until the next step.
    • html_instructions: Contains formatted instructions for this step, presented as an HTML text string.
    • maneuver: [OPTIONAL] Contains the action to take for the current step (turn left, merge, straight, etc.). This field is used to determine which icon to display, and can contain one of the following values: turn-slight-left, turn-sharp-left, uturn-left, turn-left, turn-slight-right, turn-sharp-right, uturn-right, turn-right, straight, ramp-left, ramp-right, merge, fork-left, fork-right, ferry, ferry-train, roundabout-left, roundabout-right.

An example response is shown below. Peruse the different fields to get a feel for the structure of the returned data and how you might go about parsing it.

{
   "geocoded_waypoints" : [
      {
         "geocoder_status" : "OK",
         "place_id" : "ChIJHWPcbfCdLIgRelvj4VRzHxI",
         "types" : [ "street_address" ]
      },
      {
         "geocoder_status" : "OK",
         "place_id" : "ChIJhWZvJSWeLIgRxpDCI4rT9lw",
         "types" : [
            "city_hall",
            "establishment",
            "local_government_office",
            "point_of_interest"
         ]
      }
   ],
   "routes" : [
      {
         "bounds" : {
            "northeast" : {
               "lat" : 43.344063,
               "lng" : -79.79836419999999
            },
            "southwest" : {
               "lat" : 43.326049,
               "lng" : -79.84280249999999
            }
         },
         "copyrights" : "Map data ©2018 Google",
         "legs" : [
            {
               "distance" : {
                  "text" : "5.2 km",
                  "value" : 5206
               },
               "duration" : {
                  "text" : "10 mins",
                  "value" : 599
               },
               "end_address" : "426 Brant St, Burlington, ON L7R 3Z6, Canada",
               "end_location" : {
                  "lat" : 43.326049,
                  "lng" : -79.79836419999999
               },
               "start_address" : "649 N Service Rd, Burlington, ON L7P 5B6, Canada",
               "start_location" : {
                  "lat" : 43.3310916,
                  "lng" : -79.84241089999999
               },
               "steps" : [
                  {
                     "distance" : {
                        "text" : "59 m",
                        "value" : 59
                     },
                     "duration" : {
                        "text" : "1 min",
                        "value" : 12
                     },
                     "end_location" : {
                        "lat" : 43.3306612,
                        "lng" : -79.84280249999999
                     },
                     "html_instructions" : "Head \u003cb\u003esouthwest\u003c/b\u003e",
                     "polyline" : {
                        "points" : "ibngG`fifN@@Zf@HHHF@@`@N"
                     },
                     "start_location" : {
                        "lat" : 43.3310916,
                        "lng" : -79.84241089999999
                     },
                     "travel_mode" : "DRIVING"
                  },
                  {
                     "distance" : {
                        "text" : "82 m",
                        "value" : 82
                     },
                     "duration" : {
                        "text" : "1 min",
                        "value" : 20
                     },
                     "end_location" : {
                        "lat" : 43.3308473,
                        "lng" : -79.84186149999999
                     },
                     "html_instructions" : "Turn \u003cb\u003eleft\u003c/b\u003e toward \u003cb\u003eN Service Rd\u003c/b\u003e",
                     "maneuver" : "turn-left",
                     "polyline" : {
                        "points" : "s_ngGnhifN@e@?a@AOCQEQCMGQO_@"
                     },
                     "start_location" : {
                        "lat" : 43.3306612,
                        "lng" : -79.84280249999999
                     },
                     "travel_mode" : "DRIVING"
                  },
                  {
                     "distance" : {
                        "text" : "39 m",
                        "value" : 39
                     },
                     "duration" : {
                        "text" : "1 min",
                        "value" : 18
                     },
                     "end_location" : {
                        "lat" : 43.33059739999999,
                        "lng" : -79.8415263
                     },
                     "html_instructions" : "Turn \u003cb\u003eright\u003c/b\u003e toward \u003cb\u003eN Service Rd\u003c/b\u003e",
                     "maneuver" : "turn-right",
                     "polyline" : {
                        "points" : "y`ngGrbifNZc@T]"
                     },
                     "start_location" : {
                        "lat" : 43.3308473,
                        "lng" : -79.84186149999999
                     },
                     "travel_mode" : "DRIVING"
                  },
                  {
                     "distance" : {
                        "text" : "2.0 km",
                        "value" : 2041
                     },
                     "duration" : {
                        "text" : "3 mins",
                        "value" : 179
                     },
                     "end_location" : {
                        "lat" : 43.344063,
                        "lng" : -79.82556389999999
                     },
                     "html_instructions" : "Turn \u003cb\u003eleft\u003c/b\u003e at the 1st cross street onto \u003cb\u003eN Service Rd\u003c/b\u003e",
                     "maneuver" : "turn-left",
                     "polyline" : {
                        "points" : "g_ngGp`ifNDIUWsB_CsEcFgD_EwAeBs@y@iAuAwAgBgAuAsByC{ByDOUy@uACEc@q@Y_@Ya@[][[[[QOOI[Sa@W_@M[Km@K]Ey@Mm@Ia@E[K]Oc@Y[Y_@g@Yi@M]Mc@GUGa@E[ESGc@Ig@Ko@]{BQaAI_@IWGSIYGMKSOYOWKQQUc@c@AAoAoAyBiBQOsAiACAOOGEGGIKWYm@w@u@_A"
                     },
                     "start_location" : {
                        "lat" : 43.33059739999999,
                        "lng" : -79.8415263
                     },
                     "travel_mode" : "DRIVING"
                  },
                  {
                     "distance" : {
                        "text" : "3.0 km",
                        "value" : 2985
                     },
                     "duration" : {
                        "text" : "6 mins",
                        "value" : 370
                     },
                     "end_location" : {
                        "lat" : 43.326049,
                        "lng" : -79.79836419999999
                     },
                     "html_instructions" : "Turn \u003cb\u003eright\u003c/b\u003e onto \u003cb\u003eBrant St\u003c/b\u003e/\u003cb\u003eHalton Regional Rd 18\u003c/b\u003e\u003cdiv style=\"font-size:0.9em\"\u003eContinue to follow Brant St\u003c/div\u003e\u003cdiv style=\"font-size:0.9em\"\u003eDestination will be on the right\u003c/div\u003e",
                     "maneuver" : "turn-right",
                     "polyline" : {
                        "points" : "kspgGv|efNb@o@Xe@fAaB`AwADIjA}A`@e@jAcBh@s@PYLQXc@?ALQJQFIh@w@vAkCbAeCT_@d@{@`AuAf@w@d@w@^Ub@c@bA_BVe@Pa@b@y@Xy@\\y@FUxBaHZ_AJ[LYTi@N[P]r@sAFKn@gAr@qAd@w@NYf@}@N]F[r@cA`@o@d@s@lByCn@cAn@gApA{Bt@wAhAiBlB_D^u@h@_AXe@x@uA^m@NUf@w@Va@l@aANUTa@JO^k@fAkBBC\\k@Va@z@uA\\i@f@}@p@iAt@oA\\k@h@}@T]`A}At@mAtA}BfBsCZk@h@}@"
                     },
                     "start_location" : {
                        "lat" : 43.344063,
                        "lng" : -79.82556389999999
                     },
                     "travel_mode" : "DRIVING"
                  }
               ],
               "traffic_speed_entry" : [],
               "via_waypoint" : []
            }
         ],
         "overview_polyline" : {
            "points" : "ibngG`fifN\\h@RPb@P@e@Aq@Ic@K_@O_@Zc@Zg@iCwCsEcFgD_EkC_DaD}DgAuAsByCkCoE}@{A}@qAu@_Aw@w@a@Y}@k@{@YsDi@}@QaAi@[Y_@g@g@gAUy@M}@c@oCo@}DSw@e@oA_@q@]g@e@e@oAoAyBiBeByASQq@s@cBwB|@uAhCyDpAgBlBiCz@mAt@iAR[h@w@vAkCbAeCT_@fBqClAoB^Ub@c@zAeCt@{Av@sB`CwHjA_D|AyCxCkFv@{AF[r@cAfAcB|C}E`GeKlB_D^u@bAeBpCqEjB{CfGyJlDcGvDgG|DqGdAiB"
         },
         "summary" : "N Service Rd and Brant St",
         "warnings" : [],
         "waypoint_order" : []
      }
   ],
   "status" : "OK"
}

Requests Intro

Data is exchanged with the Google Directions API using HTTP. If you are interested, you can find an introductory description of it here. However, it is unnecessary to read that for this exercise since we are only going to issue GET requests and we will abstract pretty much everything away using the Requests library.

A quickstart guide to using Requests can be found here

Installing requests is easy. Enter the following at the command prompt:

% pip3 install requests

In a nutshell, the following simple script outlines the functionality you will need:

# Import the requests library to start using it
import requests

# Make a GET request including some parameters. The response from
# the server is returned by the requests call.
payload = {'param0':'val0', 'param1':'val1'}
resp = requests.get("https://httpbin.org/get", params=payload)

# Check on the success of the request
resp.status_code

# View the URL that was generated for you by the Requests package
print(resp.url)

# Get the raw content after it has been decoded from bytes back
# to a string.
resp.text

# Optionally, use the JSON decoder built-in to the Requests module
# to convert the JSON document in the raw content back to a Python
# object.
resp.json()

As illustrated above, it is possible to check the HTTP status code returned from the server. The HTTP status codes break down into the following categories:

  • Informational - 1xx range
  • Successful - 2xx range
  • Redirection - 3xx range
  • Client Error - 4xx range
  • Server Error - 5xx range

The codes most often seen are:

  • 200 : [OK] Standard response for successful HTTP requests.
  • 400 : [Bad Request] The server cannot or will not process the request due to an apparent client error.
  • 404 : [Not Found] The requested resource could not be found but may be available in the future.

Warning

Getting a 200 - OK response only guarentees that the HTTP request was serviced and acceptable to the server. In the case of the Directions API, it does not mean that a valid route was found.

JSON

As described above, the Directions API returns a JSON document. Python includes a module for working with JSON data. A description of using the JSON module can be found here

Solution

When you are ready to see one possible solution, try this file