Real-time PM2.5 monitoring results using d3 and leaflet. Work in progress but viewable here
Last active
May 29, 2019 21:24
-
-
Save kaz-a/1f3523453855630d5c1a0957c9af57e7 to your computer and use it in GitHub Desktop.
PM2.5 dashboard
This file contains 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> | |
<html> | |
<head> | |
<title>NYCCAS Dashboard</title> | |
<meta charset="utf-8" http-equiv="X-UA-Compatible" content="IE=9" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<link rel="stylesheet" href="https://code.jquery.com/ui/1.8.10/themes/smoothness/jquery-ui.css" type="text/css"> | |
<script type="text/javascript" src="https://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.10/jquery-ui.min.js"></script> | |
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Open+Sans:400,300' type='text/css'> | |
<!-- bootstrap --> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" integrity="sha512-dTfge/zgoMYpP7QbHy4gWMEGsbsdZeCXz7irItjcC3sPUFtf0kuFbDz/ixG7ArTxmDjLXDmezHubeNikyKGVyQ==" crossorigin="anonymous"> | |
<!-- fontawesome --> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css"> | |
<!-- leaflet --> | |
<link rel="stylesheet" type="text/css" href="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.css" /> | |
<!-- my stylesheet --> | |
<link rel = "stylesheet" type="text/css" href="style.css"> | |
</head> | |
<body> | |
<div class="container-fluid legend"> | |
<h2>PM<sub>2.5</sub> in New York City</h2> | |
Fine particles (PM<sub>2.5</sub>) are tiny airborne solid and liquid particles less than 2.5 microns in diameter. | |
PM<sub>2.5</sub> is the most harmful urban air pollutant, small enough to penetrate deep into the lungs and enter the bloodstream, worsening lung and heart disease and leading to hospital admissions, premature deaths | |
and increasing risk of cancer. | |
<hr /> | |
MAP LEGEND | |
<br /><br /> | |
<p class="grey">Monitored at <i class="fa fa-circle"></i> Roof | |
<i class="fa fa-square"></i> Street</p> | |
<div class="symbols"></div> | |
</div> | |
<div class = "container-fluid site-specific"> | |
<div class="buttons col-sm-12 col-md-12"></div> | |
<div id="map" class="col-xs-12 col-sm-12 col-md-6"></div> | |
<div class="content col-xs-12 col-sm-12 col-md-6"> | |
<div id="chart"></div> | |
<div id="site" class="col-xs-12 media"> | |
<div class="site col-xs-12 col-sm-6 col-md-6"></div> | |
<div class="site-photo media-right col-xs-12 col-sm-6 col-md-6" href="#"></div> | |
</div> | |
</div> | |
</div> | |
<div class="container-fluid message"> | |
<div class="row"> | |
<hr /> | |
<h4>Preliminary Data</h4> | |
<p>Data displayed on this page is preliminary <br />and subject to change. | |
</p> | |
</div> | |
</div> | |
</body> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-alpha1/jquery.js"></script> | |
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/queue-async/1.0.7/queue.min.js"></script> | |
<script src="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js"></script> | |
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js" integrity="sha512-K1qjQ+NcF2TYO/eI3M6v8EiNYZfA95pQumfvcVrTHtwQVDG+aHRqLi/ETn2uB+1JqwYqVG3LIvdm9lj6imS/pQ==" crossorigin="anonymous"></script> | |
<script src="script.js"></script> | |
</html> |
This file contains 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
// set up leaflet | |
var map = L.map('map', { | |
zoomControl: false | |
}).setView([40.71, -74.00], 10); | |
new L.Control.Zoom({ | |
position: 'topright' | |
}).addTo(map); | |
// initialize the SVG layer | |
map._initPathRoot(); | |
// specify tile map service | |
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoiemFrc2Nsb3NldCIsImEiOiJjaWdzZGh5ZjMwMmN1dGhrbnN6ZjFtb2NjIn0.x6b7Ra4Jdtbv38M9_uM2vQ', { | |
attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a>, Imagery © <a href="http://mapbox.com">Mapbox</a>', | |
maxZoom: 18, | |
id: 'zakscloset.o4bigbgi', | |
accessToken: 'pk.eyJ1IjoiemFrc2Nsb3NldCIsImEiOiJjaWdzZGh5ZjMwMmN1dGhrbnN6ZjFtb2NjIn0.x6b7Ra4Jdtbv38M9_uM2vQ' | |
}).addTo(map); | |
//Disable drag and zoom handlers. | |
//map.dragging.disable(); | |
map.touchZoom.disable(); | |
//map.doubleClickZoom.disable(); | |
map.scrollWheelZoom.disable(); | |
map.keyboard.disable(); | |
// Disable tap handler, if present. | |
if (map.tap) map.tap.disable(); | |
// draw map markers with d3 | |
var svg = d3.select("#map").select("svg"), | |
g = svg.append("g"); | |
// tooltip | |
var tooltip = d3.select("body").append("div") | |
.attr("class", "tooltip") | |
.style("opacity", 0); | |
var data = d3.csv('https://raw.githubusercontent.com/kaz-a/nyccas_node/master/public/data/mock_data-rev.csv', ready); // fake data. remove asap | |
// var data = d3.json("http://a816-dohmeta.nyc.gov/MetadataLite/api/AirQuality?type=json", ready); | |
function ready(error, data) { | |
if (error) throw error; | |
console.log(data); | |
// Parse the date & time | |
var parseDate = d3.time.format("%m/%d/%Y %H:%M").parse; | |
// Add a LatLng object to each item in the dataset | |
data.forEach(function(d) { | |
d.LatLng = new L.LatLng(d.latitude, d.longitude); | |
//d.roll_pm25 = +d.roll_PM25; | |
d.startTime = d.startTime.replace(/(\b\d\b)/g,"0$1"); | |
d.updateTime = d.updateTime.replace(/(t)/gi, " at "); | |
d.updateTime = d.updateTime.slice(0, -3); | |
d.updateTime = d.updateTime.replace(/(-)/g, "/"); | |
d.updateTime = d.updateTime.substring(5); | |
d.siteID = +d.siteID; | |
d.roll_PM25 = (Math.round( d.roll_PM25 * 10 ) / 10); | |
d.current_PM25 = +d.current_PM25; | |
}); | |
// create an array by starttime | |
var nestedByDate = d3.nest() | |
.key(function(d) { | |
return d.startTime; | |
}) | |
.sortKeys(d3.descending) | |
.entries(data); | |
console.log("nestedByDate = ", nestedByDate); | |
var nestedBySite = d3.nest() | |
.key(function(d) { | |
return d.siteID; | |
}) | |
.sortKeys(d3.ascending) | |
.entries(data); | |
console.log("nestedBySite = ", nestedBySite); | |
// get the latest date | |
var latest = nestedByDate[0]; | |
console.log(latest); | |
latestDate = latest.key; | |
console.log("The last day/time of this dataset is: " + latestDate); | |
parsedLatestDate = parseDate(latestDate); | |
// for map legend | |
var circleColorMap = [ | |
//good | |
{ "value": 4, "color": "#80ce6f", "condition": "GOOD" }, | |
{ "value": 8, "color": "#5fb550" }, | |
{ "value": 12, "color": "#3d9331" }, | |
{ "value": "", "color": "fff" }, | |
//moderate | |
{ "value": 19.9, "color": "#f4e39a", "condition": "MODERATE" }, | |
{ "value": 27.8, "color": "#e5d06c" }, | |
{ "value": 35.4, "color": "#c9b128" }, | |
{ "value": "", "color": "fff" }, | |
//usg | |
{ "value": 42.1, "color": "#e88f7e", "condition": "UNHEALTHY FOR SENSITIVE GROUP" }, | |
{ "value": 48.8, "color": "#e27659" }, | |
{ "value": 55.4, "color": "#dd653c" }, | |
{ "value": "", "color": "fff" }, | |
//unhealthy | |
{ "value": 87.1, "color": "#d6a1a1", "condition": "UNHEALTHY" }, | |
{ "value": 118.8, "color": "#bf6b6b" }, | |
{ "value": 150.4, "color": "#aa4d4d" }, | |
{ "value": "", "color": "fff" }, | |
//very unhealthy | |
{ "value": 183.8, "color": "#9a92cc", "condition": "VERY UNHEALTHY" }, | |
{ "value": 217.2, "color": "#7f70b7" }, | |
{ "value": 250.4, "color": "#654ea5" }, | |
{ "value": "", "color": "fff" }, | |
//hazardous | |
{ "value": 300, "color": "#9e7b8f", "condition": "HAZARDOUS" }, | |
{ "value": 350, "color": "#7f5463" }, | |
{ "value": 400, "color": "#6b2d3c" } | |
] | |
console.log("circleColorMap = ", circleColorMap); | |
// color scheme for rolling-24hr data | |
var colors = function(d){ | |
//good | |
if ((d.roll_PM25 >= 0 ) && (d.roll_PM25 < circleColorMap[2].value)){ | |
if ((d.roll_PM25 >= 0) && (d.roll_PM25 < circleColorMap[0].value)){ | |
return circleColorMap[0].color; | |
} else if ((d.roll_PM25 >= (circleColorMap[0].value)) && (d.roll_PM25 < circleColorMap[1].value)){ | |
return circleColorMap[1].color; | |
} else { | |
return circleColorMap[2].color; | |
}; | |
//moderate | |
} else if ((d.roll_PM25 >= (circleColorMap[2].value)) && (d.roll_PM25 < circleColorMap[6].value)) { | |
if ((d.roll_PM25 >= (circleColorMap[2].value)) && (d.roll_PM25 < circleColorMap[4].value)){ | |
return circleColorMap[4].color; | |
} else if ((d.roll_PM25 >= (circleColorMap[4].value)) && (d.roll_PM25 < circleColorMap[5].value)) { | |
return circleColorMap[5].color; | |
} else { | |
return circleColorMap[6].color; | |
}; | |
//usg | |
} else if ((d.roll_PM25 >= (circleColorMap[6].value)) && (d.roll_PM25 < circleColorMap[10].value)) { | |
if ((d.roll_PM25 <= (circleColorMap[6].value)) && (d.roll_PM25 < circleColorMap[8].value)){ | |
return circleColorMap[8].color; | |
} else if ((d.roll_PM25 <= (circleColorMap[8].value)) && (d.roll_PM25 < circleColorMap[9].value)) { | |
return circleColorMap[9].color; | |
} else { | |
return circleColorMap[10].color; | |
}; | |
//unhealthy | |
} else if ((d.roll_PM25 >= (circleColorMap[10].value)) && (d.roll_PM25 < circleColorMap[14].value)) { | |
if((d.roll_PM25 >= (circleColorMap[10].value)) && (d.roll_PM25 < circleColorMap[12].value)){ | |
return circleColorMap[12].color; | |
} else if ((d.roll_PM25 >= (circleColorMap[12].value)) && (d.roll_PM25 < circleColorMap[13].value)){ | |
return circleColorMap[13].color; | |
} else { | |
return circleColorMap[14].color; | |
}; | |
//very unhealthy | |
} else if ((d.roll_PM25 >= (circleColorMap[14].value)) && (d.roll_PM25 < circleColorMap[16].value)) { | |
if ((d.roll_PM25 >= (circleColorMap[14].value)) && (d.roll_PM25 < circleColorMap[16].value)) { | |
return circleColorMap[16].color; | |
} else if ((d.roll_PM25 >= (circleColorMap[16].value)) && (d.roll_PM25 < circleColorMap[17].value)) { | |
return circleColorMap[17].color; | |
} else { | |
return circleColorMap[18].color; | |
}; | |
//hazardous | |
} else if (d.roll_PM25 >= (circleColorMap[18].value)) { | |
return circleColorMap[21].color; | |
} else { | |
return "grey"; | |
}; | |
}; | |
// color scheme for current data | |
var currentColors = function(d){ | |
//good | |
if ((d.current_PM25 >= 0 ) && (d.current_PM25 < circleColorMap[2].value)){ | |
if ((d.current_PM25 >= 0) && (d.current_PM25 < circleColorMap[0].value)){ | |
return circleColorMap[0].color; | |
} else if ((d.current_PM25 >= (circleColorMap[0].value)) && (d.current_PM25 < circleColorMap[1].value)){ | |
return circleColorMap[1].color; | |
} else { | |
return circleColorMap[2].color; | |
}; | |
//moderate | |
} else if ((d.current_PM25 >= (circleColorMap[2].value)) && (d.current_PM25 < circleColorMap[6].value)) { | |
if ((d.current_PM25 >= (circleColorMap[2].value)) && (d.current_PM25 < circleColorMap[4].value)){ | |
return circleColorMap[4].color; | |
} else if ((d.current_PM25 >= (circleColorMap[4].value)) && (d.current_PM25 < circleColorMap[5].value)) { | |
return circleColorMap[5].color; | |
} else { | |
return circleColorMap[6].color; | |
}; | |
//usg | |
} else if ((d.current_PM25 >= (circleColorMap[6].value)) && (d.current_PM25 < circleColorMap[10].value)) { | |
if ((d.current_PM25 <= (circleColorMap[6].value)) && (d.current_PM25 < circleColorMap[8].value)){ | |
return circleColorMap[8].color; | |
} else if ((d.current_PM25 <= (circleColorMap[8].value)) && (d.current_PM25 < circleColorMap[9].value)) { | |
return circleColorMap[9].color; | |
} else { | |
return circleColorMap[10].color; | |
}; | |
//unhealthy | |
} else if ((d.current_PM25 >= (circleColorMap[10].value)) && (d.current_PM25 < circleColorMap[14].value)) { | |
if((d.current_PM25 >= (circleColorMap[10].value)) && (d.current_PM25 < circleColorMap[12].value)){ | |
return circleColorMap[12].color; | |
} else if ((d.current_PM25 >= (circleColorMap[12].value)) && (d.current_PM25 < circleColorMap[13].value)){ | |
return circleColorMap[13].color; | |
} else { | |
return circleColorMap[14].color; | |
}; | |
//very unhealthy | |
} else if ((d.current_PM25 >= (circleColorMap[14].value)) && (d.current_PM25 < circleColorMap[16].value)) { | |
if ((d.current_PM25 >= (circleColorMap[14].value)) && (d.current_PM25 < circleColorMap[16].value)) { | |
return circleColorMap[16].color; | |
} else if ((d.current_PM25 >= (circleColorMap[16].value)) && (d.current_PM25 < circleColorMap[17].value)) { | |
return circleColorMap[17].color; | |
} else { | |
return circleColorMap[18].color; | |
}; | |
//hazardous | |
} else if (d.current_PM25 >= (circleColorMap[18].value)) { | |
return circleColorMap[21].color; | |
} else { | |
return "grey"; | |
}; | |
}; | |
// buttons to switch between data | |
d3.select(".buttons").append("button") | |
.attr("class", "btn btn-default") | |
.text("CURRENT") | |
.on("click", function() { showCurrent(); }); | |
d3.select(".buttons").append("button") | |
.attr("class", "btn btn-default") | |
.text("24-HR AVERAGE") | |
.on("click", function() { showRolling(); }); | |
///////////////////////////////////////////////////////////////////////////////////// | |
// SETUP LINECHART ////////////////////////////////////////////////////////////////// | |
///////////////////////////////////////////////////////////////////////////////////// | |
var margin = {top: 50, right: 20, bottom: 30, left: 50}; | |
var graphic = d3.select("#chart"); | |
var width = graphic.node().clientWidth - margin.left - margin.right, | |
height = 300 - margin.top - margin.bottom; | |
var xScale = d3.time.scale() | |
.range([0,width]); | |
var yScale = d3.scale.linear() | |
.range([height,0]); | |
var xAxis = d3.svg.axis() | |
.scale(xScale) | |
.orient("bottom") | |
.innerTickSize(-height) | |
.tickPadding(10) | |
.outerTickSize(3) | |
.ticks(7) | |
.tickFormat(d3.time.format("%m/%d")); | |
var yAxis = d3.svg.axis() | |
.scale(yScale) | |
.orient("left") | |
.innerTickSize(-width) | |
.tickPadding(10) | |
.outerTickSize(3) | |
.tickFormat(d3.round); | |
var chartSvg = d3.select("#chart") | |
.append("svg") | |
.attr("width", width + margin.left + margin.right) | |
.attr("height", height + margin.top + margin.bottom) | |
.append("g") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
var parseDate = d3.time.format("%m/%d/%Y %H:%M").parse; | |
data.forEach(function(d) { | |
d.roll_pm25 = +d.roll_PM25; | |
d.siteID = +d.siteID; | |
d.starttime = parseDate(d.startTime); | |
}); | |
///////////////////////////////////////////////////////////////////////////////////// | |
// DRAW MAP MARKERS ///////////////////////////////////////////////////////////////// | |
///////////////////////////////////////////////////////////////////////////////////// | |
// draw markers on the map | |
var circleWidth = 8; | |
var rectWidth = 12; | |
var pm25Circle = svg.selectAll("g>pm25Circle") | |
.data(latest.values); | |
var pm25Rect = svg.selectAll("g>pm25Rect") | |
.data(latest.values); | |
function removeCircle() { | |
pm25Circle.exit() | |
.transition() | |
.duration(500) | |
.style("opacity", "0") | |
.remove(); | |
}; | |
function removeRect() { | |
pm25Rect.exit() | |
.transition() | |
.duration(500) | |
.style("opacity", 0) | |
.remove(); | |
}; | |
function circleMouseOverRolling(d) { | |
d3.select(this).transition() | |
.ease("quad") | |
.duration("200") | |
.attr("r", circleWidth * 3) | |
tooltip.html("<h4>" + d.site + "<br />" + d.roll_PM25 + " µg/m³</h4>") | |
.style("opacity", 0.8) | |
.style("left", (d3.event.pageX)+6 + "px") | |
.style("top", (d3.event.pageY)-80 + "px"); | |
}; | |
function circleMouseOverCurrent(d) { | |
d3.select(this).transition() | |
.ease("quad") | |
.duration("200") | |
.attr("r", circleWidth * 3) | |
tooltip.html("<h4>" + d.site + "<br />" + d.current_PM25 + " µg/m³</h4>") | |
.style("opacity", 0.8) | |
.style("left", (d3.event.pageX)+6 + "px") | |
.style("top", (d3.event.pageY)-80 + "px"); | |
}; | |
function circleMouseOut(d) { | |
d3.select(this).transition() | |
.ease("quad") | |
.delay("100") | |
.duration("200") | |
.attr("r", circleWidth) | |
tooltip.style("opacity", 0); ; | |
}; | |
function rectMouseOverRolling(d) { | |
d3.select(this).transition() | |
.ease("quad") | |
.duration("200") | |
.attr({ width: rectWidth * 3.5, height: rectWidth * 3.5 }) | |
tooltip.html("<h4>" + d.site + "<br />" + d.roll_PM25 + " µg/m³</h4>") | |
.style("opacity", 0.8) | |
.style("left", (d3.event.pageX)+6 + "px") | |
.style("top", (d3.event.pageY)-80 + "px"); | |
}; | |
function rectMouseOverCurrent(d) { | |
d3.select(this).transition() | |
.ease("quad") | |
.duration("200") | |
.attr({ width: rectWidth * 3.5, height: rectWidth * 3.5 }) | |
tooltip.html("<h4>" + d.site + "<br />" + d.current_PM25 + " µg/m³</h4>") | |
.style("opacity", 0.8) | |
.style("left", (d3.event.pageX)+6 + "px") | |
.style("top", (d3.event.pageY)-80 + "px"); | |
}; | |
function rectMouseOut(d) { | |
d3.select(this).transition() | |
.ease("quad") | |
.delay("100") | |
.duration("200") | |
.attr({ width: rectWidth, height: rectWidth }); | |
}; | |
// function to update the location of circles | |
function update() { | |
pm25Circle | |
.transition() | |
.duration(1000) | |
.attr("transform", function(d) { | |
return "translate(" + | |
map.latLngToLayerPoint(d.LatLng).x + "," + | |
map.latLngToLayerPoint(d.LatLng).y + ")"; | |
}) | |
pm25Rect | |
.transition() | |
.duration(1000) | |
.attr("transform", function(d) { | |
return "translate(" + | |
map.latLngToLayerPoint(d.LatLng).x + "," + | |
map.latLngToLayerPoint(d.LatLng).y + ")"; | |
}) | |
}; | |
function removeMapSymbols(){ | |
d3.selectAll("circle").transition().duration(600).style("opacity", 0).remove(); | |
d3.selectAll("rect.pm25Circle").transition().duration(600).style("opacity", 0).remove(); | |
d3.selectAll("rect.pm25Rect").transition().duration(600).style("opacity", 0).remove(); | |
}; | |
function showCurrent() { | |
removeMapSymbols(); | |
hideSiteInfo(); | |
unHighlightLegend(); | |
pm25Circle.enter().append("g") | |
.append("circle") | |
.filter(function(d) { return d.location === "Roof"; }) | |
.attr("class", "pm25Circle") | |
.attr("r", circleWidth) | |
.style("fill", currentColors) | |
.on("click", currentCircleClick) | |
.on("mouseover", circleMouseOverCurrent) | |
.on("mouseout", circleMouseOut); | |
pm25Rect.enter().append("g") | |
.append("rect") | |
.filter(function(d) { return d.location === "Street"; }) | |
.attr("class", "pm25Circle") | |
.style("fill", currentColors) | |
.attr("width", rectWidth) | |
.attr("height", rectWidth) | |
.on("click", currentCircleClick) | |
.on("mouseover", rectMouseOverCurrent) | |
.on("mouseout", rectMouseOut); | |
map.on("viewreset", update); | |
update(); | |
drawCurrentLine(); | |
}; | |
function showRolling() { | |
removeMapSymbols(); | |
hideSiteInfo(); | |
unHighlightLegend(); | |
pm25Circle.enter().append("g") | |
.append("circle") | |
.filter(function(d) { return d.location === "Roof"; }) | |
.attr("class", "pm25Circle") | |
.attr("r", circleWidth) | |
.style("fill", colors) | |
.on("click", rollingCircleClick) | |
.on("mouseover", circleMouseOverRolling) | |
.on("mouseout", circleMouseOut); | |
pm25Rect.enter().append("g") | |
.append("rect") | |
.filter(function(d) { return d.location === "Street"; }) | |
.attr("class", "pm25Rect") | |
.style("fill", colors) | |
.attr("width", rectWidth) | |
.attr("height", rectWidth) | |
.on("click", rollingCircleClick) | |
.on("mouseover", rectMouseOverRolling) | |
.on("mouseout", rectMouseOut); | |
map.on("viewreset", update); | |
update(); | |
drawRollingLine(); | |
}; | |
// default to show rolling 24-hr average data on page load | |
window.onload = showRolling(); | |
///////////////////////////////////////////////////////////////////////////////////// | |
// DRAW LINE CHART/////////////////////////////////////////////////////////////////// | |
///////////////////////////////////////////////////////////////////////////////////// | |
function removeLine() { | |
d3.selectAll("g>text").remove(); | |
d3.selectAll(".g-group").remove(); | |
d3.selectAll("path.minMaxMean").transition().duration(400).style("opacity", 0).remove(); | |
d3.selectAll(".naaqsline").remove(); | |
d3.selectAll(".x.axis").remove(); | |
d3.selectAll(".y.axis").remove(); | |
}; | |
function drawRollingLine(){ | |
removeLine(); | |
chartSvg.append("text") | |
.attr("class", "chartTitle") | |
.attr("x", width/2) | |
.attr("y", 0 - (margin.top / 2)) | |
.attr("text-anchor", "middle") | |
.html("Rolling 24 Hour Average PM2.5 (µg/m³)"); | |
// set the yScale max value to the naaqs value (35) if roll_PM25 value is less than the naaqs value | |
// otherwise, show the max roll_PM25 value | |
var min = d3.min(data, function(d) { return d.roll_PM25; }); | |
var max = d3.max(data, function(d) { return d.roll_PM25 ? 40 : d.roll_PM25; }); | |
xScale.domain(d3.extent(data, function(d) { return d.starttime; })); | |
yScale.domain([min, max]); | |
var lineGroup = chartSvg.selectAll(".g-group") | |
.data(nestedBySite) | |
.enter() | |
.append("g") | |
.attr("class", "g-group"); | |
var line = d3.svg.line() | |
.x(function(d) { return xScale(d.starttime); }) | |
.y(function(d) { return yScale(d.roll_PM25); }); | |
lineGroup.append("path") | |
.attr("class", function(d) { return "g-line"; }) | |
.attr("d", function(d) { return line(d.values); }); | |
// add x and y axis | |
var xAxisGroup = chartSvg.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + height + ")") | |
.call(xAxis); | |
var yAxisGroup = chartSvg.append("g") | |
.attr("class", "y axis") | |
.call(yAxis) | |
.append("text") | |
.attr("y", 6) | |
.attr("dy", ".71em") | |
.style("text-anchor", "end") | |
.html("(µg/m³)"); | |
// add the min, max and mean lines | |
// nest data by starttime and add mean, max and min values to the array | |
var minMaxMean = d3.nest() | |
.key(function(d) { return d.starttime; }) | |
.rollup(function(d) { | |
return { | |
starttime: d[0].starttime, | |
//location: d.location, | |
mean: d3.mean(d, function(g) { return g.roll_PM25; }), | |
max: d3.max(d, function(g) { return g.roll_PM25; }), | |
min: d3.min(d, function(g) { return g.roll_PM25; }) | |
}; | |
}) | |
.entries(data) | |
// get the values only | |
.map(function(d){ | |
return d.values; | |
}); | |
console.log("minMaxMean = ", minMaxMean); | |
// the nest loses the sorting of the data, so need to re-sort | |
minMaxMean.sort(function(a,b){ | |
return a.starttime - b.starttime; | |
}); | |
// 3 lines for mean, min, and max | |
// draw mean line | |
chartSvg.append("path") | |
.attr("class", "minMaxMean") | |
.attr("d", d3.svg.line() | |
.x(function(d) { return xScale(d.starttime); }) | |
.y(function(d) { return yScale(d.mean); })(minMaxMean) | |
) | |
.style("stroke","#ababab") | |
.style("fill", "none"); | |
// draw max line | |
chartSvg.append("path") | |
.attr("class", "minMaxMean") | |
.attr("d", d3.svg.line() | |
.x(function(d) { return xScale(d.starttime); }) | |
.y(function(d) { return yScale(d.max); })(minMaxMean) | |
) | |
.style("stroke","#ababab") | |
.style("fill", "none"); | |
// draw min line | |
chartSvg.append("path") | |
.attr("class", "minMaxMean") | |
.attr("d", d3.svg.line() | |
.x(function(d) { return xScale(d.starttime); }) | |
.y(function(d) { return yScale(d.min); })(minMaxMean) | |
) | |
.style("stroke","#ababab") | |
.style("fill", "none"); | |
// text label for mean, min, and max lines | |
// label for mean line | |
chartSvg.append("text") | |
.attr("transform", "translate(" + (width+3) + "," + yScale(minMaxMean[0].mean) + ")") | |
.attr("dy", ".12em") | |
.attr("text-anchor", "end") | |
.style("fill", "#ababab") | |
.style("font-size", "10px") | |
.text("Mean"); | |
// label for max line | |
chartSvg.append("text") | |
.attr("transform", "translate(" + (width+3) + "," + yScale(minMaxMean[0].max) + ")") | |
.attr("dy", ".12em") | |
.attr("text-anchor", "end") | |
.style("fill", "#ababab") | |
.style("font-size", "10px") | |
.text("Max"); | |
// label for min line | |
chartSvg.append("text") | |
.attr("transform", "translate(" + (width+3) + "," + yScale(minMaxMean[0].min) + ")") | |
.attr("dy", ".12em") | |
.attr("text-anchor", "end") | |
.style("fill", "#ababab") | |
.style("font-size", "10px") | |
.text("Min"); | |
// add the NAAQS line | |
var naaqsLine = { lineValue: 35, label: "National Ambient Air Quality Standard" }; | |
var xExtent = d3.extent(data, function(d) { return d.starttime; }); | |
var yExtent = d3.extent(data, function(d) { return d.roll_PM25; }); | |
console.log(xExtent, yExtent); | |
chartSvg.append("svg:line") | |
.attr("x1", xScale(xExtent[0])) | |
.attr("y1", yScale(naaqsLine.lineValue)) | |
.attr("x2", xScale(xExtent[1])) | |
.attr("y2", yScale(naaqsLine.lineValue)) | |
.attr("class", "naaqsline"); | |
chartSvg.append("text") | |
.attr("x", xScale(xExtent[1])) | |
.attr("y", yScale(naaqsLine.lineValue)) | |
.attr("dy", "-0.5em") | |
.attr("text-anchor", "end") | |
.text(naaqsLine.label) | |
.attr("class", "naaqslinetext"); | |
resizeChart(); | |
}; | |
function drawCurrentLine(){ | |
removeLine(); | |
chartSvg.append("text") | |
.attr("class", "chartTitle") | |
.attr("x", width/2) | |
.attr("y", 0 - (margin.top / 2)) | |
.attr("text-anchor", "middle") | |
.html("Current PM2.5 (µg/m³)"); | |
// set the yScale max value to the naaqs value (35) if roll_PM25 value is less than the naaqs value | |
// otherwise, show the max roll_PM25 value | |
var min = d3.min(data, function(d) { return d.current_PM25; }); | |
var max = d3.max(data, function(d) { return d.current_PM25 ? 40 : d.current_PM25; }); | |
xScale.domain(d3.extent(data, function(d) { return d.starttime; })); | |
yScale.domain([min, max]); | |
var lineGroup = chartSvg.selectAll(".g-group") | |
.data(nestedBySite) | |
.enter() | |
.append("g") | |
.attr("class", "g-group"); | |
var line = d3.svg.line() | |
.x(function(d) { return xScale(d.starttime); }) | |
.y(function(d) { return yScale(d.current_PM25); }); | |
lineGroup.append("path") | |
.attr("class", function(d) { return "g-line"; }) | |
.attr("d", function(d) { return line(d.values); }); | |
// add x and y axis | |
var xAxisGroup = chartSvg.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + height + ")") | |
.call(xAxis); | |
var yAxisGroup = chartSvg.append("g") | |
.attr("class", "y axis") | |
.call(yAxis) | |
.append("text") | |
.attr("y", 6) | |
.attr("dy", ".71em") | |
.style("text-anchor", "end") | |
.html("(µg/m³)"); | |
// add the min, max and mean lines | |
// nest data by starttime and add mean, max and min values to the array | |
var minMaxMean = d3.nest() | |
.key(function(d) { return d.starttime; }) | |
.rollup(function(d) { | |
return { | |
starttime: d[0].starttime, | |
//location: d.location, | |
mean: d3.mean(d, function(g) { return g.current_PM25; }), | |
max: d3.max(d, function(g) { return g.current_PM25; }), | |
min: d3.min(d, function(g) { return g.current_PM25; }) | |
}; | |
}) | |
.entries(data) | |
// get the values only | |
.map(function(d){ | |
return d.values; | |
}); | |
console.log("minMaxMean = ", minMaxMean); | |
// the nest loses the sorting of the data, so need to re-sort | |
minMaxMean.sort(function(a,b){ | |
return a.starttime - b.starttime; | |
}); | |
// 3 lines for mean, min, and max | |
// draw mean line | |
chartSvg.append("path") | |
.attr("class", "minMaxMean") | |
.attr("d", d3.svg.line() | |
.x(function(d) { return xScale(d.starttime); }) | |
.y(function(d) { return yScale(d.mean); })(minMaxMean) | |
) | |
.style("stroke","#ababab") | |
.style("fill", "none"); | |
// draw max line | |
chartSvg.append("path") | |
.attr("class", "minMaxMean") | |
.attr("d", d3.svg.line() | |
.x(function(d) { return xScale(d.starttime); }) | |
.y(function(d) { return yScale(d.max); })(minMaxMean) | |
) | |
.style("stroke","#ababab") | |
.style("fill", "none"); | |
// draw min line | |
chartSvg.append("path") | |
.attr("class", "minMaxMean") | |
.attr("d", d3.svg.line() | |
.x(function(d) { return xScale(d.starttime); }) | |
.y(function(d) { return yScale(d.min); })(minMaxMean) | |
) | |
.style("stroke","#ababab") | |
.style("fill", "none"); | |
// text label for mean, min, and max lines | |
// label for mean line | |
chartSvg.append("text") | |
.attr("transform", "translate(" + (width+3) + "," + yScale(minMaxMean[0].mean) + ")") | |
.attr("dy", ".12em") | |
.attr("text-anchor", "end") | |
.style("fill", "#ababab") | |
.style("font-size", "10px") | |
.text("Mean"); | |
// label for max line | |
chartSvg.append("text") | |
.attr("transform", "translate(" + (width+3) + "," + yScale(minMaxMean[0].max) + ")") | |
.attr("dy", ".12em") | |
.attr("text-anchor", "end") | |
.style("fill", "#ababab") | |
.style("font-size", "10px") | |
.text("Max"); | |
// label for min line | |
chartSvg.append("text") | |
.attr("transform", "translate(" + (width+3) + "," + yScale(minMaxMean[0].min) + ")") | |
.attr("dy", ".12em") | |
.attr("text-anchor", "end") | |
.style("fill", "#ababab") | |
.style("font-size", "10px") | |
.text("Min"); | |
// add the NAAQS line | |
var naaqsLine = { lineValue: 35, label: "National Ambient Air Quality Standard" }; | |
var xExtent = d3.extent(data, function(d) { return d.starttime; }); | |
var yExtent = d3.extent(data, function(d) { return d.current_PM25; }); | |
console.log(xExtent, yExtent); | |
chartSvg.append("svg:line") | |
.attr("x1", xScale(xExtent[0])) | |
.attr("y1", yScale(naaqsLine.lineValue)) | |
.attr("x2", xScale(xExtent[1])) | |
.attr("y2", yScale(naaqsLine.lineValue)) | |
.attr("class", "naaqsline"); | |
chartSvg.append("text") | |
.attr("x", xScale(xExtent[1])) | |
.attr("y", yScale(naaqsLine.lineValue)) | |
.attr("dy", "-0.5em") | |
.attr("text-anchor", "end") | |
.text(naaqsLine.label) | |
.attr("class", "naaqslinetext"); | |
resizeChart(); | |
}; | |
// resize line chart | |
function resizeChart() { | |
d3.select(window).on("resize", resizeHandler); | |
function resizeHandler() { | |
width = graphic.node().clientWidth - margin.left - margin.right; | |
xScale.range([0, width]); | |
xAxisGroup.call(xAxis); | |
lineGroup.attr("path", function(d) { return xScale(d.startTime); }); | |
}; | |
}; | |
// call this function when a map circle is clicked | |
function rollingCircleClick(id) { | |
highlightLine(id); | |
showSiteInfo(id); | |
// highlight a legend box that corresponds to a clicked map circle | |
var self = d3.select(this), | |
rects = d3.selectAll(".symbols>div>svg>rect"); | |
// clear previous selection | |
rects.style({ "stroke": "none", "stroke-width": "1px" }); | |
// loop and hightlight matches | |
rects.each(function(){ | |
var r = d3.select(this); | |
if (r.style("fill") === self.style("fill")){ | |
r.style({ "stroke": "red", "stroke-width": "3px" }); | |
} | |
}); | |
// highlight the selected map markers | |
var cir = d3.selectAll("svg>g>circle"); | |
var sq = d3.selectAll("svg>g>rect"); | |
// clear the selection | |
cir.classed("circleSelected", false); | |
sq.classed("circleSelected", false); | |
// highlight the selection | |
self.classed("circleSelected", true); | |
}; | |
function currentCircleClick(id) { | |
highlightLine(id); | |
showSiteInfo(id); | |
// highlight a legend box that corresponds to a clicked map circle | |
var self = d3.select(this), | |
rects = d3.selectAll(".symbols>div>svg>rect"); | |
// clear previous selection | |
rects.style({ "stroke": "none", "stroke-width": "1px" }); | |
// loop and hightlight matches | |
rects.each(function(){ | |
var r = d3.select(this); | |
if (r.style("fill") === self.style("fill")){ | |
r.style({ "stroke": "red", "stroke-width": "3px" }); | |
} | |
}); | |
// highlight the selected map markers | |
var cir = d3.selectAll("svg>g>circle"); | |
var sq = d3.selectAll("svg>g>rect"); | |
// clear the selection | |
cir.classed("circleSelected", false); | |
sq.classed("circleSelected", false); | |
// highlight the selection | |
self.classed("circleSelected", true); | |
}; | |
// highlight a line that corresponds to a clicked map circle | |
function highlightLine(id){ | |
var lineGroup = d3.selectAll(".g-group"); | |
lineGroup.classed("g-highlight", function(d) { | |
//console.log(d); | |
return d.key == id.siteID; | |
}); | |
}; | |
// remove highlight from legend box | |
function unHighlightLegend(id){ | |
var selectedLegend = d3.selectAll(".symbols>div>svg>rect"); | |
selectedLegend.style("stroke", "none"); | |
}; | |
// show site photo | |
function showSiteInfo(id){ | |
// show site info | |
d3.select(".site").html("<h3>" + id.site + "</h3><span class='italic'>" + id.address + "</span><br /><br />" + "Measured at " + id.location + | |
"<br />Building Density: " + id.bldgarea_c + "<br />Traffic Density: " + id.traffic_cl + "<br >Data as of " + id.updateTime); | |
d3.select(".site-photo").html("<img src='assets/sites/" + id.siteID | |
+ ".jpg' class='media-object img-responsive' width='100%'><p class='small'>Image Source: Google StreetView</p>"); | |
}; | |
// remove site photo | |
function hideSiteInfo(){ | |
d3.select(".site").html(""); | |
d3.select(".site-photo").html(""); | |
}; | |
///////////////////////////////////////////////////////////////////////////////////// | |
// DRAW MAP LEGEND/////////////////////////////////////////////////////////////////// | |
///////////////////////////////////////////////////////////////////////////////////// | |
// responsive legend container | |
var legend = d3.select(".symbols").append("div") | |
.classed("svg-container", true) | |
.append("svg") | |
.attr("preserveAspectRatio", "xMinYMin meet") | |
.attr("viewBox", "0 0 463.9 74.2") | |
.classed("svg-content-responsive", true); | |
// draw map legend boxes | |
legend.selectAll("rect") | |
.data(circleColorMap) | |
.enter() | |
.append("rect") | |
.attr("x", function (d, i) { return (i+1) * 18.6; }) | |
.attr("y", 18.6) | |
.attr("class", "color-box") | |
.style("fill", function(d) { return d.color; }) | |
.on("mouseover", function(d){ | |
tooltip.html( function (e) { | |
return (d.value == "" ? "" : (d.value != 400 ? "<" + d.value + " µg/m³" : ">" + d.value + " µg/m³")); | |
}) | |
.style("opacity", 0.8) | |
.style("left", (d3.event.pageX)-30 + "px") | |
.style("top", (d3.event.pageY)-40 + "px") | |
}) | |
.on("mouseout", function(d) { | |
tooltip.style("opacity", 0); | |
}); | |
//label map legend boxes | |
var legendLabel = legend.selectAll("text") | |
.data(circleColorMap) | |
.enter() | |
.append("text") | |
.attr("x", function (d, i) { return (i+1) * 18.6; }) | |
.attr("y", 50) | |
.attr("class", "color-box-label") | |
//.text(function(d) { return d.condition; }); | |
.text(function(d) { | |
if(d.condition == "UNHEALTHY FOR SENSITIVE GROUP"){ | |
var tspan = d3.select(".color-box-label") | |
.append("tspan") | |
.attr("dy", 10) | |
.attr("x", 167.4) | |
.text("SENSITIVE GROUP"); | |
return "UNHEALTHY FOR "; | |
return tspan; | |
} else { | |
return d.condition; | |
}; | |
}); | |
}; // end of d3 data load | |
///////////////////////////////////////////////////////////////////////////////////// | |
// ADD ANNUAL AVERAGE DATA OVERLAY/////////////////////////////////////////////////// | |
///////////////////////////////////////////////////////////////////////////////////// | |
// add checkbox and label to the button div | |
var checkBox = d3.select(".buttons").append("form") | |
.attr("class", "form") | |
.append("input") | |
.attr("id", "annual") | |
.attr("type", "checkbox") | |
.style("margin-left", "5px"); | |
d3.select(".buttons").append("label") | |
.attr("class", "annualLabel") | |
.style("margin-left", "25px") | |
.style("margin-top", "18px") | |
.html("<label for='annual'>ANNUAL AVERAGE</label>"); | |
//set the annual average data shades | |
function getColor(d) { | |
return d >= 0 && d < 8 ? "#ddd266" : | |
d >= 8.1 && d < 8.6 ? "#dbba67" : | |
d >= 8.7 && d < 9.1 ? "#dbae67" : | |
d >= 9.2 && d < 9.6 ? "#bf9158" : | |
d >= 9.7 ? "#93774c" : | |
"grey"; | |
}; | |
// load annual average data | |
// note that the data values need to be divided by 1000 | |
var annualData = "https://raw.githubusercontent.com/kaz-a/nyccas_inhouse/master/app/assets/data/aa6_pm300m1_GCSWGS1984_unprojected.geojson"; | |
var legend = L.control({ position: "topleft" }); | |
// draw annual average map legend | |
legend.onAdd = function (map) { | |
var div = L.DomUtil.create("div", "legend-annualAvg"), | |
annualLevel = [0, 8.1, 8.7, 9.2, 9.7], | |
labels = []; | |
// loop through intervals and generate a label with a colored square for each interval | |
for (var i = 0; i < annualLevel.length; i++) { | |
div.innerHTML += | |
'<i style="background:' + getColor(annualLevel[i]) + '"></i> ' + | |
annualLevel[i] + (annualLevel[i + 1] ? '–' + annualLevel[i + 1] + 'µg/m³<br />' : 'µg/m³+'); | |
} | |
return div; | |
}; | |
$.getJSON(annualData, function (annualData) { | |
console.log(annualData); | |
//get unique annual average values (for legend) | |
var annualFeatures = annualData.features; | |
var uniqueValue = []; | |
var division = 1000; // gridcode values need to be divided by 1000 | |
annualFeatures.forEach(function (x) { | |
if (!annualFeatures[x.properties.gridcode]) { | |
uniqueValue.push(x.properties.gridcode/division); | |
annualFeatures[x.properties.Annual] = true; | |
} | |
}); | |
console.log(annualData); | |
console.log(uniqueValue); | |
// style properties for the grid cells | |
function style(feature) { | |
return { | |
fillColor: getColor(feature.properties.gridcode/division), | |
weight: 0.1, | |
opacity: 0.5, | |
color: "none", | |
dashArray: "1", | |
fillOpacity: 0.5 | |
}; | |
}; | |
// add annual data | |
var addAnnualData = L.geoJson(annualData, { style: style }); | |
// click event to overlay annual data and legend | |
$("#annual").click(function () { | |
if (this.checked) { | |
//console.log("checked!"); | |
addAnnualData.addTo(map); | |
addAnnualData.bringToBack(); | |
legend.addTo(map); | |
} else { | |
//console.log("unchecked!"); | |
map.removeLayer(addAnnualData); | |
map.removeControl(legend); | |
}; | |
}); | |
}); | |
This file contains 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
body{ | |
font-family: 'Open Sans', sans-serif; | |
font-weight: 300; | |
overflow: none; | |
} | |
.social { | |
top: 40px; | |
} | |
#map { | |
height: 500px; | |
} | |
.buttons { | |
padding: 0px; | |
} | |
button { | |
margin: 5px; | |
} | |
.content { | |
height: 500px; | |
} | |
.legend { | |
text-align: center; | |
width: 50%; | |
/*margin-top: 50px; | |
margin-bottom: 50px;*/ | |
} | |
.symbols { | |
/*width: 70%;*/ | |
} | |
.message { | |
text-align: center; | |
width: 50%; | |
margin-top: 50px; | |
margin-bottom: 50px; | |
} | |
.message h3 { | |
margin: 40px; | |
} | |
hr { | |
width: 55%; | |
border-top: 1px solid black; | |
} | |
#chart { | |
height: 300px; | |
/*border-top: 1px solid #ccc;*/ | |
border-width: 70%; | |
} | |
.media-body { | |
width: auto; | |
} | |
.site-photo { | |
/*height: 200px;*/ | |
padding: 20px; | |
} | |
.italic { | |
font-style: italic; | |
} | |
.small { | |
font-size: 10px; | |
} | |
.grey { | |
color: #999; | |
} | |
.large { | |
font-size: 30px; | |
} | |
.pm25Circle { | |
stroke: white; | |
stroke-width: 2px; | |
opacity: 1; | |
cursor: pointer; | |
} | |
.pm25Rect { | |
stroke: white; | |
stroke-width: 2px; | |
opacity: 1; | |
cursor: pointer; | |
} | |
.circleSelected { | |
stroke: red; | |
stroke-width: 4px; | |
opacity: 1; | |
} | |
div.tooltip { | |
position: absolute; | |
text-align: left; | |
width: auto; | |
height: auto; | |
padding: 8px; | |
font: 12px sans-serif; | |
background: white; | |
border: white 1px solid; | |
border-radius: 0px; | |
pointer-events: none; | |
} | |
.controller-button{ | |
padding: 10px; | |
margin: 10px; | |
} | |
.info { | |
position: relative; | |
} | |
.info .overlay { | |
position: absolute; | |
top: 10; | |
left: 10; | |
pointer-events: none; | |
} | |
.button { | |
padding-bottom: 20px; | |
} | |
.weather { | |
float: right; | |
padding: 0; | |
} | |
.current-date { | |
font-weight: 300; | |
text-align: center; | |
} | |
.current-date span { | |
font-size: 21px; | |
} | |
h1, h2, h3 { | |
font-weight: 300; | |
} | |
.container-fluid { | |
padding: 0 !important; | |
} | |
.date { | |
padding: 30px; | |
} | |
.date p { | |
font-size: 14px; | |
margin: 10px 0 0; | |
} | |
.aqi-container { | |
text-align: center; | |
padding: 30px; | |
height: 225px; | |
} | |
.weather-container { | |
text-align: center; | |
padding: 30px; | |
height: 225px; | |
} | |
.current-aqi h1 { | |
font-size: 60px; | |
font-weight: 300; | |
display: inline; | |
} | |
.current-cond i { | |
font-size: 70px; | |
} | |
.aqi-qual { | |
font-size: 24px; | |
font-weight: 300; | |
margin-top: 36px; | |
line-height: 0; | |
} | |
.aqi-qual p { | |
font-size: 14px; | |
font-weight: 300; | |
margin-top: 24px; | |
/*line-height: 0;*/ | |
} | |
.tempWind p { | |
margin-top: -10px; | |
} | |
.day { | |
/*background: linear-gradient( 180deg, #4750a4, #819e9a ); */ | |
background-image: url("day.jpg"); | |
background-repeat: no-repeat; | |
background-size: cover; | |
color: white; | |
} | |
.sunset { | |
/*background: linear-gradient( 180deg, #312b50, #f09c8d ); */ | |
background-image: url("sunsets.jpg"); | |
background-repeat: no-repeat; | |
background-size: cover; | |
color: white; | |
} | |
.night { | |
/*background: linear-gradient( 180deg, #061b63, #f1b999 ); */ | |
background-image: url("night.jpg"); | |
background-repeat: no-repeat; | |
background-size: cover; | |
color: white; | |
} | |
.axis line { | |
fill: none; | |
stroke: black; | |
stroke-dasharray: 2px 3px; | |
shape-rendering: crispEdges; | |
stroke-width: 1px; | |
} | |
.axis path { | |
display: none; | |
} | |
.axis text { | |
font-size: 12px; | |
pointer-events: none; | |
fill: #777; | |
} | |
.y.axis text { | |
text-anchor: end !important; | |
font-size:12px; | |
} | |
.g-line { | |
stroke: #ababab; | |
stroke-opacity: 0.2; | |
fill:none; | |
stroke-width: 1px; | |
} | |
.g-highlight path { | |
stroke: #53c2c4; | |
stroke-opacity: 1; | |
fill: none; | |
stroke-width: 3px; | |
} | |
.line-hover { | |
color: #53c2c4; | |
} | |
.tick line{ | |
opacity: 0.1; | |
} | |
footer { | |
background: black; | |
} | |
footer .container { | |
padding: 3%; | |
} | |
.bold { | |
font-weight: 300; | |
font-size: 0.8em; | |
color: white; | |
} | |
ul { | |
list-style-type: none; | |
} | |
footer div div ul li { | |
padding: 3%; | |
font-weight: 100; | |
} | |
footer ul li a { | |
color: rgba(255,255,255,0.6); | |
} | |
footer ul li a:hover { | |
color: red; | |
text-decoration: none; | |
} | |
.leaflet-control-attribution a { | |
color: grey; | |
} | |
.color-box { | |
width: 18.6px; | |
height: 18.6px; | |
opacity: 0.8; | |
cursor: pointer; | |
} | |
.color-box-label { | |
font-size: 8px; | |
} | |
.naaqsline { | |
fill: none; | |
stroke: #000; | |
stroke-width: 0.2px; | |
stroke-dasharray: 5 5; | |
} | |
.naaqslinetext { | |
fill: #000; | |
stroke: none; | |
font-size: 10px; | |
font-weight: 100; | |
} | |
.min-max-mean_line { | |
fill: none; | |
stroke: red; | |
stroke-width: 1px; | |
} | |
.min-max-mean_line_text { | |
fill: #ababab; | |
stroke: none; | |
font-size: 8px; | |
font-weight: 100; | |
} | |
.overlay { | |
fill: none; | |
pointer-events: all; | |
} | |
.focus circle { | |
fill: none; | |
stroke: red; | |
} | |
.svg-container { | |
display: inline-block; | |
position: relative; | |
width: 100%; | |
padding-bottom: 18%; /* aspect ratio */ | |
vertical-align: top; | |
overflow: hidden; | |
} | |
.svg-content-responsive { | |
display: inline-block; | |
position: absolute; | |
top: 10px; | |
left: 0; | |
} | |
form { | |
font-family: 'Open Sans', sans-serif; | |
font-size: 14px; | |
font-weight: 300; | |
width: 0px; | |
display: inline-block; | |
} | |
/*input#annual:checked:before { | |
content: "HIDE ANNUAL AVERAGE"; | |
background: white; | |
border: 1px solid #53c2c4; | |
color: black; | |
display: inline-block; | |
} | |
*/ | |
/*input#annual:before { | |
content: attr(value); | |
margin: -29px 0px; | |
display: inline-block; | |
white-space: nowrap; | |
cursor: pointer; | |
padding: 17px; | |
border: 1px solid black; | |
background: white; | |
color: black; | |
display: inline-block; | |
}*/ | |
input#annual:before { | |
content: attr(value); | |
display: inline-block; | |
white-space: nowrap; | |
width: 16px; | |
height: 16px; | |
background: white; | |
border: 1px solid black; | |
cursor: pointer; | |
} | |
input#annual:checked:before { | |
/*content: "HIDE ANNUAL AVERAGE";*/ | |
display: inline-block; | |
background: grey; | |
border: 1px solid grey; | |
color: black; | |
cursor: pointer; | |
} | |
.annualLabel { | |
font-weight: 300 !important; | |
display: inline-block; | |
} | |
.legend-annualAvg { | |
line-height: 12px; | |
color: black; | |
background: rgba(255, 255, 255, 0.7); | |
padding: 10px; | |
font-family: 'Open Sans', sans-serif; | |
font-weight: 300; | |
font-size: 10px; | |
} | |
.legend-annualAvg i { | |
width: 12px; | |
height: 12px; | |
float: left; | |
margin-right: 8px; | |
opacity: 0.7; | |
} | |
a.navbar-brand { | |
background-image: url("dohmh_logo.png"); | |
background-size: cover; | |
width: 109px; | |
height: 70px; | |
margin-top: -28px; | |
} | |
.navbar-default { | |
background-color: transparent; | |
border-color: transparent; | |
} | |
.navbar-dohmh { | |
background-color: black; | |
border-radius: 0px; | |
} | |
.navbar-default .navbar-nav>li>a { | |
color: white; | |
} | |
.navbar-default .navbar-nav>li>a:focus { | |
color: white; | |
} | |
.navbar { | |
margin-bottom: 0px; | |
} | |
.navbar-default .navbar-toggle { | |
border-color: transparent; | |
} | |
.btn { | |
border-radius: 0px !important; | |
transition: linear, ease-in 0.3s !important; | |
} | |
.btn-default { | |
color: white !important; | |
background: grey !important; | |
border-color: grey !important; | |
padding: 16px !important; | |
float: left; | |
} | |
.btn-default:hover { | |
color: black !important; | |
background: white !important; | |
border-color: black !important; | |
} | |
.btn-default:focus { | |
outline: none !important; | |
text-decoration: none !important; | |
color: white !important; | |
background: #53c2c4 !important; | |
border: 1px #53c2c4 solid !important; | |
} | |
.btn-default:active { | |
color: white !important; | |
background: #53c2c4 !important; | |
border: 1px #53c2c4 solid !important; | |
} | |
a { | |
color: white !important; | |
text-decoration: none !important; | |
} | |
a:hover { | |
color: #53c2c4 !important; | |
text-decoration: none !important; | |
} | |
a:active { | |
color: #53c2c4 !important; | |
text-decoration: none !important; | |
} | |
a.active { | |
color: white !important; | |
text-decoration: none !important; | |
} | |
.leaflet-bar a, .leaflet-bar a:hover { | |
background: grey; | |
} | |
.leaflet-control-attribution a { | |
color: grey !important; | |
} | |
/*** Works on common browsers ***/ | |
::selection { | |
background-color: #53c2c4; | |
} | |
/*** Mozilla based browsers ***/ | |
::-moz-selection { | |
background-color: #53c2c4; | |
} | |
/***For Other Browsers ***/ | |
::-o-selection { | |
background-color: #53c2c4; | |
} | |
::-ms-selection { | |
background-color: #53c2c4; | |
} | |
/*** For Webkit ***/ | |
::-webkit-selection { | |
background-color: #53c2c4; | |
} | |
/* GalaxyS5 ipone5 iphone6 */ | |
@media only screen | |
and (min-device-width : 320px) | |
and (max-device-width : 700px) { | |
.message { | |
margin-top: 230px; | |
} | |
.legend { | |
width: 80%; | |
} | |
} | |
/* Nexus5x, iphone6Plus */ | |
@media only screen | |
and (min-device-width : 411px) | |
and (max-device-width : 730px) { | |
.message { | |
margin-top: 250px; | |
} | |
.legend { | |
width: 80%; | |
} | |
} | |
/* Nexus6P */ | |
@media only screen | |
and (min-device-width : 435px) | |
and (max-device-width : 770px) { | |
.message { | |
margin-top: 250px; | |
} | |
.legend { | |
width: 80%; | |
} | |
} | |
/* ipad */ | |
@media only screen | |
and (min-device-width : 768px) | |
and (max-device-width : 1020px) { | |
.message { | |
margin-top: 75px; | |
} | |
.legend { | |
width: 70%; | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment