Commit 3ac4d0ea authored by numeroteca's avatar numeroteca

add interactive d3 cartogram to Donostia VUT

parent f01c002a
Pipeline #23 failed with stages
......@@ -76,6 +76,10 @@ En este cartograma cada barrio está representado por un cuadrado: su área repr
{{% figure src="/images/donostia.ratio.vut.201809.png" alt="Ratio de viviendas de uso turístico por cada 100 viviendas residenciales en barrios de Donostia. Septiembre 2018" title="Cartograma de ratio de viviendas de uso turístico por cada 100 viviendas residenciales en barrios de Donostia. Septiembre 2018" caption="Ratio de viviendas de uso turístico por cada 100 viviendas residenciales en barrios de Donostia. Septiembre 2018." %}}
<div style="position: relative; height: 400px; overflow: hidden;">
<iframe src="/cartogram/index_vut201809.html" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="viviendas de uso turístico por cada 100 viviendas en barrios de Donostia. Septiembre 2018"></iframe>
</div>
## Viviendas de uso turístico por tipo (habitación o apartamento) y barrio
Las habitaciones suponen un 10% del total de las Viviendas de uso turístico tramitadas o en trámite. Si miramos por barrios vemos que son el 9% de la oferta de anuncios en el barrio de Centro y el 6% en Gros y Antiguo.
......
This diff is collapsed.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
margin: 0px;
font-family: "Helvetica Neue", sans-serif;
}
.text-label {
font-size: 14px;
font-weight: 700;
}
.label {
font-size: 20px;
fill: black;
}
.tooltip {
position: absolute;
background: rgba(255, 255, 255, .85);
font-size: 14px;
padding: 10px;
border: 1px solid #ccc;
}
rect:hover {
stroke: black;
stroke-width: 2px;
}
.Gros text, .Centro text{
fill:white !important;
}
.Miracruz-Bidebieta text{
z-index:2;
}
.Ategorrieta-Ulia rect{
z-index:-1;
}
.domain {
display:none;
}
</style>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script src="https://unpkg.com/d3-composite-projections@1.0.2"></script>
<script>
var width = 960,
height = 690,
padding = 4
//var projection = d3.geoConicConformalSpain()
// .translate([width / 2, height / 2])
// .scale(3500)
// .scale(width / 2 / Math.PI)
// .scale(300)
// .translate([width / 2, height / 2])
// Rectangle size
var rectSize = d3.scaleSqrt()
.range([5, 120])
// Font size scale
var fontSize = d3.scaleLinear()
.range([8, 24])
// Position in x line
// set the ranges
var x = d3.scaleLinear()
.domain([0, 4.3])
.range([30,300])
// Party
var color = d3.scaleQuantile()
.domain([0, 4.3])
.range(['#F2FDFC','#C0D7ED','#A2B3E1','#848FD5','#656BC9','#4747BD','#2924B2'])
//console.log(color(0));
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
var tooltip = d3.select("body")
.append("div")
.attr("class", "tooltip")
var cartograma = svg.append('g').attr('id','cartograma');
var escala = svg.append('g').attr('id','escala');
d3.json("barrios.json", function(err, data) {
// move projection inside json to be able to get data
var projection = d3.geoMercator()
.fitSize([width, height], topojson.feature(data, data.objects.barrios));
var path = d3.geoPath()
.projection(projection);
// 1. Features we are painting
barrio = topojson.feature(data, data.objects.barrios).features
// Rect size scale
rectSize.domain(d3.extent(barrio, function(d) {return d.properties.totviviend }))
// 2. Create on each feature the centroid and the positions
barrio.forEach(function(d) {
d.pos = projection(d3.geoCentroid(d))
d.x = d.pos[0]
d.y = d.pos[1]
d.area = rectSize(d.properties.totviviend) / 1 // How we scale
})
// Font size scale
fontSize.domain(d3.extent(barrio, function(d) {return d.area }))
// 3. Collide force
var simulation = d3.forceSimulation(barrio)
.force("x", d3.forceX(function(d) { return d.pos[0] }).strength(.1))
.force("y", d3.forceY(function(d) { return d.pos[1] }).strength(.1))
.force("collide", collide)
// 4. Number of simulations
for (var i = 0; i < 120; ++i) simulation.tick()
// 5. Paint the cartogram
var rect = cartograma.selectAll("g")
.data(barrio)
.enter()
.append("g")
.attr("class", function(d) { return "barrio " + d.properties.BAR_DS_O })
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")" })
.on("mousemove", showTooltip) // AÑADIR EVENTO SHOW TOOLTIP
.on("mouseout", hideTooltip) // OCULTAR TOOLTIP
rect.append("rect")
.each(function(d) {
d3.select(this)
.attr("width", d.area)
.attr("height", d.area)
.attr("x", -d.area / 2)
.attr("y", -d.area / 2)
.attr("fill", color(d.properties.ratio_airbnb_201809))
.attr("stroke", "#ccc")
.attr("rx", 2)
})
rect.append("text")
.each(function(d) {
d3.select(this)
.attr("text-anchor", "middle")
.attr("dy", 3)
.text(d.properties.BAR_DS_O)
.style("fill", "black")
.style("font-size", "13px");
})
rect.append("text")
.each(function(d) {
d3.select(this)
.attr("text-anchor", "middle")
.attr("dy", 23)
.text(d.properties.ratio_airbnb_201809 )
.style("fill", "black")
.style("font-size", "10px")
})
function showTooltip(d) {
// Fill the tooltip
tooltip.html("<div class='tooltip-city'><strong>" + d.properties.BAR_DS_O + "</strong></div>" +
"<table class='tooltip-table'>" +
"<tr class='first-row'>" +
"<td><span class='table-n'>"+ d3.format(",.2f")(d.properties.ratio_airbnb_201809) +"</span> anuncios Airbnb por cada 100 viviendas</td>" +
"</tr>" +
"<tr class='second-row'>" +
"<td>" + d.properties.airbnb_201809 + " anuncios Airbnb</td>" +
"</tr>" +
"<tr class='second-row'>" +
"<td>" + d.properties.vut_201809 + " VUT</td>" +
"</tr>" +
"<tr class='third-row'>" +
"<td>" + d.properties.totviviend + " viviendas</td>" +
"</tr>" +
"</table>")
.style("opacity", 1)
tooltip.style("left", (d3.event.pageX - 20) + "px")
tooltip.style("top", (d3.event.pageY + 23) + "px")
}
function hideTooltip(d) {
// Hide tooltip
tooltip.style("opacity", 0)
}
// x scale
svg.append("g")
.attr("transform", "translate(0,125)")
.call(d3.axisBottom(x));
var vutLine = escala.selectAll("g")
.data(barrio)
.enter()
.append("g")
.attr("class", "barrioline")
.append("circle")
.each(function(d) {
d3.select(this)
.attr("r", 5)
.attr("cx", x(d.properties.ratio_airbnb_201809))
.attr("cy", function(d) { return Math.random() * 50 + 70;})
.attr("fill", color(d.properties.ratio_airbnb_201809))
.attr("stroke", "#000000")
//.attr("opacity","0.5")
})
.on("mousemove", showTooltip) // AÑADIR EVENTO SHOW TOOLTIP
.on("mouseout", hideTooltip) // OCULTAR TOOLTIP
escala.append("text")
.attr("y", 10)
.attr("x", 30)
.attr("dy", ".71em")
.attr("font-size","18")
.text("Anuncios Airbnb por cada 100 viviendas por barrios");
escala.append("text")
.attr("y", 30)
.attr("x", 30)
.attr("dy", ".71em")
.attr("font-size","12")
.text("Anuncios Airbnb en Donostia. Septiembre 2018");
escala.append("text")
.attr("y", 150)
.attr("x", 30)
.attr("dy", ".71em")
.attr("font-size","12")
.text("anunicios Airbnb por cada 100 viviendas");
})
// From http://bl.ocks.org/mbostock/4055889
function collide() {
for (var k = 0, iterations = 4, strength = 0.5; k < iterations; ++k) {
for (var i = 0, n = barrio.length; i < n; ++i) {
for (var a = barrio[i], j = i + 1; j < n; ++j) {
var b = barrio[j],
x = a.x + a.vx - b.x - b.vx,
y = a.y + a.vy - b.y - b.vy,
lx = Math.abs(x),
ly = Math.abs(y),
r = a.area/2 + b.area/2 + padding;
if (lx < r && ly < r) {
if (lx > ly) {
lx = (lx - r) * (x < 0 ? -strength : strength);
a.vx -= lx, b.vx += lx;
} else {
ly = (ly - r) * (y < 0 ? -strength : strength);
a.vy -= ly, b.vy += ly;
}
}
}
}
}
}
</script>
<div id="cartograma"></div>
</body>
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
margin: 0px;
font-family: "Helvetica Neue", sans-serif;
}
.text-label {
font-size: 14px;
font-weight: 700;
}
.label {
font-size: 20px;
fill: black;
}
.tooltip {
position: absolute;
background: rgba(255, 255, 255, .85);
font-size: 14px;
padding: 10px;
border: 1px solid #ccc;
}
rect:hover {
stroke: black;
stroke-width: 2px;
}
.Gros text, .Centro text{
fill:white !important;
}
.Miracruz-Bidebieta text{
z-index:2;
}
.Ategorrieta-Ulia rect{
z-index:-1;
}
.domain {
display:none;
}
</style>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script src="https://unpkg.com/d3-composite-projections@1.0.2"></script>
<script>
var width = 960,
height = 690,
padding = 4
//var projection = d3.geoConicConformalSpain()
// .translate([width / 2, height / 2])
// .scale(3500)
// .scale(width / 2 / Math.PI)
// .scale(300)
// .translate([width / 2, height / 2])
// Rectangle size
var rectSize = d3.scaleSqrt()
.range([5, 120])
// Font size scale
var fontSize = d3.scaleLinear()
.range([8, 24])
// Position in x line
// set the ranges
var x = d3.scaleLinear()
.domain([0, 4.3])
.range([30,300])
// Party
var color = d3.scaleQuantile()
.domain([0, 4.3])
.range(['#F2FDFC','#C0D7ED','#A2B3E1','#848FD5','#656BC9','#4747BD','#2924B2'])
//console.log(color(0));
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
var tooltip = d3.select("body")
.append("div")
.attr("class", "tooltip")
var cartograma = svg.append('g').attr('id','cartograma');
var escala = svg.append('g').attr('id','escala');
d3.json("barrios.json", function(err, data) {
// move projection inside json to be able to get data
var projection = d3.geoMercator()
.fitSize([width, height], topojson.feature(data, data.objects.barrios));
var path = d3.geoPath()
.projection(projection);
// 1. Features we are painting
barrio = topojson.feature(data, data.objects.barrios).features
// Rect size scale
rectSize.domain(d3.extent(barrio, function(d) {return d.properties.totviviend }))
// 2. Create on each feature the centroid and the positions
barrio.forEach(function(d) {
d.pos = projection(d3.geoCentroid(d))
d.x = d.pos[0]
d.y = d.pos[1]
d.area = rectSize(d.properties.totviviend) / 1 // How we scale
})
// Font size scale
fontSize.domain(d3.extent(barrio, function(d) {return d.area }))
// 3. Collide force
var simulation = d3.forceSimulation(barrio)
.force("x", d3.forceX(function(d) { return d.pos[0] }).strength(.1))
.force("y", d3.forceY(function(d) { return d.pos[1] }).strength(.1))
.force("collide", collide)
// 4. Number of simulations
for (var i = 0; i < 120; ++i) simulation.tick()
// 5. Paint the cartogram
var rect = cartograma.selectAll("g")
.data(barrio)
.enter()
.append("g")
.attr("class", function(d) { return "barrio " + d.properties.BAR_DS_O })
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")" })
.on("mousemove", showTooltip) // AÑADIR EVENTO SHOW TOOLTIP
.on("mouseout", hideTooltip) // OCULTAR TOOLTIP
rect.append("rect")
.each(function(d) {
d3.select(this)
.attr("width", d.area)
.attr("height", d.area)
.attr("x", -d.area / 2)
.attr("y", -d.area / 2)
.attr("fill", color(d.properties.ratio_vut_per_100viv_201809))
.attr("stroke", "#ccc")
.attr("rx", 2)
})
rect.append("text")
.each(function(d) {
d3.select(this)
.attr("text-anchor", "middle")
.attr("dy", 3)
.text(d.properties.BAR_DS_O)
.style("fill", "black")
.style("font-size", "13px");
})
rect.append("text")
.each(function(d) {
d3.select(this)
.attr("text-anchor", "middle")
.attr("dy", 23)
.text(d.properties.ratio_vut_per_100viv_201809 )
.style("fill", "black")
.style("font-size", "10px")
})
function showTooltip(d) {
// Fill the tooltip
tooltip.html("<div class='tooltip-city'><strong>" + d.properties.BAR_DS_O + "</strong></div>" +
"<table class='tooltip-table'>" +
"<tr class='first-row'>" +
"<td><span class='table-n'>"+ d3.format(",.2f")(d.properties.ratio_vut_per_100viv_201809) +"</span> VUT por cada 100 viviendas</td>" +
"</tr>" +
"<tr class='second-row'>" +
"<td>" + d.properties.vut_201809 + " VUT</td>" +
"</tr>" +
"<tr class='third-row'>" +
"<td>" + d.properties.totviviend + " viviendas</td>" +
"</tr>" +
"</table>")
.style("opacity", 1)
tooltip.style("left", (d3.event.pageX - 20) + "px")
tooltip.style("top", (d3.event.pageY + 23) + "px")
}
function hideTooltip(d) {
// Hide tooltip
tooltip.style("opacity", 0)
}
// x scale
svg.append("g")
.attr("transform", "translate(0,125)")
.call(d3.axisBottom(x));
var vutLine = escala.selectAll("g")
.data(barrio)
.enter()
.append("g")
.attr("class", "barrioline")
.append("circle")
.each(function(d) {
d3.select(this)
.attr("r", 5)
.attr("cx", x(d.properties.ratio_vut_per_100viv_201809))
.attr("cy", function(d) { return Math.random() * 50 + 70;})
.attr("fill", color(d.properties.ratio_vut_per_100viv_201809))
.attr("stroke", "#000000")
//.attr("opacity","0.5")
})
.on("mousemove", showTooltip) // AÑADIR EVENTO SHOW TOOLTIP
.on("mouseout", hideTooltip) // OCULTAR TOOLTIP
escala.append("text")
.attr("y", 10)
.attr("x", 30)
.attr("dy", ".71em")
.attr("font-size","18")
.text("VUT por cada 100 viviendas por barrios");
escala.append("text")
.attr("y", 30)
.attr("x", 30)
.attr("dy", ".71em")
.attr("font-size","12")
.text("Viviendas de uso turíistico registradas en Donostia. Septiembre 2018");
escala.append("text")
.attr("y", 150)
.attr("x", 30)
.attr("dy", ".71em")
.attr("font-size","12")
.text("VUT por cada 100 viviendas");
})
// From http://bl.ocks.org/mbostock/4055889
function collide() {
for (var k = 0, iterations = 4, strength = 0.5; k < iterations; ++k) {
for (var i = 0, n = barrio.length; i < n; ++i) {
for (var a = barrio[i], j = i + 1; j < n; ++j) {
var b = barrio[j],
x = a.x + a.vx - b.x - b.vx,
y = a.y + a.vy - b.y - b.vy,
lx = Math.abs(x),
ly = Math.abs(y),
r = a.area/2 + b.area/2 + padding;
if (lx < r && ly < r) {
if (lx > ly) {
lx = (lx - r) * (x < 0 ? -strength : strength);
a.vx -= lx, b.vx += lx;
} else {
ly = (ly - r) * (y < 0 ? -strength : strength);
a.vy -= ly, b.vy += ly;
}
}
}
}
}
}
</script>
<div id="cartograma"></div>
</body>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment