This post series has been created by our developer Mariano Tucat. Read part 1 here.

Routing

We need to find which of the possible places is the fastest for the user. We will use routing.jsonendpoint to compute trips from the user’s current location to all possible places, using the selected mode of transport. In order to do so, we need to send in every request, the from location (current location) with each possible place as to location, and the selected mode. Therefore, we would call the following method multiple times, with the different to locations.

Request

computeTrip(baseUrl, selectedMode, fromLoc, toLoc) {
  data = {
    fromLoc: `(${fromLoc.latitude},${fromLoc.longitude})`,
    toLoc: `(${toLoc.latitude},${toLoc.longitude})`,
    mode: selectedMode,
    wp: '(1,1,1,1)',
    v: 11,
    includeStops: true
  }
  let url = baseUrl + '/routing.json'+ 
  `?from=${data.fromLoc}&to=${data.toLoc}&modes=${data.mode}&wp=${data.wp}&v=${data.v}&includeStops=${data.includeStops}`
  return fetch(url, {
    method: 'GET',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-TripGo-Key': env.TRIPGO_API_KEY 
    }
  })
  .then((response) => response.json())
}

In this case we do a GET request, also including the X-TripGo-Key in the header, and passing the parameters in the URL, like the from and to locations, the mode selected by the user, the weighting preferences (will be explained in Advanced section) and the expected version of the result. In this case, we do not send any information about depart or arrival times, meaning that we want trips departing now. This is related to the use we need in this example, but it is important to note that you can ask for trips departing at a given time from the origin, or arriving at a given time to the destination.

Response

{
  "realTimeStamp": 0,
  "region": "AU_NSW_Sydney",
  "regions": [
    "AU_NSW_Sydney"
  ],
  "groups": [
    {
      "sources": [
        "..."
      ],
      "trips": [
        {
          "arrive": 1495154251,
          "availability": "AVAILABLE",
          "caloriesCost": 30,
          "carbonCost": 3,
          "currencySymbol": "AUD",
          "depart": 1495152642,
          "hassleCost": 10,
          "mainSegmentHashCode": -1301095336,
          "moneyCost": 4.15,
          "moneyUSDCost": 3.25,
          "plannedURL": "<baseURL>/trip/planned/dd7ada5e-ddbd-4def-927c-880b2d60e57f",
          "progressURL": "<baseURL>/trip/progress/dd7ada5e-ddbd-4def-927c-880b2d60e57f",
          "queryIsLeaveAfter": true,
          "queryTime": 1495152642,
          "saveURL": "<baseURL>/trip/save/dd7ada5e-ddbd-4def-927c-880b2d60e57f",
          "segments": [
            {
              "availability": "AVAILABLE",
              "endTime": 1495154082,
              "realTime": true,
              "segmentTemplateHashCode": -1301095336,
              "startTime": 1495152642
            }
          ],
          "temporaryURL": "<baseURL>/trip/dd7ada5e-ddbd-4def-927c-880b2d60e57f",
          "updateURL": "<baseURL>/trip/update/dd7ada5e-ddbd-4def-927c-880b2d60e57f?hash=-1553495163",
          "weightedScore": 26.3
        },
        {"...": "..."}
      ]
    }
  ],
  "segmentTemplates": [
    {
      "action": "Drive Car<DURATION>",
      "durationWithoutTraffic": 1255,
      "from": {
        "address": "George Street & Albion Place",
        "class": "Location",
        "lat": -33.87553,
        "lng": 151.2066,
        "timezone": "Australia/Sydney"
      },
      "hashCode": -1301095336,
      "metres": 6899,
      "mini": {
        "description": "Princes Highway & Albert Street",
        "instruction": "Drive Car",
        "mainValue": "7.0km"
      },
      "modeIdentifier": "me_car",
      "modeInfo": {
        "alt": "Car",
        "identifier": "me_car",
        "localIcon": "car"
      },
      "notes": "Live traffic: from 16 mins to 26 mins\n7.0km",
      "streets": [
        {
          "encodedWaypoints": "<string>"
        }
      ],
      "to": {
        "address": "Princes Highway & Albert Street",
        "class": "Location",
        "lat": -33.91288,
        "lng": 151.17905,
        "timezone": "Australia/Sydney"
      },
      "travelDirection": 323,
      "type": "unscheduled",
      "visibility": "in summary"
    },
    {"...": "..."}
  ]
}

Note that the responses are optimized to reduce their size, trying to eliminate any duplicated data. So, you will find a field in the response called segmentTemplates, which will include information of segments shared among several trips in that response. You will also get the trips grouped in a field called groups, each group takes the same modes, and similar stops and tickets. Each trip will have a depart and arrive field, and also the segments that form the trip among other useful values.

For each group of trips, we will select a representative one, which will be the one having the lower score (the field named weightedScore) . Then we will sort all the representative trips by arrive time. The one that we want to show to the user is the first one, but we may also show other alternatives.

Screenshot

Extracting detailed trip information

In order to be able to display the trip to the user, we will show how to construct a trip from a routing.json response with all the required information. We are going to do so to explain the format of the response further, and also for sake of simplicity of our example. First, we will iterate over all the trips to find the selected one, identified by its updateURL, since we know it will be unique for all the trips and it will also be needed later on to update the trip with real-time information. Once we have it, we will iterate over all the segments, and use the segment template hash code to find the corresponding segment template in the list of shared templates and copy all the field-value pairs to our trip segment.

buildSelectedTrip(routingJSON, seletedTripUpdateURL) {
  let result = null;
  let segmentTemplates = routingJSON.segmentTemplates;
  util.forEachTrip(routingJSON, (trip => {
    if (trip.updateURL !== seletedTripUpdateURL)
      return;
    trip.segments.map(segment => {
      segmentTemplate = this.getSegmentTemplate(segmentTemplates, segment.segmentTemplateHashCode);
      for (var key in segmentTemplate)
        segment[key] = segmentTemplate[key];
    })
    result = trip;
  }));
  return result;
}

Note that forEachTrip and getSegmentTemplate are helper methods we defined. You can look at them in the shared code. Now that we have a trip with all the information, we want to show it in the map. The trip will have a list of segments, which correspond to the different parts of the trip. Each segment will be of one transport mode (walking, cycling, bus, train, etc.) and will contain all the relevant information of that part of the trip, including the detailed waypoints to be shown in the map.

Advanced parameters and values

As part of each routing request the user can tweak some parameters that the routing algorithm will consider at the moment of computing the best trip for the given A-to-B pairs and transport modes. For example, it is possible to change the preferred transfer time, which may extend or reduce the time the trip can use when switching between public transport. It is also possible to set the walking and cycling speed, to improve the accuracy of the computed results. There are also flags to enable some specific features, like the wheelchair flag that will try to compute a wheelchair friendly trip, including specific information when available; or conc that will use concession pricing when possible for computing public transport costs.

Another possible parameter is what we call the weighting preferences, which is a set of four values, allowing the user to change the weights among price, environment impact, duration and convenience. The values range from 0.1 to 2 for each of them, meaning unimportant to very important, and the overal sum should not exceed 4. For example, a weighing preference equals to (1.9, 1.9, 0.1, 0.1) means that the user cares most about the price and environmental impact of the trip, and almost nothing about the duration and convenience of the trip.

Considering the values obtained in the response, each trip will include different costs, like calories (to burn), carbon (CO2 to use), hassle (inconvenience) and money (a null value doesn’t mean it’s free, but that we don’t know its cost). The response will also include some URLs to save the trip or update it for real-time changes, which then leads us to the next section, how to update our trip.

 

Stay tuned for Part 3 with details on Trip Update or check out part 1 in case you missed it.