xxxxxxxxxx
215
/*
2017.10.21 -- CitiBike Usage Visualizer
by Aidan Nelson
Desciption: Visualization of my Citibike bike-sharing usage in the past year (Oct 2016 - Oct 2017). I
use citibike often, so there are a relatively large number of trip data points (525) in that time frame.
Citibike trip data
is from their website (after login, there is a 'trips' menu where you can download data) and station
information (containing longitude/latitude) is from citibike's JSON data. Mapping will happen using
Mappa, which interfaces between p5.js and various online mapping APIs.
Citibike information index JSON: http://gbfs.citibikenyc.com/gbfs/gbfs.json
Citibike station information JSON: https://gbfs.citibikenyc.com/gbfs/en/station_information.json
Mappa: https://github.com/cvalenzuela/Mappa
QUESTIONS:
1. Are these arrays of arrays an OK way to hold the station and trip data? A JSON file seems more logical,
in that each data-point (one trip, or one station) would be held together rather than in 3 separate columns
with a single row index, but I don't know what the logic behind storing and accessing this data is... it
basically feels semantically unwise to require someone accessing trip info to know that start_time is at
index 0 and start_station is at index 1, etc.
2. Does "push" copy information or just reference it? IE does "station_info.push(names);" copy the "names" array
into "station_info" array, or just place a reference there? I am wondering this because the "names" array
is, in this case, local to the loadJSON callback function, and if it were referenced, I don't know why it was
not deleted after the function ended...
3. loop protect -- where to add //noprotect? why? what is wrong w/ addCoordinates function?
4. using map functions in object definitions (latLngToPixel..)?
TO DO:
1. figure out regular expressions: how to match entire station names, rather than parts of names?
IE how to avoid matching "jay st" with "jay st & york st"
2. infinite loop error
*/
// create arrays to hold trip and station info
let all_trips = [];
let station_info = [];
//create variable to hold the current data
let now = Date.parse("September 1, 2016");
//create variables to hold the map, canvas, and "Mappa" instance
let myMap;
let canvas;
let mappa;
// options for mappa object
let options = {
//set starting coordinates to NYC
lat: 40.7128,
lng: -73.950,
//set zoom level
zoom: 12,
style: "http://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png"
}
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
function preload() {
console.log("in preload");
loadStrings("cb_trips.txt", makeTrips);
loadJSON("https://gbfs.citibikenyc.com/gbfs/en/station_information.json", makeStations);
//load song
soundFormats('mp3');
song = loadSound('bicycle_race.mp3');
}
function setup() {
console.log("in setup");
// Create a canvas 640x640
canvas = createCanvas(640, 640);
mappa = new Mappa('Leaflet');
//call mappa object to create a tilemap at lat,long, zoom level
myMap = mappa.tileMap(options);
myMap.overlay(canvas); //create map overlay of a canvas
//get xy points for each station in station_list:
// for (let i = 0; i < station_info.length; i++) {
// station_info[i].getXY(myMap);
// console.log("station " + station_info[i].name + " is at x: " +station_info[i].pnt.x);
// }
// Associate redrawMap callback function with an "onChange" event of the map
myMap.onChange(redrawMap);
//play the song
song.setVolume(0.1);
song.play();
}
function draw() {
clear();
//every so often, increase the date
if (frameCount % 30 == 0) {
//add one week to current date
now += 1000 * 60 * 60 * 24 * 7;
}
//iterate through all_trips list
for (let i = 0; i < all_trips.length; i++) {
//if the trip happened after now
if (all_trips[i].startTime < now) {
//for each trip, iterate through station list
for (let j = 0; j < station_info.length; j++) {
//if the trip start station matches current station (by name), draw blue ellipse at that point
if (all_trips[i].startStation == station_info[j].name) {
let pnt = myMap.latLngToPixel(station_info[j].lat, station_info[j].lng);
fill(200, 0, 255);
ellipse(pnt.x, pnt.y, 7, 7);
}
}
}
// //if the trip end station matches current station (by name), draw red ellipse at that point
// if (all_trips[3][i] == station_info[0][j]) {
// let pnt = myMap.latLngToPixel(station_info[1][j], station_info[2][j]);
// // console.log(pnt);
// fill(255, 0, 0);
// ellipse(pnt.x, pnt.y, 5, 5);
// }
}
//display now on upper left
textSize(12);
textAlign(LEFT);
fill(255);
text(new Date(now), 50, 50);
}
//function to redraw points
function redrawMap() {
clear();
//draw every station up to the current date, if a station has been drawn more than once,
//increase the size
//iterate through all_trips list
for (let i = 0; i < all_trips.length; i++) {
//if the trip happened after now
if (all_trips[i].startTime > now) {
//for each trip, iterate through station list
for (let j = 0; j < station_info.length; j++) {
//if the trip start station matches current station (by name), draw blue ellipse at that point
if (all_trips[i].startStation == station_info[j].name) {}
}
// //if the trip end station matches current station (by name), draw red ellipse at that point
// if (all_trips[3][i] == station_info[0][j]) {
// let pnt = myMap.latLngToPixel(station_info[1][j], station_info[2][j]);
// // console.log(pnt);
// fill(255, 0, 0);
// ellipse(pnt.x, pnt.y, 5, 5);
// }
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
//this function takes citibike trip data and adds it to an array of arrays
//such that index i will correspond to a single trips data
function makeTrips(cb_trips) {
console.log("in makeTrips");
//initiate a new Trip object:
let t = new Trip();
//create an index for the line numbers
let lineIndex = 1;
//iterate through cb_trips
for (let i = 0; i < cb_trips.length; i++) {
//depending on which lineIndex we are at, add current string to one of the above arrays
if (lineIndex % 5 == 0) {
t.duration = trim(cb_trips[i]);
} else if (lineIndex % 4 == 0) {
t.endStation = trim(cb_trips[i]);
} else if (lineIndex % 3 == 0) {
t.endTime = Date.parse(trim(cb_trips[i]));
} else if (lineIndex % 2 == 0) {
t.startStation = trim(cb_trips[i]);
} else {
t.startTime = Date.parse(trim(cb_trips[i]));
}
lineIndex++;
if (lineIndex > 5) {
//return index to 1
lineIndex = 1;
//add trip object to the array of trip objects
all_trips.push(t);
//reuse t as a new trip object
t = new Trip();
}
}
}
function makeStations(cb_stations) {
console.log("in makeStations");
//set up a short link to the station data
let stations = cb_stations.data.stations;
//iterate through info, adding to arrays
for (let i = 0; i < stations.length; i++) {
let s = new Station(stations[i].name, stations[i].lat, stations[i].lon);
station_info.push(s);
}
}