Skip to content

Instantly share code, notes, and snippets.

@pinsterdev
Last active January 1, 2023 02:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pinsterdev/b52f2a466477d05576bc to your computer and use it in GitHub Desktop.
Save pinsterdev/b52f2a466477d05576bc to your computer and use it in GitHub Desktop.
Asking prices: urban and commuter

Houses: asking price per sq.ft, selected regions.

Select statistic. Float over lines for more info. Raw data here. (v0.5)

<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 &amp; 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 &amp; 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>
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;
}
"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;
}
<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 &amp; 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 &amp; 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