Accessing a column/field from a csv file in d3.js - d3.js

I am trying to access particular variable from a csv file as shown below:
Month,Year,leaves
Jan,2011, 20
Feb, 2011, 30
Mar,2011, 40
What I am trying to achieve is to create a bar chart with height being the leaves value. Below is the code that I used to access leaves field from the csv file imported. I am doing something wrong here, as I am new to D3.js I am pretty confused about accessing an object or referencing an object (syntax in general). I don't care about year, month, I am just trying to create a simple bar chart with leaves. Any help or pointer towards valuable resources would be much appreciated.
Thanks
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript" src="d3.min.js"></script>
<h4> D3 Bar Chart </h4>
</head>
<style>
.bar{
display:inline-block;
width: 20px;
height: 80px;
margin-right: 2px;
background-color: teal;
}
</style>
<body>
<script>
d3.csv("sl_month_year.csv", function(error, data)
{ if(error) {
console.log(error);}
else {console.log(data);
var bar = d3.selectAll("body")
.select("div")
.data(data.length)
.enter()
.append("div")
.attr("class","bar")
.style("height", function(d) { for (i=0; i <= d.length; i++) { return d.[i].leaves + "px" ;});
});

I see a few things that seem wrong to me in your code.
First, you try to access a property using d.[i].leaves in your loop; I think what you try to do is d[i].leaves.
Second, you do not actually need to loop on the elements in your collection, as d3 will handle that for you.
I would go for a different way of using selections, using a simple select for the body and a selectAll for the div's, and bind to the data directly instead of data.length.
Code below should be fine (untested though):
d3.csv("sl_month_year.csv", function(error, data){
if(error) {
console.log(error);}
else {
console.log(data);
var bar = d3.select("body")
.selectAll("div")
.data(data)
.enter()
.append("div")
.attr("class","bar")
.style("height", function(d) {
return d.leaves + "px" ;
});
});

Related

vuejs scoped styles not working with d3js

I am trying to integrate Vue.js and D3.js. What I notice is sometimes the CSS classes don't really work on the svg elements. I am giving the snippet of the vue component below.
<template>
<div>
<h1>Hello world</h1>
<svg width="300" height="100" ref="barchart"></svg>
</div>
</template>
<script>
import * as d3 from "d3";
export default {
name: "LineChart",
mounted() {
d3.select("h1").attr("class","red-text")
var data = [10,20,15,30,60];
var barHeight = 20;
var bar = d3
.select(this.$refs.barchart)
.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("class","rect")
.attr("width", function(d) {
return d;
})
.attr("height", barHeight - 1)
.attr("transform", function(d, i) {
return "translate(0," + i * barHeight + ")";
});
}
};
</script>
<style scoped>
.rect{
fill: blue;
}
.red-text{
color:red;
}
</style>
Its output is obtained as :-
scoped css output
But as sson as I remove the scoped attribute, the code works fine. New output :-
global css output
Thanks in advance!
Scoped styles work by vue assigning a unique attribute to dom elements, and then adjusting the style rules by adding an extra criteria for elements to have that attribute. Example in vue guide. However, since elements dynamically created with d3 aren't managed by vue (since they aren't part of the template), it doesn't work out of the box. One way to solve this, is to to use deep selector (e.g. svg >>> .rect { ... }), which doesn't attach the additional unique attribute criteria for the child elements.
If you just want to color the bars you don't need explicit css. You can just set:
.style("fill", function(d) { return 'blue'; })
on your bar.

Adding tooltips to d3.js map using csv file

I would like to add tooltips for a project previously discussed here:
How to apply specific colors to D3.js map based on data values?
I've added a style for the tooltip-container as follows:
<style>
#tooltip-container{
background: #eee;
box-shadow: 0 0 5px #999999;
color: #333;
display: none;
font-size: 12px;
padding: 10px;
position: absolute;
text-align: center;
right: 200px;
top: 300px;
width: 140px;
height: 230px;
z-index: 10;
}
</style>
I also added the html tooltip container in a div:
<div id="tooltip-container"></div>
I've revised my drawMap() function as follows:
function drawMap(conus) {
svg.selectAll(".feature")
.data(conus.features)
.enter().append("path")
.attr("class", "county")
.attr("id", function (d) { return d.properties.ID_1; }, true)
.attr("d", path)
.on("mouseover", function (d) {
$("#tooltip-container").show();
})
.on("mouseout", function () {
$("#tooltip-container").hide();
});
//Fill county colors
var dataRange = getDataRange();
d3.selectAll('.county')
.attr('fill', function (d) {
return getColor(d.properties[attributeArray[currentAttribute]], dataRange);
});
}
What I need to do now are as follows:
When a user clicks the STOP button, the tooltip-container should display the appropriate warning text for that day from the "warnings.csv" file. The csv file can be downloaded from here: https://nlet.brc.tamus.edu/Home/Swat) by selecting the Management tab in the SWAT download section and selecting the "Warnings csv file".
Then, as users hover over the map, the tooltip-container should switch to that county and display the appropriate warning for the county for that day.
Appreciate any help. Thanks in advance.
Never mind...solved this. I revised the following functions from this project:
<div id="tooltip-container"></div>
function drawMap(conus) {
var curr = $("#clock")[0].innerText;
var day = curr.charAt(0);
svg.selectAll(".feature") // select country objects (which don't exist yet)
.data(conus.features) // bind data to these non-existent objects
.enter().append("path") // prepare data to be appended to paths
.attr("class", "county") // give them a class for styling and access later
.attr("id", function (d) { return d.properties.ID_1; }, true) // give each a unique id for access later
.attr("d", path) // create them using the svg path generator defined above
.on("mouseover", function (d) {
$("#tooltip-container").show();
loadWarning(d, day);
})
.on("mouseout", function (d) {
$("#tooltip-container").hide();
});
var dataRange = getDataRange(); // get the min/max values from the current day's range of data values
d3.selectAll('.county')
.attr('fill', function (d) {
return getColor(d.properties[attributeArray[currentAttribute]], dataRange);
});
}
function loadWarning(d, day)
{
var html = "";
var tooltip = d3.select("#tooltip-container");
d3.csv("/data/warnings2.csv", function (error, data) {
for (var i in data) {
if (data[i].id == d.properties.ID_1) {
html += "<table><tr><strong>" + d.properties.County + "</strong></tr><br/>" +
"<tr>ID: " + d.properties.ID_1 + "</tr><br/><br/>" +
"<tr><div style='text-align:left'>" + data[i][day] + "</div></tr></table>";
tooltip.html(html);
}
}
});
}

d3 data update CSV - can't figure out how to update table cells

I've got a haproxy status endpoint which outputs the following data:
I want to parse it's output and build a nice interactive dashboard (in this case monitoring which Riak nodes are up or down at a given time.
I've written the following:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>dadsa</title>
</head>
<body>
<div id="viz"></div>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var hosts = [];
var url = "http://localhost:8666/haproxy-csv";
function update() {
d3.csv(url, function(data) {
hosts = data.filter(
function(d){
console.log(JSON.stringify( d) );
console.log(Object.keys(d));
console.log(d["# pxname"]);
return d["# pxname"] == "riak_backend_http"
&& d["svname"] != "BACKEND";
}
).map(function(d){
return {"label": d["svname"],"value": d["status"]};
});
d3.select("#viz")
.append("table")
.style("border-collapse", "collapse")
.style("border", "2px black solid")
.selectAll("tr")
.data(hosts)
.enter().append("tr")
.selectAll("td")
.data(function(d){
return [
d["label"], d["value"]
];
})
.enter().append("td")
.style("border", "1px black solid")
.style("padding", "5px")
.on("mouseover", function(){d3.select(this).style("background-color", "aliceblue")})
.on("mouseout", function(){d3.select(this).style("background-color", "white")})
.text(function(d){return d;})
.style("font-size", "12px");
});
}
update();
setInterval(update, 1000);
</script>
<button onclick="alert(JSON.stringify(hosts[0]))"> see value </button>
<div id="svg"/>
</body>
</html>
That code works well, with one small flaw.. It continuously appends a new table element on each update. I've rewritten this code many times but whatever I write seems to just update once.
Here's what the output currently looks like:
I'm really ignorant about d3 so not even sure what exactly I should be binding or hooking into.
Any pointers?
You are getting a new table with each update because you are actually appending a new table each time when you do this inside the update function.
...
d3.select("#viz")
.append("table")
...
Instead create your table beforehand either through HTML tags or using javascript. Create it outside the update function. Say it's something like:
var myTable = d3.select("#viz")
.append("table")
.style("border-collapse", "collapse")
.style("border", "2px black solid");
Now that you have your table set up we can update it using the 'D3.js way' of enter(), exit() and update() as follows:
//bind the data to rows
var rows = myTable.selectAll("tr").data(hosts);
//append as needed using enter()
rows.enter().append("tr");
//remove placeholders in exit
rows.exit().remove();
//bind the data to columns in each row
var columns = myTable.selectAll("tr")
.selectAll("td")
.data(function(d){
return [
d["label"], d["value"]
];
});
//enter and exit as above
columns.enter().append("td");
columns.exit().remove();
//finally update the columns
myTable.selectAll("td")
.style("border", "1px black solid")
.style("padding", "5px")
.on("mouseover", function(){d3.select(this).style("background-color", "aliceblue")})
.on("mouseout", function(){d3.select(this).style("background-color", "white")})
.text(function(d){return d;})
.style("font-size", "12px");
Hope this helps.
Why not remove the table element on each update.
Add this line
d3.csv(url, function(data) {
d3.select("#viz").select("table").remove();//removes the table
//other code as usual

How to embed a D3 chart in a Polymer.dart element?

Browser: Dartium Version 34.0.1847.0 (258268)
I want to embed D3 inside Polymer.dart elements and I'm just beginning to sort out how Dart and JS interoperate. I started by creating a vanilla HTML page using HTML5/CSS3/JS from D3's introductory tutorial on how to make a bar chart to be sure my D3 implementation worked in Dartium.
I then moved the D3 code into a custom polymer element (shown below) and its companion dart source file (not shown). How do I instruct D3 to perform its selection inside the shadow DOM?
<!-- D3 code copied from: http://bost.ocks.org/mike/bar/ -->
<polymer-element name="dart-js">
<template>
<!-- D3 style -->
<style>
.chart div {
font: 10px sans-serif;
background-color: steelblue;
text-align: right;
padding: 3px;
margin: 1px;
color: white;
}
</style>
<content>
<!-- D3 container -->
<div class="chart">inside dart-js shadow-root</div>
<!-- D3 JS -->
<script type="application/javascript">
console.log("inside content");
var data = [4, 8, 15, 16, 23, 42];
console.log("data: " + data); // data: 4,8,15,16,23,42
console.log("select: <" + d3.select(".chart") + ">"); // select: <>
// How do I instruct d3 to search inside the shadow DOM?
d3.select(".chart")
.selectAll("div")
.data(data)
.enter().append("div")
.style("width", function (d) { return d * 10 + "px"; })
.text(function (d) { return d; })
</script>
</content>
</template>
<script type="application/dart;component=1" src="dart_js.dart"></script>
</polymer-element>
d3 can't find elements inside a shadowDOM but you look up the element in Dart and pass it to d3's select method.
something like this (not tested)
import 'dart:js' as js;
js.context['d3'].callMethod('select', [shadowRoot.querySelector('.chart')]);
more info about Dart-JS-interop:
https://www.dartlang.org/articles/js-dart-interop/
I wrapped the D3 JS fragment that prints the bar chart in a function and invoked it from Dart as follows:
dart-js.html
var x = function(elt) {
console.log('x');
d3.select(elt)
.selectAll("div")
.data(data)
.enter().append("div")
.style("width", function (d) { return d * 10 + "px"; })
.text(function (d) { return d; });
};
dart_js.dart
import 'package:polymer/polymer.dart';
import 'dart:js' as js;
#CustomTag('dart-js')
class DartJs extends PolymerElement {
/* Life cycle events */
// required -- constructor must call superclass
DartJs.created() : super.created() {
print("dart-js started");
}
#override void enteredView() {
super.enteredView();
print('dart_js entered');
js.context.callMethod('x', [shadowRoot.querySelector('.chart')]);
}
}

d3 - updating data doesnt cause a refresh

I'm trying to write a simple d3js app which will display a graph from an array of object literals. This literal will be updated on regular basis (from the backend). But for simplicity sake, I have rewritten it using setInterval() which should update the content.
<!doctype html>
<html>
<head>
<script src="d3.js" type="text/javascript"></script>
<script src="underscore.js" type="text/javascript"></script>
</head>
<body>
<div id="graph">
</div>
<script type="text/javascript">
var data=[
{ name: 'NONE', distribution: 5}
];
var graphElement = d3.select("#graph");
// set initial data
graphElement.selectAll("p")
.data(data, function(d){return d.name} )
.enter()
.append("p")
.text(function(d){return d.name + d.distribution;});
var redraw= function() {
console.dir(data);
graphElement.selectAll("p")
.data(data, function(d){return d.name})
.enter()
.append("p")
.text(function(d){return d.name + d.distribution;});
};
setInterval(function() {
var advice = _.find(data, function(i){return i.name == 'ADVICE'});
if (!advice){
advice = {name: 'ADVICE', distribution: 0};
data.push(advice);
}
advice.distribution = advice.distribution + 1;
redraw();
}, 3000);
</script>
</body>
</html>
The page will initially displays one paragraph (as expected). Then the setInterval() kicks off, and insert an 'ADVICE' method with value of 1. Again, as expected.
But when the subsequent setInterval() kicks off, the 'ADVICE' node is not updated - i.e. it stays off as 'ADVICE1'.
Have I missed something obvious here?
Thanks for the help!
Regards,
Alex
There are a few things to note here:
1) the enter/update selections were not being properly handled;
2) your advice.distribution was not changing from 1.
UPDATE FIDDLE with refresh of ADVICE string.
var ge = graphElement.selectAll("p")
.data(data, function(d){return d.name});
ge.exit().remove();
ge.enter().append("p");
ge.text(function(d){return d.name + d.distribution;});

Resources