Feeding live CSV data into D3 Charts - javascript

I have an d3 web app which displays a chart containing some server info (throughput, ssl, etc.)
The app works fine with static data, as it should. I have a Python script set up to continually update my traffic.csv to include current data (new data is appended every 30 seconds, and old data past a certain time frame is removed.)
I am wondering what the best way to import this data into my graph is. I have tried to "store" my CSV data as a JavaScript variable but than D3 is unable to parse it.
I would prefer a method that would allow this graph to update in real-time(or close to). I have looked into storing my data inside an AngularJS model and attempting to bind it to the d3 chart to take advantage of Angular's realtime two-way data binding. Has this been done before?
Here is my D3 Bar Chart Code: http://plnkr.co/edit/SclU3HYF7yCwZgHDOEHg?p=preview
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html ng-app="CSVReader">
<head>
<title>Bar Chart</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.18/angular.js"></script>
<script src="controller.js"></script>
<!-- import main controller -->
<script src="http://code.jquery.com/jquery-1.11.1.js"></script>
<link rel="stylesheet" type="text/css" href="http://d1e24pw9mnwsl8.cloudfront.net/c/bootstrap/css/bootstrap.min.css">
<meta http-equiv="X-UA-Compatible" content="IE=9">
<script src="controller.js"></script>
<!-- import main controller -->
</head>
<body ng-controller="MainCtrl">
<div id="title" style="text-align:center;padding-bottom:20px;">
<h1>Traffic Throughput (in) by Time</h1>
<small>Time in "Unix-Time", Throughput in Bytes</small>
</div>
<div id="chart" style="text-align:center"></div>
<script src="http://d3js.org/d3.v2.min.js"></script>
<script>
function renderChart() {
var data = d3.csv.parse(d3.select('#csv').text());
var valueLabelWidth = 50; // space reserved for value labels (right)
var barHeight = 20; // height of one bar
var barLabelWidth = 100; // space reserved for bar labels
var barLabelPadding = 5; // padding between bar and bar labels (left)
var gridLabelHeight = 18; // space reserved for gridline labels
var gridChartOffset = 3; // space between start of grid and first bar
var maxBarWidth = 420; // width of the bar with the max value
// accessor functions
var barLabel = function(d) {
return d['unixtime'];
};
var barValue = function(d) {
return parseFloat(d['tput_bytes_in']);
};
// scales
var yScale = d3.scale.ordinal().domain(d3.range(0, data.length)).rangeBands([0, data.length * barHeight]);
var y = function(d, i) {
return yScale(i);
};
var yText = function(d, i) {
return y(d, i) + yScale.rangeBand() / 2;
};
var x = d3.scale.linear().domain([0, d3.max(data, barValue)]).range([0, maxBarWidth]);
// svg container element
var chart = d3.select('#chart').append("svg")
.attr('width', maxBarWidth + barLabelWidth + valueLabelWidth)
.attr('height', gridLabelHeight + gridChartOffset + data.length * barHeight);
// grid line labels
var gridContainer = chart.append('g')
.attr('transform', 'translate(' + barLabelWidth + ',' + gridLabelHeight + ')');
gridContainer.selectAll("text").data(x.ticks(10)).enter().append("text")
.attr("x", x)
.attr("dy", -3)
.attr("text-anchor", "middle")
.text(String);
// vertical grid lines
gridContainer.selectAll("line").data(x.ticks(10)).enter().append("line")
.attr("x1", x)
.attr("x2", x)
.attr("y1", 0)
.attr("y2", yScale.rangeExtent()[1] + gridChartOffset)
.style("stroke", "#ccc");
// bar labels
var labelsContainer = chart.append('g')
.attr('transform', 'translate(' + (barLabelWidth - barLabelPadding) + ',' + (gridLabelHeight + gridChartOffset) + ')');
labelsContainer.selectAll('text').data(data).enter().append('text')
.attr('y', yText)
.attr('stroke', 'none')
.attr('fill', 'black')
.attr("dy", ".35em") // vertical-align: middle
.attr('text-anchor', 'end')
.text(barLabel);
// bars
var barsContainer = chart.append('g')
.attr('transform', 'translate(' + barLabelWidth + ',' + (gridLabelHeight + gridChartOffset) + ')');
barsContainer.selectAll("rect").data(data).enter().append("rect")
.attr('y', y)
.attr('height', yScale.rangeBand())
.attr('width', function(d) {
return x(barValue(d));
})
.attr('stroke', 'white')
.attr('fill', 'green');
// bar value labels
barsContainer.selectAll("text").data(data).enter().append("text")
.attr("x", function(d) {
return x(barValue(d));
})
.attr("y", yText)
.attr("dx", 3) // padding-left
.attr("dy", ".35em") // vertical-align: middle
.attr("text-anchor", "start") // text-align: right
.attr("fill", "blue")
.attr("stroke", "none")
.text(function(d) {
return d3.round(barValue(d), 2);
});
// start line
barsContainer.append("line")
.attr("y1", -gridChartOffset)
.attr("y2", yScale.rangeExtent()[1] + gridChartOffset)
.style("stroke", "#000");
}
</script>
<script id="csv" type="text/csv">
// Data goes here until I can figure out how to link to the .csv
</script>
<script>
renderChart();
</script>
</body>
</html>
I was hoping I could find a one-line solution for importing data that like this:
var data = d3.csv.parse((traffic.csv).text());

Related

Strange Bug in my code with D3.JS and Bar Charts

Hi guys im learning D3 and im trying to make dynamic charts which with changing the chart group will show the same group charts let me show you with two images:
i followed the documents tutorials which are working ! but the behave iof my code is
weird when i apply the filter by selecting the group
it shows bars in random locations or on top of each others
also after filter apply it never shows the very first bar of array
here is the code
var cereals;
if (manufacturer === 'All')
cereals = data.filter(d => d.manufacturer !== manufacturer);
else cereals = data.filter(d => d.manufacturer === manufacturer);
// **** Draw and Update your chart here ****
var bars = chartG.selectAll('.bar')
.data(cereals,function(d,i){
return d.sugar;
});
var barsEnter = bars.enter()
.append('g')
.attr('class', 'bar');
barsEnter.merge(bars)
.attr('transform', function(d,i){
return 'translate('+[i * barBand + 4,0]+')';
});
/*barsEnter.append('rect')
.attr("class", "bar")
.attr("width", barWidth)
.attr("height", function(d) { return chartHeight - sugarScale(d.sugar); });
*/
barsEnter.append('rect')
.attr("class", "bar")
.attr("y", function(d) { return sugarScale(d.sugar); })
.attr("width", barWidth)
.attr("height", function(d) { return chartHeight - sugarScale(d.sugar); });
barsEnter.append('text')
.attr('dy', '0.9em')
.attr('dx', '0.6em')
.attr('class','axis-label')
.attr("transform", "translate(0," + chartHeight + ")")
.text(function(d){
console.log(d)
return d.cerealName;
});
bars.exit().remove();
Here is full code if you want to check!
main.js
// Global function called when select element is changed
function onCategoryChanged() {
var select = d3.select('#categorySelect').node();
var category = select.options[select.selectedIndex].value;
// Update chart with the selected category of cereal
updateChart(category);
}
// recall that when data is loaded into memory, numbers are loaded as strings
// this function helps convert numbers into string during data preprocessing
function dataPreprocessor(row) {
return {
cerealName: row['Cereal Name'],
manufacturer: row['Manufacturer'],
sugar: +row['Sugars']
};
}
var svg = d3.select('svg');
// Get layout parameters
var svgWidth = +svg.attr('width');
var svgHeight = +svg.attr('height');
var padding = { t: 60, r: 20, b: 80, l: 60 };
// Compute chart dimensions
var chartWidth = svgWidth - padding.l - padding.r;
var chartHeight = svgHeight - padding.t - padding.b;
// Variable for the spacing of bar charts
var barBand;
var barWidth;
// scales
var sugarScale; // y axis
var xBandScale; // x axis
// Create a group element for appending chart elements
var chartG = svg.append('g')
.attr('transform', `translate(${padding.l}, ${padding.t})`);
var data;
d3.csv('cereals.csv', dataPreprocessor).then(function(dataset) {
// Create global variables here and intialize the chart
data = dataset;
// Compute the spacing for bar bands based on number of cereals
barBand = chartWidth / data.length;
barWidth = 0.7 * barBand;
// **** Your JavaScript code goes here ****\
dataset
// Add axes to chart
addAxes();
// Main Part
sugarScale.domain([0, d3.max(dataset, function(d) { return d.sugar; })]);0
chartG.append("g").call(d3.axisLeft(sugarScale)).append("text").text("value");
// Update the chart for All cereals to initialize
updateChart('All');
});
function addAxes() {
// **** Draw the axes here ****
sugarScale = d3.scaleLinear().range ([chartHeight,0]),
xBandScale = d3.scaleLinear().range([0, chartWidth])
}
function updateChart(manufacturer) {
// Create a filtered array of cereals based on the manufacturer
var cereals;
if (manufacturer === 'All')
cereals = data.filter(d => d.manufacturer !== manufacturer);
else cereals = data.filter(d => d.manufacturer === manufacturer);
// **** Draw and Update your chart here ****
var bars = chartG.selectAll('.bar')
.data(cereals,function(d,i){
return d.sugar;
});
var barsEnter = bars.enter()
.append('g')
.attr('class', 'bar');
barsEnter.merge(bars)
.attr('transform', function(d,i){
return 'translate('+[i * barBand + 4,0]+')';
});
/*barsEnter.append('rect')
.attr("class", "bar")
.attr("width", barWidth)
.attr("height", function(d) { return chartHeight - sugarScale(d.sugar); });
*/
barsEnter.append('rect')
.attr("class", "bar")
.attr("y", function(d) { return sugarScale(d.sugar); })
.attr("width", barWidth)
.attr("height", function(d) { return chartHeight - sugarScale(d.sugar); });
barsEnter.append('text')
.attr('dy', '0.9em')
.attr('dx', '0.6em')
.attr('class','axis-label')
.attr("transform", "translate(0," + chartHeight + ")")
.text(function(d){
console.log(d)
return d.cerealName;
});
bars.exit().remove();
}
// Remember code outside of the data callback function will run before the data loads
index.html
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Lab 4 - ABCs of D3</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div id="main">
<svg width="600" height="330" style="border: 1px solid #777;">
</svg>
<div>
<label for="categorySelect">Manufacturer: </label>
<select class="custom-select" id="categorySelect" onchange="onCategoryChanged()">
<option selected value="All">All</option>
<option value="General Mills">General Mills</option>
<option value="Quaker Oats">Quaker Oats</option>
<option value="Ralston Purina">Ralston Purina</option>
<option value="Kelloggs">Kelloggs</option>
<option value="Nabisco">Nabisco</option>
<option value="Post">Post</option>
</select>
</div>
</div>
</body>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="./main.js"></script>
</html>
Styles
.axis-label {
text-anchor: middle;
font-size: 12px;
font-weight: semibold;
}
body {
font-family: "Open Sans";
margin: 0;
}
.bar {
fill: #4571C9;
}
#main {
margin: 20px;
}
cereals.csv
Cereal Name,Manufacturer,Sugars
Kix,General Mills,3
Life,Quaker Oats,6
Trix,General Mills,12
Smacks,Kelloggs,15
Basic 4,General Mills,8
Crispix,Kelloggs,3
All-Bran,Kelloggs,5
Cheerios,General Mills,1
Clusters,General Mills,7
Wheaties,General Mills,3
100% Bran,Nabisco,6
Bran Chex,Ralston Purina,6
Corn Chex,Ralston Purina,3
Corn Pops,Kelloggs,12
Rice Chex,Ralston Purina,2
Special K,Kelloggs,3
Grape-Nuts,Post,3
Honey-comb,Post,11
Wheat Chex,Ralston Purina,3
Apple Jacks,Kelloggs,14
This is example of strange bug :
I can see 2 possible things:
You're adding class bar to your 'g' elements AND the 'rect' elements within the 'g' element - I can see that causing issues when you selectAll on that class
You're appending new rects and texts after you merged the new and existing bars. This will add more texts and rects to the existing bars. You want to append the text and rect elements before the merge.
Not necessary but might be nice:
2a) You might want to after the merge operation, 'select' the rect and do the bar Y/width/height calculation there so existing bars are updated if the values changed since the last operation. This looks like static data at the moment though.

