Select statistic. Float over lines for more info. Raw data here. (v0.5)
Last active
January 1, 2023 02:25
-
-
Save pinsterdev/b52f2a466477d05576bc to your computer and use it in GitHub Desktop.
Asking prices: urban and commuter
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
<html> | |
<meta charset="utf-8"> | |
<head> | |
<style> | |
body { | |
font: 12px sans-serif; | |
} | |
.hide { | |
display: none; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: black; | |
shape-rendering: crispEdges; | |
} | |
.axis2 .domain { | |
display: none; | |
} | |
path.line { | |
fill: none; | |
stroke-width: 3; | |
} | |
.tick line{ | |
stroke: darkgrey; | |
opacity: 1.0; | |
} | |
.overlay { | |
fill: none; | |
pointer-events: all; | |
} | |
.focus circle { | |
fill: none; | |
stroke: steelblue; | |
} | |
.focus text { | |
font-weight: bold; | |
} | |
#readme { | |
width: 960px; | |
} | |
#chart { | |
width: 960px; | |
height: 500px; | |
} | |
</style> | |
</head> | |
<body> | |
<br><br> | |
<div id="chart" style="float: left;"></div> | |
<div style="float: left; margin: 250px 0px 0px 40px;"> | |
<div> | |
<strong>Statistic</strong> | |
<form name="selS" action=""> | |
<input type="radio" id="radio11" name="selS" onclick="selStat(0)" checked>Urban & commuter<br> | |
<input type="radio" id="radio12" name="selS" onclick="selStat(1)">Urban<br> | |
<input type="radio" id="radio13" name="selS" onclick="selStat(2)">Counties ex. Dublin & Wicklow<br> | |
</form> | |
</div> | |
</div> | |
<br> | |
<div style="clear: both"></div> | |
<div id="readme"></div> | |
<script src="//d3js.org/d3.v3.min.js"></script> | |
<script src="//cdn.rawgit.com/showdownjs/showdown/1.3.0/dist/showdown.min.js"></script> | |
<script src="/pinsterdev/raw/a2fcc47fae884342e190/util.js"></script> | |
<script src="https://api.github.com/gists/0f82dc8380acfd77299724369184a01d?callback=gistDataLoaded"></script> | |
<script> | |
readme(document.getElementById("readme")); | |
var statSelections = [ | |
['south-county-dublin','dublin-south','dublin-county','dublin','dublin-north','wicklow', | |
'north-county-dublin','dublin-west','cork-city','galway-city','kildare','meath','louth','limerick-city'], | |
['south-county-dublin','dublin-south','dublin-county','dublin','dublin-north', | |
'north-county-dublin','dublin-west','cork-city','galway-city','limerick-city'], | |
['carlow','cavan','clare','cork','donegal','galway','kerry','kildare','kilkenny', | |
'laois','leitrim','limerick','longford','louth','mayo','meath','monaghan','offaly', | |
'roscommon','sligo','tipperary','waterford','westmeath','wexford'] | |
]; | |
var colors = [d3.scale.category10(), d3.scale.category10(), d3.scale.category20()]; | |
var chartElem = d3.select("body #chart"); | |
var yAxisLabel = ["Price / sq.ft. (€)"]; | |
var margin = { | |
top : 20, | |
right : 112, | |
bottom : 40, | |
left : 40 | |
}, | |
width = 960 - margin.left - margin.right, | |
height = 500 - margin.top - margin.bottom; | |
// Set the ranges | |
var x = d3.scale.ordinal().rangePoints([0, width]); | |
var y = d3.scale.linear().range([height, 0]); | |
// Define the axes | |
var xAxis = d3.svg.axis().scale(x).orient("bottom") | |
.tickFormat(function(d) { d = d.replace("_"," "); return d.substring(d.indexOf(' ')+1);}) | |
.innerTickSize(-4); | |
var xAxis2 = d3.svg.axis().scale(x).orient("bottom") | |
.tickFormat(function(d) { return d.endsWith("Q1")? d.substring(0, d.indexOf(' ')) : "";}) | |
.innerTickSize(-30); | |
var yAxis = d3.svg.axis().scale(y) | |
.orient("left").ticks(5) | |
.tickSize(-width, 0, 0); | |
var svg, focus, data; | |
// Munge loaded data into usable form | |
var colNames, rowName, allData = mungeData(); | |
var selectableStats = ['commuter','urban','counties'] | |
var selectedStat; | |
window.onpopstate = windowPopState; | |
windowPopState(); | |
var urlinit = updateURLParameter(window.location.pathname, 's', selectableStats[selectedStat]); | |
if (urlinit != window.location.pathname) { | |
window.history.pushState("", "", urlinit); | |
} | |
function windowPopState(event) { | |
selectedStat = parseParamWithDefault('s', selectableStats); | |
document.selS.selS[selectedStat].checked = true; | |
svg = d3.select("svg"); | |
if (svg != null) { | |
svg.remove(); | |
} | |
doSvg(); | |
} | |
/** | |
* Selection changed - remove and redo graph | |
*/ | |
function selStat(s) { | |
selectedStat = s; | |
var url = updateURLParameter(window.location.pathname, 's', selectableStats[s]); | |
window.history.pushState("", "", url); | |
d3.select("svg").remove(); | |
doSvg(); | |
} | |
/** | |
* http://stackoverflow.com/a/10997390/11236 | |
*/ | |
function updateURLParameter(url, param, paramVal){ | |
var newAdditionalURL = ""; | |
var tempArray = url.split("?"); | |
var baseURL = tempArray[0]; | |
var additionalURL = tempArray[1]; | |
var temp = ""; | |
if (additionalURL) { | |
tempArray = additionalURL.split("&"); | |
for (var i=0; i<tempArray.length; i++){ | |
if(tempArray[i].split('=')[0] != param){ | |
newAdditionalURL += temp + tempArray[i]; | |
temp = "&"; | |
} | |
} | |
} | |
var rows_txt = temp + "" + param + "=" + paramVal; | |
return baseURL + "?" + newAdditionalURL + rows_txt; | |
} | |
function doSvg() { | |
data = allData.filter(function (d) { | |
return statSelections[selectedStat].indexOf(d.region) > -1; | |
}); | |
// Adds the svg canvas | |
svg = chartElem.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 + ")"); | |
// Scale the range of the data | |
var color = colors[selectedStat]; | |
color.domain(colNames); | |
x.domain(colNames); | |
var ymin = d3.min(data, function(d) {return d3.min(d.values, function(v){return v.value > 0? v.value : 1000000 })}); | |
var ymax = d3.max(data, function(d) {return d3.max(d.values, function(v){return v.value})}); | |
y.domain([ymin, ymax]); | |
// Add the X Axis | |
svg.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + height + ")") | |
.call(xAxis) | |
.selectAll("text") | |
.style("text-anchor", "start") | |
.attr("dy", "1.15em") | |
; | |
svg.append("g") | |
.attr("class", "x axis2") | |
.attr("transform", "translate(0," + (height+25) + ")") | |
.call(xAxis2) | |
.selectAll("text") | |
.style("text-anchor", "start"); | |
// Add the Y Axis | |
svg.append("g") | |
.attr("class", "y axis") | |
.call(yAxis).append("text") | |
.attr("transform", "rotate(-90)").attr("y", 6).attr("dy", | |
".71em").style("text-anchor", "end").text(yAxisLabel[0]); | |
// Define the line | |
var valueline = d3.svg.line() | |
.x(function(d) { return x(d.period); }) | |
.y(function(d) { return y(d.value); }) | |
.defined(function(d){ return d.value > 0; }); | |
var series = svg.selectAll(".series").data(data).enter() | |
.append("g").attr("class", "series"); | |
series.append("path") | |
.attr("class", "line") | |
.attr("d", function(d) { return valueline(d.values); }) | |
.style({stroke: function(d) {return color(d.region);}}); | |
series.append("text") | |
.datum(function(d) { return d.values[d.values.length - 1]; }) | |
.attr("transform", function(d) {return "translate(" + x(d.period) + "," + | |
y(d.value) + ")"; }) | |
.attr("x", 6) | |
.attr("dy", ".35em") | |
.style({fill: function(d) {return color(d.region);}}) | |
.text(function(d) { return d.region; }); | |
// Add focus circle and overlays | |
focus = svg.append("g") | |
.attr("class", "focus") | |
.style("display", "none"); | |
focus.append("circle") | |
.attr("r", 4.5); | |
focus.append("text") | |
.attr("x", 9) | |
.attr("dy", ".35em"); | |
svg.append("rect") | |
.attr("class", "overlay") | |
.attr("width", width) | |
.attr("height", height) | |
.on("mouseover", function() { focus.style("display", null); }) | |
.on("mouseout", function() { focus.style("display", "none"); }) | |
.on("mousemove", mousemove); | |
} | |
function mousemove() { | |
var mx = d3.mouse(svg[0][0])[0], my = d3.mouse(svg[0][0])[1], | |
i = 0; | |
data[0].values.forEach(function (v, n) { | |
if (Math.abs(x(v.period) - mx) < Math.abs(x(data[0].values[i].period) - mx)) { | |
i = n; | |
} | |
}); | |
var y0 = y.invert(my), | |
j = 0; | |
data.forEach(function (d, n) { | |
if (Math.abs(d.values[i].value - y0) < Math.abs(data[j].values[i].value - y0)) { | |
j = n; | |
} | |
}); | |
var dj = data[j]; | |
focus.attr("transform", "translate(" + x(dj.values[i].period) + "," + | |
y(dj.values[i].value) + ")"); | |
var s1 = (dj.values[i].yoy == "")? | |
dj.values[i].period.replace("_", " ") :dj.values[i].yoy + " yoy"; | |
var str = dj.values[i].value + " (" + s1 + ", " + dj.region + ")"; | |
focus.select("text").text(str); | |
} | |
function mungeData(sheet) { | |
var sheet = extractGistData(gistData["MyhomeData.csv"].content); | |
colNames = sheet.colNames; | |
rowName = sheet.rowName; | |
data = sheet.data; | |
var yoyFormat = d3.format("+.0f"); // formatter for yoy | |
data.forEach(function(d) { | |
d.region = d[rowName]; | |
d.values = colNames.map(function(c, i) { | |
// calculate YoY for years after first | |
var yoy = ((i < 4) || (c.search(" Q") < 0)) ? "" : | |
yoyFormat(d[c] / d[colNames[i - 4]] * 100 - 100) + "%"; | |
return {region: d.region, period : c, value : +d[c], yoy : yoy}; | |
}); | |
}); | |
return data; | |
} | |
</script> | |
</body> | |
</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
body { | |
font: 12px sans-serif; | |
} | |
.hide { | |
display: none; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: black; | |
shape-rendering: crispEdges; | |
} | |
.axis2 .domain { | |
display: none; | |
} | |
path.line { | |
fill: none; | |
stroke-width: 3; | |
} | |
.tick line{ | |
stroke: darkgrey; | |
opacity: 1.0; | |
} | |
.overlay { | |
fill: none; | |
pointer-events: all; | |
} | |
.focus circle { | |
fill: none; | |
stroke: steelblue; | |
} | |
.focus text { | |
font-weight: bold; | |
} | |
#readme { | |
width: 960px; | |
} | |
#chart { | |
width: 960px; | |
height: 500px; | |
} |
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
"use strict"; | |
readme(document.getElementById("readme")); | |
var statSelections = [ | |
['south-county-dublin','dublin-south','dublin-county','dublin','dublin-north','wicklow', | |
'north-county-dublin','dublin-west','cork-city','galway-city','kildare','meath','louth','limerick-city'], | |
['south-county-dublin','dublin-south','dublin-county','dublin','dublin-north', | |
'north-county-dublin','dublin-west','cork-city','galway-city','limerick-city'], | |
['carlow','cavan','clare','cork','donegal','galway','kerry','kildare','kilkenny', | |
'laois','leitrim','limerick','longford','louth','mayo','meath','monaghan','offaly', | |
'roscommon','sligo','tipperary','waterford','westmeath','wexford'] | |
]; | |
var colors = [d3.scale.category10(), d3.scale.category10(), d3.scale.category20()]; | |
var chartElem = d3.select("body #chart"); | |
var yAxisLabel = ["Price / sq.ft. (€)"]; | |
var margin = { | |
top : 20, | |
right : 112, | |
bottom : 40, | |
left : 40 | |
}, | |
width = 960 - margin.left - margin.right, | |
height = 500 - margin.top - margin.bottom; | |
// Set the ranges | |
var x = d3.scale.ordinal().rangePoints([0, width]); | |
var y = d3.scale.linear().range([height, 0]); | |
// Define the axes | |
var xAxis = d3.svg.axis().scale(x).orient("bottom") | |
.tickFormat(function(d) { d = d.replace("_"," "); return d.substring(d.indexOf(' ')+1);}) | |
.innerTickSize(-4); | |
var xAxis2 = d3.svg.axis().scale(x).orient("bottom") | |
.tickFormat(function(d) { return d.endsWith("Q1")? d.substring(0, d.indexOf(' ')) : "";}) | |
.innerTickSize(-30); | |
var yAxis = d3.svg.axis().scale(y) | |
.orient("left").ticks(5) | |
.tickSize(-width, 0, 0); | |
var svg, focus, data; | |
// Munge loaded data into usable form | |
var colNames, rowName, allData = mungeData(); | |
var selectableStats = ['commuter','urban','counties'] | |
var selectedStat; | |
window.onpopstate = windowPopState; | |
windowPopState(); | |
var urlinit = updateURLParameter(window.location.pathname, 's', selectableStats[selectedStat]); | |
if (urlinit != window.location.pathname) { | |
window.history.pushState("", "", urlinit); | |
} | |
function windowPopState(event) { | |
selectedStat = parseParamWithDefault('s', selectableStats); | |
document.selS.selS[selectedStat].checked = true; | |
svg = d3.select("svg"); | |
if (svg != null) { | |
svg.remove(); | |
} | |
doSvg(); | |
} | |
/** | |
* Selection changed - remove and redo graph | |
*/ | |
function selStat(s) { | |
selectedStat = s; | |
var url = updateURLParameter(window.location.pathname, 's', selectableStats[s]); | |
window.history.pushState("", "", url); | |
d3.select("svg").remove(); | |
doSvg(); | |
} | |
/** | |
* http://stackoverflow.com/a/10997390/11236 | |
*/ | |
function updateURLParameter(url, param, paramVal){ | |
var newAdditionalURL = ""; | |
var tempArray = url.split("?"); | |
var baseURL = tempArray[0]; | |
var additionalURL = tempArray[1]; | |
var temp = ""; | |
if (additionalURL) { | |
tempArray = additionalURL.split("&"); | |
for (var i=0; i<tempArray.length; i++){ | |
if(tempArray[i].split('=')[0] != param){ | |
newAdditionalURL += temp + tempArray[i]; | |
temp = "&"; | |
} | |
} | |
} | |
var rows_txt = temp + "" + param + "=" + paramVal; | |
return baseURL + "?" + newAdditionalURL + rows_txt; | |
} | |
function doSvg() { | |
data = allData.filter(function (d) { | |
return statSelections[selectedStat].indexOf(d.region) > -1; | |
}); | |
// Adds the svg canvas | |
svg = chartElem.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 + ")"); | |
// Scale the range of the data | |
var color = colors[selectedStat]; | |
color.domain(colNames); | |
x.domain(colNames); | |
var ymin = d3.min(data, function(d) {return d3.min(d.values, function(v){return v.value > 0? v.value : 1000000 })}); | |
var ymax = d3.max(data, function(d) {return d3.max(d.values, function(v){return v.value})}); | |
y.domain([ymin, ymax]); | |
// Add the X Axis | |
svg.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + height + ")") | |
.call(xAxis) | |
.selectAll("text") | |
.style("text-anchor", "start") | |
.attr("dy", "1.15em") | |
; | |
svg.append("g") | |
.attr("class", "x axis2") | |
.attr("transform", "translate(0," + (height+25) + ")") | |
.call(xAxis2) | |
.selectAll("text") | |
.style("text-anchor", "start"); | |
// Add the Y Axis | |
svg.append("g") | |
.attr("class", "y axis") | |
.call(yAxis).append("text") | |
.attr("transform", "rotate(-90)").attr("y", 6).attr("dy", | |
".71em").style("text-anchor", "end").text(yAxisLabel[0]); | |
// Define the line | |
var valueline = d3.svg.line() | |
.x(function(d) { return x(d.period); }) | |
.y(function(d) { return y(d.value); }) | |
.defined(function(d){ return d.value > 0; }); | |
var series = svg.selectAll(".series").data(data).enter() | |
.append("g").attr("class", "series"); | |
series.append("path") | |
.attr("class", "line") | |
.attr("d", function(d) { return valueline(d.values); }) | |
.style({stroke: function(d) {return color(d.region);}}); | |
series.append("text") | |
.datum(function(d) { return d.values[d.values.length - 1]; }) | |
.attr("transform", function(d) {return "translate(" + x(d.period) + "," + | |
y(d.value) + ")"; }) | |
.attr("x", 6) | |
.attr("dy", ".35em") | |
.style({fill: function(d) {return color(d.region);}}) | |
.text(function(d) { return d.region; }); | |
// Add focus circle and overlays | |
focus = svg.append("g") | |
.attr("class", "focus") | |
.style("display", "none"); | |
focus.append("circle") | |
.attr("r", 4.5); | |
focus.append("text") | |
.attr("x", 9) | |
.attr("dy", ".35em"); | |
svg.append("rect") | |
.attr("class", "overlay") | |
.attr("width", width) | |
.attr("height", height) | |
.on("mouseover", function() { focus.style("display", null); }) | |
.on("mouseout", function() { focus.style("display", "none"); }) | |
.on("mousemove", mousemove); | |
} | |
function mousemove() { | |
var mx = d3.mouse(svg[0][0])[0], my = d3.mouse(svg[0][0])[1], | |
i = 0; | |
data[0].values.forEach(function (v, n) { | |
if (Math.abs(x(v.period) - mx) < Math.abs(x(data[0].values[i].period) - mx)) { | |
i = n; | |
} | |
}); | |
var y0 = y.invert(my), | |
j = 0; | |
data.forEach(function (d, n) { | |
if (Math.abs(d.values[i].value - y0) < Math.abs(data[j].values[i].value - y0)) { | |
j = n; | |
} | |
}); | |
var dj = data[j]; | |
focus.attr("transform", "translate(" + x(dj.values[i].period) + "," + | |
y(dj.values[i].value) + ")"); | |
var s1 = (dj.values[i].yoy == "")? | |
dj.values[i].period.replace("_", " ") :dj.values[i].yoy + " yoy"; | |
var str = dj.values[i].value + " (" + s1 + ", " + dj.region + ")"; | |
focus.select("text").text(str); | |
} | |
function mungeData(sheet) { | |
var sheet = extractGistData(gistData["MyhomeData.csv"].content); | |
colNames = sheet.colNames; | |
rowName = sheet.rowName; | |
data = sheet.data; | |
var yoyFormat = d3.format("+.0f"); // formatter for yoy | |
data.forEach(function(d) { | |
d.region = d[rowName]; | |
d.values = colNames.map(function(c, i) { | |
// calculate YoY for years after first | |
var yoy = ((i < 4) || (c.search(" Q") < 0)) ? "" : | |
yoyFormat(d[c] / d[colNames[i - 4]] * 100 - 100) + "%"; | |
return {region: d.region, period : c, value : +d[c], yoy : yoy}; | |
}); | |
}); | |
return data; | |
} | |
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
<html> | |
<meta charset="utf-8"> | |
<head> | |
<link rel="stylesheet" type="text/css" href="main.css"> | |
</head> | |
<body> | |
<br><br> | |
<div id="chart" style="float: left;"></div> | |
<div style="float: left; margin: 250px 0px 0px 40px;"> | |
<div> | |
<strong>Statistic</strong> | |
<form name="selS" action=""> | |
<input type="radio" id="radio11" name="selS" onclick="selStat(0)" checked>Urban & commuter<br> | |
<input type="radio" id="radio12" name="selS" onclick="selStat(1)">Urban<br> | |
<input type="radio" id="radio13" name="selS" onclick="selStat(2)">Counties ex. Dublin & Wicklow<br> | |
</form> | |
</div> | |
</div> | |
<br> | |
<div style="clear: both"></div> | |
<div id="readme"></div> | |
<script src="//d3js.org/d3.v3.min.js"></script> | |
<script src="//cdn.rawgit.com/showdownjs/showdown/1.3.0/dist/showdown.min.js"></script> | |
<script src="/pinsterdev/raw/a2fcc47fae884342e190/util.js"></script> | |
<script src="https://api.github.com/gists/0f82dc8380acfd77299724369184a01d?callback=gistDataLoaded"></script> | |
<script src="main.js"></script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment