nvd3 not loading json - d3.js

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

Related

d3js zooming in one direction, panning in both directions

I'm trying to build a d3js chart that zooms only on the X-axis but allows panning on both axes. The example below has the effect I desire:
https://jsfiddle.net/xpr364uo/
However, I'm having trouble translating this into my own code. For one, I'm rendering to canvas so I don't have the ability to set the "transform" attribute on some element. Also my zooming uses rescaleX/rescaleY on copies of the scales, as is the "new way" to do zooming via d3-zoom, from what I understand:
const zoomBehavior = zoom().on('zoom', () => {
const xDomain = event.transform.rescaleX(x2).domain();
const yDomain = event.transform.rescaleY(y2).domain();
xScale.domain(xDomain);
yScale.domain(yDomain);
render();
});
This works to zoom/pan on both axes. How can I modify it to get the same affect as in the fiddle? What am I supposed to do with deltaPanY (from the fiddle), in my code?
You could keep track of a second zoom transform (I'll call this yTransform) and use this to rescale the y axis. As you want the x to zoom normally, you can still use d3.event.transform.rescaleX() to rescale on the X axis, while the yTransform can be used to rescale on the Y axis.
When panning, the y translate value of yTransform should be updated with the current zoom state. Conversely, when zooming, yTransform should be used to override the change in the zoom state's y translate.
Perhaps something like:
var yTransform = d3.zoomIdentity; // initial state for the y transform
var zoom = d3.zoom()
.on("zoom", function() {
var t = d3.event.transform; // zoom state
x2 = t.rescaleX(x); // rescale x as normal (t.y is irrelevant)
// for a pan event, update the y translate
if (d3.event.sourceEvent.type != "wheel") yTransform.y = t.y;
// for a scroll, use the current y translate
else t.y = yTransform.y;
y2 = yTransform.rescaleY(y); // rescale y.
render();
})
The k and x values for yTranslate don't matter: the scale is always 1 as we aren't zooming in, and the x translate is irrelevant to rescale on the y axis. The above doesn't account for double click events, but I'll add that below.
var dots = d3.range(100)
.map(function() {
return {x: Math.random(), y: Math.random()}
})
var x = d3.scaleLinear().range([0,500])
var x2 = x.copy();
var y = d3.scaleLinear().range([0,300])
var y2 = y.copy();
var canvas = d3.select("canvas")
var context = canvas.node().getContext("2d");
// Just for reference:
var axis = d3.axisRight(y);
var g = d3.select("svg").append("g");
g.call(d3.axisRight(y2))
render();
var yTransform = d3.zoomIdentity;
var zoom = d3.zoom()
.on("zoom", function() {
var t = d3.event.transform;
x2 = t.rescaleX(x);
// For dbl clicks, d3.event.sourceEvent is null.
if (d3.event.sourceEvent && d3.event.sourceEvent.type != "wheel") yTransform.y = t.y;
else t.y = yTransform.y;
y2 = yTransform.rescaleY(y);
render();
})
canvas.call(zoom);
function render() {
context.clearRect(0,0,500,300);
dots.forEach(function(d) {
context.beginPath();
context.arc(x2(d.x), y2(d.y), 5, 0, 2 * Math.PI);
context.stroke();
})
g.call(d3.axisRight(y2));
}
canvas, svg {
position: absolute;
top: 0;
left: 0;
}
svg {
pointer-events:none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<canvas width="500" height="300"></canvas>
<svg width="500" height="300"></svg>
As I'm modifying properties directly - which is not the most ideal.
Alternatively, we could track a translate offset on the y (the difference between a y translate with dbl clicks/wheel events and without those events). Both y offset and y translate could be used to create an appropriate zoom transform:
var yOffset = 0;
var lastY = 0;
var zoom = d3.zoom()
.on("zoom", function() {
var t = d3.event.transform;
x2 = t.rescaleX(x);
// For dbl clicks, d3.event.sourceEvent is null.
if (d3.event.sourceEvent && d3.event.sourceEvent.type != "wheel") {
lastY = t.y - yOffset;
y2 = d3.zoomIdentity.translate(0,t.y-yOffset).rescaleY(y);
}
else {
yOffset = t.y - lastY; // ignore change in y for dbl click and wheel events
}
render();
})
var dots = d3.range(100)
.map(function() {
return {x: Math.random(), y: Math.random()}
})
var x = d3.scaleLinear().range([0,500])
var x2 = x.copy();
var y = d3.scaleLinear().range([0,300])
var y2 = y.copy();
var canvas = d3.select("canvas")
var context = canvas.node().getContext("2d");
// Just for reference:
var axis = d3.axisRight(y);
var g = d3.select("svg").append("g");
g.call(d3.axisRight(y2))
render();
var yOffset = 0;
var lastY = 0;
var zoom = d3.zoom()
.on("zoom", function() {
var t = d3.event.transform;
x2 = t.rescaleX(x);
// For dbl clicks, d3.event.sourceEvent is null.
if (d3.event.sourceEvent && d3.event.sourceEvent.type != "wheel") {
lastY = t.y - yOffset;
y2 = d3.zoomIdentity.translate(0,t.y-yOffset).rescaleY(y);
}
else {
yOffset = t.y - lastY; // ignore change in y for dbl click and wheel events
}
render();
})
canvas.call(zoom);
function render() {
context.clearRect(0,0,500,300);
dots.forEach(function(d) {
context.beginPath();
context.arc(x2(d.x), y2(d.y), 5, 0, 2 * Math.PI);
context.stroke();
})
g.call(d3.axisRight(y2));
}
canvas, svg {
position: absolute;
top: 0;
left: 0;
}
svg {
pointer-events:none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<canvas width="500" height="300"></canvas>
<svg width="500" height="300"></svg>

How to show labels beside the bar in Char js 2?

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>

How customize Cubism.js plugin to accept data from other sources like a two dimensional data array

I'm new to Javascript. There's a requirement to draw a time series graph using cubism.js with the data from a two-dimensional data array.
Is there anyone who have tried this already?
Can anyone help me to solve this tricky part?
Here is my code, in evo-timetest.js (also available on Dropbox):
define( [ "jquery", "css!./style.css","./d3.v3.min", "./cubism.v1"],
function ( $, d3,cubism) {
paint: function ( $element, layout ) {
//Check data in console
console.log($element);
console.log(layout);
// Data Array
var qMatrix = layout.qHyperCube.qDataPages[0].qMatrix;
//Dimension Lables
var DimensionLabels = layout.qHyperCube.qDimensionInfo.map(function (d) {
return d.qFallbackTitle;
});
var test = DimensionLabels.map(function(i){return i;})
console.log(test);
//Store each data into objects
var data = qMatrix.map(function(d){
return{
"Dim1":d[0].qText,
"Metric1":d[1].qNum
}
});
var map = data.map(function(i){return i.Metric1;})
console.log(map);
// Store chart object width, height, and Id
var width = $element.width();
var height = $element.height();
var id = "container_" + layout.qInfo.qId;
if(document.getElementById(id)){
$('#' + id).empty();
}
else{
$element.append($('<div/>').attr("id",id).width(width).height(height));
}
// Call visualization function
viz(data,DimensionLabels,width,height,id);
}
};
} );
var viz = function(data,lables,width,height,id){
/*
// create context and horizon
var context = cubism.context().size(960)
var horizon = context.horizon().extent([0,2])
// define metric accessor
function random_ma(name) {
return context.metric(function(start,stop,step,callback){
var values = [];
while (+start < +stop){
start = +start +step;
values = data.map(function(i){return i;})
console.log(values);
}
callback(null, values);
}, name);
}
// draw graph
var metrics = ["foo","bar","baz"];
horizon.metric(random_ma);
d3.select("#graph").selectAll(".horizon")
.data(metrics)
.enter()
.append("div")
.attr("class", "horizon")
.call(horizon);
// set rule
d3.select("#"+id).append("div")
.attr("class", "rule")
.call(context.rule());
// set focus
context.on("focus", function(i) {
d3.selectAll(".value")
.style( "right", i == null ? null : context.size() - i + "px");
});
// set axis
var axis = context.axis()
d3.select("#graph").append("div").attr("class", "axis").append("g").call(axis);
}
*/
//Defenition of Visualization function
//***************************************************************************************************************************
/* console.log(data);
console.log(lables); */
/* //var dF = new Date(2013,12,31)
var context = cubism.context();
// .serverDelay(Date.now() - dF)
// .step(864e5)
// .size(1280)
// .stop();
d3.select("#" + id).selectAll(".axis")
.data(["top", "bottom"])
.enter().append("div")
.attr("class", function(d) { return d + " axis"; })
.each(function(d) { d3.select(this).call(context.axis().ticks(12).orient(d)); });
d3.select("body").append("div")
.attr("class", "rule")
.call(context.rule());
var Data = lables.map(function(i){return i;})
// var primary = Data[1];
// var secondary = primary.shift(-864e5*30);
// Data[2] = secondary;
d3.select("body").selectAll(".horizon")
.data(Data)
.enter().insert("div", ".bottom")
.attr("class", "horizon")
.call(context.horizon()
.format(d3.format("+,.2p")));
context.on("focus", function(i) {
d3.selectAll(".value").style("right", i == null ? null : context.size() - i + "px");
});
*/
/****************************************************************************************************************/
/* console.clear();
// create new cubism.js context to render
var graphContext = cubism.context();
// .serverDelay(1000) // allow 1second delay
// .step(1000) // 1 second steps
// .size(650); // 50px wide
// create a new metric
// this allows you to retrieve values from some source
// this is data-source agnostic, so this does not matter.
var graphMetric = graphContext.metric(function(start, stop, step, callback) {
var values = [];
start = +start;
stop = +stop;
while (start < stop) {
start += step;
values.push(Math.random());
}
callback(null, values);
});
// here we create a new element and then append it to our
// parent container. Then we call d3 to select the newly created
// div and then we can create a chart
var graphElement = document.createElement("div");
$("#insertElement").append(graphElement);
d3.select(graphElement).call(function(div) {
div.selectAll(".horizon")
.data([graphMetric])
.enter().append("div")
.attr("class", "horizon")
.call(graphContext.horizon()
.height(150)
);
div.append("div")
.attr("class", "axis bottom")
.call(graphContext.axis().orient("top"));
div.append("div")
.attr("class", "rule")
.call(graphContext.rule());
*/
/**********************************************************************************************************/
/* d3.select("#" + id).selectAll(".axis")
.data(["top", "bottom"])
.enter().append("div");
var chart = d3.timeseries()
.addSerie(data,{x:'date',y:'n',diff:'n3'},{interpolate:'monotone',color:"#333"})
.width(900);
chart('#chart');
*/
}

How can I get pointRadius() updated on geoChoroplethChart redraw?

On a dc.geoChoroplethChart, I'm setting the radius of geojson points using the pointRadius method of the path:
.geoPath().pointRadius(function(feature, index) {
var v = placeGroup.all().filter(function(item) { return item.key === feature.id; })[0].value;
return (v == 0)? 0 : pointScale(v);
});
I'm finding that it works well, but on redraw() the sizes of the points are not adjusted. They are adjusted on a render(). How do I get them to be adjusted with a redraw() as well?
Here's the full chunk of code for the geo chart, in case it's relevant
var projection = d3.geo.mercator()
.scale(1)
.translate([0, 0]);
var path = d3.geo.path()
.projection(projection);
var width = 280,
height = 200,
b = path.bounds(places), // [[left, top], [right, bottom]]
x_extent = Math.abs(b[1][0] - b[0][0]),
y_extent = Math.abs(b[1][1] - b[0][1]),
s = (.95 / Math.max(x_extent / width, y_extent / height)),
t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2];
// Update projection with our actual data
projection
.scale(s)
.translate(t)
;
var mapchart = dc.geoChoroplethChart("#map-chart");
var valueDomain = [0, placeGroup.top(1)[0].value];
var maxPointRadius = Math.min(width, height) / 40,
minPointRadius = maxPointRadius / 2;
var pointScale = d3.scale.linear()
.domain(valueDomain)
.range([minPointRadius, maxPointRadius]);
mapchart.width(width)
.height(height)
.projection(projection)
.dimension(placeDim)
.group(placeGroup)
.colors(d3.scale.quantize().range(['#feb24c','#fd8d3c','#fc4e2a','#e31a1c','#bd0026'])) //first three '#ffffcc','#ffeda0', '#fed976', last one,'#800026'
.colorDomain(valueDomain)
.colorCalculator(function (d) { return d ? mapchart.colors()(d) : '#ccc'; })
.overlayGeoJson(places.features, "placeLayer", function (d) {
return d.id;
}).geoPath().pointRadius(function(feature, index) {
var v = placeGroup.all().filter(function(item) { return item.key === feature.id; })[0].value;
return (v == 0)? 0 : pointScale(v);
});
It looks like the geoChoroplethChart won't redraw the geojson unless the projection has changed. (It isn't expecting you to change the geoPath - as stated in the documentation, that's mostly a convenience method for reading and determining the center.)
https://github.com/dc-js/dc.js/blob/develop/src/geo-choropleth-chart.js#L169-L171
As a workaround, I'd suggest forcing a redraw by resetting the projection each time the chart redraws. Something like:
mapchart.on('preRedraw', function() {
mapchart.projection(projection);
});
https://github.com/dc-js/dc.js/blob/develop/web/docs/api-latest.md#basemixinon--basemixin

How do I display dates on the x-axis for nvd3 / d3.js?

I'm using nvd3, but I think this is a general d3.js question about time scale and formatting. I've created a simple example that illustrates the problem (see code below):
If I omit .tickFormat for the xAxis, it works fine without date formatting. With the example below I get the error:
Uncaught TypeError: Object 1326000000000 has no method 'getMonth'
nv.addGraph(function() {
var chart = nv.models.lineChart();
chart.xAxis
.axisLabel('Date')
.rotateLabels(-45)
.tickFormat(d3.time.format('%b %d')) ;
chart.yAxis
.axisLabel('Activity')
.tickFormat(d3.format('d'));
d3.select('#chart svg')
.datum(fakeActivityByDate())
.transition().duration(500)
.call(chart);
nv.utils.windowResize(function() { d3.select('#chart svg').call(chart) });
return chart;
});
function days(num) {
return num*60*60*1000*24
}
/**************************************
* Simple test data generator
*/
function fakeActivityByDate() {
var lineData = [];
var y = 0;
var start_date = new Date() - days(365); // One year ago
for (var i = 0; i < 100; i++) {
lineData.push({x: new Date(start_date + days(i)), y: y});
y = y + Math.floor((Math.random()*10) - 3);
}
return [
{
values: lineData,
key: 'Activity',
color: '#ff7f0e'
}
];
}
The example (now fixed) is in nvd3 with date axis.
Try creating a new Date object before the tick for the x-axis gets passed to the formatter:
.tickFormat(function(d) { return d3.time.format('%b %d')(new Date(d)); })
See the documentation for d3.time.format to see how you can customize the formatting string.
Adding on to seliopou's answer, to correctly align the dates with the x-axis, try this:
chart.xAxis
.tickFormat(function(d) {
return d3.time.format('%d-%m-%y')(new Date(d))
});
chart.xScale(d3.time.scale()); //fixes misalignment of timescale with line graph

Resources