Here is what i have got so far with help of stackoverflow answers.
I am able to add description to bars on the bar chart but now struggling with putting label and scale on Y axis.
window.onload = function ()
{
var r = Raphael("diagram"),
data3 = [25, 20, 13, 32, 15, 5, 6, 10],
txtattr = { font: "24px 'Allerta Stencil', sans-serif", fill: "rgb(105, 136, 39)" };
r.text(250, 10, "Sample Chart").attr(txtattr);
var bc = r.barchart(10, 10, 500, 400, data3,
{
stacked: false,
type: "sharp"
});
bc.attr({ fill: "#2f69bf" });
var x = 1;
labelBarChart(r, bc,
['abc', 'b', 'card', 'd', 'elph', 'fun', 'gurr', 'ha'],
{ fill: "#2f69bf", font: "16px sans-serif" }
);
};
function labelBarChart(r, bc, labels, attrs)
{
for (var i = 0; i < bc.bars.length; i++)
{
var bar = bc.bars[i];
var gutterY = bar.w * 0.4;
var labelX = bar.x;
var labelY = bar.y + bar.h + gutterY;
var labelText = labels[i];
var labelAttr = { fill: "#2f69bf", font: "16px sans-serif" };
r.text(labelX, labelY, labelText).attr(labelAttr);
}
}
I would like the graph to look like this with description and units on Y Axis, don’t need anything fancy like emboss effects etc.
!
Related
I have a logic problem. I need to distribute an array of values evenly to another array of values. To illustrate:
const colors = ['green', 'yellow', 'red']
const plots = [0, 10, 40, 90, 150, 230, 250]
const withColors = plots.map(e => ({
value: e, color: ???
}))
/* expected output:
[
{value: 0, color: 'green'},
{value: 10, color: 'green'},
{value: 40, color: 'yellow'},
{value: 90, color: 'yellow'},
{value: 150, color: 'red'},
{value: 230, color: 'red'},
{value: 250, color: 'red'},
]
*/
current solution, I definitely have no idea yet, and I will update my question as I am currently brainstorming how to solve this.
Here's a brute force method where I iterate through the array and give each third of the plots a color.
Another way would be to write a helper function that handles turning the values of i/plots.length into 0, 1, or 2 and return that.
const colors = ['green', 'yellow', 'red'];
const plots = [0, 10, 40, 90, 150, 230, 250];
const result = {};
for (var i=0; i<plots.length; i++) {
var thisPlot = plots[i];
if (i / plots.length < 1/3.0) {
result[thisPlot] = colors[0];
}
else if (i / plots.length < 2/3.0) {
result[thisPlot] = colors[1];
}
else {
result[thisPlot] = colors[2];
}
}
console.log(result);
Can't see any logic behind this how this pattern will grow in future, but certainly you can try something like below.
// const colors = ['green', 'yellow', 'red']
const plots = [0, 10, 40, 90, 150, 230, 250]
function getColors(val) {
if(val >= 150 && val <=250) return "red";
if(val >= 40 && val <= 90) return "yellow";
return "green"
}
const withColors = plots.map(e => ({
value: e, color: getColors(e)
}))
Assigning colors one by one to each index
This will spread colors evenly, and is the simplest algorithm, but does not keep the colors grouped as in your example.
const colors = ['green', 'yellow', 'red']
const plots = [0, 10, 40, 90, 150, 230, 250, 260]
const withColors = plots.map((plot, i) => ({ value: plot, color: colors[i % colors.length]}));
console.log(withColors);
Keeping colors grouped
Just calculate the number of plots to map to each color, and assign each color to the appropriate number of plots.
One caveat: We require plots per color to be at least one in the case that there are less plots than colors. In this case, some colors will not be included.
There is one thing you haven't specified: what to do with the remainder. In your example plots.length / colors.length == 7 / 3 is 2 with a remainder of 1. In other words, 2 plots per color, and 1 plot left over. Having a remainder of 1 is easy: just assign any color to an extra plot. But what about larger remainders? There's a few strategies.
Assigning the last color to all trailing plots
Just round down plots per color and keep using the last color for any extra plots.
const colors = ['green', 'yellow', 'red']
const plots = [0, 10, 40, 90, 150, 230, 250, 260]
const plotsPerColor = Math.max(1, Math.floor(plots.length / colors.length));
let colorIdx = 0;
let count = 0;
const withColors = plots.map((plot) => {
const result = { value: plot, color: colors[colorIdx]};
if (++count === plotsPerColor && colorIdx < colors.length-1) {
colorIdx++;
count = 0;
}
return result;
})
console.log(withColors);
Spreading remainder evenly at end
Round down plots per color and increase it by one at the appropriate time.
const colors = ['green', 'yellow', 'red']
const plots = [0, 10, 40, 90, 150, 230, 250, 260]
let plotsPerColor = Math.max(1, Math.floor(plots.length / colors.length));
let remainder = plots.length % colors.length;
let colorIdx = 0;
let count = 0;
const withColors = plots.map((plot) => {
const result = { value: plot, color: colors[colorIdx]};
if (++count === plotsPerColor) {
if (++colorIdx === colors.length - remainder) plotsPerColor++;
count = 0;
}
return result;
});
console.log(withColors);
Spreading remainder evenly at beginning
Round up plots per color and decrease it by one at the appropriate time.
const colors = ['green', 'yellow', 'red']
const plots = [0, 10, 40, 90, 150, 230, 250, 260]
let plotsPerColor = Math.ceil(plots.length / colors.length);
let remainder = plots.length % colors.length;
let colorIdx = 0;
let count = 0;
const withColors = plots.map((plot) => {
const result = { value: plot, color: colors[colorIdx]};
if (++count === plotsPerColor) {
if (++colorIdx === remainder) plotsPerColor--;
count = 0;
}
return result;
});
console.log(withColors);
I have some data saved and it is huge, 100.000 plus points saved in the database. In this format:
database_data = [
{ x: 100, y: 100, value: 1},
{ x: 200, y: 100, value: 1},
{ x: 120, y: 320, value: 1},
...
]
I am giving the value of 1 for each point as you can see above. I would like to use:
heatmap.setData({
max: max_value,
data: database_data
});
To plot the data on the screen. How can I calculate the max_value of the heatmap? Because I wanna have the information been displayed on the screen where the most red color is the points where we have more frequently in the database and the less red color would be the point where we have only once in the database. But as we know, (x:100, y:100) and x:120, y: 120) will have some intersection between them, and that would also need to be consider, just like the normal heat map.
In other words, I want to know how to automatically calculate the value for "max" in the heatmap using setData function!
Here below it is a function that is working fine, but it takes too much time:
var body = document.body;
var bodyStyle = getComputedStyle(body);
var hmEl = document.querySelector(".heatmap-wrapper");
hmEl.style.width = bodyStyle.width;
hmEl.style.height = bodyStyle.height;
hmEl.style.background = "#3e1852";
hmEl.style.zIndex = "1000";
var hm = document.querySelector(".heatmap");
var heatmap = h337.create({
container: hm,
radius: 40,
});
var dbh_x = <?php echo $dbh_x;?>; //LIKE THIS->["100", "200", "300","400", "420", "500", "600", "600"];
var dbh_y = <?php echo $dbh_y;?>;//LIKE THIS->["100", "200", "300","400", "420", "500", "600", "600"];
for ( var i =0; i< dbh_x.length; i++){
heatmap.addData({
x: dbh_x[i],
y: dbh_y[i],
});
};
Because of the loading time, I was trying to use setData instead:
var body = document.body;
var bodyStyle = getComputedStyle(body);
var hmEl = document.querySelector(".heatmap-wrapper");
hmEl.style.width = bodyStyle.width;
hmEl.style.height = bodyStyle.height;
hmEl.style.background = "#3e1852";
hmEl.style.zIndex = "1000";
var hm = document.querySelector(".heatmap");
var heatmap = h337.create({
container: hm,
radius: 40,
});
heatmap.setData({//This function will show the data kind a fast for 100000+points
max: 10, //This is the problem
data: [{ x: 891, y: 50, value: 1},{ x: 891, y: 50, value: 1},...]
});
Appreciate your guys time!
I made a line chart using Chart.js version 2.1.3.
var canvas = $('#gold_chart').get(0);
var ctx = canvas.getContext('2d');
var fillPatternGold = ctx.createLinearGradient(0, 0, 0, canvas.height);
fillPatternGold.addColorStop(0, '#fdca55');
fillPatternGold.addColorStop(1, '#ffffff');
var goldChart = new Chart(ctx, {
type: 'line',
animation: false,
data: {
labels: dates,
datasets: [{
label: '',
data: prices,
pointRadius: 0,
borderWidth: 1,
borderColor: '#a97f35',
backgroundColor: fillPatternGold
}]
},
title: {
position: 'bottom',
text: '\u7F8E\u5143 / \u76CE\u53F8'
},
options: {
legend: {
display: false
},
tooltips: {
callback: function(tooltipItem) {
return tooltipItem.yLabel;
}
},
scales: {
xAxes: [{
ticks: {
maxTicksLimit: 8
}
}]
}
}
});
The output is as follow:
As you can see, I limited the maximum count of ticks to 8 via maxTicksLimit. However, the distribution is not even. How can I make the ticks distribute evenly?
p.s. there are always 289 records in the dataset, and the data is recorded every 5 minutes. Sample values of prices variable are:
[
{"14:10", 1280.3},
{"14:15", 1280.25},
{"14:20", 1282.85}
]
I tried different values of maxTicksLimit, and the results are still not distributed evenly.
Chart.js uses an integral skipRatio (to figure out how many labels to skip). With Chart.js v2.1.x, you can write your own plugin to use a fractional skipRatio
Preview
Script
Chart.pluginService.register({
afterUpdate: function (chart) {
var xScale = chart.scales['x-axis-0'];
if (xScale.options.ticks.maxTicksLimit) {
// store the original maxTicksLimit
xScale.options.ticks._maxTicksLimit = xScale.options.ticks.maxTicksLimit;
// let chart.js draw the first and last label
xScale.options.ticks.maxTicksLimit = (xScale.ticks.length % xScale.options.ticks._maxTicksLimit === 0) ? 1 : 2;
var originalXScaleDraw = xScale.draw
xScale.draw = function () {
originalXScaleDraw.apply(this, arguments);
var xScale = chart.scales['x-axis-0'];
if (xScale.options.ticks.maxTicksLimit) {
var helpers = Chart.helpers;
var tickFontColor = helpers.getValueOrDefault(xScale.options.ticks.fontColor, Chart.defaults.global.defaultFontColor);
var tickFontSize = helpers.getValueOrDefault(xScale.options.ticks.fontSize, Chart.defaults.global.defaultFontSize);
var tickFontStyle = helpers.getValueOrDefault(xScale.options.ticks.fontStyle, Chart.defaults.global.defaultFontStyle);
var tickFontFamily = helpers.getValueOrDefault(xScale.options.ticks.fontFamily, Chart.defaults.global.defaultFontFamily);
var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
var tl = xScale.options.gridLines.tickMarkLength;
var isRotated = xScale.labelRotation !== 0;
var yTickStart = xScale.top;
var yTickEnd = xScale.top + tl;
var chartArea = chart.chartArea;
// use the saved ticks
var maxTicks = xScale.options.ticks._maxTicksLimit - 1;
var ticksPerVisibleTick = xScale.ticks.length / maxTicks;
// chart.js uses an integral skipRatio - this causes all the fractional ticks to be accounted for between the last 2 labels
// we use a fractional skipRatio
var ticksCovered = 0;
helpers.each(xScale.ticks, function (label, index) {
if (index < ticksCovered)
return;
ticksCovered += ticksPerVisibleTick;
// chart.js has already drawn these 2
if (index === 0 || index === (xScale.ticks.length - 1))
return;
// copy of chart.js code
var xLineValue = this.getPixelForTick(index);
var xLabelValue = this.getPixelForTick(index, this.options.gridLines.offsetGridLines);
if (this.options.gridLines.display) {
this.ctx.lineWidth = this.options.gridLines.lineWidth;
this.ctx.strokeStyle = this.options.gridLines.color;
xLineValue += helpers.aliasPixel(this.ctx.lineWidth);
// Draw the label area
this.ctx.beginPath();
if (this.options.gridLines.drawTicks) {
this.ctx.moveTo(xLineValue, yTickStart);
this.ctx.lineTo(xLineValue, yTickEnd);
}
// Draw the chart area
if (this.options.gridLines.drawOnChartArea) {
this.ctx.moveTo(xLineValue, chartArea.top);
this.ctx.lineTo(xLineValue, chartArea.bottom);
}
// Need to stroke in the loop because we are potentially changing line widths & colours
this.ctx.stroke();
}
if (this.options.ticks.display) {
this.ctx.save();
this.ctx.translate(xLabelValue + this.options.ticks.labelOffset, (isRotated) ? this.top + 12 : this.options.position === "top" ? this.bottom - tl : this.top + tl);
this.ctx.rotate(helpers.toRadians(this.labelRotation) * -1);
this.ctx.font = tickLabelFont;
this.ctx.textAlign = (isRotated) ? "right" : "center";
this.ctx.textBaseline = (isRotated) ? "middle" : this.options.position === "top" ? "bottom" : "top";
this.ctx.fillText(label, 0, 0);
this.ctx.restore();
}
}, xScale);
}
};
}
},
});
Fiddle - http://jsfiddle.net/bh63pe1v/
A simpler solution until this is permanently fixed by the Chart JS contributors is to include a decimal in maxTicksLimit.
For example:
maxTicksLimit: 8,
produces a huge gap at the end.
maxTicksLimit: 8.1,
Does not produce a huge gap at the end.
Depending on what you want to set your maxTicksLimit to, you need to play around with different decimals to see which one produces the best result.
Just do this:
yAxes: [{
ticks: {
stepSize: Math.round((Math.max.apply(Math, myListOfyValues) / 10)/5)*5,
beginAtZero: true,
precision: 0
}
}]
10 = the number of ticks
5 = rounds tick values to the nearest 5 - all y values will be incremented evenly
Similar will work for xAxes too.
I need to have more than ~400 text objects on the scene. Each text object is one sentence of a decimal number with two digit. Echa text is reading from a JSON file. Each text object have a position (x,y,z) in the scene.
Using a basic scene i'm having, it takes a lot of times to load each text.
The code is here:
function setText(text, textColor, textSize, positionX, positionY, positionZ) {
var textGeo = new THREE.TextGeometry(text, {
height: 0,
curveSegments: 4,
font: "helvetiker",
//font: "optimer",
weight: "normal",
style: "normal",
size: textSize,
//
//bevelThickness: bevelThickness,
//bevelSize: bevelSize,
//bevelEnabled: bevelEnabled,
material: 0,
extrudeMaterial: 1
});
textGeo.computeBoundingBox();
textGeo.computeVertexNormals();
var textMaterial = new THREE.MeshBasicMaterial({
shading: THREE.FlatShading,
transparent: true,
depthWrite: false,
transparent: true,
needsUpdate: true,
color: textColor,
side:THREE.DoubleSide
});
var text = new THREE.Mesh(textGeo, textMaterial);
text.position.x = positionX;
text.position.y = positionY;
text.position.z = positionZ;
return text;
}
Examples of texts:
"model", "metamodel", "syntax", "sentence" ...
Examples of numbers:
7.32,
7.81,
8.30,
8.78,
Could you please help me? Thanks!
EDIT: The new code is here :
function createTextGeo(textSize, text) {
var tGeo = new THREE.TextGeometry(text, {
height: 0,
curveSegments: 4,
font: "helvetiker",
weight: "normal",
style: "normal",
size: textSize,
material: 0,
extrudeMaterial: 1
});
tGeo.computeBoundingBox();
tGeo.computeVertexNormals();
return tGeo;
}
function createGeoMaterial(textColor) {
var tMat = new THREE.MeshBasicMaterial({
color: textColor,
});
return tMat;
}
function setText(text, textColor, tMat, tGeo, X, Y, Z) {
tMat.color = textColor;
tGeo.text = text;
var text = new THREE.Mesh(tGeo, tMat);
text.position.set( X, Y, Z );
return text;
}
function createTexts(value) {
var text;
var mesh;
var x = 5;
var y = 8;
var z = 10;
var keyColor = new THREE.Color('#0B0B61');
var tMat = createGeoMaterial(keyColor);
var tGeo = createTextGeo(5, "");
for (var i = 0; i < value; i++) {
mesh = setText(i, keyColor, tMat, tGeo, x * i, y * i, z * i);
scene.add(mesh);
}
}
Here is the code: http://jsfiddle.net/MTDpC/
function drawArrow(firstShape, lastShape){
var group = new Kinetic.Group();
group.previousShape = firstShape;
group.nextShape = lastShape;
var beginX = group.previousShape.getX() + group.previousShape.getChildren()[0].getWidth() / 2;
var beginY = group.previousShape.getY() + group.previousShape.getChildren()[0].getHeight();
var endX = group.nextShape.getX() + group.nextShape.getChildren()[0].getWidth() / 2;
var endY = group.nextShape.getY() - 8;
var linha = new Kinetic.Line({
points: [ beginX, beginY, endX, endY],
name: 'linha',
stroke: '#555',
strokeWidth: 4,
lineCap: 'butt'
});
var seta = new Kinetic.RegularPolygon({
x: endX,
y: endY,
sides: 3,
radius: 4,
fill: '#555',
stroke: '#555',
strokeWidth: 4,
name: 'seta'
});
seta.rotateDeg(180);
group.add(seta);
group.add(linha);
firstShape.arrow = group;
lastShape.arrow = group;
group.reposition = function(){
var beginX = this.previousShape.getX() + this.previousShape.getChildren()[0].getWidth() / 2;
var beginY = this.previousShape.getY() + this.previousShape.getChildren()[0].getHeight();
var endX = this.nextShape.getX() + this.nextShape.getChildren()[0].getWidth() / 2;
var endY = this.nextShape.getY() - 8;
this.get('.linha')[0].transitionTo({
points:[ beginX, beginY, endX, endY],
duration: 0.0000001,
});
this.get('.seta')[0].transitionTo({
x: endX,
y: endY,
duration: 0.0000001,
});
};
return group;
}
function getProcess (processText, x, y, width) {
var group = new Kinetic.Group();
var complexText = new Kinetic.Text({
text: processText + '\nX: ' + x + '\nY: ' + y,
fontSize: 12,
fontFamily: 'Calibri',
fill: '#555',
padding: 20,
align: 'center',
name: 'Texto-Processo'
});
var rect = new Kinetic.Rect({
stroke: '#555',
strokeWidth: 5,
fill: '#ddd',
width: complexText.getWidth(),
height: complexText.getHeight(),
shadowColor: 'black',
shadowBlur: 10,
shadowOffset: [10, 10],
shadowOpacity: 0.2,
cornerRadius: 10,
name: 'Quadro-Processo'
});
group.add(rect);
group.add(complexText);
group.setDraggable(true);
group.on('mouseover', function() {
document.body.style.cursor = 'pointer';
});
group.on('mouseout', function() {
document.body.style.cursor = 'default';
});
group.on('dragmove', function(){
group.getChildren()[1].setText(this.customText + "\nX: " + this.getX() + "\nY: " + this.getY());
group.arrow.reposition();
group.arrow.draw();
});
group.setX(x);
group.setY(y);
group.customText = processText;
return group;
}
var stage = new Kinetic.Stage({
container: 'container',
width: 800,
height: 600,
id: 'myCanvas'
});
var layer = new Kinetic.Layer();
var processo = getProcess('Processo de Teste de canvas 1', (stage.getWidth() / 2) - 100, 10 , 100);
processo.setId('canvas1');
layer.add(processo);
var processo2 = getProcess('Processo de Teste de canvas 2', (stage.getWidth() / 2) -100, processo.getY() + 300, processo.getX());
layer.add(processo2);
layer.add(drawArrow(processo, processo2));
// add the layer to the stage
stage.add(layer);
When one of the boxes are dragged, the arrow connecting them moves too.
As the arrow was made using 2 shapes, I want to know if is there some way to use .transtionTo at the same time, because you can see some delay between the line and the triangle that forms the arrrow when you move the bottom box.
The problem is that the transitionTo function does not support transitioning points. Also, each time you move the object, you are creating a new transition object... which is extremely slow. You do not need transitions for this, transitions are only useful for animating things automatically when the user is not meant to control them.
Try this instead:
this.get('.linha')[0].setPoints([beginX, beginY, endX, endY]);
this.get('.seta')[0].setPosition(endX,endY);
this.getLayer().draw();
/* this.get('.linha')[0].transitionTo({ //cant transition points
points: [beginX, beginY, endX, endY],
duration: 0.0000001,
});
this.get('.seta')[0].transitionTo({ // lags because everytime you move, a new transition is created, no this would be extremely slow
x: endX,
y: endY,
duration: 0.0000001,
});
*/
http://jsfiddle.net/MTDpC/1/
works fast eh?