I am following this tutorial https://bytutorial.com/blogs/open-source-chart/learn-how-to-create-multiple-bar-charts-with-multiple-line-charts-in-nvd3 which is great .
Even though, it has a colorfull way to display the key values. Instead of the name of the key, shows a div with the background color of the key:
<div class='tooltip'>
<span>Legend:</span>
<div style='background:" + key.series[0].color + ";display:inline-block;height:150px;width:150px;'> </div>
</div>
How can I show the name of the key?, instead of its color.
This is my graph: http://codepen.io/neonpulp/pen/zBZgrv . Below is the full code also.
function buildGraph(){
//this will hold of our main data consists of multiple chart data
var data = [];
//variables to hold monthly month
var monthList = ['Ene 2016','Feb 2016','Mar 2016','Apr 2016','May 2014','Jun 2016','Jul 2016','Aug 2016', 'Sep 2016', 'Oct 2016', 'Nov 2016', 'Dec 2016'];
var monthlyIncome = [0, 2757820, 3447270, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var companiesNumber = [0, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0];
//Array to hold each individual coordinate x and y values
var monthlyIncomeValues = [];
var companiesNumberValues = [];
//Looping the data and fetch into array
for(var i = 0; i < monthList.length; i++){
var xyIncome = {x: i, y: monthlyIncome[i]};
monthlyIncomeValues.push(xyIncome);
var xyCompanies = {x: i, y: companiesNumber[i]};
companiesNumberValues.push(xyCompanies);
}
//bar chart
var dataIncome = { key: "Monthly Income", values: monthlyIncomeValues, type: "bar", yAxis: 2, color: '#00913B' }
//line chart
var dataCompanies = { key: "Amount of companies", values: companiesNumberValues, type: "line", yAxis: 1, color: '#00C3D9' }
//Insert the values array into data variable
data.push(dataIncome);
data.push(dataCompanies);
//build the graph
nv.addGraph(function () {
//build as multichart graphs and set the margin right and left to 100px.
var chart = nv.models.multiChart()
.margin({left: 100, right: 100})
//customize the tool tip
chart.tooltip.contentGenerator(function (key, x, y, e, graph) {
return "<div class='tooltip'><span>Month:</span> " + monthList[key.index] + "</div>" + "<div class='tooltip'><span>Value:</span> " + key.series[0].value + "</div><div class='tooltip'><span>Legend:</span> <div style='background:" + key.series[0].color + ";display:inline-block;height:15px;width:15px;'> </div></div>";
});
//Overwrite the x axis label and replace it with the month name
chart.xAxis.tickFormat(function (d) { return monthList[d] });
//Chart Interpolate
chart.interpolate("linear")
//Dollar Sign
chart.yAxis2.tickFormat(function(d) { return '$' + d3.format(',f')(d) });
//get the chart svg object and fecth the data to build the chart
d3.select('#chart svg')
.datum(data)
.transition().duration(500).call(chart);
return chart;
});
}
//call the function to build the graph.
buildGraph();
Related
With the help of TextGeometry, I am drawing text around meshes. PerspectiveCamera is in steady mode, My cube is moving itself in the scene. How can text take a look to the object rotation?
Check this I want to do like this same.
var storeCamera = new THREE.PerspectiveCamera(75, $domElement.innerWidth() / $domElement.innerHeight(), 1, 10000);
dist = (largestSide - 0) / 2 + (largestSide - 0) / (2 * Math.tan(storeCamera.fov * Math.PI / 360));
storeCamera.position.z = dist;
var axisHelperScene = new THREE.Scene();
var axisHelperCamera = new THREE.PerspectiveCamera(75, $domElement.find('.axis-helper').innerWidth() / $domElement.find('.axis-helper').innerHeight(), 1, 10000);
axisHelperCamera.position.z = 350;
var axisHelperRenderer = new THREE.WebGLRenderer({
antialias: true, alpha: true
});
axisHelperRenderer.setSize($domElement.find('.axis-helper').innerWidth(), $domElement.find('.axis-helper').innerHeight());
$domElement.find('.axis-helper').append(axisHelperRenderer.domElement);
var directionX = new THREE.Vector3(1, 0, 0);
var directionY = new THREE.Vector3(0, 0, -1);
var directionZ = new THREE.Vector3(0, 1, 0);
var origin = new THREE.Vector3(0, 0, 0);
var length = 150;
var color = 0x4a4f65;
var headLength = 45;
var headWidth = 30;
// creating axes
var arrowHelperX = new THREE.ArrowHelper(directionX, origin, length, color, headLength, headWidth);
arrowHelperX.line.material.linewidth = 4;
var arrowHelperY = new THREE.ArrowHelper(directionY, origin, length, color, headLength, headWidth);
arrowHelperX.line.material.linewidth = 4;
var arrowHelperZ = new THREE.ArrowHelper(directionZ, origin, length, color, headLength, headWidth);
arrowHelperX.line.material.linewidth = 4;
// origin sphere
var axisSphere = new THREE.Mesh(new THREE.SphereGeometry(15, 40, 40, 40),
new THREE.MeshLambertMaterial({ color: 0x4a4f65 }));
var axisGroup = new THREE.Group();
axisGroup.add(arrowHelperX);
axisGroup.add(arrowHelperY);
axisGroup.add(arrowHelperZ);
axisGroup.add(axisSphere);
axisGroup.rotation.x = 0.60;
axisGroup.rotation.y = -0.50;
// #region Adding axis labels
var fontLoader = new THREE.FontLoader();
var jsonFont = String.format("{0}Scripts/threejs/json/helvetiker_regular.typeface.json", GetResourceFromClient("RootUrl"));
fontLoader.load(jsonFont, function (theFont) {
var xLabel = new THREE.TextGeometry("X", {
font: theFont,
size: 35,
height: 10,
curveSegments: 12,
bevelThickness: 1,
bevelSize: 1,
bevelEnabled: true
});
var yLabel = new THREE.TextGeometry("Y", {
font: theFont,
size: 35,
height: 10,
curveSegments: 12,
bevelThickness: 1,
bevelSize: 1,
bevelEnabled: true
});
var zLabel = new THREE.TextGeometry("Z", {
font: theFont,
size: 35,
height: 10,
curveSegments: 12,
bevelThickness: 1,
bevelSize: 1,
bevelEnabled: true
});
var textMaterial = new THREE.MeshLambertMaterial({
color: 0x4a4f65
});
var xMesh = new THREE.Mesh(xLabel, textMaterial);
xMesh.position.x = 160;
xMesh.position.y = -10;
axisGroup.add(xMesh);
var yMesh = new THREE.Mesh(yLabel, textMaterial);
yMesh.position.x = -10;
yMesh.position.z = -160;
axisGroup.add(yMesh);
var zMesh = new THREE.Mesh(zLabel, textMaterial);
zMesh.position.x = -10;
zMesh.position.y = 160;
axisGroup.add(zMesh);
});
// #endregion Adding axis labels
function render() {
storeRenderer.render(storeScene, storeCamera);
axisHelperRenderer.render(axisHelperScene, axisHelperCamera);
requestAnimFrame(render);
}
Instead of using a TextGeometry for the axis labels a sprite could be an option
var xLabel = makeTextSprite(" X ", { fontsize: 100, borderColor: { r: 0, g: 0, b: 0, a: 1.0 }, backgroundColor: { r: 255, g: 255, b: 255, a: 0.8 } , borderThickness: 5});
xLabel.position.set(150, 0, 0);
axisGroup.add(xLabel);
// creates a THREE.Sprite object with the given text and parameters
function makeTextSprite(theMessage, theParameters) {
if (theParameters === undefined) theParameters = {
};
var fontface = theParameters.hasOwnProperty("fontface") ?
theParameters["fontface"] : "Arial";
var fontsize = theParameters.hasOwnProperty("fontsize") ?
theParameters["fontsize"] : 18;
var borderThickness = theParameters.hasOwnProperty("borderThickness") ?
theParameters["borderThickness"] : 4;
var borderColor = theParameters.hasOwnProperty("borderColor") ?
theParameters["borderColor"] : {
r: 0, g: 0, b: 0, a: 1.0
};
var backgroundColor = theParameters.hasOwnProperty("backgroundColor") ?
theParameters["backgroundColor"] : {
r: 255, g: 255, b: 255, a: 1.0
};
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.font = "Bold " + fontsize + "px " + fontface;
// get size data (height depends only on font size)
var metrics = context.measureText(theMessage);
var textWidth = metrics.width;
// background color
context.fillStyle = "rgba(" + backgroundColor.r + "," + backgroundColor.g + ","
+ backgroundColor.b + "," + backgroundColor.a + ")";
// border color
context.strokeStyle = "rgba(" + borderColor.r + "," + borderColor.g + ","
+ borderColor.b + "," + borderColor.a + ")";
context.lineWidth = borderThickness;
roundRect(context, borderThickness / 2, borderThickness / 2, textWidth + borderThickness, fontsize * 1.4 + borderThickness, 6);
// 1.4 is extra height factor for text below baseline: g,j,p,q.
// text color
context.fillStyle = "rgba(0, 0, 0, 1.0)";
context.fillText(theMessage, borderThickness, fontsize + borderThickness);
// canvas contents will be used for a texture
var texture = new THREE.Texture(canvas)
texture.needsUpdate = true;
var spriteMaterial = new THREE.SpriteMaterial(
{
map: texture, useScreenCoordinates: false
});
var sprite = new THREE.Sprite(spriteMaterial);
sprite.scale.set(100, 50, 1.0);
return sprite;
}
// function for drawing rounded rectangles
function roundRect(ctx, x, y, w, h, r) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.quadraticCurveTo(x + w, y, x + w, y + r);
ctx.lineTo(x + w, y + h - r);
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
ctx.lineTo(x + r, y + h);
ctx.quadraticCurveTo(x, y + h, x, y + h - r);
ctx.lineTo(x, y + r);
ctx.quadraticCurveTo(x, y, x + r, y);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
}
I wanted to display the labels just like how it is done here, within chartjs documentation samples.
However, along with one label, I want to display another label beside the first label. Both the labels have different font colors. Something like this:
Is that possible? If yes, kindly let me know how can that be achieved.
Currently I am able to display one label using following code:
Chart.plugins.register({
afterDatasetsDraw: function(chart, easing) {
// To only draw at the end of animation, check for easing === 1
var ctx = chart.ctx;
chart.data.datasets.forEach(function (dataset, i) {
var meta = chart.getDatasetMeta(i);
if (!meta.hidden) {
meta.data.forEach(function(element, index) {
// Draw the text in black, with the specified font
ctx.fillStyle = 'rgb(0, 0, 0)';
var fontSize = 16;
var fontStyle = 'normal';
var fontFamily = 'Helvetica Neue';
ctx.font = Chart.helpers.fontString(fontSize, fontStyle, fontFamily);
// Just naively convert to string for now
var dataString = dataset.data[index].toString();
// Make sure alignment settings are correct
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
var padding = 5;
var position = element.tooltipPosition();
ctx.fillText(dataString, position.x, position.y - (fontSize / 2) - padding);
});
}
});
}
});
Yes, that is possible. You can achieve that using the following chart plugin :
Chart.plugins.register({
afterDatasetsDraw: function(chart, ease) {
var barLabels = chart.options.barLabels;
if (!barLabels) return;
var ctx = chart.ctx;
chart.data.datasets.forEach(function(dataset, index) {
var meta = chart.getDatasetMeta(index);
if (!meta.hidden) {
meta.data.forEach(function(segment, index) {
var model = segment._model,
position = segment.tooltipPosition(),
x = position.x,
y = position.y,
height = model.height,
padding = height / 4;
ctx.save();
ctx.textBaseline = 'middle';
ctx.font = 'bold ' + height / 2 + 'px Arial';
ctx.fillStyle = '#777'; //first label's font color
var text1 = barLabels.first[index],
text2 = barLabels.second[index],
textWidth = ctx.measureText(text1).width + padding;
ctx.fillText(text1, x + padding, y);
ctx.fillStyle = '#000'; //second label's font color
ctx.fillText(text2, x + padding + textWidth, y);
ctx.restore();
});
}
});
}
});
To utilize the plugin, define the following properties in your chart options :
barLabels: {
first: ['0.4M', '1.6M', '0.6M', '0.7M', '1.5M'],
second: ['19.3%', '19.1%', '14.1%', '9.0%', '8.9%']
}
ᴡᴏʀᴋɪɴɢ ᴇxᴀᴍᴘʟᴇ ⧩
Chart.plugins.register({
afterDatasetsDraw: function(chart, ease) {
var barLabels = chart.options.barLabels;
if (!barLabels) return;
var ctx = chart.ctx;
chart.data.datasets.forEach(function(dataset, index) {
var meta = chart.getDatasetMeta(index);
if (!meta.hidden) {
meta.data.forEach(function(segment, index) {
var model = segment._model,
position = segment.tooltipPosition(),
x = position.x,
y = position.y,
height = model.height,
padding = height / 4;
ctx.save();
ctx.textBaseline = 'middle';
ctx.font = 'bold ' + height / 2 + 'px Arial';
ctx.fillStyle = '#777'; //first label's font color
var text1 = barLabels.first[index],
text2 = barLabels.second[index],
textWidth = ctx.measureText(text1).width + padding;
ctx.fillText(text1, x + padding, y);
ctx.fillStyle = '#000'; //second label's font color
ctx.fillText(text2, x + padding + textWidth, y);
ctx.restore();
});
}
});
}
});
var chart = new Chart(ctx, {
type: 'horizontalBar',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
datasets: [{
label: 'Statistics',
data: [1, 4, 2, 3, 4],
backgroundColor: ['#fd625e', '#01b8aa', '#01b8aa', '#01b8aa', '#fd625e'],
}]
},
options: {
scales: {
xAxes: [{
ticks: {
beginAtZero: true,
stepSize: 1,
max: 6
}
}]
},
barLabels: {
first: ['0.4M', '1.6M', '0.6M', '0.7M', '1.5M'],
second: ['19.3%', '19.1%', '14.1%', '9.0%', '8.9%']
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>
<canvas id="ctx"></canvas>
I'm trying to create a "linePlusBar" chart with NVD3 . The x-axis values are the months of the year:
var monthList = ['Ene 2016','Feb 2016','Mar 2016','Apr 2016','May 2014','Jun 2016','Jul 2016','Aug 2016', 'Sep 2016', 'Oct 2016', 'Nov 2016', 'Dec 2016'];
For some reason only the bar graph shows this value in the tooltip, for instance Month:Aug 2016.
The line graph instead shows this value in the tooltip as "Month: undefined".
What could be the problem?
This is my graph: http://codepen.io/neonpulp/pen/zBZgrv . Below is the code also.
function buildGraph(){
//this will hold of our main data consists of multiple chart data
var data = [];
//variables to hold monthly month
var monthList = ['Ene 2016','Feb 2016','Mar 2016','Apr 2016','May 2014','Jun 2016','Jul 2016','Aug 2016', 'Sep 2016', 'Oct 2016', 'Nov 2016', 'Dec 2016'];
var monthlyIncome = [0, 2757820, 3447270, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var companiesNumber = [0, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0];
//Array to hold each individual coordinate x and y values
var monthlyIncomeValues = [];
var companiesNumberValues = [];
//Looping the data and fetch into array
for(var i = 0; i < monthList.length; i++){
var xyIncome = {x: i, y: monthlyIncome[i]};
monthlyIncomeValues.push(xyIncome);
var xyCompanies = {x: i, y: companiesNumber[i]};
companiesNumberValues.push(xyCompanies);
}
//bar chart
var dataIncome = { key: "Monthly Income", values: monthlyIncomeValues, type: "bar", yAxis: 2, color: '#00913B' }
//line chart
var dataCompanies = { key: "Amount of companies", values: companiesNumberValues, type: "line", yAxis: 1, color: '#00C3D9' }
//Insert the values array into data variable
data.push(dataIncome);
data.push(dataCompanies);
//build the graph
nv.addGraph(function () {
//build as multichart graphs and set the margin right and left to 100px.
var chart = nv.models.multiChart()
.margin({left: 100, right: 100})
//customize the tool tip
chart.tooltip.contentGenerator(function (key, x, y, e, graph) {
return "<div class='tooltip'><span>Month:</span> " + monthList[key.index] + "</div>" + "<div class='tooltip'><span>Value:</span> " + key.series[0].value + "</div><div class='tooltip'><span>Legend:</span> <div style='background:" + key.series[0].color + ";display:inline-block;height:15px;width:15px;'> </div></div>";
});
//Overwrite the x axis label and replace it with the month name
chart.xAxis.tickFormat(function (d) { return monthList[d] });
//Chart Interpolate
chart.interpolate("linear")
//Dollar Sign
chart.yAxis2.tickFormat(function(d) { return '$' + d3.format(',f')(d) });
//get the chart svg object and fecth the data to build the chart
d3.select('#chart svg')
.datum(data)
.transition().duration(500).call(chart);
return chart;
});
}
//call the function to build the graph.
buildGraph();
The reason is that key for line chart doesn't include index - it has pointIndex instead.
Try this:
monthList[key.index || key.pointIndex]
Demo: http://codepen.io/LukaszWiktor/pen/xOdKmQ
I've done a nvd3 graphic, but it does not load the json.
What changes do I have to do in nvd3? I'm a little lost... sorry.
nv.addGraph(function() {
var chart = nv.models.lineChart();
var fitScreen = false;
var width = 600;
var height = 400;
var zoom = 1;
chart.useInteractiveGuideline(true);
chart.xAxis
.tickFormat(d3.time.format("%Y%m%d").parse);
//.tickFormat(d3.format(',r'));
chart.yAxis
.axisLabel('Alumnos')
.tickFormat(d3.format(',.2f'));
d3.select('#chart1 svg')
.attr('perserveAspectRatio', 'xMinYMid')
.attr('width', width)
.attr('height', height)
.datum(datos);
setChartViewBox();
resizeChart();
// These resizes both do the same thing, and require recalculating the chart
//nv.utils.windowResize(chart.update);
//nv.utils.windowResize(function() { d3.select('#chart1 svg').call(chart) });
nv.utils.windowResize(resizeChart);
d3.select('#zoomIn').on('click', zoomIn);
d3.select('#zoomOut').on('click', zoomOut);
function setChartViewBox() {
var w = width * zoom,
h = height * zoom;
chart
.width(w)
.height(h);
d3.select('#chart1 svg')
.attr('viewBox', '0 0 ' + w + ' ' + h)
.transition().duration(500)
.call(chart);
}
function zoomOut() {
zoom += .25;
setChartViewBox();
}
function zoomIn() {
if (zoom <= .5) return;
zoom -= .25;
setChartViewBox();
}
// This resize simply sets the SVG's dimensions, without a need to recall the chart code
// Resizing because of the viewbox and perserveAspectRatio settings
// This scales the interior of the chart unlike the above
function resizeChart() {
var container = d3.select('#chart1');
var svg = container.select('svg');
if (fitScreen) {
// resize based on container's width AND HEIGHT
var windowSize = nv.utils.windowSize();
svg.attr("width", windowSize.width);
svg.attr("height", windowSize.height);
} else {
// resize based on container's width
var aspect = chart.width() / chart.height();
var targetWidth = parseInt(container.style('width'));
svg.attr("width", targetWidth);
svg.attr("height", Math.round(targetWidth / aspect));
}
};
return chart;
});
And the json is:
[
{
"date": "20140701",
"Baja": 0,
"No admitido": 3851,
"Pre-inscrito": 0,
"Admitido": 0,
"En reserva": 0,
"Alta": 0,
"Inactivo": 0
},
{
"date": "20140701",
"Baja": 0,
"No admitido": 3851,
"Pre-inscrito": 2468,
"Admitido": 0,
"En reserva": 0,
"Alta": 0,
"Inactivo": 0
}
]
I was looking for it but all I found is to modify the json. I did it, but it did not work. I prefer to preserve this json format if it is possible, it simplifies the process at all for me.
Thank u so much!
Mira creo que esto es lo que quieres :3
var chart = c3.generate({
data: {
json: datos,
keys: {
x : 'date', // it's possible to specify 'x' when category axis
value: ['Baja', 'No admitido', 'Pre-inscrito', 'Admitido', 'En reserva', 'Alta', 'Inactivo']
}
}
});
http://codepen.io/CristianG540/pen/bNegqV
Im trying to set up a line chart nvd3 graphic, but im getting time value on x axis not vertically aligned, heres the code:
function fillData() {
var test1 = [],
test2 = [],
test3 = [];
var now = new Date(),
day = 1000*60*60*24;
var cont = 0;
for (var i = now - 9*day; i < now; i+=day)
{
var arr = [400,431,401,430,429,450,448,498,421,421];
var arr1 = [420,415,421,410,439,430,468,448,441,421];
var arr2 = [410,425,431,420,459,420,458,438,451,421];
test1.push({x: i, y: arr[cont]});
test2.push({x: i, y: arr1[cont]});
test3.push({x: i, y: arr2[cont]});
cont+=1;
} // fin for
return [
{
values: test1,
area: true,
key: 'test1',
color: '#81BA63'
},
{
values: test2,
area: true,
key: 'test2',
color: '#EAEAEA'
},
{
values: test3,
area: true,
key: 'test3',
color: '#6389BA'
}
];
}
nv.addGraph(function() {
var chart = nv.models.lineChart()
.margin({top: 0, bottom: 25, left: 45, right: 0})
.showLegend(true)
.forceY([300,500])
chart.yAxis
.showMaxMin(true)
.tickFormat(d3.format('.02'))
chart.xAxis
.showMaxMin(false)
.tickFormat(function(d) { return d3.time.format('%d - %b')(new Date(d)) });
chart.xScale(d3.time.scale());
d3.select('#sources-chart-line svg')
.datum(fillData())
.transition().duration(500)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
problem screenshot: http://oi57.tinypic.com/i6gq2t.jpg
Thanks in Advance!!
The problem is that the number of data points (9) and axis ticks (8) is different. D3 picks "representative" ticks for the scale, which aren't necessarily aligned with the data points. Therefore, you get ticks between data points for which the date is still correct, but not at exactly midnight like the data points.
One way of fixing this is to explicitly specify the tick values:
var data = fillData();
chart.xAxis
.tickValues(data[0].values.map(function(d) { return d.x; }));
This is a bit ugly, but it shows the principle and you can refactor the code to make these values better accessible.