diff options
author | emkael <emkael@tlen.pl> | 2021-01-19 12:17:22 +0100 |
---|---|---|
committer | emkael <emkael@tlen.pl> | 2021-01-19 12:17:22 +0100 |
commit | 5545d87ba4e7a59ef0e5d128d16b4bde9b716cbc (patch) | |
tree | ea30dd1d7204fdbe5e3be3ea0567c65f1f27f211 | |
parent | a90be36660d84d83b070dd0c1e95ee197ead58d1 (diff) |
First version of all 4 charts
-rw-r--r-- | result/index.html | 95 | ||||
-rw-r--r-- | result/krafcik.css | 73 | ||||
-rw-r--r-- | result/krafcik.js | 323 |
3 files changed, 491 insertions, 0 deletions
diff --git a/result/index.html b/result/index.html new file mode 100644 index 0000000..4a8009c --- /dev/null +++ b/result/index.html @@ -0,0 +1,95 @@ + +<!doctype html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>Polski K***cik</title> + + <!-- Bootstrap core CSS --> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous"> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/11.0.2/css/bootstrap-slider.min.css" integrity="sha512-3q8fi8M0VS+X/3n64Ndpp6Bit7oXSiyCnzmlx6IDBLGlY5euFySyJ46RUlqIVs0DPCGOypqP8IRk/EyPvU28mQ==" crossorigin="anonymous" /> + + <link href="krafcik.css" rel="stylesheet" /> + + </head> + <body> + + <main class="container"> + + <h1>Polski Krafcik <span class="lead">2012-2020</span></h1> + + + <form id="filters"> + <div id="date-filter" class="form-group"> + <label>Zakres dat</label> + <input type="text" name="date" data-slider-id="date-slider" /> + </div> + <div id="brewery-filter" class="form-group"> + <label>Browary</label><br /> + <div class="form-check"> + <input class="form-check-input" id="brewery-all" type="checkbox" checked="checked" /> + <label class="form-check-label" for="brewery-all"> + Wszystkie + </label> + </div> + </div> + </form> + + <ul class="nav nav-tabs" id="tab-menu" role="tablist"> + <li class="nav-item"> + <a class="nav-link active" id="styles-tab" data-toggle="tab" href="#styles" role="tab" aria-controls="styles" aria-selected="true"> + Rozkład stylów + </a> + </li> + <li class="nav-item"> + <a class="nav-link" id="abv-ratings-tab" data-toggle="tab" href="#abv-ratings" role="tab" aria-controls="abv-ratings" aria-selected="false"> + ABV / l. ocen + </a> + </li> + <li class="nav-item"> + <a class="nav-link" id="abv-average-tab" data-toggle="tab" href="#abv-average" role="tab" aria-controls="abv-average" aria-selected="false"> + ABV / ocena + </a> + </li> + <li class="nav-item"> + <a class="nav-link" id="ratings-average-tab" data-toggle="tab" href="#ratings-average" role="tab" aria-controls="ratings-average" aria-selected="false"> + L. ocen / ocena + </a> + </li> + </ul> + <div class="tab-content" id="tab-content"> + <div class="tab-pane fade show active" id="styles" role="tabpanel" aria-labelledby="styles-tab"> + <canvas id="styles-chart" width="100%"></canvas> + </div> + <div class="tab-pane fade" id="abv-ratings" role="tabpanel" aria-labelledby="abv-ratings-tab"> + <canvas id="abv-ratings-chart" width="100%"></canvas> + </div> + <div class="tab-pane fade" id="abv-average" role="tabpanel" aria-labelledby="abv-average-tab"> + <canvas id="abv-average-chart" width="100%"></canvas> + </div> + <div class="tab-pane fade" id="ratings-average" role="tabpanel" aria-labelledby="ratings-average-tab"> + <canvas id="ratings-average-chart" width="100%"></canvas> + </div> + </div> + + </main><!-- /.container --> + + <template id="brewery-template"> + <div class="form-check form-check-inline my-2"> + <input class="form-check-input" type="checkbox" name="brewery" checked="checked" value="" /> + <label class="form-check-label" for="" title=""> + <img src="" /> + </label> + </div> + </template> + + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script> + <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/11.0.2/bootstrap-slider.min.js" integrity="sha512-f0VlzJbcEB6KiW8ZVtL+5HWPDyW1+nJEjguZ5IVnSQkvZbwBt2RfCBY0CBO1PsMAqxxrG4Di6TfsCPP3ZRwKpA==" crossorigin="anonymous"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js" integrity="sha512-d9xgZrVZpmmQlfonhQUvTR7lMPtO7NkZMkA0ABN3PHCbKA5nqylQ/yWlFAyY6hYgdF1Qh6nYiuADWwKB4C2WSw==" crossorigin="anonymous"></script> + + <script src="krafcik.js" type="text/javascript"></script> + + </body> +</html> diff --git a/result/krafcik.css b/result/krafcik.css new file mode 100644 index 0000000..55a8a98 --- /dev/null +++ b/result/krafcik.css @@ -0,0 +1,73 @@ +html, body { + max-width: 100%; + overflow-x: hidden; +} + +#date-slider { + width: 100%; +} + +#date-filter .slider-rangeHighlight.odd { + background: #0000CC; +} + +#date-filter .slider-rangeHighlight.even { + background: #8888FF; +} + +#date-filter .slider-track-low, +#date-filter .slider-track-high { + background: #CCCCCC; + z-index: 1; +} + +#date-filter .slider-handle { + z-index: 2; +} + +#brewery-filter .form-check label { + cursor: pointer; +} + +#brewery-filter label img { + width: 30px; +} + +#chart-tooltip { + position: absolute; + background: rgba(255, 255, 200, 0.75); + padding: 0.5em; + font-size: 0.7em; + border: solid 1px black; + pointer-events: none; +} + +#chart-tooltip ul { + margin: 0; + padding: 0; +} + +#chart-tooltip ul ul { + padding: 0 0 0 1em; +} + +#chart-tooltip li { + list-style: none; +} + +#chart-tooltip dl { + display: flex; + margin: 0; +} + +#chart-tooltip dt { + width: 80%; + white-space: nowrap; +} + +#chart-tooltip dd { + margin: 0; + width: 20%; + min-width: 30px; + text-align: right; +} diff --git a/result/krafcik.js b/result/krafcik.js new file mode 100644 index 0000000..a65a73b --- /dev/null +++ b/result/krafcik.js @@ -0,0 +1,323 @@ +$(document).ready(function() { + + $.when( + $.getJSON('beers.json'), + $.getJSON('breweries.json'), + $.getJSON('styles.json') + ).done(function(beerData, breweryData, styleData) { + var beers = beerData[0]; + $.each(beers, function(idx, beer) { + beer['scatter-data'] = [ + { + x: beer.abv, + y: beer.ratings + }, + { + x: beer.abv, + y: beer.average + }, + { + x: beer.ratings, + y: beer.average + } + ]; + }); + var minYear = Math.min.apply(null, beers.map(b => b.year)); + var maxYear = Math.max.apply(null, beers.map(b => b.year)); + var minMonth = Math.min.apply(null, beers.filter(b => (b.year == minYear)).map(b => b.month)); + var maxMonth = Math.min.apply(null, beers.filter(b => (b.year == maxYear)).map(b => b.month)); + var fullYears = maxYear - minYear - 1; + var dateCount = fullYears * 12 + (12 - minMonth + 1) + maxMonth; + var dateMapping = []; + var year = minYear; + var month = minMonth; + for (var c = 0; c < dateCount; c++) { + dateMapping[c] = year + '-' + ('0' + month).slice(-2); + month++; + if (month > 12) { + year++; + month = 1; + } + } + var valSpan = [0, dateCount-1]; + var currRange = 12-minMonth+1; + var lastRange = 'odd'; + var yearRanges = [{'start': 0, 'end': currRange-1, 'class': lastRange}]; + while (currRange < dateCount) { + lastRange = lastRange == 'odd' ? 'even' : 'odd'; + yearRanges.push({ + 'start': currRange, + 'end': Math.min(currRange+11, dateCount-1), + 'class': lastRange + }); + currRange += 12; + } + $('#date-filter input[name="date"]').slider({ + min: 0, + max: dateCount-1, + value: valSpan, + ticks: valSpan, + ticks_labels: valSpan.map(v => dateMapping[v]), + rangeHighlights: yearRanges, + formatter: function(val) { + if (isNaN(val)) { + return val.map(v => dateMapping[v]).join(' - '); + } + else { + return dateMapping[val]; + } + } + }); + + var breweries = breweryData[0]; + var breweryTemplate = $('#brewery-template').html(); + var breweryFilter = $('#brewery-filter'); + $.each(breweries, function(brewery, data) { + var fieldId = 'brewery-' + brewery; + var field = $(breweryTemplate); + field.find('input').attr('value', brewery).attr('id', fieldId); + field.find('label').attr('for', fieldId).attr('title', data['name']).find('img').attr('src', 'img/' + data['logo']); + breweryFilter.append(field); + field.find('label').tooltip(); + }); + $('#brewery-all').change(function() { + if ($(this).is(':checked')) { + $('#brewery-filter .form-check input').prop('checked', 'checked'); + } else { + $('#brewery-filter .form-check input').prop('checked', null); + } + }); + + var styles = styleData[0]; + $.each(styles, function(idx, style) { + style.styleTree = {}; + $.each(style.styles.map(s => s.split(' - ')), function(idx, splitStyle) { + if (splitStyle.length > 1) { + if (!style.styleTree[splitStyle[0]]) { + style.styleTree[splitStyle[0]] = []; + } + style.styleTree[splitStyle[0]].push(splitStyle[1]); + } else { + style.styleTree[splitStyle[0]] = null; + } + }); + }); + var styleNames = styles.map(s => s.name); + var styleCounts = styles.map(s => 0); + var substyleCounts = []; + var styleSubstyles = styles.map(s => s.styles); + var styleColours = styles.map(s => s.colour); + var scatterChartData = []; + for (var i = 0; i < 3; i++) { + scatterChartData[i] = { + datasets: styles.map(function(style) { + return { + borderColor: style.colour, + backgroundColor: Color(style.colour).alpha(0.5).rgbString(), + data: [] + }; + }) + }; + } + var stylesChart; + $('a#styles-tab').on('shown.bs.tab', function() { + if (!stylesChart) { + var ctx = $('#styles-chart')[0].getContext('2d'); + stylesChart = new Chart(ctx, { + type: 'bar', + data: { + labels: styleNames, + datasets: [{ + data: styleCounts, + backgroundColor: styleColours, + borderColor: '#000000', + borderWidth: 1 + }] + }, + options: { + legend: { + display: false + }, + scales: { + yAxes: [{ + ticks: { + beginAtZero: true + } + }] + }, + tooltips: { + enabled: false, + custom: function(tooltip) { + var container = $('#chart-tooltip'); + if (!container.length) { + container = $('<div id="chart-tooltip"></div>'); + $('body').append(container); + } + if (tooltip.body) { + var content = $('<ul>'); + $.each(styles[tooltip.dataPoints[0].index].styleTree, function(style, substyles) { + var getItem = function(style, substyle) { + var count = substyleCounts[substyle ? (style + ' - ' + substyle) : style]; + if (count) { + var term = $('<dt>'); + var value = $('<dd>'); + var item = $('<li>').append($('<dl>').append(term).append(value)); + term.html(substyle || style); + value.html(count); + return item; + } + return null; + }; + if (substyles) { + var sublist = $('<ul>'); + var nonZero = false; + $.each(substyles, function(id, substyle) { + var item = getItem(style, substyle); + if (item) { + sublist.append(item); + nonZero = true; + } + }); + if (nonZero) { + content.append($('<li>').append(style).append(sublist)); + } + } else { + content.append(getItem(style)); + } + }); + var positionY = this._chart.canvas.offsetTop; + var positionX = this._chart.canvas.offsetLeft + container.html(content).show(); + container.css({ + 'top': Math.min( + positionY + tooltip.caretY, + $('body').height() - container.outerHeight() + ), + 'left': Math.min( + positionX + tooltip.caretX, + $('body').width() - container.outerWidth() + ) + }).show(); + } else { + container.html('').hide(); + } + } + } + } + }); + } + }); + var abvRatingsChart; + $('a#abv-ratings-tab').on('shown.bs.tab', function() { + if (!abvRatingsChart) { + var ctx = $('#abv-ratings-chart')[0].getContext('2d'); + abvRatingsChart = new Chart.Scatter(ctx, { + data: scatterChartData[0], + options: { + scales: { + yAxes: [{ + type: 'logarithmic', + }] + } + } + + }); + } + }); + var abvAverageChart; + $('a#abv-average-tab').on('shown.bs.tab', function() { + if (!abvAverageChart) { + var ctx = $('#abv-average-chart')[0].getContext('2d'); + abvAverageChart = new Chart.Scatter(ctx, { + data: scatterChartData[1] + }); + } + }); + var ratingsAverageChart; + $('a#ratings-average-tab').on('shown.bs.tab', function() { + if (!ratingsAverageChart) { + var ctx = $('#ratings-average-chart')[0].getContext('2d'); + ratingsAverageChart = new Chart.Scatter(ctx, { + data: scatterChartData[2], + options: { + scales: { + xAxes: [{ + type: 'logarithmic', + }] + } + } + }); + } + }); + + var getFilteredBeers = function(beers) { + var dates = $('#date-filter input[name="date"]').val().split(',').map(d => dateMapping[parseInt(d)]); + var breweries = $('#brewery-filter input:checked').map(function(i, input) { return input.value; }).toArray(); + return beers.filter(function(beer) { + var added = beer.year + '-' + ('0' + beer.month).slice(-2); + return (breweries.indexOf(beer.brewery) > -1) + && (added >= dates[0] && added <= dates[1]); + }); + }; + + var updateCharts = function() { + var filtered = getFilteredBeers(beers); + var bySubstyle = {}; + for (var style in substyleCounts) { + substyleCounts[style] = 0; + } + for (var beer in filtered) { + var style = filtered[beer].style.join(' - '); + if (!substyleCounts[style]) { + substyleCounts[style] = 0; + } + substyleCounts[style]++; + if (!bySubstyle[style]) { + bySubstyle[style] = []; + } + bySubstyle[style].push(filtered[beer]['scatter-data']); + } + for (var s in styleCounts) { + styleCounts[s] = 0; + for (var i = 0; i < 3; i++) { + scatterChartData[i].datasets[s].data = []; + } + for (var ss in styleSubstyles[s]) { + var substyle = styleSubstyles[s][ss]; + if (substyleCounts[substyle]) { + styleCounts[s] += substyleCounts[substyle]; + for (var i = 0; i < 3; i++) { + scatterChartData[i].datasets[s].data = scatterChartData[i].datasets[s].data.concat(bySubstyle[substyle].map(d => d[i])); + } + } + } + } + if (stylesChart) { + stylesChart.update(); + } + if (abvRatingsChart) { + abvRatingsChart.update(); + } + if (abvAverageChart) { + abvAverageChart.update(); + } + if (ratingsAverageChart) { + ratingsAverageChart.update(); + } + }; + + var changeTimeout = null; + $('#date-filter, #brewery-filter').find('input').change(function() { + clearTimeout(changeTimeout); + changeTimeout = setTimeout(updateCharts, 500); + }); + + $('a#styles-tab').trigger('shown.bs.tab'); + updateCharts(); + }); + + $('#tab-menu a').on('click', function (e) { + e.preventDefault() + $(this).tab('show') + }) +}); |