MUNI Greenhouse Gas Emissions (Realtime)
-
-
Save mayblue9/2bd7d4c89292ab795b1c1cb1a7553bf9 to your computer and use it in GitHub Desktop.
MUNI Greenhouse Gas Emissions (Realtime)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"> | |
<style> | |
html{ | |
background-color: #000; | |
} | |
body { | |
width: 1024px; | |
margin-top: 0; | |
margin: auto; | |
font-family: "Lato", "PT Serif", serif; | |
color: #000; | |
padding: 0; | |
font-weight: 300; | |
line-height: 24px; | |
overflow-x: hidden; | |
-webkit-font-smoothing: antialiased; | |
} | |
.lines{ | |
fill: none; | |
stroke:#95a5a6; | |
opacity: 0.3; | |
} | |
.vehPoint{ | |
fill-opacity: 0.8; | |
stroke-width: 2px; | |
} | |
.key path { | |
display: none; | |
} | |
.key line { | |
stroke: #fff; | |
shape-rendering: crispEdges; | |
} | |
.legend-title { | |
font-weight: bold; | |
} | |
.legend-box { | |
fill: none; | |
stroke: #888; | |
font-size: 10px; | |
} | |
#title{ | |
position: absolute;; | |
top: 10px; | |
left: 20px; | |
} | |
#basemap{ | |
position: absolute;; | |
top: 80px; | |
left: 20px; | |
} | |
#legend{ | |
position: fixed; | |
top: 85px; | |
left: 20px; | |
z-index: 100; | |
} | |
#more-info{ | |
position: fixed; | |
bottom: 15px; | |
right: 30px; | |
z-index: 100; | |
} | |
#more-info:hover{ | |
opacity: 0.8; | |
} | |
</style> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/queue-async/1.0.7/queue.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js"></script> | |
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script> | |
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script> | |
<body> | |
<div> | |
<div id='title'><h1 style='color:#fff'>SF MUNI bus greenhouse gas emissions in <em><u>realtime</u></em>.</h1></div> | |
<div id='basemap'></div> | |
<div id='legend'></div> | |
<div id='more-info'><button type="button" class="btn btn-xs" data-toggle="modal" data-target="#myModal"><img src='question.png'></button></div> | |
<!-- Modal --> | |
<div id="myModal" class="modal fade" role="dialog"> | |
<div class="modal-dialog"> | |
<!-- Modal content--> | |
<div class="modal-content"> | |
<div class="modal-header"> | |
<button type="button" class="close" data-dismiss="modal">×</button> | |
<h4 class="modal-title">Methods</h4> | |
</div> | |
<div class="modal-body"> | |
<p> The greenhouse gas emission rates for MUNI buses take into account all emissions from | |
the production and distribution of the fuels as well as any the direct tailpipe emissions. | |
For electric-powered buses, which sources its power from the Hetch Hetchy reservoir, we | |
assign an carbon-intensity of 4 kWh/km.</p> | |
<p><strong>Sources:<strong></p> | |
<ul> | |
<li><a href='http://www.arb.ca.gov/msei/categories.htm' target='_blank'>California Air Resources Board EMFAC2014 model</a></li> | |
<li><a href='https://greet.es.anl.gov/' target='_blank'>Argonne National Laboratory GREET model</a></li> | |
<li><a href='https://www.nextbus.com/' target='_blank'>NextBus, Inc. API</a></li> | |
</ul> | |
</div> | |
<div class="modal-footer"> | |
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<body> | |
<script> | |
(function(){ | |
var width = 1500, | |
height = 1250, | |
TRANSITION_DELAY = 3000, | |
formatNumber = d3.format("0,000"); | |
var muniColors = { | |
'F': "none", | |
'J': "none", | |
'K': "none", | |
'L': "none", | |
'M': "none", | |
'N': "none", | |
'T': "none" | |
}; | |
var radius = d3.scale.quantile() | |
.domain([1510,4450]) | |
.range(d3.range(9).map(function(i) { return i })); | |
var rateById = d3.map(); | |
var color = d3.scale.quantile() | |
.domain([0,4450]) | |
.range(['#a50026','#d73027','#f46d43','#fdae61','#fee08b','#ffffbf','#d9ef8b','#a6d96a','#66bd63','#1a9850','#006837'].reverse()); | |
var projection = d3.geo.mercator() | |
.center([-122.438701, 37.758]) | |
.scale(520000) | |
.translate([width / 2, height / 2]); | |
var path = d3.geo.path() | |
.projection(projection); | |
var svg = d3.select("#basemap").append("svg") | |
.attr("width", width) | |
.attr("height", height); | |
var tooltip = d3.select("body") | |
.append("div") | |
.style("position", "absolute") | |
.style("z-index", "10") | |
.style("visibility", "hidden") | |
.style("color", "#000") | |
.style("padding", "8px") | |
.style("background-color", "rgba(256, 256, 256, 0.85)") | |
.style("border-radius", "6px") | |
.style("font", "18px sans-serif") | |
.html("tooltip"); | |
queue() | |
.defer(d3.json, "sfmuni.json") | |
.await(ready); | |
function ready(error, sf) { | |
if (error) throw error; | |
svg.append("g") | |
.attr("class", "counties") | |
.selectAll("path") | |
.data(topojson.feature(sf, sf.objects.sfmuni).features) | |
.enter().append("path") | |
.attr('class', 'lines') | |
.attr("d", path); | |
d3.xml('http://webservices.nextbus.com/service/publicXMLFeed?command=vehicleLocations&a=sf-muni&t=0', drawmuni); | |
// Reload muni data every 15s | |
setInterval(function() { | |
d3.xml('http://webservices.nextbus.com/service/publicXMLFeed?command=vehicleLocations&a=sf-muni&t=0', drawmuni); | |
}, 3000); | |
} | |
// Emission Models | |
var v2e = function (x) { | |
if(x<=0) return {"e":4445, "dv":4445} | |
var avg = 2120; | |
var arr = [8,16,24,32,40,48,56,64,72,80,88,97,105,113] | |
var orr = [4445,3890,2895,2195,1930,1790,1685,1595,1510,1450,1425,1430,1440,1510] | |
var max = function (arrr) { return Math.max.apply(null, arrr); }; | |
var min = function (arrr) { return Math.min.apply(null, arrr); }; | |
var l = [], h = []; | |
arr.forEach(function (v) { | |
((v < x) && l.push(v)) || ((v > x) && h.push(v)); | |
}); | |
var H = arr.indexOf(min(h)) | |
var L = arr.indexOf(max(l)) | |
if(L<0) return {"e":orr[0], "dv":(orr[0]-avg)/avg} | |
if(H<0) return {"e": orr[orr.length-1], "dv":(orr[orr.length-1] - avg)/avg} | |
else{ | |
var e = (x - arr[L]) / (arr[H] - arr[L]) * (orr[H] - orr[L]) + orr[L]; | |
return {"e":parseInt(e), "dv":(e-avg)/avg} | |
} | |
}; | |
var is_electric = function(x){ | |
var yes_i_am_electric = ["1","14","21","22","24","3","30","33", "41","45","5", "59","6", "60", "61"]; | |
return yes_i_am_electric.indexOf(x) > -1 | |
} | |
function vehid(d) { | |
return d.getAttribute("id"); | |
} | |
function drawmuni(munidata) { | |
var translation = function(d) { | |
var pt = projection([d.getAttribute("lon"), d.getAttribute("lat")]); | |
return "translate(" + pt[0] + "," + pt[1] + ")" + | |
"rotate(" + d.getAttribute("heading") + ")" + | |
"scale(0.9)"; | |
}; | |
vehicles = munidata.getElementsByTagName('vehicle'); | |
vehPoints = svg.selectAll(".vehPoint") | |
.data(vehicles, vehid) | |
.on("mouseover", function(d) { | |
tooltip.html( "<strong>Route:</strong> <span style='color:red'>" + d.getAttribute("routeTag") + "</span><br><span style='color:red; font-size:14px;'>" + formatNumber(Math.round(v2e(+d.getAttribute("speedKmHr")).e /100)*100) + "</span><strong style='font-size:10px;'> g CO<sub>2,e</sub> / km</strong>"); | |
tooltip.style("visibility", "visible"); | |
}) | |
.on("mousemove", function() { | |
return tooltip.style("top", (d3.event.pageY-70)+"px").style("left",(d3.event.pageX-55)+"px"); | |
}) | |
.on("mouseout", function(){return tooltip.style("visibility", "hidden");}); | |
var triangle = d3.svg.symbol().type("triangle-up"); | |
vehPoints.transition().duration(TRANSITION_DELAY) | |
.attr("fill", function(d) { | |
var tag = d.getAttribute("routeTag"); | |
if(is_electric(tag)){ return color(4)} // Electricity sourced From Hetch Hetchy hydro | |
else{return muniColors[tag] || color(v2e(+d.getAttribute("speedKmHr")).e)} | |
}) | |
.attr("transform", translation) | |
vehPoints.enter() | |
.append("svg:path") | |
.attr("class", "vehPoint") | |
.attr("transform", translation) | |
.attr("d", triangle) | |
.attr("fill", function(d) { | |
var tag = d.getAttribute("routeTag"); | |
if(is_electric(tag)){ return color(4)} // Electricity sourced From Hetch Hetchy hydro | |
else{return muniColors[tag] || color(v2e(+d.getAttribute("speedKmHr")).e)} | |
}) | |
.attr("opacity", 0.0) | |
.transition().duration(TRANSITION_DELAY) | |
.attr("opacity", 0.9); | |
vehPoints.exit().transition().duration(TRANSITION_DELAY) | |
.attr("opacity", 0.0) | |
.remove(); | |
} | |
d3.select(self.frameElement).style("height", height + "px"); | |
})() | |
</script> | |
<script> | |
(function(){ | |
var svg = d3.select("#legend").append("svg") | |
.attr("width", 140) | |
.attr("height", 290) | |
.style('background', '#000'); | |
var boxmargin = 10, | |
lineheight = 18, | |
keyheight = 15, | |
keywidth = 60, | |
boxwidth = 2 * keywidth, | |
formatNumber = d3.format("0,000"), | |
margin = { "left": 10, "top": 10 }; | |
var color = d3.scale.quantile() | |
.domain([0,4450]) | |
.range(['#a50026','#d73027','#f46d43','#fdae61','#fee08b','#ffffbf','#d9ef8b','#a6d96a','#66bd63','#1a9850','#006837'].reverse()); | |
var ranges = color.range().length; | |
var title = ['MUNI Bus','g CO2,e / km'], | |
titleheight = title.length*lineheight + boxmargin; | |
var legend = svg.append("g") | |
.attr("transform", "translate ("+margin.left+","+margin.top+")") | |
.attr("class", "legend"); | |
legend.selectAll("text") | |
.data(title) | |
.enter().append("text") | |
.attr("class", "legend-title") | |
.attr('x', boxwidth/2) | |
.attr("y", function(d, i) { return (i+1)*lineheight*1.4-15; }) | |
.html(function(d) { return d; }) | |
.style('fill', '#eee') | |
.style('text-anchor', 'middle') | |
.style('font-size', '24') | |
// make legend box | |
var lb = legend.append("rect") | |
.attr("transform", "translate (0,"+titleheight+")") | |
.attr("class", "legend-box") | |
.attr("width", boxwidth) | |
.attr("height", ranges*lineheight+2*boxmargin+lineheight-keyheight); | |
// make quantized key legend items | |
var li = legend.append("g") | |
.attr("transform", "translate (8,"+(titleheight+boxmargin)+")") | |
.attr("class", "legend-items"); | |
li.selectAll("rect") | |
.data(d3.range(ranges).map(function(d){return (ranges-d-1)*500})) | |
.enter().append("rect") | |
.attr("y", function(d, i) { return i*lineheight+lineheight-keyheight; }) | |
.attr("width", keywidth) | |
.attr("height", keyheight) | |
.style("fill", function(d) { return color(d); }); | |
li.selectAll("text") | |
.data(d3.range(ranges).map(function(d){return (ranges-d-1)*500})) | |
.enter().append("text") | |
.attr("x", keywidth+5) | |
.attr("y", function(d, i) { return (i+1)*lineheight + 5; }) | |
.text(function(d) { return formatNumber(Math.round(d/10)*10); }) | |
.style('fill', '#eee'); | |
})() | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment