The more green a marker corresponds to how full a station is (how few empty slots there are). The entire application is updated in real time via polling the xml.

The application is available to see at:

https://bikemap-43dbc.firebaseapp.com/

and on my github: https://github.com/emars/bikemap.

Side Note

What is a bike station?

A bike station — place to rent bikes

Getting Started

Create a new index.html file that we will be using for the base of the project:

Now the styles in bikemap.css which make the map fit any browser window:

html, body, #map {

height: 100%;

width: 100%;

}

Setting Up The Map

Now to set up the map we’ll need access to a tile map, this is the actual map layer (how it looks). There are a number of tile map providers depending on what you’re looking for. I’ll be using Mapbox because it was fast to get up and running and I liked to look of their dark tile map.

All of this code is located in bikemap.js:



var ZOOM_LEVEL = 13;

var MAP_ID = 'map';

var MAPBOX_ACCESS_TOKEN = 'your.mapbox.access.token';

var MAPBOX_PROJECT_ID = 'your.mapbox.project.id';

var MAPBOX_DARK_TILEMAP = 'https://api.mapbox.com/styles/v1/mapbox/dark-v9/tiles/256/{z}/{x}/{y} var TORONTO_COORDS = [43.653, -79.383];var ZOOM_LEVEL = 13;var MAP_ID = 'map';var MAPBOX_ACCESS_TOKEN = 'your.mapbox.access.token';var MAPBOX_PROJECT_ID = 'your.mapbox.project.id';var MAPBOX_DARK_TILEMAP = 'https://api.mapbox.com/styles/v1/mapbox/dark-v9/tiles/256/{z}/{x}/{y} @2x ?access_token={accessToken}'; var map = L.map(MAP_ID).setView(TORONTO_COORDS, ZOOM_LEVEL); L.tileLayer(MAPBOX_DARK_TILEMAP, {

maxZoom: 18,

id: MAPBOX_PROJECT_ID,

accessToken: MAPBOX_ACCESS_TOKEN

}).addTo(map);

Here is what the app should look like so far

Pretty sweet looking setup so far with very little work.

Fetching and Parsing Data

Now we need to actually pull the data from the feed, I set up a small proxy resource on heroku called: Resourcely (super creative). This simply converts the xml to JSON and sets the appropriate CORS headers so that we can request it from the client. Feel free use Resoucely or to set up your own service to handle that too.

I also set up a helper function parseStation() to get all the required information from each station into the proper format.

const BIKE_DATA_URL = ' http://feeds.bikesharetoronto.com/stations/stations.xml' const requestURL = 'https://resourcely.herokuapp.com/r/' + encodeURIComponent(BIKE_DATA_URL); function fetchStations(){

return fetch(requestURL)

.then(res => res.json())

} function populateMap(){

fetchStations()

.then(data => {

// parse the data

const stations = data.stations.station.map(parseStation);

console.log(stations); });

} function parseStation(station){

var numBikes = parseInt(station.nbBikes[0]) return {

numBikes, id: station.id[0],

name: station.name[0],

latlng: [station.lat[0], station.long[0]],

totalDocks: numBikes + parseInt(station.nbEmptyDocks[0])

}

} populateMap();

Should have a large array of all the stations

Creating Markers

Now we can place markers onto the map which correspond to each bike station location, we also easily add a popup so that a user can click on a station to get details about it. This is fairly trivial using Leaflet:

function populateMap(){

fetchStations()

.then(data => {

const stations = data.stations.station.map(parseStation);

// Add a line here:

stations.forEach(addMarker);

});

} function addMarker(station){

var stationMarkerOptions = {

radius: 50

}; var popupContent = '<h5>' + station.name + '</h5>'

+ '<p>' + station.numBikes + '/' + station.totalDocks

+ 'bikes</p>'; L.circle(station.latlng, stationMarkerOptions)

.bindPopup(popupContent)

.addTo(map);

}

Now the application should have a bunch of neat markers that are populated that corresponds to the location as well as has a popup for each marker, pretty powerful for ~50 lines of code.

Color Coding

It’d be great if the marker colors could represent how many bikes are at a station so we’ll do that next. First we write a simple helper to output an rgb string based on how full a station is:

function mapColor(ratio){

const amount = Math.floor(255.0 * ratio);

return `rgb(${255 - amount}, ${amount}, 0)`;

}

Then we add a couple lines to addMarker():

function addMarker(station){

// ADD THIS

var stationColor = mapColor(station.numBikes / station.totalDocks); var stationMarkerOptions = {

radius: 50,

color: stationColor // ADD THIS

}; var popupContent = '<h5>' + station.name[0] + '</h5>'

+ '<p>' + station.numBikes + '/' + station.totalDocks

+ 'bikes</p>'; L.circle(station.latlng, stationMarkerOptions)

.bindPopup(popupContent)

.addTo(map);

}

Make it Real Time

Now to changes show up while the user is on the page, just simply start polling:

// Polling the data

populateMap();

setInterval(populateMap, 5000);

Summary

This tutorial took you through building a very simple application with Leaflet. However, there is much more you can do with the framework.

Here are a few ideas that I’ll be working on in the future to improve this application:

Add a real time log that shows which stations are receiving / losing bicycles.

Add snapshots of the map so people can compare different times of day.

Add animations to the map markers that pulse.

Going Further

This post just outlined what Leaflet is capable of, here are some more resources on the library: