API overview

Content

In the following chapters we will describe

  • What is the reference architecture
  • What access token / ids you need to call the API
  • How to execute an autocomplete query
  • How to call the trip planner from the autocomplete results

Reference architecture

Triplinx services are split into two main components:

  • The Triplinx API
    • Which includes trip-planning, timetables, routes detail, stops detail.
    • The Triplinx API communicates with 3Scale, an API management solution.
  • The Algolia API
    • Which is an API targeting a single need: The autocomplete / Search of trip points (Stops, Streets, POI).
    • Algolia has its own API management solution.

The recommended usage is to call the Triplinx API from your back end as its model is quite large and should be mapped to a model closer to your front end needs. It also have the benefit of hiding the access key within your backend and provide an easier way to compose different calls.

The Algolia API should instead be directly call from your front end for performance purposes:

  • Client calls are distributed through several servers with the help of a load balancer
    • If you call the Algolia API from a single IP (Your back end) you loose the benefit of the load balancer (Due to Algolia design choice).

Prerequisites

In order to access each web service you need to obtain:

  • Triplinx API
    • An user_key which is available on the Developer portal.
      • This key is used to call the Cityway APIs and identify the client.
  • Algolia API
    • API key, Application Id, Index Name
      • These values identify the client and the targeted database
    • These values are not available on the developer portal and should have been sent to you during your account creation. If not please contact Cityway.

Accessing Triplinx API

To obtain the user_key please sign in on the developer portal with the credential you received:

Then use the applications tab to display your application(s). You should have a default app already created. This screen will display each application access key. (Note: If you want additional applications, you can request additional keys using the Create new application button. (Staging / Production envs, Mobile / Web clients, ...))

This key must be added as a query parameter, user_key, it must be the first one in the query string: http://.../.../method?user_key=xxx&another_parameter=yyy).

Accessing Algolia

In order to use the Algolia functionality, you must gather the following information: the Application ID, the API Key, the Index Name. These three values should have been sent to you, if not, please contact Cityway.

Once you have these values you can call Algolia API with two differents methods:

  • The recommended method is to use Algolia provided clients, Algolia provide clients for a very large set of languages and technologies (JS, PHP, Rails, Django, ...)
  • Implement your own client calling the Algolia REST API

For JavaScript usage, Algolia supports Cross-Origin Resource Sharing (CORS), so that you can use these headers in conjunction with XMLHttpRequest.

Auto complete / Searching trip points

Search request

The two following examples use the query "20 Bay Street Toronto". In real case usage the user will usually only input partial query like "20 bay s" and find his/hers expected result within the autocomplete proposal.

Here is an example using the Algolia JS client:

var client = algoliasearch('{Application Id}', '{API Key}');
var index = client.initIndex('{Index Name}');

index.search('20 Bay Street Toronto', function searchDone(err, content) {
  if (err) {
    console.error(err);
    return;
  }

  for (var h in content.hits) {
    console.log('Hit(' + content.hits[h].objectID + '): ' + content.hits[h].toString());
  }
});

Here is an example using the Algolia REST API:

curl -X GET \
     -H "X-Algolia-API-Key: {API Key}" \
     -H "X-Algolia-Application-Id: {Application Id}" \
     "https://{Application Id}-dsn.algolia.net/1/indexes/{Index Name}?query=20%20Bay%20Street%20Toronto"

Handling the search results

As a response to the previous request, you should receive a result object.

This is the JSON representation:

{
"hits" : [{
  "TRIPPOINT_PK" : "12078-3-21055288",
  "ID_TRIPPOINT" : 12078,
  "NAME" : "Bay Street",
  "SIMPLIFIEDNAME" : "BAY STREET",
  "NAME_LENGTH" : 10,
  "LOCALITYNAME" : "TORONTO",
  "LOCALITYCODE" : "21055288",
  "_geoloc" : {
    "lng" : -79.38426404987403,
    "lat" : 43.65710250001331
  },
  "STARTNUMROAD" : 11,
  "ENDNUMROAD" : 1333,
  "ROADLENGTH" : 4059,
  "POINTTYPE" : 3,
  "POICATEGORY" : null,
  "WEIGHT" : 1.1158303790564494,
  "TRANO" : null,
  "TRANOM" : null,
  "TRAIDTRANSPORTEUR" : null,
  "STATIONCODE": null,
  "ISSTATION" : 0,
  "HASSTATIONTERM" : 0,
  "NAMEWITHSN" : "<streetnumber1to100> Bay Street",
  "LOCATIONS" : {
    "11" : {
      "lng" : -79.37705259277648,
      "lat" : 43.641528997023116,
      "calctype" : 0
    },
    "20" : {
      "lng" : -79.37727683652372,
      "lat" : 43.64186319151011,
      "calctype" : 0
    },
    "27" : {...}, /* truncated */
    "..." : {...}
  },
  "objectID" : "12078-3-21055288-0-1",
  "_highlightResult" : {
    "NAME" : {
      "value" : "<em>Bay</em> <em>Street</em>",
      "matchLevel" : "partial",
      "fullyHighlighted" : true,
      "matchingWords" : ["bay", "street"]
    },
    "SIMPLIFIEDNAME" : {
      "value" : "<em>BAY</em> <em>STREET</em>",
      "matchLevel" : "partial",
      "fullyHighlighted" : true,
      "matchingWords" : ["bay", "street"]
    },
    "LOCALITYNAME" : {
      "value" : "<em>TORONTO</em>",
      "matchLevel" : "partial",
      "fullyHighlighted" : true,
      "matchingWords" : ["toronto"]
    },
    "NAMEWITHSN" : {
      "value" : "<em>20</em> <em>Bay</em> <em>Street</em>",
      "matchLevel" : "partial",
      "fullyHighlighted" : true,
      "matchingWords" : ["20", "bay", "street"]
    }
  },
  "_rankingInfo" : {
    "nbTypos" : 0,
    "firstmatchingWord" : 1000,
    "proximityDistance" : 10,
    "userScore" : 1,
    "geoDistance" : 0,
    "geoPrecision" : 1,
    "nbExactWords" : 3,
    "words" : 4,
    "filters" : 0
  }
}],
"nbHits" : 1,
"page" : 0,
"nbPages" : 1,
"hitsPerPage" : 10,
"processingTimeMS" : 3,
"facets" : {
  "POINTTYPE" : {
    "3" : 1
  },
  "LOCALITYNAME" : {
    "TORONTO" : 1
  }
},
"facets_stats" : {
  "POINTTYPE" : {
    "min" : 3,
    "max" : 3,
    "avg" : 3,
    "sum" : 3
  }
},
"exhaustiveFacetsCount" : false,
"exhaustiveNbHits" : true,
"query" : "20 Bay Street Toronto",
"params" : "query=20%20Bay%20Street%20Toronto&getRankingInfo=1&facets=*&attributesToRetrieve=*&highlightPreTag=%3Cem%3E&highlightPostTag=%3C%2Fem%3E&hitsPerPage=10&facetFilters=%5B%5D&maxValuesPerFacet=100",
"serverUsed" : "c4-fr-3.algolia.net",
"parsedQuery" : "20 bay street toronto"
}

The query which generated this result is "20 Bay Street Toronto".

The following section describes each field of that JSON data :

Field Description
hits The array of matching results
nbHits The number of matching results
page The requested results page
nbPages The total number of results pages
hitsPerPage The number of matching results per page
processingTimeMS The response processing time
facets The fields used to filter data on the index 1*
facets_stats Some stats about facets
exhaustiveFacetsCount Whether the counts of the facet values are exhaustive or approximate
exhaustiveNbHits Whether the nbHits is exhaustive or approximate
query The root user query
params The query parameters
serverUsed The server used to return results regarding to DSN
parsedQuery The parsed query

1* The field "facets" maps each facet name to the corresponding facet counts

Understand the search result

The search results are stored in the JSON data under the "hits" property. It is an array of objects corresponding to trip points with specific locations along streets, roads...

Below is the description of a single object:

Field Description
TRIPPOINT_PK The trip point primary key
ID_TRIPPOINT The trip point identifier
NAME The trip point name
SIMPLIFIEDNAME The trip point simplified name
NAME_LENGTH The trip point name length
LOCALITYNAME The trip point locality name
LOCALITYCODE The trip point locality code
_geoloc The trip point geographic location (longitude and latitude)
STARTNUMROAD The trip point start number
ENDNUMROAD The trip point end number
ROADLENGTH The trip point road length
POINTTYPE The trip point type (1, 3 or 4) 1*
POICATEGORY The trip point POI category
WEIGHT The trip point weight
TRANO The trip point transporter number
TRANOM The trip point transporter name
TRAIDTRANSPORTEUR The trip point transporter identifier
STATIONCODE The station code if applicable. Is the stop_id column value from GTFS dataset.
ISSTATION Whether or not this trip point is a station
HASSTATIONTERM Whether or not this trip point has station terms
NAMEWITHSN The name with the right placeholder
LOCATIONS The geographic locations for each known street number on this trip point placeholder 2*
objectID The Algolia internal object identifier
_highlightResult The highlighted attributes if specified in the Algolia backend
_rankingInfo Some precisions about the ranking info of the matching result

1* Three (3) trip points types are available in the database:

  • 1 -> POI (Point of interest)
  • 3 -> Road networks
  • 4 -> Logical stops

2* A known street number with its exact location

"40" : {
      "lng" : -79.379342176695,
      "lat" : 43.64359329237533,
      "calctype" : 0
    }

40 is the street number, lng is the longitude, lat is the latitude and calctype indicates whether this location was a single street number 0 or interpolated from a numbers range 1.

What to do with results

With this JSON hits results, geographic positions per street numbers can be extracted to draw an itinerary by example. Both start and end points positions can be extracted according to their street numbers in the JSON data. All hits are sorted according to their ranks and reliability.

Case where no street number match the query

In case no street number match the query you can either use the 'closest' street number of the selected item or call the Triplinx API /api/map/v2/getroadsection/(xml|json) to get an approximate location coordinates, to call this API you need:

  • ID_TRIPPOINT which goes in the Road parameter
  • Number where you put the researched road number your user inputed

Like this: http://apihost/api/map/v2/GetRoadSection/json?user_key=someKey&Road=12078&Number=20000

You will get a reply like this:

{
  "Data": {
    "Id": 12078,
    "Latitude": 43.67310092174979,
    "Locality": {
      "Id": 21055288,
      "InseeCode": "21055288",
      "Latitude": 43.64856000024389,
      "Longitude": -79.3853700002253,
      "Name": "TORONTO"
    },
    "LocalityId": 21055288,
    "Longitude": -79.39069142215777,
    "Name": "Bay Street",
    "PointType": 3,
    "FirstNumber": 11,
    "IdSection": 555972,
    "LastNumber": 1333,
    "Length": 4059,
    "MaxOddNumber": 1333,
    "MinOddNumber": 11,
    "Number": 0
  },
  "Message": null,
  "StatusCode": 200
}

Data.Latitude && Data.Longitude are the values to use to call the trip planner.

Calling the trip planner

To call the trip planner you need the following mandatory values:

  • A departure point AND an arrival point each one can be either
    • A WGS84 latitude / longitude pair
    • A trip point id with a type id
  • An arrival OR a departure time
    • If you specify an arrival time the trip planner will try to "Arrive at {arrival time}"
  • A date
  • An algorithm
    • FASTEST (time optimization)
    • SHORTEST (distance optimization)
    • MINCHANGES (transfer count optimization)
  • A list of authorised modes (PT Public Transport, CAR, BIKE)

There is also a couple of optional parameters, the most used being:

  • AllowedOperatorIds: Constrain the trip planner to only use a sub set of the availables PT networks (Default: All)
  • Modes: Constrain the trip planner to use only this set of PT modes (TRAIN, BUS, ...) (Default: All)
  • MaxWalkDistance: Limit the walk distance

For a full listing, please consult the full API documentation.

Using the autocomplete result

{
"hits" : [{
  "TRIPPOINT_PK" : "12078-3-21055288",
  "ID_TRIPPOINT" : 12078,
  "NAME" : "Bay Street",
  // ...
  "_geoloc" : {
    "lng" : -79.38426404987403,
    "lat" : 43.65710250001331
  },
  // ...
  "POINTTYPE" : 3,
  //..
  "LOCATIONS" : {
    //...
    , "20" : {
      "lng" : -79.37727683652372,
      "lat" : 43.64186319151011,
      "calctype" : 0
    },
    // "..." : {...}
   },
  // ...
 }
}

From our previous query on Algolia (20 Bay street Toronto) we extract the first word and as it is an integer we know that the road number is 20, we look inside the LOCATIONS collection and find LOCATIONS['20']. From this item, we now have the latitude and longitude of the trip point.

For the departure, we will be using a stop point to demonstrate the usage of a different trip point type: Kitchener GO

Algolia query results are:

{
  "hits": [
    {
      "TRIPPOINT_PK": "14413-4-21086187",
      "ID_TRIPPOINT": 14413,
      "STATIONCODE": "KI",
      "NAME": "Kitchener GO",
      "SIMPLIFIEDNAME": "KITCHENER GO",
      "NAME_LENGTH": null,
      "LOCALITYNAME": "KITCHENER",
      "LOCALITYCODE": "21086187",
      "_geoloc": {
        "lng": -80.49313354492188,
        "lat": 43.455684661865234
      },
      // ... ,
      "POINTTYPE": 4,
      // ... ,
      "_highlightResult": { /* ... */ },
      "_rankingInfo": { /* ... */ }
    }, { /* ... */ }
  ],
// ...
}

Since the result is not an address (!= 3) we just need to extract the ID_TRIPPOINT and POINTTYPE.

We now have the following values:

  • Departure
    • DepartureType = COORDINATES
    • DepartureLatitude = 43.64186319151011
    • DepartureLongitude = -79.37727683652372
  • Arrival
    • ArrivalType = STOP_PLACE
      • Note: I've provided a translation table for converting from int value (POINTTYPE) to enum value used in the trip planner queries. It's at the end of the documentation try CTRL-F TripPointTypeId <=> TripPointTypeName
    • TripPointId = 14413

We can now launch a trip planner query from the developer portal once we picked missing parameters:

  • Date = 2017-04-20_08-00
    • Format is yyyy-MM-dd_HH-mm
  • DateType = DEPARTURE
    • Our date value is a "Departure time"
  • Algorithm = FASTEST
    • Try to optimize duration
  • TripModes = PT
    • Public transport query

Useful mapping data

TripPointTypeId <=> TripPointTypeName

This table is useful for mapping Algolia POINTTYPE to Triplinx API DepartureType and ArrivalType

TripPointTypeId TripPointName
1 POI
3 ADDRESS
4 STOP_PLACE

OperatorId <=> Operator Name

This table is useful to filter Triplinx API queries by OperatorIds

OperatorId OperatorName
1 GO Transit
4 TTC
5 York Region Transit
6 Milton Transit
7 Oakville Transit
8 Burlington Transit
9 Hamilton Street Railway
10 Brampton Transit
11 MiWay
12 Durham Region Transit
20 Up Express
21 Pearson Terminal Link
23 Toronto Ferries