I recently started to look into d3.js. Though im pretty new in html/css/javascript; my current goal is to build a simple (static) dashboard using nvd3.js (for easier graphs) and bootstrap as layout component.
I started off with nvd3.js example "pie.html" and try to arrange the graphs in one line using bootstraps grid system. I got it working with d3 only using the d3noob.org example. Sadly, its not working with nvd3.
I pasted the full code below:
<!DOCTYPE html>
<meta charset="utf-8">
<link href="nvd3/src/nv.d3.css" rel="stylesheet" type="text/css">
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen">
<style>
body {
overflow-y:scroll;
}
text {
font: 12px sans-serif;
}
</style>
<div class="row">
<div class="span5" id="area1"></div>
<div class="span2"> This is some random text which should be in the middle of these two charts </div>
<div class="span5" id="area2"></div>
</div>
<body class='with-3d-shadow with-transitions'>
<!-- load the libraries -->
<script src="nvd3/lib/d3.v3.js"></script>
<script src="nvd3/nv.d3.js"></script>
<script src="nvd3/src/models/pie.js"></script>
<script src="nvd3/src/utils.js"></script>
<script src="http://code.jquery.com/jquery.js"></script>
<script src="bootstrap/js/bootstrap.min.js"></script>
<script>
var testdata = [
{
key: "One",
y: 5
},
{
key: "Two",
y: 2
},
{
key: "Three",
y: 9
},
{
key: "Four",
y: 7
},
{
key: "Five",
y: 4
},
{
key: "Six",
y: 3
}
];
nv.addGraph(function() {
var width = nv.utils.windowSize().width - 40,
height = nv.utils.windowSize().height / 2 - 40;
var chart = nv.models.pie()
.values(function(d) { return d })
.width(width)
.height(height);
d3.select("#area1").append("svg")
.datum([testdata])
.transition().duration(1200)
.attr('width', width)
.attr('height', height)
.call(chart);
return chart;
});
nv.addGraph(function() {
var width = nv.utils.windowSize().width - 40,
height = nv.utils.windowSize().height / 2 - 40;
var chart = nv.models.pie()
.values(function(d) { return d })
.width(width)
.height(height)
.donut(true);
d3.select("#area2").append("svg")
.datum([testdata])
.transition().duration(1200)
.attr('width', width)
.attr('height', height)
.call(chart);
return chart;
});
</script>
</body>
What it looks like:
The d3+bootstrap example: https://gist.github.com/d3noob/6a3b59149cf3ebdb3fc4
What it should look like:
class "spanX" is outdated in Bootstrap 3.x
Using the class "col-xx-x" is making it work.
Related
C3 version: 0.6.7
D3 version: 5
Browser: Firefox ESR
OS: Debian 9
I want to make a web page that has four rows of three donut charts each. I'm using Bootstrap with C3.js.
My code looks like this.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>U.S. presidential vote, 1972-2016, by race</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link href="css/c3.min.css" rel="stylesheet">
<style type="text/css">
.c3-chart-arcs-title {
font-size: 1.5rem;
}
</style>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="js/c3.min.js"></script>
<script type="text/javascript">
var dataDir = "vote-by-race";
d3.csv(dataDir + "/white.csv").then(function(data) {
var row;
data.forEach(function(d, i) {
if (i % 3 === 0) {
row = document.createElement("div");
row.className = "row";
}
var col = document.createElement("div");
col.className = "col-sm";
var chartDiv = document.createElement("div");
chartDiv.id = "chart" + i;
col.appendChild(chartDiv);
row.appendChild(col);
document.getElementById("charts").appendChild(row);
var chartDataObj = Object.assign({}, d);
delete chartDataObj["Year"];
var chart = c3.generate({
data: {
json: chartDataObj,
type: "donut",
colors: {
Democrat: "#2166ac",
Republican: "#b2182b",
Other: "#bbb"
}
},
tooltip: {
show: false
},
legend: {
show: false
},
donut: {
title: d["Year"],
label: {
format: function(value, ratio, id) {
return d3.format('.0%')(value / 100);
}
}
},
bindto: '#' + chartDiv.id
});
});
});
</script>
</head>
<body>
<div id="charts" class="container"></div>
</body>
</html>
My CSV...
Year,Democrat,Republican,Other
1972,32,66,2
1976,47,52,1
1980,35,56,9
1984,35,64,1
1988,40,59,1
1992,39,40,21
1996,43,46,11
2000,42,54,4
2004,41,58,1
2008,43,55,2
2012,39,59,2
2016,37,58,5
When I load the page in my browser, I see 12 donut charts in a single vertical column. But when I resize the browser window to its thinnest, then expand it again, I see four rows of three donut charts each.
How do I make the browser show four rows of three charts each when the charts are first loaded?
Please take a look at the below sample from NDV3.js chart library:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/novus/nvd3/v1.8.1/build/nv.d3.min.js"></script>
<link href="https://cdn.rawgit.com/novus/nvd3/v1.8.1/build/nv.d3.css" rel="stylesheet" type="text/css">
<style>
text {
font: 12px sans-serif;
}
svg {
display: block;
}
html, body, #chart1, svg {
margin: 0px;
padding: 0px;
height: 100%;
width: 100%;
}
.dashed {
stroke-dasharray: 5,5;
}
</style>
</head>
<body class='with-3d-shadow with-transitions'>
<div style="position:absolute; top: 0; left: 0;">
<button onclick="updateChart();">Update Chart</button>
<script>
var updateChart = function() {
// GET CHART HERE
chart.update();
}
</script>
</div>
<div id="chart1"></div>
<script>
// Wrapping in nv.addGraph allows for '0 timeout render', stores rendered charts in nv.graphs, and may do more in the future... it's NOT required
var chart;
var data;
var legendPosition = "top";
nv.addGraph(function() {
chart = nv.models.lineChart()
.options({
duration: 300,
useInteractiveGuideline: true
})
;
// chart sub-models (ie. xAxis, yAxis, etc) when accessed directly, return themselves, not the parent chart, so need to chain separately
chart.xAxis
.axisLabel("Time (s)")
.tickFormat(d3.format(',.1f'))
.staggerLabels(true)
;
chart.yAxis
.axisLabel('Voltage (v)')
.tickFormat(function(d) {
if (d == null) {
return 'N/A';
}
return d3.format(',.2f')(d);
})
;
data = sinAndCos();
d3.select('#chart1').append('svg')
.datum(data)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
function sinAndCos() {
var sin = [];
for (var i = 0; i < 100; i++) {
sin.push({x: i, y: i % 10 == 5 ? null : Math.sin(i/10) }); //the nulls are to show how defined works
}
return [
{
area: true,
values: sin,
key: "Sine Wave",
color: "#ff7f0e",
strokeWidth: 4,
classed: 'dashed'
}
];
}
</script>
</body>
</html>
This sample is working fine. Please look at the "updateChart" function. The problem is that I have to keep a reference to my chart and update it there. Is there an alternative for that like select it by "d3.select(...)" and update it?
This may be a late reply. Nevertheless...
Tried the below solution where the chart is still maintained as an instance and the update button's onclick triggers an update to the data.
(Added a toggle to return different data to demonstrate update functionality)
<html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/novus/nvd3/v1.8.1/build/nv.d3.min.js"></script>
<link href="https://cdn.rawgit.com/novus/nvd3/v1.8.1/build/nv.d3.css" rel="stylesheet" type="text/css">
</head>
<body class='with-3d-shadow with-transitions'>
<div style="position:absolute; top: 0; left: 0;">
<button onclick="update();">Update Chart</button>
</div>
<div id="chart1">
<svg></svg>
</div>
<script>
var chart;
var toggle = true;
var chartData;
nv.addGraph(function () {
chart = nv.models.lineChart()
.options({
duration: 300,
useInteractiveGuideline: true
});
chart.xAxis
.axisLabel("Time (s)")
.tickFormat(d3.format(',.1f'))
.staggerLabels(true);
chart.yAxis
.axisLabel('Voltage (v)')
.tickFormat(function (d) {
if (d == null) {
return 'N/A';
}
return d3.format(',.2f')(d);
});
var data = sinAndCos();
chartData = d3.select('#chart1 svg').datum(data);
chartData.transition().duration(500).call(chart);
d3.select('#chart1').append('svg')
.datum(data)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
function update() {
var data = sinAndCos();
chartData.datum(data).transition().duration(500).call(chart);
nv.utils.windowResize(chart.update);
};
function sinAndCos() {
var sin = [],
sin2 = [];
for (var i = 0; i < 100; i++) {
sin2.push({
x: i,
y: i % 10 == 5 ? null : Math.sin(i / 10) * 0.25 + 0.5
});
sin.push({
x: i,
y: i % 10 == 5 ? null : Math.sin(i / 10)
});
}
if (toggle) {
toggle = !toggle;
return [{
area: false,
values: sin2,
key: "Sine 2 Wave",
color: "#2ca02c",
strokeWidth: 4,
classed: 'dashed'
}];
} else {
toggle = !toggle;
return [{
area: true,
values: sin,
key: "Sine Wave",
color: "#ff7f0e",
strokeWidth: 4,
classed: 'dashed'
}];
}
}
</script>
<style>
text {
font: 12px sans-serif;
}
svg {
display: block;
}
html,
body,
#chart1,
svg {
margin: 0px;
padding: 0px;
height: 100%;
width: 100%;
}
.dashed {
stroke-dasharray: 5, 5;
}
</style>
</body>
</html>
I'm trying to create a way to automatically visualise many different types of data on an online dashboard. I'm using plotly.js (built on top of d3) to make these visualisations. For now, next to the default chart, I want to create a drop down box that allows you to switch to other chart types, but I can't get it to work and haven't found examples of something similar done in plotly. Any help would be great!
My code is below which queries the server for the data, and then plots it in a histogram. I can get individual charts to work, just not with the drop down box. I'm not even sure if what I've done by making the drop down object a function is allowed, and if I can use it to change the data to be plotted.
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>Simple Bar Chart</title>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script> </script>
<style>
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
</style>
</head>
<body>
<div id="chart" style="width:90%;height:600px;"></div>
<!-- For plotly code to bind to -->
<div id="drop-down"></div>
<script>
// set the dimensions and margins of the graph
var margin = { top: 20, right: 20, bottom: 80, left: 40 };
var width = 960 - margin.left - margin.right;
var height = 500 - margin.top - margin.bottom;
// append the svg object to the body of the page
// append a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("body").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 + ")");
// sends asynchronous request to the url
var HttpClient = function() {
this.get = function(aUrl, aCallback) {
var anHttpRequest = new XMLHttpRequest();
anHttpRequest.onreadystatechange = function() {
if (anHttpRequest.readyState == 4 && anHttpRequest.status == 200) {
aCallback(anHttpRequest.responseText);
}
}
anHttpRequest.open("GET", aUrl, true);
anHttpRequest.send(null);
}
};
var client = new HttpClient();
//hard coded URL for now, will accept from UI later
myURL = "https://neel-dot-village-test.appspot.com/_ah/api/searchApi/v1/fetchChartData?chartSpecs=%7B%22axis1%22%3A+%22name%22%2C+%22axis2%22%3A%22cumulativeNumbers.totalBudget%22%7D&topicType=%2Ftype%2Ftata%2Fproject";
client.get(myURL, function(response) {
var jresp = JSON.parse(response); //get response as JS object
plotHist(JSON.parse(jresp.json));
});
var chooseChart = function(data){
buttons: [{
method: plotHist,
args: data,
label: 'Histogram'
}, {
method: plotBar,
args: data,
label: 'Bar Chart'
}]
};
var plotHist = function(data) {
var plotdata = [{
x: data.y.values,
type: 'histogram',
marker: {
//color: 'rgba(100,250,100,0.7)'
},
}];
var layout = {
xaxis: {
title: data.y.label,
rangeslider: {} }, //does not automatically adjust bin sizes though
yaxis: { title: "Count" },
updatemenus: chooseChart(data),
autobinx: true
};
Plotly.newPlot('chart', plotdata, layout);
};
var plotBar = function(data) { //using plotly (built on d3)
var plotdata = [{
x: data.x.values,
y: data.y.values,
type: 'bar'
}];
var layout = {
xaxis: { title: data.x.label },
yaxis: { title: data.y.label },
updatemenus: chooseChart(data)
};
Plotly.newPlot('chart', plotdata, layout);
};
</script>
</body>
I am using sigma.js and have a custom node renderer that draws a node as a rectangle. Inside the rectangles are more shapes (basically just more rectangles and some text). I am also using the dragNodes plugin.
Right now, when a user wants to click on a node or drag it around the canvas, the user must go to the top left coordinate. However, the rectangle (the outermost one encompassing all the shapes inside it) represents the whole node. How do I modify my code to specify the dimensions of a node?
Also, although not shown in the code, if a user clicks inside a node rectangle (on one of the inner rectangles), I also want that to be detected and have a callback. Is this possible?
An example demonstrating my problem is shown here:
<!DOCTYPE html>
<html>
<head>
<script data-require="jquery#*" data-semver="2.2.0" src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/1.1.0/sigma.min.js"></script>
<script src="http://rawgit.com/jacomyal/sigma.js/master/plugins/sigma.plugins.dragNodes/sigma.plugins.dragNodes.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
<script>
$(document).ready(function() {
console.log('ready');
var g = { nodes: [], edges: [] };
g.nodes.push({
id: 'n1',
label: 'node1',
type: 'rtype',
x: Math.random(),
y: Math.random(),
size: 1,
color: '#666'
});
g.nodes.push({
id: 'n2',
label: 'node2',
type: 'rtype',
x: Math.random(),
y: Math.random(),
size: 1,
color: '#666'
});
sigma.renderers.def = sigma.renderers.canvas;
sigma.canvas.labels.rtype = function(node, context, settings) { };
sigma.canvas.nodes.rtype = function(node, context, settings) {
var prefix = settings('prefix') || '';
var size = node[prefix + 'size'];
var x = node[prefix + 'x'],
y = node[prefix + 'y'];
context.strokeStyle = 'rgb(0,0,0)';
context.strokeRect(x, y, 200, 200);
};
sigma.canvas.drawLabels = true;
var s = new sigma({
graph: g,
container: 'graph-container'
});
var dragListener = sigma.plugins.dragNodes(s, s.renderers[0]);
});
</script>
</head>
<body>
<div id="container">
<style>
#graph-container {
top: 0;
bottom: 0;
left: 0;
right: 0;
position: absolute;
}
</style>
<div id="graph-container"></div>
</div>
</body>
</html>
I created a graph with VivaGraphJS and everything is well but the graph is moving .there is an example about constantLayout but the problem you should specify for every node a position.
here is the code:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="../js/vivagraph.js"></script>
<script type="text/javascript">
function main () {
// create a graph object.
var graph = Viva.Graph.graph();
add nodes and edges to the graph:
graph.addLink(1, 2);
graph.addLink(1, 3);
graph.addLink(1, 4);
var renderer = Viva.Graph.View.renderer(graph);
renderer.run();
}
</script>
<style type="text/css" media="screen">
html, body, svg { width: 100%; height: 100%;}
</style>
</head>
<body onload='main()'>
</body>
</html>
and here is the code about the constant Layout :
<!DOCTYPE html>
<html>
<head>
<title>VivaGraphs constant layout demo page</title>
<script src="../../dist/vivagraph.js"></script>
<script type='text/javascript'>
function onLoad() {
var graph = Viva.Graph.graph(),
nodePositions = [{x : -50, y: 0}, {x : 0, y: -50}, {x : 50, y: 0}], // predefined node positions
layout = Viva.Graph.Layout.constant(graph),
renderer = Viva.Graph.View.renderer(graph, {
layout : layout, // use our custom 'constant' layout
}),
i, nodesCount = nodePositions.length; // convinience variables.
// Add nodes
for(i = 0; i < nodesCount; ++i) {
graph.addNode(i, nodePositions[i]);
}
// and make them connected in cycle:
for (i = 0; i < nodesCount; ++i) {
graph.addLink(i % nodesCount, (i + 1) % nodesCount);
}
// set custom node placement callback for layout.
// if you don't do this, constant layout performs random positioning.
layout.placeNode(function(node) {
// node.id - points to its position but you can do your
// random logic here. E.g. read from specific node.data
// attributes. This callback is expected to return object {x : .. , y : .. }
return nodePositions[node.id];
});
renderer.run();
}
</script>
<style type="text/css" media="screen">
body, html, svg { width: 100%; height: 100%; overflow: hidden; }
</style>
</head>
<body onload="onLoad()">
</body>
Any suggestion !?
I wish I explain well my problem. Thanks
If you want to have dynamic layout, but want to stop animation at some point use renderer.pasue() method.
If you want to have a constant layout, you can add some things in your code to tell it the locations of your nodes. Here's the code you'll need:
function main () {
// create a graph object.
var graph = Viva.Graph.graph(),
nodePositions = [{x : -50, y: 0}, {x : 0, y: -50}, {x : 50, y: 0}, {x: 60, y: 20}],
layout = Viva.Graph.Layout.constant(graph);
for(var i = 1; i < 5; i++) {
graph.addNode(i, nodePositions[i-1]);
}
graph.addLink(1, 2);
graph.addLink(1, 3);
graph.addLink(1, 4);
layout.placeNode(function(node) {
return nodePositions[node.id-1];
});
var renderer = Viva.Graph.View.renderer(graph, { layout:layout });
renderer.run();
}