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 | .. |