Skip to content

Instantly share code, notes, and snippets.

@ypetya
Last active October 27, 2019 09:20
Show Gist options
  • Save ypetya/328fb23ab9c557990ed59c281ae6a776 to your computer and use it in GitHub Desktop.
Save ypetya/328fb23ab9c557990ed59c281ae6a776 to your computer and use it in GitHub Desktop.
vadallomany

Vadallomany 1995 - 2018

ksh

Nagyvadak, aprovadak

Szamlalas februar vege

const
title = {height: 30, text: 'Vadállomány - Kilőtt vad [ezer db]'},
legend = {height: 50},
margin = {top: 20, right: 20, bottom: 30, left: 40},
bar = {marginRatio: 0.8, paddingRatio: 0.1},
width = document.body.clientWidth - margin.left - margin.right,
height = document.documentElement.clientHeight,
y = d3.scaleLinear().range([
height - margin.top - margin.bottom - legend.height - title.height, // the bottom
margin.top + margin.bottom + legend.height]), // the top
xOuter = d3.scaleBand().range([0, width]),
xInner = d3.scaleBand(),
colorScale = d3.scaleBand().range([0, 1]),
colors = x => d3.schemeTableau10[Math.floor(10 * colorScale(x))],
xAxis = d3.axisBottom().scale(xOuter),
yAxis = d3.axisLeft().scale(y).tickFormat(d => `${d}`),
isNumber = v => Number.isFinite(Number(v)),
rowKeys = data => data && Object.keys(data).filter(isNumber),
sanitize = str => typeof str == 'string' ? str.replace(/,/g, '') : str,
rowVals = rowData => Object.values(rowData).map(sanitize).filter(isNumber),
dataRows1 = [2, 3, 4, 5, 6, 8, 9, 10],
breeds = data => data.filter((d, i) => (dataRows1.includes(i))).map(d => d.megnevezes), // 2-6, 8-10
maxVal = data => {
const rowMaxArr = data.map(d => Math.max(...rowVals(d)));
return Math.max(...rowMaxArr);
};
const svgNode = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom + legend.height + title.height)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
svgNode.append('text')
.text(title.text)
.attr('x', width / 2)
.attr('y', margin.top / 2)
.attr('text-anchor', 'middle')
.attr('style', 'font-face:Times;font-size:20px;');
// global data
let legendData = null;
let labels;
// fix nodes
let legendNode;
const chartNode = svgNode
.append("g")
.attr('class', 'chart');
const tooltipNode = d3.select('body')
.append('div')
.attr('class', 'tooltip')
.style('opacity', 0),
tooltipContent = (text, value, killed) => `${text}<br/>` +
`<span style='color:green'>${value}</span> | <span style='color:red'>${killed}</span>`;
const selectedData = d => {
const selected = legendData.filter(l => l.selected).map(l => l.name);
return d.filter(d => selected.includes(d.megnevezes));
};
const createLegend = (data) => {
legendData = breeds(data).map((d, i) => ({name: d, selected: i < 2}));
labels = rowKeys(data[dataRows1[0]]);
xOuter.domain(labels);
colorScale.domain(breeds(data));
legendNode = svgNode.append('g')
.attr('class', 'legend')
.attr('transform', `translate(${margin.left},${title.height})`);
};
const drawLegend = data => {
const breedsJoin = legendNode.selectAll('.breed')
.data(legendData);
const breed = breedsJoin
.enter()
.append('g')
.attr('class', 'breed');
breedsJoin
.join()
.select('circle')
.attr('style', d => {
const fillColor = d.selected ? colors(d.name) : 'none';
const strokeColor = colors(d.name);
return `fill:${fillColor};fill-opacity:0.6;stroke:${strokeColor};stroke-width:1`;
});
breed.on('click', d => {
d.selected = !d.selected;
draw(data);
drawLegend(data);
});
breed
.append('circle')
.attr('cx', 5).attr('cy', 5)
.attr('r', 5)
.attr('style', d => {
const fillColor = d.selected ? colors(d.name) : 'none';
const strokeColor = colors(d.name);
return `fill:${fillColor};fill-opacity:0.6;stroke:${strokeColor};stroke-width:1`;
})
.attr('transform', 'translate(0,-8)');
breed.append('text')
.text(d => d.name)
.attr('transform', 'translate(15,0)');
// adjust legend nodes width to text width
breed.call(nodeList => {
let lastLabelWidth = 0;
nodeList.nodes().forEach(node => {
const n = d3.select(node);
n.attr('transform', `translate(${lastLabelWidth},0)`);
lastLabelWidth += n.select('text').node().getComputedTextLength() + 15 + 15;
});
})
};
const draw = (data) => {
// filteredData -> breeds selected on legend
const filteredData = selectedData(data);
xInner.range([0, xOuter.bandwidth() * bar.marginRatio]).domain(filteredData.map(f => f.megnevezes));
y.domain([0, maxVal(filteredData)]); // always recalc
// Always redraw Y axis REMOVE + APPEND
svgNode.selectAll('g.axis').remove();
svgNode.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".7em")
.style("text-anchor", "end");
svgNode.append("g")
.attr("class", "x axis")
// do we need this ?
.attr("transform", `translate(0,${y(0)})`)
.call(xAxis);
const years = rowKeys(filteredData[0]);
const nodeByYear = chartNode
.selectAll('g.year-node')
.data(years)
.join('g')
.attr('class', 'year-node')
.attr('transform',
(d, i) => `translate(${xOuter.bandwidth() * i + (xOuter.bandwidth() * bar.paddingRatio / 2)},0)`);
const barsData = filteredData.slice(0, filteredData.length / 2),
capsData = filteredData.slice(filteredData.length / 2);
const barStyle = opacity => d =>
`fill:${colors(d.megnevezes)};` +
`stroke:${colors(d.megnevezes)};stroke-width:1px;` +
`stroke-opacity:0.6;fill-opacity:${opacity};`;
nodeByYear
.selectAll('rect')
.data(
year => barsData.map((f, i) => ({
megnevezes: f.megnevezes,
val: isNumber(f[year]) ? f[year] : 0,
killed: capsData[i][year],
year: year
})),
d => d.megnevezes // the key function !!!
)
.join(enter =>
enter.append('rect')
.attr('class', 'bar')
.attr('x', d => xInner(d.megnevezes) + xOuter.bandwidth() * bar.paddingRatio)
.attr('width', xInner.bandwidth() * bar.marginRatio)
.attr('style', barStyle(0.6))
.attr('y', y(0))
.attr('height', 0)
.call(
enter => enter
.transition()
.duration(1000)
.attr('y', d => y(d.val))
.attr('height', d => y(0) - y(d.val))
),
update => update
.call(
update => update
.transition()
.duration(1000)
.attr('x', d => xInner(d.megnevezes) + xOuter.bandwidth() * bar.paddingRatio)
.attr('width', xInner.bandwidth() * bar.marginRatio)
.attr('y', d => y(d.val))
.attr('height', d => y(0) - y(d.val))
),
exit => exit
.call(
exit => exit
.transition()
.duration(1000)
.attr('width', xInner.bandwidth() * bar.marginRatio)
.attr('y', y(0))
.attr('height', 0)
.remove()
)
)
.on('mouseover', function (d, i) {
d3.select(this)
.attr('style', barStyle(0.4));
let [x1, y1] = d3.mouse(this);
// because we used transform on nodeByYear
x1 += xOuter(d.year);
tooltipNode
.html(tooltipContent(d.megnevezes, d.val, d.killed))
.style('left', `${x1}px`) // hard to not miss the px
.style('top', `${y1}px`)
.call(node => node.transition().duration(200)
.style('opacity', 0.9));
})
.on('mouseout', function (d) {
d3.select(this)
.transition()
.duration(200)
.attr('style', barStyle(0.6));
tooltipNode.transition().duration(500)
.style('opacity', 0);
})
;
nodeByYear
.selectAll('line')
.data(
year => capsData.map(f => ({
megnevezes: f.megnevezes,
val: isNumber(f[year]) ? f[year] : 0
})),
d => d.megnevezes
)
.join(
enter => enter.append('line')
.attr('class', 'cap')
.attr('x1', d => xInner(d.megnevezes) + xOuter.bandwidth() * bar.paddingRatio)
.attr('x2', d => xInner(d.megnevezes) + xOuter.bandwidth() * bar.paddingRatio +
xInner.bandwidth() * bar.marginRatio)
.attr('y1', d => y(0))
.attr('y2', d => y(0))
.attr('style',
d =>
`stroke:red;stroke-width:3px;` +
'stroke-opacity:0.6;fill-opacity:0.6;')
.call(enter => enter
.transition()
.duration(1000)
.attr('x1', d => xInner(d.megnevezes) + xOuter.bandwidth() * bar.paddingRatio)
.attr('x2', d => xInner(d.megnevezes) + xOuter.bandwidth() * bar.paddingRatio +
xInner.bandwidth() * bar.marginRatio)
.attr('y1', d => y(d.val))
.attr('y2', d => y(d.val))
)
,
update => update.call(
update => update.transition()
.duration(1000)
.attr('x1', d => xInner(d.megnevezes) + xOuter.bandwidth() * bar.paddingRatio)
.attr('x2', d => xInner(d.megnevezes) + xOuter.bandwidth() * bar.paddingRatio +
xInner.bandwidth() * bar.marginRatio)
.attr('y1', d => y(d.val))
.attr('y2', d => y(d.val))
),
exit => exit.call(
exit => exit.transition()
.duration(1000)
.attr('y1', d => y(0))
.attr('y2', d => y(0))
.remove()
)
);
};
// Let's kick-off the party
d3.csv("vadallomany.csv").then(data => {
createLegend(data);
draw(data);
drawLegend(data);
});
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.y.axis path {
display: none;
}
g.legend {
cursor: pointer;
}
.breed circle {
filter: drop-shadow( 1px 1px 1px rgba(0, 0, 0, .7));
}
div.tooltip {
position: absolute;
text-align: center;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
font-weight: bolder;
}
svg {
user-select: none;
}
</style>
<body>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="chart.js"></script>
</body>
megnevezes 1990 1995 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018
$Vadállomány, ezer db (február)
Nagyvad
gímszarvas 42.2 50.1 77.8 82.6 82.6 82.6 78.5 74.1 69.2 76.9 85.1 87.1 92.6 94.1 96.5 101.6 102.1 101.4 99.8 101.5 111.5
dámszarvas 10.9 16.0 20.6 22.1 20.9 20.9 20.6 21.6 21.8 23.9 25.9 26.7 30.0 30.5 33.2 32.7 35.1 33.8 33.7 34.7 35.6
őz 173.1 233.4 293.8 319.5 324.4 324.4 320.9 316.2 310.9 312.0 340.4 349.6 366.6 355.7 365.6 375.1 370.6 359.2 357.3 361.5 381.6
muflon 10.6 8.5 10.5 9.6 9.3 9.3 7.9 8.3 8.8 10.1 9.9 10.5 11.0 11.5 12.3 13.2 12.6 11.9 12.1 10.8 13.3
vaddisznó 38.8 39.4 76.1 91.1 86.6 86.6 77.8 78.1 77.7 77.8 95.6 99.3 106.7 105.8 109.8 120.2 105.2 105.7 101.7 102.6 105.2
Apróvad
mezei nyúl 795.7 597.5 514.8 582.5 630.9 630.8 535.1 520.8 535.2 472.1 522.9 523.8 538.7 454.5 497.2 480.0 445.6 461.0 414.5 387.6 381.7
fácán 1,099.3 784.5 789.8 824.8 880.6 880.6 691.0 737.4 796.9 723.7 790.4 795.4 761.7 612.8 678.8 611.2 560.1 630.4 581.5 558.8 556.2
fogoly 50.5 73.2 65.9 51.4 50.8 50.9 40.0 41.0 42.7 38.0 39.8 36.6 32.4 24.8 22.8 17.7 16.3 16.0 14.1 12.4 11.6
$Kilőtt vad, ezer db
Nagyvad
gímszarvas 35.2 21.8 29.0 34.1 41.7 41.6 39.1 36.7 32.0 34.0 36.2 39.3 41.1 47.7 47.7 53.1 53.7 53.6 55.1 58.1 ..
dámszarvas 4.6 5.5 6.0 6.8 9.0 6.6 7.6 8.9 8.4 9.3 9.7 10.5 10.8 11.7 10.1 12.3 13.9 11.6 14.3 13.7 ..
őz 41.5 37.9 52.8 61.9 72.5 76.6 85.6 89.9 80.6 79.5 86.1 89.8 88.6 93.1 96.3 100.4 111.5 114.5 113.7 113.6 ..
muflon 3.0 2.3 2.3 2.8 3.7 2.5 2.5 2.8 2.3 2.6 2.9 3.1 3.4 3.5 3.4 3.2 4.1 3.3 3.6 3.6 ..
vaddisznó 46.7 35.0 67.7 94.5 94.0 72.1 77.2 79.5 64.4 94.0 94.4 111.2 112.4 128.9 144.7 128.4 135.8 125.6 143.1 158.1 ..
Apróvad
mezei nyúl 128.5 132.4 85.2 170.8 132.1 102.4 104.3 105.1 89.3 95.7 104.0 106.8 78.8 98.9 85.1 76.5 84.7 69.1 62.8 54.7 ..
fácán 812.2 543.5 430.3 536.9 558.5 391.3 439.1 474.0 361.6 432.3 420.8 377.7 306.5 375 328.6 331.4 399.4 369.8 382 378.5 ..
fogoly 4.0 3.1 1.2 1.4 2.1 2.4 2.9 2.9 2.9 3.8 2.3 3.8 2.2 2.2 2.8 1.5 3.2 2.4 3.7 2.7 ..
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment