var ROUTES_FOR_LOCATION = '//api.onebusaway.org/api/where/routes-for-location.json';
var TRIPS_FOR_ROUTE = '//api.onebusaway.org/api/where/trips-for-route/%ROUTEID%.json';
var TRIP_FOR_VEHICLE = '//api.onebusaway.org/api/where/trip-for-vehicle/%VEHICLEID%.json';
var PROXY = '//localhost/proxy/proxy.php';
var API_KEY = '78911936-facb-4db5-9feb-5788590ded87';
var MAP, BOUNDS, FRAME, SPAN;
var CENTER = new google.maps.LatLng(47.654952, -122.308058);
var FRAME_PADDING = .25;
var ROUTE_IDS, VEHICLE_IDS;
var VEHICLE_QUEUE, VEHICLE_TRIP_DATA;
var RESIZE_TIMEOUT, REFRESH_TRIP_LIST, VEHICLE_UPDATE_CYCLE;
var MARKERS = {};
$.ajaxSetup({
data: { key: API_KEY },
error: ajaxError
});
$(document).ready(function() {
createMap();
// $.ajax('')
});
function fetchRoutesInBounds() {
RESIZE_TIMEOUT = null;
// Clear previous interval timers which fetched trip lists / individual trips
// for previous bounds.
stopTimer('REFRESH_TRIP_LIST');
stopTimer('VEHICLE_UPDATE_CYCLE');
// Set new bounds, center, and span.
BOUNDS = MAP.getBounds();
CENTER = BOUNDS.getCenter();
SPAN = BOUNDS.toSpan();
// Calculate bounds for "frame," with extra padding on all sides.
var sw = BOUNDS.getSouthWest();
var ne = BOUNDS.getNorthEast();
var vert_padding = SPAN.lng() * FRAME_PADDING;
var horz_padding = SPAN.lat() * FRAME_PADDING;
var new_sw = new google.maps.LatLng(sw.lat() - horz_padding, sw.lng() - vert_padding);
var new_ne = new google.maps.LatLng(ne.lat() + horz_padding, ne.lng() + vert_padding);
FRAME = new google.maps.LatLngBounds(new_sw, new_ne);
// Fetch routes for these bounds.
$.ajax(ROUTES_FOR_LOCATION, {
data: {
lat: CENTER.lat(),
lon: CENTER.lng(),
latSpan: SPAN.lat(),
lonSpan: SPAN.lng(),
maxCount: 200,
},
dataType: 'jsonp',
success: gotRoutesInBounds,
});
}
//
function gotRoutesInBounds(data) {
// Update route list for these bounds.
ROUTE_IDS = [];
var routes = data.data.routes;
for (var i = 0; i < routes.length; i++) {
ROUTE_IDS.push(routes[i].id);
}
updateTripsForAllRoutes();
// Fetch new list of trips for each route every 10 minutes.
REFRESH_TRIP_LIST = setInterval(updateTripsForAllRoutes, 10 * 60 * 1000);
}
function updateTripsForAllRoutes() {
// pauseLiveVehicleUpdates();
VEHICLE_IDS = [];
VEHICLE_QUEUE = [];
VEHICLE_TRIP_DATA = {};
throttle(fetchTripsForRoute, ROUTE_IDS.slice(), 50, beginLiveVehicleUpdates);
}
function fetchTripsForRoute(routeid) {
$.get(TRIPS_FOR_ROUTE.replace(/%ROUTEID%/, routeid), {
includeStatus: true
}, function(data) {
gotTripsForRoute(data, routeid);
}, 'jsonp');
}
function gotTripsForRoute(data, routeid) {
if (data.code == 401) {
setTimeout(fetchTripsForRoute, 50, routeid);
return;
}
// Update vehicle/trip list for these routes' trips.
var trips = {};
console.log("Recording trip data for route ", routeid, ': ', data);
$.each(data.data.references.trips, function(i, trip) {
trips[trip.id] = {
direction: trip.directionId,
headsign: trip.tripHeadsign,
route: trip.routeId
}
});
$.each(data.data.list, function(i, trip) {
var tripid = trip.tripId;
var vehicleid = trip.status.vehicleId;
var position = trip.status.position;
var loc = new google.maps.LatLng(position.lat, position.lon);
// Remember this trip's data if it's within FRAME.
if (FRAME.contains(loc)) {
// Add this vehicle (uniquely) to VEHICLE_IDS.
if (!(vehicleid in VEHICLE_TRIP_DATA)) {
VEHICLE_TRIP_DATA[vehicleid] = {};
VEHICLE_IDS.push(vehicleid);
VEHICLE_QUEUE.push(vehicleid);
}
// Remember the trip data.
if (!(tripid in VEHICLE_TRIP_DATA[vehicleid])) {
trips[tripid].position = loc;
trips[tripid].orientation = trip.status.orientation;
trips[tripid].next_stop = trip.status.nextStop;
VEHICLE_TRIP_DATA[vehicleid][tripid] = trips[tripid];
}
var tooltip = routeid + " #" + vehicleid + ": " + position.lat + ',' + position.lon;
// Add or update the marker.
if (!(vehicleid in MARKERS)) {
MARKERS[vehicleid] = marker(loc, '', tooltip);
} else {
MARKERS[vehicleid].setOptions({
position: loc,
title: tooltip
});
}
console.log("Vechicle ", vehicleid, " of route ", routeid, " (trip ", tripid, ") in bounds: " + loc);
} else {
if (vehicleid in MARKERS) {
MARKERS[vehicleid].setMap(null); // Remove marker that's moved out of bounds.
}
console.log("Vechicle ", vehicleid, " of route ", routeid, " (trip ", tripid, ") out of bounds: " + loc);
}
});
}
function pauseLiveVehicleUpdates() {
stopTimer('VEHICLE_UPDATE_CYCLE');
}
function beginLiveVehicleUpdates() {
console.log("beginLiveVehicleUpdates:");
console.log("VEHICLE_IDS: ", VEHICLE_IDS);
console.log("VEHICLE_QUEUE: ", VEHICLE_QUEUE);
console.log("VEHICLE_TRIP_DATA: ", VEHICLE_TRIP_DATA);
// INDIV_TRIP_INT_TIMER = setInterval(function() {
// var vehicleid = VEHICLE_QUEUE.shift();
// VEHICLE_QUEUE.push(vehicleid);
// console.log("Would update vehicle ", vehicleid, " now.");
// $.get(TRIP_FOR_VEHICLE.replace(/%VEHICLEID%/, vehicleid), function(data) {
// var pos = data.entry.status.position;
// var loc = new google.maps.LatLng(pos.lat, pos.lon);
// if (vehicleid in MARKERS) {
// MARKERS[vehicleid].setPosition(loc);
// } else {
// MARKERS[vehicleid] = marker(loc, '', vehiclid);
// }
// });
// }, 10);
}
// Iteratively passes each member of list to fn, with the specified delay
// between function calls.
function throttle(fn, list, delay, postfn) {
if (list.length) {
fn(list.shift());
setTimeout(throttle, delay, fn, list, delay, postfn);
} else {
postfn();
}
}
function stopTimer(name) {
clearTimeout(window[name]);
window[name] = null;
}
function createMap() {
// an object that we'll use to specify options
var mapOptions = {
center: CENTER,
zoom: 16,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
// the DOM object we're going to put the map into
var mapElement = document.getElementById("busmap");
// create the map inside mapElement with options specified by mapOptions
MAP = new google.maps.Map(mapElement, mapOptions);
google.maps.event.addListener(MAP, 'bounds_changed', function() {
// 1 second after the bounds of the map has changed, repaint all stops &
// trips
if (RESIZE_TIMEOUT) {
clearTimeout(RESIZE_TIMEOUT);
}
RESIZE_TIMEOUT = setTimeout(fetchRoutesInBounds, 500);
});
}
function marker(location, info, tooltip) {
var marker = new google.maps.Marker({
position: location,
map: MAP,
title: tooltip
});
var infowindow = new google.maps.InfoWindow({
content: info
});
google.maps.event.addListener(marker, 'click', function() {
infowindow.open(MAP, marker);
});
return marker;
}
// Provided Ajax error handler function. (Displays useful debugging message.)
function ajaxError(jqxhr, type, error) {
var msg = "An Ajax error occurred!\n\n";
if (type == 'error') {
if (jqxhr.readyState == 0) {
// Request was never made - security block?
msg += "Looks like the browser security-blocked the request.";
} else {
// Probably an HTTP error.
msg += 'Error code: ' + jqxhr.status + "\n" +
'Error text: ' + error + "\n" +
'Full content of response: \n\n' + jqxhr.responseText;
}
} else {
msg += 'Error type: ' + type;
if (error != "") {
msg += "\nError text: " + error;
}
}
alert(msg);
}