Live Map of Portland Buses and Trains with 18 Lines of JavaScript

by Phil Lanier May 17, 2017

The Satori platform makes it ridiculously easy to publish and subscribe to live streaming data. To demonstrate just how easy we’re going to build a real time map of Portland Transit activity using the TriMet Portland GTFS live data channel and just 18 lines of JavaScript code.

Step 1: The libraries we’ll be using

We’re going to use Leaflet for the map, Angular and the excellent Angular Leaflet library to handle the data binding, and of course, Satori will provide the data. All told, we will need to include the following JavaScript references:

And one stylesheet:

Tip: You could take a similar approach with Google Maps using the Angular Google Maps library.

Step 2: A basic map

The HTML structure we need for this is extremely simple:

<div ng-app="app">
<div ng-controller="appController">
<leaflet id="map" lf-center="pdx" markers="vehicles" ></leaflet>

That’s it. If you’re at all familiar with Angular, this is pretty straightforward. We’re just referencing a controller, and <leaflet> is a directive defined by the Angular Leaflet library.

The Angular controller is just as simple:

angular.module('app', ['ui-leaflet']).controller('appController', function($scope){
$scope.pdx = {lat: 45.526477, lng:-122.635928, zoom: 14};
$scope.vehicles = {};

All we’re doing here is adding a dependency to ui-leaflet (Angular Leaflet), and then defining the two scope variables referenced by the directive. $scope.pdx specifies the map’s initial latitude, longitude, and zoom level, while $scope.vehicles is the data bound object that will hold all of our map markers.

Finally, a couple lines of CSS allows the map to fill the browser window:

html, body, #map{
padding: 0;
margin: 0;
height: 100vh;
width: 100%;

At this point, we should see a map of Portland with nothing on it.

Step 3: Consume the Satori Feed and render it

For the last step, we’ll let Satori do the hard work of streaming live data to us, and we’ll use the data to update our vehicles object. To subscribe to the live data feed, we can copy the JavaScript code right out of the TriMet Portland channel on Satori:

var endpoint = "wss://";
var appKey = "3B24aEDe2d13Feed98bbDC92bbFFbC54";
var channel = "RT-GTFS-TriMet";
var subscription = rtm.subscribe(channel, RTM.SubscriptionMode.SIMPLE);
subscription.on('rtm/subscription/data', function (pdu) {
pdu.body.messages.forEach(function (msg) {

The page on Satori does a great job explaining what this all means, but to summarize:

  • First we specify the Satori Endpoint and Channel for our feed, along with an Appkey. Satori provides a public Appkey for each channel that’s good for 7 days, but you can sign up to get a permanent Appkey for free.
  • Next we create the subscription and start listening for new messages.
  • Finally, we use rtm.start() to turn on the Satori data stream.

Since we’re counting lines of code, we’ll simplify a bit. But we can pretty much drop this right into our controller method. If we run it at this point, we should see bus and train updates streaming into the browser console:

Now, we just need to populate our vehicles object with markers using the Satori data. We’ll replace the forEach loop in the code above with the following:

msg.entity.forEach(u => $scope.vehicles[] = 
{lat: u.vehicle.position.latitude,
lng: u.vehicle.position.longitude,
message: "Route " + u.vehicle.vehicle.label},
marker: {type: 'div', iconSize: [16,16], popupAnchor: [0,0], html: '<strong></strong>'} );

Angular Leaflet expects each marker to be a key/value pair, where the key is a unique identifier (we’ll use the id of the vehicle) and the value defines attributes of the marker. In this case, we will specify latitude, longitude, message (popup text), and a custom marker style. The last line, $scope.$apply(), triggers Angular to re-render the map after each message batch is received.

Last, but not least, we’ll style our custom map markers:

border-radius: 10px;
background: #ff284e;
opacity: .8;
border-color: black;

Putting it all together

Jump In

The Portland Transit map is just one example of how Satori makes it easy to use live data to create new apps. Just 18 lines of code that has near infinite scalability built-in with ultra low latency. So you can add in not just more bus data, but all kinds of disparate data — millions of messages from billions of endpoints. Create apps that leverage live data like never before. And best of all it’s free! And, if you’ve got some streaming live data to republish, get on it, you could win $500,000! Just go to, create an account and get started.

Connect with us on Twitter, Facebook, and LinkedIn. And remember, we’re hiring!