Skip to content

Instantly share code, notes, and snippets.

@pinsterdev
Last active Jun 19, 2020
Embed
What would you like to do?
PPR number of sales

PPR volume and median sale price.

Select region, period, and volumes / median prices. Float over bars for more info. Recent periods may not be complete. Raw data here. (v0.9)

<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>Region</strong>
<form name="selR" action="">
<input type="radio" id="radio11" name="selR" onclick="selRegion(0)" checked>Ireland<br>
<input type="radio" id="radio12" name="selR" onclick="selRegion(1)">Dublin<br>
<input type="radio" id="radio13" name="selR" onclick="selRegion(2)">ex-Dublin<br>
</form>
</div>
<div>
<strong>by period</strong>
<form name="selP" action="">
<input type="radio" id="radio21" name="selP" onclick="selPeriod(0)" checked>Month<br>
<input type="radio" id="radio22" name="selP" onclick="selPeriod(1)">Quarter<br>
</form>
</div>
<div>
<strong>statistic</strong>
<form name="selS" action="">
<input type="radio" id="radio31" name="selS" onclick="selStat(0)" checked>Volume<br>
<input type="radio" id="radio32" name="selS" onclick="selStat(1)">Median Price<br>
</form>
</div>
<div><br><h1><span id="lastupdate" style="line-height: 0px;"></span>
</h1><p><span style="line-height: 0px;"><small>(LAST UPDATE)</small></span></div>
</div>
<br>
<div style="clear: both"></div>
<div id="readme"></div>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.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>
body {
font: 12px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
.tick line{
stroke: lightgrey;
opacity: 0.4;
}
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}
.d3-tip .h {
font-weight: bold;
}
.d3-tip .vm {
font-weight: bold;
color: #ff8080;
}
.d3-tip .vp {
font-weight: bold;
color: lightgreen;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "\25BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
#readme {
width: 960px;
}
#chart {
width: 960px;
height: 500px;
}
readme(document.getElementById("readme"));
var chartElem = d3.select("body #chart");
var yAxisLabel = ["Number of PPR Sales","PPR Median Price (€)"];
//Pop up tip when floating over bar
var tip = d3.tip().attr('class', 'd3-tip').offset([ -10, 0 ]).html(tipHtml);
var margin = {
top : 20,
right : 60,
bottom : 30,
left : 40
}, width = 960 - margin.left - margin.right, height = 500 - margin.top
- margin.bottom;
var x0 = d3.scale.ordinal().rangeRoundBands([ 0, width ], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear().range([ height, 0 ]);
var color = d3.scale.ordinal().range(pinColours());
var xAxis = d3.svg.axis().scale(x0).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left")
.tickFormat(d3.format(".2s"))
.tickSize(-width, 0, 0);
var dataSets = mungeData(), colNames, rowName, data;
// default selections
var selectedRegion = parseParamWithDefault('r',['ireland','dublin', 'exdublin']);
var selectedPeriod = parseParamWithDefault('p',['month','quarter']);
var selectedStat = parseParamWithDefault('s',['volume','median']);
document.selR.selR[selectedRegion].checked = true;
document.selP.selP[selectedPeriod].checked = true;
document.selS.selS[selectedStat].checked = true;
selectionChanged();
/**
* Selection changed - remove and redo graph
*/
function selRegion(r) {
selectedRegion = r;
selectionChanged();
}
function selPeriod(p) {
selectedPeriod = p;
selectionChanged();
}
function selStat(s) {
selectedStat = s;
selectionChanged();
}
function selectionChanged() {
d3.select("svg").remove();
var selection = 6 * selectedStat + 2 * selectedRegion + selectedPeriod;
data = dataSets[selection];
doSvg();
}
function doSvg() {
d3.select("#lastupdate").html(rowName.replace("X","").replace(/_/g, "-"));
var svg = chartElem.append("svg").attr("width",
width + margin.left + margin.right).attr("height",
height + margin.top + margin.bottom).call(tip).append("g").attr(
"transform", "translate(" + margin.left + "," + margin.top + ")");
x0.domain(data.map(function(d) {
return d[rowName];
}));
x1.domain(colNames).rangeRoundBands([ 0, x0.rangeBand() ]);
y.domain([ 0, d3.max(data, function(d) {
return d3.max(d.cols, function(d) {
return d.value;
});
}) ]);
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("transform", "rotate(-90)").attr("y", 6).attr("dy",
".3em").style("text-anchor", "end").text(yAxisLabel[selectedStat]);
var state = svg.selectAll(".Xlabel").data(data).enter().append("g")
.attr("class", "Xlabel").attr("transform", function(d) {
return "translate(" + x0(d[rowName]) + ",0)";
});
state.selectAll("rect").data(function(d) {
return d.cols;
}).enter().append("rect").attr("width", x1.rangeBand()).attr("x",
function(d) {
return x1(d.name);
}).attr("y", function(d) {
return y(d.value);
}).attr("height", function(d) {
return height - y(d.value);
}).style("fill", function(d) {
return color(d.name);
}).on('mouseover', tip.show).on('mouseout', tip.hide);
var legend = svg.selectAll(".legend").data(colNames.slice().reverse())
.enter().append("g").attr("class", "legend").attr("transform",
function(d, i) {
return "translate(5," + i * 20 + ")";
});
legend.append("rect").attr("x", width + margin.left).attr("width", 18).attr(
"height", 18).style("fill", color);
legend.append("text").attr("x", width + margin.left - 6).attr("y", 9).attr("dy",
".35em").style("text-anchor", "end").text(function(d) {
return d;
});
}
function tipHtml(d) {
var s = "<span class='h'>" + d.name + ":</span> <span class='v'>" + d.value + "</span>";
// Include YoY value if any
if (d.yoy != "") {
s += "<br><span class='h'>YoY:</span> <span class='"
+ (d.yoy.charAt(0) == '+' ? "vp" : "vm") + "'>" + d.yoy + "</span>";}
return s;
}
function mungeData(sheet) {
var dataSets = [];
var yoyFormat = d3.format("+.0f");
var pprNames = ["VolIrlM","VolIrlQ","VolDubM","VolDubQ","VolXdbM","VolXdbQ",
"MedIrlM","MedIrlQ","MedDubM","MedDubQ","MedXdbM","MedXdbQ"];
pprNames.forEach(function (n) {
var pprName = "PPR" + n + ".csv";
var sheet = extractGistData(gistData[pprName].content);
colNames = sheet.colNames;
rowName = sheet.rowName;
var data = sheet.data;
data.forEach(function(d) {
d.cols = colNames.map(function(name) {
var idx = colNames.indexOf(name);
// calculate YoY for years after first
var value = +d[name].replace(',','');
var yoy = idx > 0 ? yoyFormat(value / d[colNames[idx - 1]].replace(',','')
* 100 - 100)
+ "%" : "";
return {
name : name,
value : value,
yoy : yoy
};
});
});
dataSets.push(data);
});
return dataSets;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment