404: Not Found
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="weatherHourly.css">
</head>
<body>
<select id="metric" name="Select Metric:">
<option value="cloudCover" selected>Percent Cloudy</option>
<option value="normalTemperature">Temperature</option>
<option value="heatIndex">Temperature with Heat Index</option>
<option value="windChill">Temp with Wind Chill</option>
<option value="aveWindSpeed">Ave Wind Speed</option>
</select>
<p id="citySelection"></p>
<div id="instructions">
<p><span id="info">The graph shows the normal temperature, percent cloudy skies, or other metric in a particular city by day of the year and hour of the day, based on the last 30 years of data. Each line represents a different hour of the day. Click on a line or on the legend to highlight a particular hour.</span></p>
<p><span id="info">Data from <a href="https://gis.ncdc.noaa.gov/geoportal/catalog/search/resource/details.page?id=gov.noaa.ncdc:C00824">NOAA</a></span></p>
</div>
<section id="weatherLines">
<p></p>
</section>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.4/moment.min.js"></script>
<script src="viz.js"></script>
<script src="state.js"></script>
<script src="data.js"></script>
<script src="selectionViz.js"></script>
<script src="weatherHourly.js"></script>
</body>
/////////////////////////////
// Manage Data
/////////////////////////////
function dataObj() {
this.inputData = {};
this.filteredData = {};
this.pathData = [];
};
dataObj.prototype.updateData = function(data, state) {
this.inputData = data;
self = this;
for (var i = 0; i < 24; i++) {
this.pathData[i] = [];
};
this.filteredData = this.inputData
.filter(function(d,i) {
if (d.city == state.getCity()) {
self.pathData[+d.hour][moment(d.day).dayOfYear() - 1] = d[state.getMetric()];
return d;
}});
}
dataObj.prototype.updateState = function(state) {
self = this;
for (var i = 0; i < 24; i++) {
this.pathData[i] = [];
};
this.filteredData = this.inputData
.filter(function(d,i) {
if (d.city == state.getCity()) {
self.pathData[+d.hour][moment(d.day).dayOfYear() - 1] = d[state.getMetric()];
return d;
}});
}
dataObj.prototype.getInputData = function(){
return this.inputData;
}
dataObj.prototype.getPathData = function(){
return this.pathData;
}
dataObj.prototype.getFilteredData = function(){
return this.filteredData;
}
df <- read.csv('Downloads/454789.csv', header = TRUE, as.is = TRUE)
require(lubridate)
weather <- df
weather$hour <- hour(weather$Date)
weather$Date <- strptime(weather$DATE, "%Y%m%d %H:%M")
weather$hour <- hour(weather$Date)
weather$day <- as.Date(weather$Date)
weather <- subset(weather, select = -STATION)
weather <- subset(weather, select = -DATE)
weather <- subset(weather, select = -Date)
weather$HLY.TEMP.NORMAL <- weather$HLY.TEMP.NORMAL / 10
weather$HLY.CLOD.PCTOVC <- weather$HLY.CLOD.PCTOVC / 10
stations <- read.csv('station_name.csv', header = TRUE, as.is = TRUE)
names(stations) = c("STATION_NAME", "city")
k <- merge(weather, stations, all.y = TRUE)
k <- subset(k, select = -STATION_NAME)
names(k) <- c("HLYCLODPCTOVC", "HLYTEMPNORMAL", "hour", "day", "city")
write.csv(k, 'learninghtml/webProjects/weatherHourlyAltTooltip/twentyCities.csv', row.names = FALSE, quote = FALSE)
// -------------------------------
// map to select cities
// -------------------------------
setUpMap = function() {
// place city selection text
d3.select('#citySelection')
.style("left", (width + margin.left + 45) + "px")
.style("top", (margin.top - 10) + "px");
// label with current city
document.getElementById('citySelection').innerHTML = "City: " + cState.getCity();
// defines size of map, and location on the screen
var projection = d3.geo.albersUsa()
.translate([width + margin.left + 20, 55])
.scale([200]);
var path = d3.geo.path().projection(projection);
// read in US geometry
d3.json("us.json", function(error, topology) {
// limit to continental US
topology.objects.cb_2013_us_state_20m.geometries =
topology.objects.cb_2013_us_state_20m.geometries.filter(
function(d) {
if (["Alaska", "Hawaii", "Puerto Rico"].indexOf(d.id) == -1) {
return d
}
}
)
// attach path for US boundary
svg.append("path")
.datum(topojson.feature(topology, topology.objects.cb_2013_us_state_20m))
.attr("d", path)
.attr("class", "map");
function mapSelections(k) {
k.on("mouseover", function(d) {
document.getElementById('citySelection').innerHTML = "Click to choose " + d.city;
})
.on("mouseout", function(d) {
document.getElementById('citySelection').innerHTML = "City: " + cState.getCity();
})
}
cities = svg.append("g")
.attr("class", "cities");
cities.selectAll(".citiesBackground")
.data(citiesData)
.enter().append("circle")
.attr("transform", function(d) {
return "translate(" + projection([
d.location.longitude,
d.location.latitude
]) + ")"
})
.attr("r", 7)
.attr("class", "citiesBackground")
.attr("opacity", function(d) {
if (d.city.toLowerCase() == cState.getCity().toLowerCase()) {
return 1
} else {
return 0
}
})
.call(mapSelections)
.on("click", function(d) {
updateCity(d.city);
});
cities.selectAll(".cityForeground")
.data(citiesData)
.enter().append("circle")
.attr("transform", function(d) {
return "translate(" + projection([
d.location.longitude,
d.location.latitude
]) + ")"
})
.attr("r", 3)
.attr("class", "citiesForeground")
.call(mapSelections)
.on("click", function(d) {
updateCity(d.city);
});
});
}
updateCities = function(city) {
// update text above US map
document.getElementById('citySelection').innerHTML = "City: " + city;
// update opacity of background circle for cities on map
d3.selectAll(".citiesBackground")
.data(citiesData)
.attr("opacity", function(d) {
if (d.city.toLowerCase() == city.toLowerCase()) {
return 1
} else {
return 0
}
})
}
// CITIES TO MAP, AND LOCATIONS
// use city lookup tool to get other cities http://bl.ocks.org/zanarmstrong/raw/b7381e04dcded29b2b6f/
// city list
var citiesData = [
{
"city": "DENVER",
"country": "USA",
"location": {
"latitude": 39.858333333333334,
"longitude": -104.66694444444445
}
},
{
"city": "AUSTIN",
"country": "USA",
"location": {
"latitude": 30.194444444444446,
"longitude": -97.66972222222223
}
},
{
"city": "CHARLOTTE",
"country": "USA",
"location": {
"latitude": 35.21388888888889,
"longitude": -80.94305555555556
}
},
{
"city": "CHICAGO",
"country": "USA",
"location": {
"latitude": 41.78583333333333,
"longitude": -87.75222222222222
}
},
{
"city": "COLUMBUS",
"country": "USA",
"location": {
"latitude": 39.99777777777778,
"longitude": -82.89166666666668
}
},
{
"city": "DALLAS",
"country": "USA",
"location": {
"latitude": 32.846944444444446,
"longitude": -96.85166666666666
}
},
{
"city": "DETROIT",
"country": "USA",
"location": {
"latitude": 42.409166666666664,
"longitude": -83.00972222222222
}
},
{
"city": "EL PASO",
"country": "USA",
"location": {
"latitude": 31.849444444444444,
"longitude": -106.38
}
},
{
"city": "HOUSTON",
"country": "USA",
"location": {
"latitude": 29.607222222222223,
"longitude": -95.15861111111111
}
},
{
"city": "INDIANAPOLIS",
"country": "USA",
"location": {
"latitude": 39.717222222222226,
"longitude": -86.29416666666667
}
},
{
"city": "JACKSONVILLE",
"country": "USA",
"location": {
"latitude": 30.49388888888889,
"longitude": -81.68777777777778
}
},
{
"city": "LOS ANGELES",
"country": "USA",
"location": {
"latitude": 33.942499999999995,
"longitude": -118.40805555555556
}
},
{
"city": "MEMPHIS",
"country": "USA",
"location": {
"latitude": 35.04222222222222,
"longitude": -89.97666666666667
}
},
{
"city": "NEW YORK",
"country": "USA",
"location": {
"latitude": 40.63972222222222,
"longitude": -73.77888888888889
}
},
{
"city": "PHILADELPHIA",
"country": "USA",
"location": {
"latitude": 39.871944444444445,
"longitude": -75.24111111111111
}
},
{
"city": "PHOENIX",
"country": "USA",
"location": {
"latitude": 33.535,
"longitude": -112.38305555555554
}
},
{
"city": "SAN ANTONIO",
"country": "USA",
"location": {
"latitude": 29.529444444444444,
"longitude": -98.27888888888889
}
},
{
"city": "SAN DIEGO",
"country": "USA",
"location": {
"latitude": 32.69916666666666,
"longitude": -117.21527777777779
}
},
{
"city": "SAN FRANCISCO",
"country": "USA",
"location": {
"latitude": 37.61888888888889,
"longitude": -122.37472222222222
}
},
{
"city": "SEATTLE",
"country": "USA",
"location": {
"latitude": 47.52972222222222,
"longitude": -122.30194444444444
}
},
{
"city": "MINNEAPOLIS",
"country": "USA",
"location": {
"latitude": 44.88027777777778,
"longitude": -93.21666666666667
}
},
{
"city": "MIAMI",
"country": "USA",
"location": {
"latitude": 25.793055555555558,
"longitude": -80.29055555555556
}
},
{
"city": "GREAT FALLS",
"country": "USA",
"location": {
"latitude": 47.481944444444444,
"longitude": -111.37055555555555
}
},
{
"city": "PORTLAND",
"country": "USA",
"location": {
"latitude": 45.58861111111111,
"longitude": -122.5975
}
},
{
"city": "ATLANTA",
"country": "USA",
"location": {
"latitude": 33.640277777777776,
"longitude": -84.42694444444444
}
},
{
"city": "LAS VEGAS",
"country": "USA",
"location": {
"latitude": 36.08027777777778,
"longitude": -115.15222222222222
}
},
{
"city": "FARGO",
"country": "USA",
"location": {
"latitude": 46.87719,
"longitude": -96.78980
}
}
]
"use strict";
function state(city, metric, yDomain, dimensions, legendRectHeight) {
this.city = city;
this.metric = metric;
this.yDomain = yDomain;
this.selectedHours= [];
this.dimensions = dimensions;
this.scales = {
// x axis
x: d3.scale.linear().domain([0, 365]).range([0, dimensions.width]),
// y axis
y: d3.scale.linear().domain(this.yDomain[this.metric]).range([dimensions.height, 0]),
// color for hours 0-23
color: d3.scale.linear().domain([0, 6, 12, 18, 23]).range(["#0A4D94", "#87B5E6", "#FFC639", "#9F8DE9", "#2C109D"]),
// x axis formatted for tick marks
xTime: d3.time.scale().domain([moment("2010-01-01"), moment("2010-12-31")]).range([0, dimensions.width]),
// for hour legend on right
legendY: d3.scale.linear().domain([0, 23]).range([dimensions.height / 2 + legendRectHeight * 12 + 30, dimensions.height / 2 - legendRectHeight * 12 + 30])
};
};
state.prototype.setCity = function(city) {
this.city = city;
};
state.prototype.getCity = function() {
return this.city;
};
state.prototype.setMetric = function(metric) {
this.metric = metric;
this.scales.y.domain(this.yDomain[this.metric]);
};
state.prototype.getMetric = function() {
return this.metric;
};
state.prototype.getScales = function(){
return this.scales;
}
state.prototype.updateSelectedHourList = function(hour) {
if (this.selectedHours.indexOf(hour) == -1) {
this.selectedHours.push(hour)
} else {
this.selectedHours.splice(this.selectedHours.indexOf(hour), 1);
}
}
state.prototype.getSelectedHours = function() {
return this.selectedHours;
}
state.prototype.getTitle = function(){
var metric = "";
if(this.metric == "normalTemperature"){
metric = "Normal Temperature";
} else if (this.metric == "cloudCover"){
metric = "Percent of Cloud Cover ";
} else if (this.metric == "heatIndex"){
metric = "Heat Index (what temperature it feels like due to humidity) ";
} else if (this.metric == "windChill"){
metric = "Wind Chill (what temperature it feels like due to wind) ";
} else if (this.metric == "aveWindSpeed"){
metric = "Average Wind Speed ";
}
return metric + " in " + this.city + " by hour of day, based on last 30 years";;
}
state.prototype.getYText = function(){
if(["normalTemperature", "heatIndex", "windChill"].indexOf(this.metric) != -1){
return "°F";
} else if (this.metric == "cloudCover"){
return "%";
} else if (this.metric == "aveWindSpeed"){
return "mph";
} else {
return "";
}
}
state.prototype.getYTextAxis = function(){
if(this.metric == "normalTemperature"){
return "Typical Temperature (°F)";
} else if (this.metric == "cloudCover"){
return "Typical Cloud Cover, as % of sky";
} else if (this.metric == "headIndex"){
return "Apparent Temperature, taking into account humidity (°F)";
} else if (this.metric == "windChill"){
return "Apparent Temperature, taking into account wind (°F)";
} else if (this.metric == "aveWindSpeed"){
return "Average Wind Speed, in mph";
} else {
return "";
}
}
state.prototype.showCrosshairs = function(bool){
d3.select(".crosshairs").classed("hidden", !bool);
}
"use strict";
///////////////////////////
// view objects
///////////////////////////
function view() {
this.setUpDot();
this.setUpCrosshairs();
this.voronoi = {};
};
view.prototype.setView = function(state, data, filteredData) {
this.drawLines(state, data);
this.drawAxis(state);
this.drawTitle(state);
this.drawLegend(cState);
this.drawClock();
this.voronoi = d3.geom.voronoi()
.x(function(d) {
return state.getScales().xTime(moment(d.day));
})
.y(function(d) {
return state.getScales().y(d[state.getMetric()]);
})
.clipExtent([[0, 0],[state.dimensions.width, state.dimensions.height]]);
this.drawVoronoi(this.voronoi(filteredData), state);
}
view.prototype.updateView = function(state, data, filteredData) {
this.updateLines(state, data);
this.updateTitle(state);
this.updateAxis(state);
this.updateVoronoi(this.voronoi(filteredData));
};
// manage hour selections //
view.prototype.updateSelectedHoursView = function(state) {
var selectedHours = state.getSelectedHours();
// check if selected line is already selected or not
if (selectedHours.length == 0) {
d3.selectAll(".hourlyLines").attr("opacity", 1).classed("selected", false);
d3.selectAll(".legend rect").attr("opacity", .7).attr("width", 50);
d3.selectAll(".legend text")
.text(function(d, i) {if ([0, 6, 12, 18].indexOf(i) != -1) {return formatHours(i);}});
} else {
d3.selectAll(".hourlyLines")
.attr("opacity", function(d, i) {
if (selectedHours.indexOf(i) != -1) {
return 1
} else {
return .4
}
})
.classed('selected', function(d, i) {
if (selectedHours.indexOf(i) != -1) {
return true
} else {
return false
}
});
d3.selectAll(".legend rect")
.attr("opacity", function(d, i) {
if (selectedHours.indexOf(i) != -1) {
return 1
} else {
return .7
}
})
.attr("width", function(d, i) {
if (selectedHours.indexOf(i) != -1) {
return 60
} else {
return 50
}
});
d3.selectAll(".legend text")
.text(function(d, i) {
if (selectedHours.indexOf(i) != -1) {
return formatHours(i);
} else {
return ""
}
})
}
}
// voronoi for mouseovers //
view.prototype.drawVoronoi = function(data, state) {
var self = this;
// define mouseover and mouseout functions
function vMouseover(d) {
var xpos = state.getScales().xTime(moment(d.day));
var ypos = state.getScales().y(d[state.getMetric()])
var dot = svg.select(".dot")
.classed('hidden', false);
dot.attr("transform", "translate(" + (xpos - 2) + "," + (ypos) + ")");
viz.setCrosshairs(xpos, ypos, d);
cState.showCrosshairs(true);
self.activateClock(d.hour);
}
function vMouseout(d) {
svg.select('.dot').classed('hidden', true);
state.showCrosshairs(false);
self.resetClock();
}
var self = this;
svg.append("g")
.attr("class", "voronoi")
.selectAll("path")
.data(data)
.enter()
.append("path")
.call(definePathAndDatum)
.on("mouseover", vMouseover)
.on("mouseout", vMouseout)
.on('click', function(d, i) {
cState.updateSelectedHourList(+d.hour);
self.updateSelectedHoursView(state);
});
}
view.prototype.updateVoronoi = function(data) {
svg.selectAll('.voronoi')
.selectAll("path")
.data(data).call(definePathAndDatum);
}
// dot to show selected point
view.prototype.setUpDot = function() {
svg.append("g")
.attr("transform", "translate(-100,-100)")
.attr("class", "dot hidden")
.append("circle")
.attr("r", 2);
}
// title
view.prototype.drawTitle = function(state) {
svg.append("text")
.text(state.getTitle())
.attr("x", width / 2)
.attr("y", -20)
.style('text-anchor', 'middle')
.attr("class", "title");
}
view.prototype.updateTitle = function(state) {
svg.select('.title').text(state.getTitle());
}
// ----- AXIS --------
view.prototype.drawAxis = function(state) {
var xAxis = d3.svg.axis()
.tickFormat(d3.time.format("%b"))
.scale(state.getScales().xTime)
.orient('bottom');
var yAxis = d3.svg.axis()
.scale(state.getScales().y)
.orient('left');
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
svg.append('g')
.attr('class', 'y axis')
.call(yAxis)
.append('text')
.attr('class', 'label')
.attr('x', 10)
.attr('y', -40)
.attr("transform", "rotate(-90)")
.text(state.getYTextAxis());
// adjust text labels
svg.selectAll('.x')
.selectAll('text')
.attr('transform', 'translate(' + width / 24 + ',0)')
}
view.prototype.updateAxis = function(state) {
svg.select(".y")
.call(d3.svg.axis().scale(state.getScales().y).orient('left'))
svg.select(".label").text(state.getYTextAxis());
}
// ----- LINES -------
view.prototype.drawLines = function(state, data) {
var lineFunction = d3.svg.line()
.x(function(d, i) {
return state.getScales().x(i);
})
.y(function(d) {
return state.getScales().y(d);
})
.interpolate('basis');
// line graph
svg.selectAll('path')
// need to improve this
.data(data)
.enter()
.append('path')
.attr("d", function(d) {
return lineFunction(d)
})
.attr("stroke", function(d, i) {
return state.getScales().color(i);
})
.attr("class", "hourlyLines");
}
view.prototype.updateLines = function(state, data) {
var lineFunction = d3.svg.line()
.x(function(d, i) {
return state.getScales().x(i);
})
.y(function(d) {
return state.getScales().y(d);
})
.interpolate('basis');
svg.selectAll('.hourlyLines').data(data)
.attr("d", function(d) {
return lineFunction(d)
});
}
// -----------------------------
// set up crosshairs
// -----------------------------
view.prototype.setUpCrosshairs = function() {
// set up hover cross-hairs
var crosshairs = svg.append("g").attr("class", "crosshairs hidden");
crosshairs.append("line")
.attr({
x1: 0,
x2: width,
y1: 0,
y2: 0
})
.classed("xLine", true);
crosshairs.append("line")
.attr({
x1: 0,
x2: 0,
y1: 0,
y2: height
})
.classed("yLine", true);
crosshairs.append("text")
.attr("x", 10)
.attr("y", 0)
.classed("xText", true);
crosshairs.append("text")
.attr("x", 0)
.attr("y", height - 10)
.classed("yText", true);
crosshairs.append("text")
.attr("x", 0)
.attr("y", 0)
.classed("zText", true);
}
view.prototype.setCrosshairs = function(xpos, ypos, d) {
// move crosshairs
d3.select(".xLine")
.attr("transform", "translate(0," + (ypos) + ")")
.attr("x2", xpos)
d3.select(".yLine").attr("transform", "translate(" + (xpos) + ",0)").attr("y1", ypos);
// show text for crosshairs
d3.select(".crosshairs").select('.xText').text(d[cState.getMetric()] + cState.getYText()).attr("y", ypos - 10);
d3.select(".crosshairs").select('.zText').text(formatHours(d.hour)).attr("y", ypos - 10).attr("x", xpos + 8);
d3.select(".crosshairs").select('.yText').text(moment(d.day).format("MMM DD")).attr("x", xpos + 8);
}
// ----- LEGEND -----
view.prototype.drawLegend = function(state) {
var twentyFourHours = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23];
var self = this;
// colored rectangles for legend
var legend = svg.append("g").attr("class", "legend");
legend.selectAll('rect')
.data(twentyFourHours)
.enter()
.append('rect')
.attr('class', 'legend')
.attr("x", width + margin.right / 2 - 25)
.attr("y", function(d, i) {
return state.getScales().legendY(i)
})
.attr("height", legendRectHeight + 1)
.attr("width", 50)
.attr("fill", function(d, i) {
return state.getScales().color(i)
})
.attr("opacity", .7)
.on('click', function(d, i) {
state.updateSelectedHourList(i);
self.updateSelectedHoursView(state);
});
// text labels for hours in legend, show only midnight, 6am, noon, and 6pm
svg.select(".legend").selectAll('text')
.data(twentyFourHours)
.enter()
.append('text')
.attr('x', width + margin.right / 2 + 40)
.attr('y', function(d, i) {
return state.getScales().legendY(i) + legendRectHeight
})
.on('click', function(d, i) {
state.updateSelectedHourList(i);
sView.updateSelectedHoursView(state);
})
.text(function(d, i) {
if ([0, 6, 12, 18].indexOf(i) != -1) {
return formatHours(i);
}
});
}
// ----- CLOCK ------ //
view.prototype.drawClock = function() {
var hour = 0;
var rotate = 360 / 12 * hour;
var clock = svg.append("g").attr("class", "clock");
clock.append("circle")
.attr("stroke", "grey")
.attr("stroke-width", 2)
.attr("fill", "none")
.attr("r", 50)
.attr("cx", clockPosition.x)
.attr("cy", clockPosition.y)
clock.append("circle")
.attr("stroke", "none")
.attr("fill", "grey")
.attr("r", 2)
.attr("cx", clockPosition.x)
.attr("cy", clockPosition.y)
clock.append("line")
.attr("stroke", "grey")
.attr("stroke-width", 2)
.attr("x1", clockPosition.x)
.attr("x2", clockPosition.x)
.attr("y1", clockPosition.y)
.attr("y2", clockPosition.y - 30)
.attr("transform", "rotate(" + rotate + " ," + clockPosition.x + "," + clockPosition.y + ")");
clock.append('text').attr("class", "am partOfDay")
.attr("fill", "grey")
.attr("x", clockPosition.x - 50 - 13.5)
.attr("y", clockPosition.y - 45)
.text("AM")
clock.append('text').attr("class", "pm partOfDay")
.attr("fill", "grey")
.attr("x", clockPosition.x + 50 - 13.5)
.attr("y", clockPosition.y - 45)
.text("PM")
d3.select(".clock").selectAll(".clockHour").data(clockHours).enter().append('text')
.attr("class", "clockHour")
// .attr("fill", function(d){if(hour == d){return "white"} else {return "grey"}})
.attr("fill", "grey")
.attr("font-size", 14)
.attr("x", function(d) {
if ([10, 11, 12].indexOf(d) == -1) {
return clockPosition.x - 3.5
} else {
return clockPosition.x - 3.5 - 4
}
})
.attr("y", clockPosition.y + 5)
.attr("transform", function(d) {
var rotateN = 360 / 12 * d;
return "translate(" + (39 * Math.cos((rotateN - 90) * Math.PI / 180)) + "," + (39 * Math.sin((rotateN - 90) * Math.PI / 180)) + ")"
})
.text(function(d) {
return d
});
}
view.prototype.activateClock = function(hour) {
var partOfDay = 'am'
if (hour > 11) {
hour = hour - 12;
partOfDay = 'pm';
}
// AM vs PM
d3.select("." + partOfDay).attr("fill", "white");
// line
var rotate = 360 / 12 * hour;
d3.select(".clock")
.select("line")
.attr("transform", "rotate(" + rotate + " ," + clockPosition.x + "," + clockPosition.y + ")").attr("stroke", "white");
// time in text
d3.selectAll(".clockHour").data(clockHours)
.attr("fill", function(d) {
if (hour == d % 12) {
return "white"
} else {
return "grey"
}
});
}
view.prototype.resetClock = function() {
d3.select(".clock").selectAll("text").attr("fill", "grey");
d3.select(".clock").selectAll("line").attr("stroke", "grey")
}
// helper function for voronoi mouseovers
function definePathAndDatum(selection){
selection.attr("d", function(d) {
if(typeof(d) != 'undefined'){
return "M" + d.join("L") + "Z"};
})
.datum(function(d) {
if(typeof(d) != 'undefined'){
return d.point;
}})
}
// helper function for formatting hours into normal readable
function formatHours(num) {
if (num == 0) {
return "midnight";
} else if (num < 12) {
return num + "am";
} else if (num == 12) {
return "noon";
} else {
return (num - 12) + "pm";
}
}
body {
background-color: #3F3F3F;
}
p {
color: white;
font-size: 14px;
}
.title {
font: 20px;
fill: white;
}
.hourlyLines {
stroke-width: 1;
fill: none;
}
.selected {
stroke-width: 3;
fill: none;
}
.hidden {
display: none;
}
.axis path,
.axis line {
fill: none;
stroke: white;
shape-rendering: crispEdges;
}
.legend text {
fill: white;
text-anchor: left;
font-size: 14px;
}
.tick {
fill: white;
}
.y .label {
fill: white;
font-size: 14px;
text-anchor: end;
}
.voronoi path {
stroke: none;
fill: none;
pointer-events: all;
}
.crosshairs {
stroke: white;
stroke-width: 0.2;
}
.crosshairs text {
fill: white;
}
#metric {
position: absolute;
top: 10px;
left: 1000px;
width: auto;
height: auto;
}
#instructions {
position: absolute;
top: 10px;
left: 105px;
width: 850px;
height: auto;
padding: 5px 10px;
background-color: rgba(255, 255, 255, 0.7);
-webkit-box-shadow: 0 0 5px rgba(0, 0, 0, 0.7);
-moz-box-shadow: 0 0 5px rgba(0, 0, 0, 0.7);
box-shadow: 0 0 5px rgba(0, 0, 0, 0.7);
pointer-events: none;
}
#instructions p {
margin: 0;
line-height: 20px;
color: black;
font-size: 14px;
}
.dot circle {
fill: rgba(255, 255, 255, 1);
}
.crosshairs {
stroke: white;
stroke-width: 0.2;
}
.crosshairs text {
fill: white;
}
#citySelection {
position: absolute;
}
.map {
fill: white;
stroke: none;
}
.cities {
fill: #FF7F15;
}
.citiesBackground:hover {
opacity: 1;
}
.citiesForeground:hover {
stroke: #FF7F15;
stroke-width: 8;
}
"use strict";
// Data location
var dataFile = "moreCities.csv";
// STANDARD VARIABLES
var margin = {
top: 120,
right: 250,
bottom: 60,
left: 100
},
width = 1200 - margin.left - margin.right,
height = 700 - margin.top - margin.bottom;
// other variables
var legendRectHeight = 13,
clockPosition = {x: width - 60, y: 60},
clockHours = [12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
// initialize state
var cState = new state('SAN FRANCISCO',
"cloudCover",
{normalTemperature: [-5,105], heatIndex: [-5,105], windChill: [-5,105], cloudCover: [0,100], aveWindSpeed: [0,25]},
{width: width, height: height},
legendRectHeight);
// initialize data
var data = new dataObj();
// STANDARD SVG SETUP
var svg = d3.select('#weatherLines')
.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 + ')');
// initialize view
var viz = new view();
// when metric changes, update data and view
d3.select('#metric')
.on("change", function() {
console.log(this.value)
cState.setMetric(this.value);
updateDataAndView();
})
// -----------------------------
// READ IN DATA AND DRAW GRAPH
// -----------------------------
d3.csv(dataFile, function(error, inputData) {
if (error) return console.error(error);
// transform data to useable format (better way to do this?)
data.updateData(inputData, cState);
// draw lines
viz.setView(cState, data.getPathData(), data.getFilteredData());
setUpMap();
});
// ----- helper functions ----- //
// update selected city
function updateCity(city) {
cState.setCity(city);
updateCities(city);
updateDataAndView();
}
// update data and view
function updateDataAndView() {
data.updateState(cState);
viz.updateView(cState, data.getPathData(), data.getFilteredData());
}