How to put the state name into the Geo chart/D3.js

I met a problem that I want to put the state name on my geo chart. I tried to use others' method, but they cannot well-matched with my chart. Could you please let me know what's wrong with my code and how to improve it.Thank you in advance!
Index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="stylemap.css">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id = "chart"></div>
<script src="https://d3js.org/d3.v5.js"></script>
<script src="mainmap.js"></script>
</body>
</html>
Mainmap.js:
//weight and height
var chart_height = 600;
var chart_width = 800;
var color = d3.scaleQuantize().range([ 'rgb(255,245,240)','rgb(254,224,210)','rgb(252,187,161)',
'rgb(252,146,114)','rgb(251,106,74)','rgb(239,59,44)',
'rgb(203,24,29)','rgb(165,15,21)','rgb(103,0,13)']);
//Projection
var projection = d3.geoAlbersUsa()
.scale([chart_width])
.translate([chart_width / 2, chart_height / 2 ]);
// .translate([0, 100]);
var path = d3.geoPath(projection);
// .projection(projection);
//create svg
var svg = d3.select('#chart')
.append("svg")
.attr('width', chart_width)
.attr('height', chart_height);
// svg.append("rect")
// .attr("class", "background")
// .attr("width", width)
// .attr("height", height);
var g = svg.append("g")
.attr("transform", "translate(" + chart_width / 2 + "," + chart_height / 2 + ")")
.append("g")
.attr("id", "states");
//Data
d3.json('zombie-attacks.json').then(function(zombie_data){
color.domain([
d3.min(zombie_data, function(d){
return d.num;
}),
d3.max(zombie_data, function(d){
return d.num;
})
]);
d3.json('us.json').then(function(us_data){
us_data.features.forEach(function(us_e, us_i){
zombie_data.forEach(function(z_e,z_i){
if(us_e.properties.name !== z_e.state){
return null;
}
us_data.features[us_i].properties.num = parseFloat(z_e.num)
});
});
// console.log(us_data)
svg.selectAll('path')
.data(us_data.features)
.enter()
.append('path')
.attr('d',path)
.attr('fill', function(d){
var num = d.properties.num;
return num ? color(num) : '#ddd';
})
.text(function(d){
return d.properties.name;
})
.attr('stroke', '#fff')
.attr('stroke-width',1)
.attr("class", "country-label")
.append("text")
// .attr("transform", function(d) { console.log("d", d); return "translate(" + path.centroid(d) + ")"; })
// .text(function(d) { return d.properties.name; })
.attr("dy", function (d) {
return "0.35em";
})
.style('fill', 'black');
g.selectAll("text")
.data(us_data.features)
.enter()
.append("text")
.text(function(d){
return d.properties.name;
})
.attr("x", function(d){
return path.centroid(d)[0];
})
.attr("y", function(d){
return path.centroid(d)[1];
})
.attr("text-anchor","middle")
.attr('font-size','6pt')
.style('fill', 'green');
})
})
// Add names of the states to a map in d3.js
You're appending text and path to different parents: svg and g. This is an issue because:
var g = svg.append("g")
.attr("transform", "translate(" + chart_width / 2 + "," + chart_height / 2 + ")")
Your g, with the text has a transform that the svg doesn't. Which is why your text is pushed width/2, height/2 further than the projected paths. Just use svg.selectAll for the text.
The projection already has a translate applied to it, you can either apply the translation to the parent or to the projection, but shouldn't use both.

d3 is not defined - ReferenceError

I am trying to use a "fancy graph" found at http://bl.ocks.org/kerryrodden/7090426:
The way I've done it was to download the code and simply edit the CSV file to match my data. Then I simply open the .html-file in Firefox to see the interactive graph. However, using it at a another computer I get the following errors:
ReferenceError: d3 is not defined sequences.js:25
ReferenceError: d3 is not defined index.html:28
As I have almost no knowledge of d3 or javascript I am a bit lost.
Can any of you give me a hint to what is causing the errors and how I should correct the code?
I've done a single alteration to the code making it the following:
Javascript:
// Dimensions of sunburst.
var width = 750;
var height = 600;
var radius = Math.min(width, height) / 2;
// Breadcrumb dimensions: width, height, spacing, width of tip/tail.
var b = {
w: 75, h: 30, s: 3, t: 10
};
// Mapping of step names to colors.
var colors = {
"G0": "#5687d1",
"G1": "#5c7b61",
"G2": "#de783b",
"G3": "#6ab975",
"G4": "#a173d1",
"G5": "#72d1a1",
"Afgang": "#615c7b"
};
// Total size of all segments; we set this later, after loading the data.
var totalSize = 0;
var vis = d3.select("#chart").append("svg:svg")
.attr("width", width)
.attr("height", height)
.append("svg:g")
.attr("id", "container")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var partition = d3.layout.partition()
.size([2 * Math.PI, radius * radius])
.value(function(d) { return d.size; });
var arc = d3.svg.arc()
.startAngle(function(d) { return d.x; })
.endAngle(function(d) { return d.x + d.dx; })
.innerRadius(function(d) { return Math.sqrt(d.y); })
.outerRadius(function(d) { return Math.sqrt(d.y + d.dy); });
// Use d3.text and d3.csv.parseRows so that we do not need to have a header
// row, and can receive the csv as an array of arrays.
d3.text("sequences.csv", function(text) {
var csv = d3.csv.parseRows(text);
var json = buildHierarchy(csv);
createVisualization(json);
});
// Main function to draw and set up the visualization, once we have the data.
function createVisualization(json) {
// Basic setup of page elements.
initializeBreadcrumbTrail();
drawLegend();
d3.select("#togglelegend").on("click", toggleLegend);
// Bounding circle underneath the sunburst, to make it easier to detect
// when the mouse leaves the parent g.
vis.append("svg:circle")
.attr("r", radius)
.style("opacity", 0);
// For efficiency, filter nodes to keep only those large enough to see.
var nodes = partition.nodes(json)
.filter(function(d) {
return (d.dx > 0.005); // 0.005 radians = 0.29 degrees
});
nodes = nodes.filter(function(d) {
return (d.name != "end"); // BJF: Do not show the "end" markings.
});
var path = vis.data([json]).selectAll("path")
.data(nodes)
.enter().append("svg:path")
.attr("display", function(d) { return d.depth ? null : "none"; })
.attr("d", arc)
.attr("fill-rule", "evenodd")
.style("fill", function(d) { return colors[d.name]; })
.style("opacity", 1)
.on("mouseover", mouseover);
// Add the mouseleave handler to the bounding circle.
d3.select("#container").on("mouseleave", mouseleave);
// Get total size of the tree = value of root node from partition.
totalSize = path.node().__data__.value;
};
// Fade all but the current sequence, and show it in the breadcrumb trail.
function mouseover(d) {
var percentage = (100 * d.value / totalSize).toPrecision(3);
var percentageString = percentage + "%";
if (percentage < 0.1) {
percentageString = "< 0.1%";
}
d3.select("#percentage")
.text(percentageString);
d3.select("#explanation")
.style("visibility", "");
var sequenceArray = getAncestors(d);
updateBreadcrumbs(sequenceArray, percentageString);
// Fade all the segments.
d3.selectAll("path")
.style("opacity", 0.3);
// Then highlight only those that are an ancestor of the current segment.
vis.selectAll("path")
.filter(function(node) {
return (sequenceArray.indexOf(node) >= 0);
})
.style("opacity", 1);
}
// Restore everything to full opacity when moving off the visualization.
function mouseleave(d) {
// Hide the breadcrumb trail
d3.select("#trail")
.style("visibility", "hidden");
// Deactivate all segments during transition.
d3.selectAll("path").on("mouseover", null);
// Transition each segment to full opacity and then reactivate it.
d3.selectAll("path")
.transition()
.duration(1000)
.style("opacity", 1)
.each("end", function() {
d3.select(this).on("mouseover", mouseover);
});
d3.select("#explanation")
.transition()
.duration(1000)
.style("visibility", "hidden");
}
// Given a node in a partition layout, return an array of all of its ancestor
// nodes, highest first, but excluding the root.
function getAncestors(node) {
var path = [];
var current = node;
while (current.parent) {
path.unshift(current);
current = current.parent;
}
return path;
}
function initializeBreadcrumbTrail() {
// Add the svg area.
var trail = d3.select("#sequence").append("svg:svg")
.attr("width", width)
.attr("height", 50)
.attr("id", "trail");
// Add the label at the end, for the percentage.
trail.append("svg:text")
.attr("id", "endlabel")
.style("fill", "#000");
}
// Generate a string that describes the points of a breadcrumb polygon.
function breadcrumbPoints(d, i) {
var points = [];
points.push("0,0");
points.push(b.w + ",0");
points.push(b.w + b.t + "," + (b.h / 2));
points.push(b.w + "," + b.h);
points.push("0," + b.h);
if (i > 0) { // Leftmost breadcrumb; don't include 6th vertex.
points.push(b.t + "," + (b.h / 2));
}
return points.join(" ");
}
// Update the breadcrumb trail to show the current sequence and percentage.
function updateBreadcrumbs(nodeArray, percentageString) {
// Data join; key function combines name and depth (= position in sequence).
var g = d3.select("#trail")
.selectAll("g")
.data(nodeArray, function(d) { return d.name + d.depth; });
// Add breadcrumb and label for entering nodes.
var entering = g.enter().append("svg:g");
entering.append("svg:polygon")
.attr("points", breadcrumbPoints)
.style("fill", function(d) { return colors[d.name]; });
entering.append("svg:text")
.attr("x", (b.w + b.t) / 2)
.attr("y", b.h / 2)
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.name; });
// Set position for entering and updating nodes.
g.attr("transform", function(d, i) {
return "translate(" + i * (b.w + b.s) + ", 0)";
});
// Remove exiting nodes.
g.exit().remove();
// Now move and update the percentage at the end.
d3.select("#trail").select("#endlabel")
.attr("x", (nodeArray.length + 0.5) * (b.w + b.s))
.attr("y", b.h / 2)
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.text(percentageString);
// Make the breadcrumb trail visible, if it's hidden.
d3.select("#trail")
.style("visibility", "");
}
function drawLegend() {
// Dimensions of legend item: width, height, spacing, radius of rounded rect.
var li = {
w: 75, h: 30, s: 3, r: 3
};
var legend = d3.select("#legend").append("svg:svg")
.attr("width", li.w)
.attr("height", d3.keys(colors).length * (li.h + li.s));
var g = legend.selectAll("g")
.data(d3.entries(colors))
.enter().append("svg:g")
.attr("transform", function(d, i) {
return "translate(0," + i * (li.h + li.s) + ")";
});
g.append("svg:rect")
.attr("rx", li.r)
.attr("ry", li.r)
.attr("width", li.w)
.attr("height", li.h)
.style("fill", function(d) { return d.value; });
g.append("svg:text")
.attr("x", li.w / 2)
.attr("y", li.h / 2)
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.key; });
}
function toggleLegend() {
var legend = d3.select("#legend");
if (legend.style("visibility") == "hidden") {
legend.style("visibility", "");
} else {
legend.style("visibility", "hidden");
}
}
// Take a 2-column CSV and transform it into a hierarchical structure suitable
// for a partition layout. The first column is a sequence of step names, from
// root to leaf, separated by hyphens. The second column is a count of how
// often that sequence occurred.
function buildHierarchy(csv) {
var root = {"name": "root", "children": []};
for (var i = 0; i < csv.length; i++) {
var sequence = csv[i][0];
var size = +csv[i][1];
if (isNaN(size)) { // e.g. if this is a header row
continue;
}
var parts = sequence.split("-");
var currentNode = root;
for (var j = 0; j < parts.length; j++) {
var children = currentNode["children"];
var nodeName = parts[j];
var childNode;
if (j + 1 < parts.length) {
// Not yet at the end of the sequence; move down the tree.
var foundChild = false;
for (var k = 0; k < children.length; k++) {
if (children[k]["name"] == nodeName) {
childNode = children[k];
foundChild = true;
break;
}
}
// If we don't already have a child node for this branch, create it.
if (!foundChild) {
childNode = {"name": nodeName, "children": []};
children.push(childNode);
}
currentNode = childNode;
} else {
// Reached the end of the sequence; create a leaf node.
childNode = {"name": nodeName, "size": size};
children.push(childNode);
}
}
}
return root;
};
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Flow for G1 customers</title>
<script src="http://d3js.org/d3.v3.min.js"></script>
<link rel="stylesheet" type="text/css"
href="https://fonts.googleapis.com/css?family=Open+Sans:400,600">
<link rel="stylesheet" type="text/css" href="sequences.css"/>
</head>
<body>
<div id="main">
<div id="sequence"></div>
<div id="chart">
<div id="explanation" style="visibility: hidden;">
<span id="percentage"></span><br/>
of G1 customers follow this flow.
</div>
</div>
</div>
<div id="sidebar">
<input type="checkbox" id="togglelegend"> Legend<br/>
<div id="legend" style="visibility: hidden;"></div>
</div>
<script type="text/javascript" src="sequences.js"></script>
<script type="text/javascript">
// Hack to make this example display correctly in an iframe on bl.ocks.org
d3.select(self.frameElement).style("height", "700px");
</script>
</body>
</html>
Had the same issue, though I initially thought it is because of security restrictions of browser, that wasn't the case. It worked when I added the charset to the script tag as shown below:
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
The same is shown in the d3 docs, though it doesn't mention this issue specifically: http://d3js.org/
Yes, having it locally works too.
<script src="d3.min.js"></script>
Here is the full example:
<!doctype html>
<html>
<head>
<title>D3 tutorial</title>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<!--<script src="d3.min.js"></script>-->
</head>
<body>
<script>
var canvas = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 500);
var circle = canvas.append("circle")
.attr("cx",250)
.attr("cy", 250)
.attr("r", 50)
.attr("fill", "red");
</script>
</body>
</html>
There may be security restrictions that prevent your browser from downloading the D3 script. What you can do is to download the scripts, place them in the same folder as your files, and change the referenced paths in your source.
You may also need to add:
<meta charset="utf-8">
or
<meta content="utf-8" http-equiv="encoding">
to your head section
in case browser does not prevent it from downloading and still getting the error, d3.js should be placed before jquery.
I just moved my reference to the package as the first import in my head tag:
<head>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
...
...
</head>
Seemed to work for me
Replace <meta charset="ISO-8859-1"> with <meta charset="UTF-8">
I had to do a grunt build to get get rid of this error. (Using Yeoman and Ember.js.)
And for JavaScript noobs like me - problem could be that you don't import it correctly. Try reading import docs and things like:
import * as d3 from 'd3-transition'
If you are using Visual Studio you can go to Tools -> Options -> Text Editor -> JavaScript -> IntelliSense and check the box "Download remote references". That did the trick for me.
UPDATE: there is now a d3-webpack-loader package which makes it easy to load d3 in webpack. I am not the creator of the package, I've only used it to see if it works. Here's a quick example.
// Install the loader
npm install --save d3-webpack-loader
// Install the d3 microservices you need
npm install --save d3-color
npm install --save d3-selection
In our entry.js file we'll require d3 using the d3-webpack-loader with:
const d3 = require('d3!');
and can then use some of the d3 methods with:
d3.selectAll("p").style("color", d3.color("red").darker());
Super late to this response but none of the above solutions worked out for me. I found a fix though!
I am on macOS Catalina. For some bizarre reason, it turned out to be a decompression/unpack issue with the .tgz file from Observable's website. I use an application called The Unarchiver to decompress files, but in this case, the .tgz file did not properly unpack. There was a folder and file missing compared to a friend's computer not using the same program.
Solution: I unpacked .tgz without a third party program – just used macOS (simply double clicking on the file). Then, I loaded the page locally and it worked!
If double clicking on the file fails to unpack, try running tar -xzf filename.tgz in Terminal.
I assume that you are importing the d3 from online.
In your HTML, make sure that you are importing the d3 before connecting your JavaScript file.
// Importing D3.js
<script src="https://d3js.org/d3.v5.min.js" charset="utf-8" defer></script>
// Importing D3-Scale
<script src="https://d3js.org/d3-scale.v3.min.js"></script>
// Connecting my JS file
<script src="app.js" defer></script>

D3.js code in Reveal.js slide

I'm trying to make D3.js work on Reveal.js slides, but I can't get it to run even the most basic snippet:
<section>
<h2>Title</h2>
<div id="placeholder"></div>
<script type="text/javascript">
d3.select("#placeholder").append("p").text("TEST");
</script>
</section>
Doesn't show the "TEST" word. What am I doing wrong?
Okay here we go.
I have made a basic example with Reveal.js & D3.js and it works well.
There are two slides
First slide contains Bar chart
Second slide renders Bubble chart by taking data from a json input file
Your code works fine if it is placed outside of the section at the bottom. I have placed all D3.js code at the end of the html page before body closer.
The folder structure is show below (in snapshot)
I placed my JS inside the HTML (in order to make it easier to read/comprehend)
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Reveal.js with D3 JS</title>
<link rel="stylesheet" href="css/reveal.min.css">
<link rel="stylesheet" href="css/theme/default.css" id="theme">
<style>
.chart rect {
fill: #63b6db;
}
.chart text {
fill: white;
font: 10px sans-serif;
text-anchor: end;
}
text {
font: 10px sans-serif;
}
</style>
</head>
<body>
<div class="reveal">
<div class="slides">
<section>
<h2>Barebones Presentation</h2>
<p>This example contains the bare minimum includes and markup required to run a reveal.js presentation.</p>
<svg class="chart"></svg>
</section>
<section id="sect2">
<h2>No Theme</h2>
<p>There's no theme included, so it will fall back on browser defaults.</p>
<svg class="bubleCharts"></svg>
</section>
</div>
</div>
<script src="js/reveal.min.js"></script>
<script>
Reveal.initialize();
</script>
<script src="js/d3.min.js"></script>
<script type="text/javascript">
//------ code to show D3 Bar Chart on First Slide-------
var data = [44, 28, 15, 16, 23, 5];
var width = 420,
barHeight = 20;
var x = d3.scale.linear()
.domain([0, d3.max(data)])
.range([0, width]);
var chart = d3.select(".chart")
.attr("width", width)
.attr("height", barHeight * data.length);
var bar = chart.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * barHeight + ")"; });
bar.append("rect")
.attr("width", x)
.attr("height", barHeight - 1);
bar.append("text")
.attr("x", function(d) { return x(d) - 3; })
.attr("y", barHeight / 2)
.attr("dy", ".35em")
.text(function(d) { return d; });
//---Code below will show Bubble Charts on the secon Slide -------
var diameter = 560,
format = d3.format(",d"),
color = d3.scale.category20c();
var bubble = d3.layout.pack()
.sort(null)
.size([diameter, diameter])
.padding(1.5);
var svg = d3.select(".bubleCharts")
.attr("width", diameter)
.attr("height", diameter)
.attr("class", "bubble");
d3.json("flare.json", function(error, root) {
var node = svg.selectAll(".node")
.data(bubble.nodes(classes(root))
.filter(function(d) { return !d.children; }))
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
node.append("title")
.text(function(d) { return d.className + ": " + format(d.value); });
node.append("circle")
.attr("r", function(d) { return d.r; })
.style("fill", function(d) { return color(d.packageName); });
node.append("text")
.attr("dy", ".3em")
.style("text-anchor", "middle")
.text(function(d) { return d.className.substring(0, d.r / 3); });
});
// Returns a flattened hierarchy containing all leaf nodes under the root.
function classes(root) {
var classes = [];
function recurse(name, node) {
if (node.children) node.children.forEach(function(child) { recurse(node.name, child); });
else classes.push({packageName: name, className: node.name, value: node.size});
}
recurse(null, root);
return {children: classes};
}
d3.select(self.frameElement).style("height", diameter + "px");
</script>
</body>
</html>
Output/results
Slide 1
Slide 2
Download complete code https://github.com/aahad/D3.js/tree/master/Reveal JS with D3 JS
To learn more about how Bar Chart or Bubble Chart code works: check followings:
Bubble Chart examples: http://bl.ocks.org/mbostock/4063269
Bar Chart examples: http://bost.ocks.org/mike/bar/
Both existing answers are fine, but I'd like to point out a 3rd approach that worked for me and has some advantages.
Reveal.js has an event system and it also works with the per-slide data states.
This means that you can have a separate javascript block for each slide and have it execute only when that slide is loaded. This lets you do D3-based animations upon load of the slide and has the further advantage of placing the code for the slide closer to the text/markup of it.
For example, you could set your slide like this. Note the data-state attribute:
<section data-state="myslide1">
<h2>Blah Blah</h2>
<div id="slide1d3container"></div>
</section>
And then have an associated script block:
<script type="text/javascript">
Reveal.addEventListener( 'myslide1', function() {
var svg = d3.select("#slide1d3container").append("svg")
// do more d3 stuff
} );
</script>
Here's an example of a presentation that uses this technique:
http://explunit.github.io/d3_cposc_2014.html
I found out by myself. Of course I cannot match against ids that are not loaded yet: it works if I put the d3 javascript code after the Reveal.initialize script block at the end of the index.html file.

Updating D3 circle pack layout

I'm trying to dynamically update a d3 circle pack layout with data I receive in json. Every second I call d3.json() to get the new json. Instead of updating the existing visualization, my implementation just creates a new one under the old one. I want to to dynamically update the existing layout instead...
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="d3.v2.js">
</script>
<script type="text/javascript" src="jquery-1.4.min.js"></script>
<link rel="stylesheet" href="style.css" type="text/css">
<link rel="stylesheet" href="syntax.css" type="text/css">
<link rel="stylesheet" href="pack.css" type="text/css">
</head>
<body>
<div id="chart"> </div>
<script type="text/javascript">
var width = 960,
height = 960,
format = d3.format(",d");
var pack = d3.layout.pack()
.size([width - 4, height -4])
.value(function(d) { return d.size; });
var vis = null;
var node = null;
vis = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height)
.attr("class", "pack");
/* vis.append("g")
.attr("transform", "translate(2, 2)"); */
function update(json){
// Creating the circle packed core
var gNodes = vis.data([json]).selectAll("g.node")
.data(pack.nodes);
//remove old data
gNodes.exit().remove();
}
setInterval(function(){
d3.json("http://10.0.1.4:8080/cluster", function(json) {
update(json);
//update the visualization
vis
.selectAll("circle")
.data([json]).selectAll("g.node")
.data(pack.nodes)
.attr("class", function(d) { return d.children ? "node" : "leaf node"; })
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.transition()
.duration(500)
.attr("r", function(d) { return d.children ? coreSize : d.r; });
var node = gNodes
.enter().append("g")
.attr("class", function(d) { return d.children ? "node" : "leaf node"; })
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
node.append("title")
.text(function(d) { return (d==null? "": d.name + (d.children ? "" : ": " + format(d.size))); });
node.append("circle")
.attr("r", function(d) { return (d==null? 0: d.r); });
node.filter(function(d) { return (d==null? "" : !d.children); }).append("text")
.attr("text-anchor", "middle")
.attr("dy", ".3em")
.text(function(d) { return (d==null?"":d.name.substring(0, d.r / 3)); });
});
}, 1000);
</script>
Take a look at my example here.
Basically, there is code for initial load, where all circles, tooltips, etc. are created and positioned in initial places. As well, the layout (pack) is created.
Than, on each button press, new data is loaded into pack, and the pack is recalculated. That crucial code is here:
if (dataSource == 0)
pack.value(function(d) { return d.size; });
if (dataSource == 1)
pack.value(function(d) { return 100; });
if (dataSource == 2)
pack.value(function(d) { return 1 +
Math.floor(Math.random()*501); });
var data1 = pack.nodes(data);
( I have three buttons, thats why 3 ifs)
After that, elements are tranistioned to new positions, and its attributes are changed as you determine.
Here are some pics with transition in action:
Start:
Transition:
End:

Categories