parent
a4c23e116d
commit
3f59a07483
@ -1,528 +0,0 @@
|
|||||||
function Rect(value, name, x, y, w, h){
|
|
||||||
this.value = value;
|
|
||||||
this.name = name;
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.w = w;
|
|
||||||
this.h = h;
|
|
||||||
this.checkHit = function (mouseX, mouseY) {
|
|
||||||
return (mouseX >= x && mouseX <= x + w && mouseY >= y && mouseY <= y + h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Circle(value, name, x, y, r){
|
|
||||||
this.value = value;
|
|
||||||
this.name = name;
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.r = r;
|
|
||||||
this.checkHit = function (mouseX, mouseY) {
|
|
||||||
return Math.pow((mouseX - x),2) + Math.pow((mouseY - y),2) <= Math.pow(r, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function PieSlice(value, name, x, y, r, sAngle, eAngle){
|
|
||||||
this.value = value;
|
|
||||||
this.name = name;
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.r = r;
|
|
||||||
this.sAngle = sAngle;
|
|
||||||
this.eAngle = eAngle;
|
|
||||||
this.checkHit = function (mouseX, mouseY) {
|
|
||||||
if (Math.pow((mouseX - x),2) + Math.pow((mouseY - y),2) <= Math.pow(r, 2)){
|
|
||||||
var dy = mouseY - y;
|
|
||||||
var dx = mouseX - x;
|
|
||||||
var theta = Math.atan2(dy, dx); // range (-PI, PI]
|
|
||||||
if (theta < 0) theta += 2*Math.PI;
|
|
||||||
|
|
||||||
return (theta > sAngle && theta < eAngle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let objects = [];
|
|
||||||
|
|
||||||
function checkHit(pos) {
|
|
||||||
for (let i = 0; i < objects.length; i++){
|
|
||||||
if (objects[i].checkHit(pos.x, pos.y)){
|
|
||||||
return objects[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLargest(data) {
|
|
||||||
let largest = data[0].values[0];
|
|
||||||
|
|
||||||
data.forEach(function (categ) {
|
|
||||||
for (let i = 0; i < categ.values.length; i++)
|
|
||||||
if (categ.values[i] > largest) {
|
|
||||||
largest = categ.values[i];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return largest;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSmallest(data) {
|
|
||||||
let smallest = data[0].values[0];
|
|
||||||
|
|
||||||
data.forEach(function (categ) {
|
|
||||||
for (let i = 0; i < categ.values.length; i++)
|
|
||||||
if (categ.values[i] < smallest) {
|
|
||||||
smallest = categ.values[i];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return smallest;
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawAxis(bounds, largest, smallest, arrayLen, ctx, graphSettings, drawValues = true) {
|
|
||||||
ctx.font = "16px Arial";
|
|
||||||
if (graphSettings.y_step <= 0) graphSettings.y_step = 1;
|
|
||||||
|
|
||||||
ctx.beginPath();
|
|
||||||
for (let i = (smallest < 0)?smallest:0; i <= ((largest>=0)?largest:0); i += parseFloat(graphSettings.y_step)){
|
|
||||||
|
|
||||||
ctx.strokeStyle = "#BBB";
|
|
||||||
ctx.lineWidth = 1;
|
|
||||||
let scale = bounds.height - ((largest >= 0)?(bounds.bottom - bounds.xAxis):0);
|
|
||||||
let extreme = (largest<=0)?Math.abs(smallest):Math.abs(largest);
|
|
||||||
let yPos = Math.round((bounds.xAxis - i * scale / extreme));
|
|
||||||
|
|
||||||
//support line
|
|
||||||
if (graphSettings.display_support_lines) {
|
|
||||||
|
|
||||||
ctx.moveTo( bounds.left, yPos );
|
|
||||||
ctx.lineTo( bounds.right, yPos );
|
|
||||||
}
|
|
||||||
|
|
||||||
//Y axis value
|
|
||||||
ctx.fillStyle = "black";
|
|
||||||
ctx.textAlign = "center";
|
|
||||||
ctx.textAlign = "end";
|
|
||||||
ctx.fillText( i, bounds.left - 3, yPos);
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
//X axis value
|
|
||||||
if (drawValues) {
|
|
||||||
ctx.fillStyle = "black";
|
|
||||||
ctx.textAlign = "center";
|
|
||||||
for (let i = 0; i < arrayLen; i++){
|
|
||||||
let x = bounds.left + bounds.width / (arrayLen - 1) * i;
|
|
||||||
let text = (i + 1).toString();
|
|
||||||
if (graphSettings.custom_x_values !== "")
|
|
||||||
text = graphSettings.custom_x_values.split(';')[i];
|
|
||||||
ctx.fillText(text, x, bounds.bottom + 18);
|
|
||||||
}
|
|
||||||
ctx.closePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
//X and Y axis
|
|
||||||
ctx.strokeStyle = "black";
|
|
||||||
ctx.lineWidth = "2px";
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo( bounds.left, bounds.top );
|
|
||||||
ctx.lineTo( bounds.left, bounds.bottom );
|
|
||||||
ctx.moveTo( bounds.left, bounds.xAxis);
|
|
||||||
ctx.lineTo( bounds.right, bounds.xAxis );
|
|
||||||
ctx.stroke();
|
|
||||||
|
|
||||||
//Axis labels
|
|
||||||
//X axis text
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.font = "20px Arial";
|
|
||||||
ctx.fillStyle = "black";
|
|
||||||
ctx.textAlign = "center";
|
|
||||||
ctx.fillText(graphSettings.x_label, bounds.width/2 + bounds.left, bounds.height + 2*bounds.top - 5);
|
|
||||||
|
|
||||||
//Y axis text
|
|
||||||
ctx.save();
|
|
||||||
ctx.rotate(-Math.PI / 2);
|
|
||||||
ctx.textAlign = "center";
|
|
||||||
ctx.fillText(graphSettings.y_label, -(bounds.left + bounds.height/2), 18);
|
|
||||||
ctx.restore();
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawTitle(canvas, graphSettings) {
|
|
||||||
let ctx = canvas.getContext("2d");
|
|
||||||
let x = canvas.width / 2;
|
|
||||||
let y = 25;
|
|
||||||
|
|
||||||
ctx.font = "30px Arial";
|
|
||||||
ctx.fillStyle = "black";
|
|
||||||
ctx.textAlign = "center";
|
|
||||||
ctx.fillText( graphSettings.title, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawPoints(ctx, bounds, values, name, arrayLen, largest, color) {
|
|
||||||
ctx.fillStyle = color;
|
|
||||||
let radius = 3;
|
|
||||||
|
|
||||||
for( let i = 0; i < arrayLen; i++ ){
|
|
||||||
ctx.beginPath();
|
|
||||||
if(values[i] === null) continue;
|
|
||||||
let scale = bounds.height - ((largest >= 0)?(bounds.bottom - bounds.xAxis):0);
|
|
||||||
let extreme = (largest<=0)?Math.abs(smallest):Math.abs(largest);
|
|
||||||
let x = bounds.left + bounds.width / (arrayLen - 1) * i;
|
|
||||||
let y = (bounds.xAxis - values[i] / extreme * scale);
|
|
||||||
ctx.arc(x, y, radius, 0, 2 * Math.PI);
|
|
||||||
ctx.fill();
|
|
||||||
ctx.stroke();
|
|
||||||
ctx.closePath();
|
|
||||||
|
|
||||||
let new_object = new Circle(values[i], name, x, y, radius);
|
|
||||||
objects.push(new_object);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawSlice(ctx, centerX, centerY, radius, startAngle, endAngle, color ){
|
|
||||||
ctx.fillStyle = color;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(centerX,centerY);
|
|
||||||
ctx.arc(centerX, centerY, radius, startAngle, endAngle);
|
|
||||||
ctx.fill();
|
|
||||||
ctx.closePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBounds(canvas, graphMargin) {
|
|
||||||
return {
|
|
||||||
top: graphMargin,
|
|
||||||
bottom: canvas.height - graphMargin,
|
|
||||||
left: graphMargin,
|
|
||||||
right: canvas.width - graphMargin,
|
|
||||||
height: canvas.height - 2*graphMargin,
|
|
||||||
width: canvas.width - 2*graphMargin,
|
|
||||||
xAxis: canvas.height - graphMargin
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawPieChart(canvas, data, graphSettings) {
|
|
||||||
|
|
||||||
let ctx = canvas.getContext("2d");
|
|
||||||
let index = 0;
|
|
||||||
let start_angle = 0;
|
|
||||||
let total_value = 0;
|
|
||||||
let bounds = getBounds(canvas, graphSettings.margin);
|
|
||||||
|
|
||||||
|
|
||||||
data.forEach(function (categ) {
|
|
||||||
let val = categ.values[0];
|
|
||||||
if (val !== null)
|
|
||||||
total_value += val;
|
|
||||||
});
|
|
||||||
|
|
||||||
data.forEach(function (categ) {
|
|
||||||
let val = categ.values[0];
|
|
||||||
let slice_angle = 2 * Math.PI * val / total_value;
|
|
||||||
|
|
||||||
let x = canvas.width/2;
|
|
||||||
let y = canvas.height/2;
|
|
||||||
let r = Math.min(bounds.width/2, bounds.height/2);
|
|
||||||
let end_angle = start_angle + slice_angle;
|
|
||||||
|
|
||||||
drawSlice(ctx, x, y, r, start_angle, end_angle, categ.color);
|
|
||||||
let new_object = new PieSlice(val + " (" + Math.round(val/total_value*100) + "%)", categ.name, x, y, r, start_angle, end_angle);
|
|
||||||
objects.push(new_object);
|
|
||||||
|
|
||||||
start_angle = end_angle;
|
|
||||||
index++;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawPointChart(canvas, data, graphSettings){
|
|
||||||
let ctx = canvas.getContext( "2d" );
|
|
||||||
|
|
||||||
let bounds = getBounds(canvas, graphSettings.margin);
|
|
||||||
let largest = getLargest(data);
|
|
||||||
let smallest = getSmallest(data);
|
|
||||||
|
|
||||||
if (smallest < 0)
|
|
||||||
bounds.xAxis = bounds.bottom - (bounds.height / (((largest<=0)?0:Math.abs(largest)) + Math.abs(smallest)) * Math.abs(smallest));
|
|
||||||
let arrayLen = data[0].values.length;
|
|
||||||
|
|
||||||
drawAxis(bounds, largest, smallest, arrayLen, ctx, graphSettings);
|
|
||||||
|
|
||||||
data.display_points = true;
|
|
||||||
data.forEach(function (categ) {
|
|
||||||
//Points
|
|
||||||
if (graphSettings.display_points)
|
|
||||||
drawPoints(ctx, bounds, categ.values, categ.name, arrayLen, largest, categ.color);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawLineChart(canvas, data, graphSettings){
|
|
||||||
let ctx = canvas.getContext( "2d" );
|
|
||||||
|
|
||||||
let bounds = getBounds(canvas, graphSettings.margin);
|
|
||||||
let largest = getLargest(data);
|
|
||||||
let smallest = getSmallest(data);
|
|
||||||
|
|
||||||
if (smallest < 0)
|
|
||||||
bounds.xAxis = bounds.bottom - (bounds.height / (((largest<=0)?0:Math.abs(largest)) + Math.abs(smallest)) * Math.abs(smallest));
|
|
||||||
let arrayLen = data[0].values.length;
|
|
||||||
|
|
||||||
drawAxis(bounds, largest, smallest, arrayLen, ctx, graphSettings);
|
|
||||||
|
|
||||||
data.forEach(function (categ) {
|
|
||||||
//Lines
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.lineJoin = "round";
|
|
||||||
ctx.strokeStyle = categ.color;
|
|
||||||
|
|
||||||
for (let i = 0; i < arrayLen; i++) {
|
|
||||||
if (categ.values[i] === null) continue;
|
|
||||||
|
|
||||||
let scale = bounds.height - ((largest >= 0)?(bounds.bottom - bounds.xAxis):0);
|
|
||||||
let extreme = (largest<=0)?Math.abs(smallest):Math.abs(largest);
|
|
||||||
let x = bounds.left + bounds.width / (arrayLen - 1) * i;
|
|
||||||
let y = (bounds.xAxis - categ.values[i] / extreme * scale);
|
|
||||||
|
|
||||||
ctx.lineTo(x, y);
|
|
||||||
}
|
|
||||||
ctx.stroke();
|
|
||||||
ctx.closePath();
|
|
||||||
|
|
||||||
//Points
|
|
||||||
if (graphSettings.display_points)
|
|
||||||
drawPoints(ctx, bounds, categ.values, categ.name, arrayLen, largest, categ.color);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawAreaChart(canvas, data, graphSettings){
|
|
||||||
let ctx = canvas.getContext( "2d" );
|
|
||||||
|
|
||||||
let bounds = getBounds(canvas, graphSettings.margin);
|
|
||||||
let largest = getLargest(data);
|
|
||||||
let smallest = getSmallest(data);
|
|
||||||
|
|
||||||
if (smallest < 0)
|
|
||||||
bounds.xAxis = bounds.bottom - (bounds.height / (((largest<=0)?0:Math.abs(largest)) + Math.abs(smallest)) * Math.abs(smallest));
|
|
||||||
let arrayLen = data[0].values.length;
|
|
||||||
|
|
||||||
drawAxis(bounds, largest, smallest, arrayLen, ctx, graphSettings);
|
|
||||||
|
|
||||||
data.forEach(function (categ) {
|
|
||||||
//Lines
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.lineJoin = "round";
|
|
||||||
ctx.strokeStyle = categ.color;
|
|
||||||
|
|
||||||
let xmax = 0;
|
|
||||||
for (let i = 0; i < arrayLen; i++) {
|
|
||||||
if (categ.values[i] === null) continue;
|
|
||||||
let scale = bounds.height - ((largest >= 0)?(bounds.bottom - bounds.xAxis):0);
|
|
||||||
let extreme = (largest<=0)?Math.abs(smallest):Math.abs(largest);
|
|
||||||
let x = bounds.left + bounds.width / (arrayLen - 1) * i;
|
|
||||||
let y = (bounds.xAxis - categ.values[i] / extreme * scale);
|
|
||||||
xmax = x;
|
|
||||||
|
|
||||||
ctx.lineTo(x, y);
|
|
||||||
}
|
|
||||||
ctx.stroke();
|
|
||||||
|
|
||||||
ctx.lineTo(xmax, bounds.xAxis);
|
|
||||||
ctx.lineTo(bounds.left, bounds.xAxis);
|
|
||||||
ctx.globalAlpha = 0.5;
|
|
||||||
ctx.fillStyle = categ.color;
|
|
||||||
ctx.closePath();
|
|
||||||
ctx.fill();
|
|
||||||
|
|
||||||
ctx.globalAlpha = 1;
|
|
||||||
|
|
||||||
//Points
|
|
||||||
if (graphSettings.display_points)
|
|
||||||
drawPoints(ctx, bounds, categ.values, categ.name, arrayLen, largest, categ.color);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawBarChart(canvas, data, graphSettings) {
|
|
||||||
let ctx = canvas.getContext( "2d" );
|
|
||||||
|
|
||||||
let bounds = getBounds(canvas, graphSettings.margin);
|
|
||||||
ctx.shadowOffsetX = 15;
|
|
||||||
ctx.shadowOffsetY = 15;
|
|
||||||
ctx.shadowBlur = 4;
|
|
||||||
|
|
||||||
let largest = getLargest(data);
|
|
||||||
let barCount = data.length;
|
|
||||||
let dataCount = data[0].values.length;
|
|
||||||
|
|
||||||
let smallest = getSmallest(data);
|
|
||||||
|
|
||||||
if (smallest < 0)
|
|
||||||
bounds.xAxis = bounds.bottom - (bounds.height / (((largest<=0)?0:Math.abs(largest)) + Math.abs(smallest)) * Math.abs(smallest));
|
|
||||||
|
|
||||||
drawAxis(bounds, largest, smallest, dataCount, ctx, graphSettings, false);
|
|
||||||
|
|
||||||
let size = bounds.width / dataCount;
|
|
||||||
let innerSize = size * 0.8;
|
|
||||||
let bar_width = innerSize * 0.7 / barCount;
|
|
||||||
|
|
||||||
for (let i = 0; i < dataCount; i++) {
|
|
||||||
let num = 0;
|
|
||||||
data.forEach(function (categ) {
|
|
||||||
ctx.beginPath();
|
|
||||||
let value = categ.values[i];
|
|
||||||
let left = bounds.left + (size * (i + 0.15) + (innerSize * num / barCount));
|
|
||||||
let scale = bounds.height - ((largest >= 0)?(bounds.bottom - bounds.xAxis):0);
|
|
||||||
let extreme = (largest<=0)?Math.abs(smallest):Math.abs(largest);
|
|
||||||
let bar_height = value / extreme * scale;
|
|
||||||
let top = (bounds.xAxis - categ.values[i] / extreme * scale);
|
|
||||||
|
|
||||||
ctx.fillStyle = categ.color;
|
|
||||||
ctx.fillRect(left, top, bar_width, bar_height);
|
|
||||||
|
|
||||||
//x value
|
|
||||||
if (num === 0){
|
|
||||||
let text = (i + 1).toString();
|
|
||||||
if (graphSettings.custom_x_values !== ""){
|
|
||||||
text = graphSettings.custom_x_values.split(';')[i];
|
|
||||||
}
|
|
||||||
ctx.font = "16px Arial";
|
|
||||||
ctx.fillStyle = "black";
|
|
||||||
ctx.textAlign = "center";
|
|
||||||
ctx.fillText(text, bounds.width / dataCount * i + size / 2 + bounds.left, bounds.bottom + 15);
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
num++;
|
|
||||||
let new_object = new Rect(value, categ.name, left, top, bar_width, bar_height);
|
|
||||||
objects.push(new_object);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function stackedChart(canvas, data, graphSettings) {
|
|
||||||
let ctx = canvas.getContext( "2d" );
|
|
||||||
|
|
||||||
let bounds = getBounds(canvas, graphSettings.margin);
|
|
||||||
ctx.shadowOffsetX = 15;
|
|
||||||
ctx.shadowOffsetY = 15;
|
|
||||||
ctx.shadowBlur = 4;
|
|
||||||
|
|
||||||
let dataCount = data[0].values.length;
|
|
||||||
|
|
||||||
let largest = 0;
|
|
||||||
for (let i = 0; i < dataCount; i++){
|
|
||||||
let sum = 0;
|
|
||||||
data.forEach(function (categ) {
|
|
||||||
categ.values[i] = Math.abs(categ.values[i]);
|
|
||||||
sum += categ.values[i];
|
|
||||||
});
|
|
||||||
if (sum > largest) largest = sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
let smallest = getSmallest(data);
|
|
||||||
if (smallest < 0)
|
|
||||||
bounds.xAxis = bounds.bottom - (bounds.height / (((largest<=0)?0:Math.abs(largest)) + Math.abs(smallest)) * Math.abs(smallest));
|
|
||||||
|
|
||||||
drawAxis(bounds, largest, smallest, dataCount, ctx, graphSettings, false);
|
|
||||||
|
|
||||||
let size = bounds.width / dataCount;
|
|
||||||
let bar_width = size * 0.7;
|
|
||||||
|
|
||||||
for (let i = 0; i < dataCount; i++) {
|
|
||||||
let last_top = bounds.xAxis;
|
|
||||||
let num = 0;
|
|
||||||
data.forEach(function (categ) {
|
|
||||||
ctx.beginPath();
|
|
||||||
|
|
||||||
let value = categ.values[i];
|
|
||||||
let bar_height = value / largest * bounds.height;
|
|
||||||
let left = bounds.left + (size * (i + 0.15));
|
|
||||||
let top = last_top - bar_height;
|
|
||||||
|
|
||||||
ctx.fillStyle = categ.color;
|
|
||||||
ctx.fillRect(left, top, bar_width, bar_height);
|
|
||||||
|
|
||||||
last_top = top;
|
|
||||||
|
|
||||||
//x value
|
|
||||||
if (num === 0){
|
|
||||||
let text = (i + 1).toString();
|
|
||||||
if (graphSettings.custom_x_values !== ""){
|
|
||||||
text = graphSettings.custom_x_values.split(';')[i];
|
|
||||||
}
|
|
||||||
ctx.font = "16px Arial";
|
|
||||||
ctx.fillStyle = "black";
|
|
||||||
ctx.textAlign = "center";
|
|
||||||
ctx.fillText(text, bounds.width / dataCount * i + size / 2 + bounds.left, bounds.bottom + 15);
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
num++;
|
|
||||||
|
|
||||||
let new_object = new Rect(value, categ.name, left, top, bar_width, bar_height);
|
|
||||||
objects.push(new_object);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function resizeCanvas(canvas, parent, legendHeight = 0, bgColor) {
|
|
||||||
|
|
||||||
if (legendHeight > 0) legendHeight += 3.1;
|
|
||||||
//set size
|
|
||||||
canvas.style.width = parent.clientWidth.toString();
|
|
||||||
canvas.style.height = (parent.clientHeight - legendHeight).toString();
|
|
||||||
canvas.width = parent.clientWidth;
|
|
||||||
canvas.height = parent.clientHeight - legendHeight;
|
|
||||||
|
|
||||||
//reset canvas color
|
|
||||||
let ctx = canvas.getContext( "2d" );
|
|
||||||
if (bgColor == null){
|
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
||||||
} else {
|
|
||||||
ctx.fillStyle = bgColor;
|
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateLegend(displayLegend, data) {
|
|
||||||
if (displayLegend){
|
|
||||||
let legendHTML = "";
|
|
||||||
data.forEach(function (categ) {
|
|
||||||
legendHTML += "<div><span style='display:inline-block;width:20px;background-color:" + categ.color + ";'> </span> " + categ.col_name + "</div>";
|
|
||||||
});
|
|
||||||
|
|
||||||
legend.innerHTML = legendHTML;
|
|
||||||
legend.style.display = "block";
|
|
||||||
} else {
|
|
||||||
legend.style.display = "none";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawChart(graphSettings, data) {
|
|
||||||
updateLegend(graphSettings.display_legend, data);
|
|
||||||
resizeCanvas(canvas, parent, legend.offsetHeight, graphSettings.backgroundColor);
|
|
||||||
objects = [];
|
|
||||||
|
|
||||||
//Choose the correct graph
|
|
||||||
switch (graphSettings.type) {
|
|
||||||
case "point":
|
|
||||||
drawPointChart(canvas, data, graphSettings);
|
|
||||||
break;
|
|
||||||
case "line":
|
|
||||||
drawLineChart(canvas, data, graphSettings);
|
|
||||||
break;
|
|
||||||
case "pie":
|
|
||||||
drawPieChart(canvas, data, graphSettings);
|
|
||||||
break;
|
|
||||||
case "bar":
|
|
||||||
drawBarChart(canvas, data, graphSettings);
|
|
||||||
break;
|
|
||||||
case "area":
|
|
||||||
drawAreaChart(canvas, data, graphSettings);
|
|
||||||
break;
|
|
||||||
case "stacked":
|
|
||||||
stackedChart(canvas, data, graphSettings);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (graphSettings.display_title)
|
|
||||||
drawTitle(canvas, graphSettings);
|
|
||||||
}
|
|
||||||
@ -0,0 +1,128 @@
|
|||||||
|
class ChartLoader {
|
||||||
|
constructor(canvas, detectionCanvas, parent, legend, dataDiv) {
|
||||||
|
this.canvas = canvas
|
||||||
|
this.detectionCanvas = detectionCanvas
|
||||||
|
this.parent = parent
|
||||||
|
this.legend = legend
|
||||||
|
this.dataDiv = dataDiv
|
||||||
|
}
|
||||||
|
|
||||||
|
//Načtení dat
|
||||||
|
loadData(code) {
|
||||||
|
fetch("/api/charts/" + code, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
let metadata = data.metadata;
|
||||||
|
metadata.custom_x_values = ""
|
||||||
|
let table = data.table
|
||||||
|
|
||||||
|
if (data !== null) {
|
||||||
|
this.drawChart(metadata, table)
|
||||||
|
this.parent.style.backgroundColor = metadata.backgroundColor
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.parent.innerHTML = "graph not found."
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
addListener(newChart) {
|
||||||
|
//Click
|
||||||
|
document.addEventListener('mousemove', (e) => {
|
||||||
|
const pos = {
|
||||||
|
x: e.clientX,
|
||||||
|
y: e.clientY
|
||||||
|
}
|
||||||
|
|
||||||
|
var ctx = this.detectionCanvas.getContext('2d');
|
||||||
|
var pixelData = ctx.getImageData(pos.x, pos.y, 1, 1).data;
|
||||||
|
|
||||||
|
if (pixelData[3] === 255) {
|
||||||
|
let index = (pixelData[0] * 256 + pixelData[1]) * 256 + pixelData[2]
|
||||||
|
let obj = newChart.objects[index]
|
||||||
|
|
||||||
|
let divHitBox = this.dataDiv
|
||||||
|
divHitBox.style.display = "block"
|
||||||
|
|
||||||
|
// make the info fit into the chart div
|
||||||
|
if (pos.x + divHitBox.clientWidth <= this.parent.clientWidth - 2)
|
||||||
|
divHitBox.style.left = pos.x + "px"
|
||||||
|
else
|
||||||
|
this.dataDiv.style.left = pos.x - divHitBox.clientWidth + "px"
|
||||||
|
|
||||||
|
if (pos.y + divHitBox.clientHeight <= parent.clientHeight - 2)
|
||||||
|
this.dataDiv.style.top = pos.y + "px"
|
||||||
|
else
|
||||||
|
this.dataDiv.style.top = pos.y - divHitBox.clientHeight + "px"
|
||||||
|
|
||||||
|
this.dataDiv.style.display = "block"
|
||||||
|
this.dataDiv.innerHTML = "<b>" + obj.name + "</b><br><p>" + obj.value + "</p>"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.dataDiv.style.display = "none"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
window.addEventListener("resize", e => {
|
||||||
|
//newChart.updateLegend(graphSettings.displayLegend, data)
|
||||||
|
newChart.resizeCanvas(this.parent, this.legend.offsetHeight)
|
||||||
|
newChart.draw()
|
||||||
|
|
||||||
|
this.detectionCanvas.width = this.canvas.width
|
||||||
|
this.detectionCanvas.height = this.canvas.height
|
||||||
|
this.chart.drawDetectionMap(this.detectionCanvas.getContext("2d"))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
drawChart(graphSettings, data) {
|
||||||
|
//objects = []
|
||||||
|
this.chart = new Chart(this.canvas, data, graphSettings)
|
||||||
|
this.chart.updateLegend(graphSettings.displayLegend, this.legend)
|
||||||
|
|
||||||
|
this.chart.resizeCanvas(this.parent, this.legend.offsetHeight)
|
||||||
|
|
||||||
|
this.detectionCanvas.width = this.canvas.width
|
||||||
|
this.detectionCanvas.height = this.canvas.height
|
||||||
|
|
||||||
|
//Choose the correct graph
|
||||||
|
switch (graphSettings.type) {
|
||||||
|
case "point":
|
||||||
|
this.chart = new PointChart(this.canvas, data, graphSettings)
|
||||||
|
break
|
||||||
|
case "line":
|
||||||
|
this.chart = new LineChart(this.canvas, data, graphSettings)
|
||||||
|
break
|
||||||
|
case "smoothline":
|
||||||
|
this.chart = new SmoothLineChart(this.canvas, data, graphSettings)
|
||||||
|
break
|
||||||
|
case "pie":
|
||||||
|
this.chart = new PieChart(this.canvas, data, graphSettings)
|
||||||
|
break
|
||||||
|
case "donut":
|
||||||
|
this.chart = new DonutChart(this.canvas, data, graphSettings)
|
||||||
|
break
|
||||||
|
case "bar":
|
||||||
|
this.chart = new BarChart(this.canvas, data, graphSettings)
|
||||||
|
break
|
||||||
|
case "area":
|
||||||
|
this.chart = new AreaChart(this.canvas, data, graphSettings)
|
||||||
|
break
|
||||||
|
case "smootharea":
|
||||||
|
this.chart = new SmoothAreaChart(this.canvas, data, graphSettings)
|
||||||
|
break
|
||||||
|
case "stacked":
|
||||||
|
this.chart = new StackedChart(this.canvas, data, graphSettings)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
this.chart.draw()
|
||||||
|
this.chart.drawDetectionMap(this.detectionCanvas.getContext("2d"))
|
||||||
|
|
||||||
|
this.addListener(this.chart)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
class DonutChart extends Chart {
|
||||||
|
constructor(canvas, data, settings) {
|
||||||
|
super(canvas, data, settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
let index = 0
|
||||||
|
let start_angle = 0
|
||||||
|
let total_value = 0
|
||||||
|
|
||||||
|
this.data.forEach(categ => {
|
||||||
|
let val = categ.values[0]
|
||||||
|
if (val !== null)
|
||||||
|
total_value += val
|
||||||
|
})
|
||||||
|
|
||||||
|
this.clear()
|
||||||
|
|
||||||
|
this.data.forEach(categ => {
|
||||||
|
let val = categ.values[0]
|
||||||
|
let slice_angle = 2 * Math.PI * val / total_value
|
||||||
|
|
||||||
|
let x = this.canvas.width / 2
|
||||||
|
let y = this.canvas.height / 2
|
||||||
|
let r = Math.min(this.bounds.width / 2, this.bounds.height / 2)
|
||||||
|
let end_angle = start_angle + slice_angle
|
||||||
|
|
||||||
|
this.ctx.fillStyle = categ.color
|
||||||
|
let new_slice = new DonutSlice(this.ctx, val + " (" + Math.round(val / total_value * 100) + "%)", categ.col_name, x, y, r, start_angle, end_angle, r / 2)
|
||||||
|
new_slice.draw()
|
||||||
|
this.objects.push(new_slice)
|
||||||
|
|
||||||
|
start_angle = end_angle
|
||||||
|
index++
|
||||||
|
})
|
||||||
|
|
||||||
|
if (this.settings.displayTitle)
|
||||||
|
this.drawTitle()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
class SmoothAreaChart extends PointChart {
|
||||||
|
constructor(canvas, data, settings) {
|
||||||
|
super(canvas, data, settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
this.clear()
|
||||||
|
this.drawAxis()
|
||||||
|
|
||||||
|
this.data.forEach(categ => {
|
||||||
|
//Lines
|
||||||
|
this.ctx.beginPath()
|
||||||
|
this.ctx.lineJoin = "round"
|
||||||
|
this.ctx.strokeStyle = categ.color
|
||||||
|
|
||||||
|
let x = this.bounds.left
|
||||||
|
let y = (this.bounds.xAxis - categ.values[0] / this.extreme * this.scale)
|
||||||
|
this.ctx.moveTo(x, y)
|
||||||
|
let xmax = 0
|
||||||
|
|
||||||
|
for (let i = 0; i < this.dataLen - 1; i++) {
|
||||||
|
let x1 = this.bounds.left + this.bounds.width / (this.dataLen - 1) * i
|
||||||
|
let y1 = (this.bounds.xAxis - categ.values[i] / this.extreme * this.scale)
|
||||||
|
|
||||||
|
let x2 = this.bounds.left + this.bounds.width / (this.dataLen - 1) * (i + 1)
|
||||||
|
let y2 = (this.bounds.xAxis - categ.values[i + 1] / this.extreme * this.scale)
|
||||||
|
|
||||||
|
let xm = (x1 + x2) / 2
|
||||||
|
let xl = (x1 + xm) / 2
|
||||||
|
let xr = (x2 + xm) / 2
|
||||||
|
|
||||||
|
this.ctx.bezierCurveTo(xl, y1, xr, y2, x2, y2);
|
||||||
|
xmax = x2
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ctx.lineTo(xmax, this.bounds.xAxis)
|
||||||
|
this.ctx.lineTo(this.bounds.left, this.bounds.xAxis)
|
||||||
|
this.ctx.globalAlpha = 0.5
|
||||||
|
this.ctx.fillStyle = categ.color
|
||||||
|
this.ctx.closePath()
|
||||||
|
this.ctx.fill()
|
||||||
|
|
||||||
|
this.ctx.globalAlpha = 1
|
||||||
|
})
|
||||||
|
|
||||||
|
// Points
|
||||||
|
if (this.settings.displayPoints)
|
||||||
|
this.data.forEach(categ => {this.drawPoints(categ.values, categ.col_name, categ.color)})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
class SmoothLineChart extends PointChart {
|
||||||
|
constructor(canvas, data, settings) {
|
||||||
|
super(canvas, data, settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
this.clear()
|
||||||
|
this.drawAxis()
|
||||||
|
|
||||||
|
this.data.forEach(categ => {
|
||||||
|
this.ctx.beginPath()
|
||||||
|
this.ctx.lineJoin = "round"
|
||||||
|
this.ctx.strokeStyle = categ.color
|
||||||
|
|
||||||
|
let x = this.bounds.left
|
||||||
|
let y = (this.bounds.xAxis - categ.values[0] / this.extreme * this.scale)
|
||||||
|
this.ctx.moveTo(x, y)
|
||||||
|
|
||||||
|
for (let i = 0; i < this.dataLen - 1; i++) {
|
||||||
|
let x1 = this.bounds.left + this.bounds.width / (this.dataLen - 1) * i
|
||||||
|
let y1 = (this.bounds.xAxis - categ.values[i] / this.extreme * this.scale)
|
||||||
|
|
||||||
|
let x2 = this.bounds.left + this.bounds.width / (this.dataLen - 1) * (i + 1)
|
||||||
|
let y2 = (this.bounds.xAxis - categ.values[i + 1] / this.extreme * this.scale)
|
||||||
|
|
||||||
|
let xm = (x1 + x2) / 2
|
||||||
|
let xl = (x1 + xm) / 2
|
||||||
|
let xr = (x2 + xm) / 2
|
||||||
|
|
||||||
|
this.ctx.bezierCurveTo(xl, y1, xr, y2, x2, y2);
|
||||||
|
}
|
||||||
|
this.ctx.stroke()
|
||||||
|
this.ctx.closePath()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Points
|
||||||
|
if (this.settings.displayPoints)
|
||||||
|
this.data.forEach(categ => {this.drawPoints(categ.values, categ.col_name, categ.color)})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
:root {
|
||||||
|
--legend-height: 30px
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chartCanvas,
|
||||||
|
#detectionCanvas {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 1;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#graphLegend {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 2;
|
||||||
|
display: none;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#graphLegend div {
|
||||||
|
display: inline;
|
||||||
|
margin: 0 10px 0 10px;
|
||||||
|
width: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dataDiv {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
height: 35px;
|
||||||
|
width: 70px;
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid lightgrey;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dataDiv p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dataDiv b {
|
||||||
|
position: center;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#detectionCanvas {
|
||||||
|
opacity: 0;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
image-rendering: crisp-edges;
|
||||||
|
}
|
||||||
Loading…
Reference in new issue