Skip to content

Instantly share code, notes, and snippets.

@mtaptich
Last active May 28, 2019 05:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save mtaptich/d5447f42dc0919dc75baa553654d15d8 to your computer and use it in GitHub Desktop.
Save mtaptich/d5447f42dc0919dc75baa553654d15d8 to your computer and use it in GitHub Desktop.
MUNI Greenhouse Gas Emissions (Realtime)

MUNI Greenhouse Gas Emissions (Realtime)

<!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">&times;</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>
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment