245 lines
6.7 KiB
JavaScript
245 lines
6.7 KiB
JavaScript
|
var margin = {top: 20, right: 10, bottom: 30, left: 10};
|
||
|
var width = 700 - margin.left - margin.right,
|
||
|
height = 500 - margin.top - margin.bottom;
|
||
|
|
||
|
const mat = [];
|
||
|
|
||
|
const interpol = d3.interpolateViridis;
|
||
|
const colorScaleRel = d3.scaleSequential(interpol).domain([0, 100]);
|
||
|
const colorScaleAbs = d3.scaleSequential(interpol).domain([0, 306]); //oeps harcoded
|
||
|
|
||
|
const svg = d3.select('#gridlo')
|
||
|
.append('svg')
|
||
|
.attr("width", width + margin.left + margin.right)
|
||
|
.attr("height", height + margin.top + margin.bottom)
|
||
|
.append("g")
|
||
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||
|
|
||
|
const products = [];
|
||
|
const locations = [];
|
||
|
d3.csv("/csvdata/elodata.csv", d => {
|
||
|
d.product_id = +d.product_id;
|
||
|
d.location_id = +d.location_id;
|
||
|
|
||
|
if (!mat[d.product_id]) {
|
||
|
products[d.product_id] = d.name_x;
|
||
|
mat[d.product_id] = Array(...Array(110)).map(Number.prototype.valueOf, 0);
|
||
|
}
|
||
|
// at this point tha array is made d.location_id
|
||
|
if (!mat[d.product_id][d.location_id]) {
|
||
|
locations[d.location_id] = d.name_y;
|
||
|
mat[d.product_id][d.location_id] = 1;
|
||
|
} else {
|
||
|
mat[d.product_id][d.location_id] += 1;
|
||
|
}
|
||
|
return d;
|
||
|
}).then((data) => {
|
||
|
const mat2 = [];
|
||
|
let prod2 = [];
|
||
|
mat.forEach((a, i) => {
|
||
|
prod2.push(products[i]);
|
||
|
mat2.push(a);
|
||
|
});
|
||
|
let mat3 = [];
|
||
|
let loc2 = [];
|
||
|
for (var i = 0; i < prod2.length; i++) {
|
||
|
mat3[i] = [];
|
||
|
}
|
||
|
var i = 0;
|
||
|
let mat_scaled = [];
|
||
|
locations.forEach((l, k) => {
|
||
|
loc2.push(l);
|
||
|
for (let j = 0; j < prod2.length; j++) {
|
||
|
mat3[j][i] = mat2[j][k];
|
||
|
}
|
||
|
i++;
|
||
|
});
|
||
|
|
||
|
//rectangles
|
||
|
let comb = _.sortBy(_.zip(prod2, mat3), e => -_.sum(e[1]));
|
||
|
prod2 = _.unzip(comb)[0];
|
||
|
mat3 = _.unzip(comb)[1];
|
||
|
|
||
|
comb = _.sortBy(_.zip(loc2, _.unzip(mat3)), e => -_.sum(e[1]));
|
||
|
loc2 = _.unzip(comb)[0];
|
||
|
mat3 = _.unzip(_.unzip(comb)[1]);
|
||
|
|
||
|
max = 0;
|
||
|
sums = []
|
||
|
for (var i = 0; i < mat3[0].length; i++) {
|
||
|
sums[i] = 0;
|
||
|
}
|
||
|
mat3.forEach(d => {
|
||
|
d.forEach((e, i) => {
|
||
|
sums[i] += e;
|
||
|
});
|
||
|
});
|
||
|
mat_scaled = mat3.map((r, i) => r.map((e, j) => 100 * e / sums[j]));
|
||
|
relative = false; // 0 is false -> scaled
|
||
|
data = [mat_scaled, mat3];
|
||
|
scales = [colorScaleRel, colorScaleAbs];
|
||
|
const legends = [[0, 20, 40, 60, 80, 100], [0, 60, 120, 180, 240, 300]];
|
||
|
let d = data[+relative];
|
||
|
const top = svg.append("g").attr("id", "top");
|
||
|
|
||
|
const boxScale = d3.scaleBand()
|
||
|
.domain(d3.range(Math.max(d.length, d[0].length)))
|
||
|
.range([0, Math.min(height, width)])
|
||
|
.round(true)
|
||
|
;
|
||
|
|
||
|
d3.select("#switch").on("click", updateData);
|
||
|
|
||
|
function updateData() {
|
||
|
relative = !relative;
|
||
|
d = data[+relative];
|
||
|
scale = scales[+relative]
|
||
|
|
||
|
// ENTER -- Rows
|
||
|
top.selectAll('g').data(d, (_, i) => i).enter().append('g')
|
||
|
.attr('opacity', 0.95)
|
||
|
.attr("transform", (_, i) => `translate(0, ${(boxScale(i))})`)
|
||
|
.attr('data-row-idx', (_, i) => i)
|
||
|
// ENTER -- Boxes
|
||
|
.selectAll('rect').data(d => d, (_, i) => i).enter().append("rect")
|
||
|
.attr('opacity', 0.95)
|
||
|
.attr("class", (_, i) => `id${i}`)
|
||
|
.attr('width', boxScale.bandwidth() * 1.01)
|
||
|
.attr('height', boxScale.bandwidth() * 1.01)
|
||
|
.attr("x", (_, i) => boxScale(i) + 150)
|
||
|
.on("mouseover", mouse_over_rect)
|
||
|
.on("mouseout", mouse_out_rect);
|
||
|
|
||
|
const boxes = top.selectAll('g').data(d, (_, i) => i).selectAll('rect').data(d => d, (_, i) => i);
|
||
|
|
||
|
boxes
|
||
|
.transition()
|
||
|
.attr('fill', d => scale(d));
|
||
|
|
||
|
svg.selectAll(".legend text")
|
||
|
.transition().duration(500)
|
||
|
.text((_, i) => {
|
||
|
const d = legends[+relative][i];
|
||
|
return relative ? d : d + '%';
|
||
|
});
|
||
|
|
||
|
svg.selectAll(".legend rect")
|
||
|
.transition().duration(500)
|
||
|
.style("fill", (d, i) => scale(legends[+relative][i]));
|
||
|
|
||
|
};
|
||
|
updateData();
|
||
|
|
||
|
function mouse_over_rect(d, i, j) { // Add interactivity
|
||
|
const row = d3.select(this.parentNode);
|
||
|
const block = d3.select(this);
|
||
|
const tooltip = d3.select('.tooltip');
|
||
|
|
||
|
//row
|
||
|
row.attr("opacity", 1);
|
||
|
d3.selectAll(`.id${i}`).attr("opacity", 1);
|
||
|
|
||
|
tooltip
|
||
|
.style("opacity", .9)
|
||
|
.html(relative ? d : `${d.toFixed(2)}%`);
|
||
|
|
||
|
// We calculate the bounding rects after setting the html
|
||
|
let rect = block.node().getBoundingClientRect();
|
||
|
let t_rect = tooltip.node().getBoundingClientRect();
|
||
|
|
||
|
tooltip
|
||
|
.style("left", (rect.left + rect.width / 2 - t_rect.width / 2) + "px")
|
||
|
.style("top", (rect.top - t_rect.height - 5) + "px");
|
||
|
|
||
|
//Labels
|
||
|
d3.select(`#rest${i}`)
|
||
|
.attr("fill", scale(d))
|
||
|
.attr("font-weight", "bold")
|
||
|
;
|
||
|
|
||
|
d3.select(`#prod${row.attr('data-row-idx')}`)
|
||
|
.attr("fill", scale(d))
|
||
|
.attr("font-weight", "bold")
|
||
|
;
|
||
|
}
|
||
|
function mouse_out_rect(d, i, j) {
|
||
|
const row = d3.select(this.parentNode);
|
||
|
const block = d3.select(this);
|
||
|
const tooltip = d3.select('.tooltip');
|
||
|
|
||
|
//rm border and text
|
||
|
row.attr("stroke", "none");
|
||
|
row.attr("opacity", 0.95);
|
||
|
d3.selectAll(`.id${i}`).attr("opacity", 0.95);
|
||
|
|
||
|
tooltip.style('opacity', 0);
|
||
|
|
||
|
d3.select(`#rest${i}`)
|
||
|
.attr("fill", "black")
|
||
|
.attr("font-weight", "")
|
||
|
;
|
||
|
d3.select(`#prod${row.attr('data-row-idx')}`)
|
||
|
.attr("fill", "black")
|
||
|
.attr("font-weight", "")
|
||
|
;
|
||
|
}
|
||
|
|
||
|
// product labels
|
||
|
let prodLabels = svg.append("g")
|
||
|
.selectAll("g")
|
||
|
.data(prod2)
|
||
|
.enter()
|
||
|
.append("text")
|
||
|
.text(d => d)
|
||
|
.attr("x", 30)
|
||
|
.attr("y", (_, i) => boxScale(i) + 15)
|
||
|
.attr("id", (_, i) => `prod${i}`)
|
||
|
.attr("font-family", "sans-serif")
|
||
|
.attr("font-size", "11px")
|
||
|
.attr("fill", "black");
|
||
|
|
||
|
// location labels
|
||
|
let locLabels = svg.append("g")
|
||
|
.selectAll("g")
|
||
|
.data(loc2)
|
||
|
.enter()
|
||
|
.append("text")
|
||
|
.text(d => d)
|
||
|
.attr("text-anchor", "middle")
|
||
|
.attr("transform", (_, i) => `translate(${(boxScale(i) + 150)},${boxScale(prodLabels.size() - 1) + 70}) rotate(-65)`)
|
||
|
.attr("font-family", "sans-serif")
|
||
|
.attr("id", (_, i) => `rest${i}`)
|
||
|
.attr("font-size", "11px")
|
||
|
.attr("fill", "black");
|
||
|
|
||
|
//Legend
|
||
|
let legend = svg.append('g')
|
||
|
.classed('legendWrapper', 'true')
|
||
|
.attr("transform", `translate(${boxScale(locLabels.size() - 1) + boxScale.bandwidth() + 160}, 0)`);
|
||
|
;
|
||
|
|
||
|
let legendAbs = legend.selectAll(".legend")
|
||
|
.data(legends[+relative])
|
||
|
.enter().append("g")
|
||
|
.attr("class", "legend")
|
||
|
.attr("transform", (d, i) => `translate(0 ,${boxScale(i) + 20})`);
|
||
|
|
||
|
legendAbs.append("rect")
|
||
|
.attr("width", 20)
|
||
|
.attr("height", 20)
|
||
|
.style("fill", d => colorScaleAbs(d));
|
||
|
|
||
|
legendAbs.append("text")
|
||
|
.attr("x", 26)
|
||
|
.attr("y", 10)
|
||
|
.attr("dy", ".35em")
|
||
|
.text(d => relative ? d : `${d.toFixed(2)}%`);
|
||
|
|
||
|
legend.append("text")
|
||
|
.attr("class", "label")
|
||
|
.attr("x", 0)
|
||
|
.attr("y", 10)
|
||
|
.attr("dy", ".35em")
|
||
|
.text("Count");
|
||
|
});
|