Why does my saved D3 selection have no effect in some cases? - javascript

I'm confused about how to save a D3 selection for later use. In the code below, I have a "global" variable for my axes, to which I save them when they are first created. Later, I'm able to use this variable for certain things (here, setting some text) but not others (here, updating the scale), which only work if I explicitly (re)select the axes.
Is there perhaps something about JavaScript variable scoping or lifetime that I don't understand? Any assistance is appreciated!
The relevant code, much condensed from the full context:
// Top level of page
var gxaxis, gyaxis;
var updatePlot = function (view, first) {
d3.csv("data.csv", function (error, data) {
data.forEach(function (d) {
...
});
var x, y;
x = d3.scale.linear().range(...);
y = d3.scale.linear().range(...);
x.domain(d3.extent(data, function (d) {...})).nice();
y.domain(d3.extent(data, function (d) {...})).nice();
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left");
// The variable 'first' is true on page load, so this code the if clause will be executed before any executions of the else clause
if (first) {
gxaxis = svg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end");
gyaxis = svg.append("g").attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end");
} else {
// Why is it necessary to explicitly select; why can't I use my gxaxis variable (as I do below for the text)?
svg.select(".x.axis")
.transition().duration(0).ease("in")
.call(xAxis);
// Using gyaxis.transition()... has NO EFFECT
svg.select(".y.axis")
.transition().duration(0).ease("in")
.call(yAxis);
}
var xLabel = ...;
var yLabel = ...;
// This use of gxaxis works.
gxaxis.text(xLabel);
gyaxis.text(yLabel);
});
};
// Set up handlers for switching among views
d3.selectAll(".view-select").on("click", function () {
d3.event.preventDefault();
updatePlot(this.id, false);
});
updatePlot('a', true);

This code:
gxaxis = svg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end");
Creates this DOM:
<svg>
<...>
<g class="x axis">
<g class="tick">
<line ...>
<text ...>
</g>
</g>
</...>
</svg>
And saves the text to the selection, because it was appended last.
If you want the axis, save the axis to the selection before you edit the text.
gxaxis = svg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
gxaxis.selectAll("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end");

Related

D3.js <div> not working

Maybe not a perfect title, but I don't know how to actually describe it since I'm fairly new to web design and programming, as well as to d3. I have the following code:
function draw(data) {
"use strict";
var container_dimensions = {width: 900, height: 400},
margins = {top:10, right:20, bottom:30, left:60},
chart_dimensions = {
width: container_dimensions.width - margins.left - margins.right,
height: container_dimensions.height - margins.top - margins.bottom
};
var chart = d3.select("#timeseries")
.append("svg")
.attr("width", container_dimensions.width)
.attr("height", container_dimensions.height)
.append("g")
.attr("transform", "translate (" + margins.left + "," + margins.top + ")")
.attr("id", "chart");
var time_scale = d3.scaleTime()
.range([0, chart_dimensions.width])
.domain([new Date(2008, 0, 1), new Date(2011, 3, 1)]);
var percent_scale = d3.scaleLinear()
.range([chart_dimensions.height, 0])
.domain([65, 90]);
var time_axis = d3.axisBottom(time_scale);
var count_axis = d3.axisLeft(percent_scale);
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0, " + chart_dimensions.height + ")")
.call(time_axis);
chart.append("g")
.attr("class", "y axis")
.call(count_axis);
d3.select(".y.axis")
.append("text")
.attr("text-anchor", "middle")
.text("percent on time")
.attr("transform", "translate(-35, 135)rotate (-270)")
.attr("x", container.height/2)
.attr("y", 50);
//comment
var key_items = d3.select("#key")
.selectAll("div")
.data(data)
.enter()
.append("div")
.attr("class", "key_line")
.attr("id", function(d) {return d.line_id;})
key_items.append("div")
.attr("id", function(d){return "key_square_" + d.line_id})
.attr("class", "key_square")
key_items.append("div")
.attr("class", "key_label")
.text(function(d){return d.line_name})
}
I added two div's in the body, but only one seems to be working at the time. With the above code, only the first (timeseries) works. If I delete everything in the function before the "comment" comment, the second div (key) works just fine.
<div id="timeseries"></div>
<div id="key"></div>
<script>
d3.json("data/subway_wait_mean.json", draw);
</script>
What needs to be changed so this could work properly?
You need to learn how to use your browser's developer tools.
When I ran your code in Chrome, Chrome's dev tools showed me the error Uncaught ReferenceError: container is not defined, pointing to this line:
.attr("x", container.height/2)
See the problem?
I think you want
.attr("x", container_dimensions.height/2)
instead.

Trouble adding label to axes in d3

I have this code and I can't seem to create labels for the axes. I've tried copying what other scripts do but my knowledge of D3 is pretty lacking.
In particulare I was trying to merge this code with what I have to disasterous results:
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Temperature (ºF)");
And what I have:
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("transform", "translate(" + width + ",0)")
.attr("y", -5)
.style("text-anchor", "end")
.text("Frequency");
Here is the bl.ocks.org link
And here is the gist
Your label is here in the DOM at the position you specified. You just need to style it to make it visible:
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("fill", "black") // <= Here
.attr("transform", "translate(" + width + ",0)")
.attr("y", -5)
.style("text-anchor", "end")
.text("Frequency");
Or simply use CSS:
.label {
fill: black;
}

Using techanJS D3 examples with JSON data

I what to use a couple of the techanJS examples in my application but am struggling use my own data.
Here is the example code for the close chart which I want to use.
I have a play! framework application which passes in a List[CloseList] array, this is then converted to a JSON object using:
var closesJSON = #{Html(new Gson().toJson(closes))};
I am then assuming that I will be able to replace d3.csv() with d3.json() but cannot find a working example and my hacking hasn't got me anywhere so far.
d3.csv("data.csv", function(error, data) {
var accessor = close.accessor();
data = data.slice(0, 200).map(function(d) {
return {
date: parseDate(d.Date),
open: +d.Open,
high: +d.High,
low: +d.Low,
close: +d.Close,
volume: +d.Volume
};
}).sort(function(a, b) { return d3.ascending(accessor.d(a), accessor.d(b)); });
x.domain(data.map(accessor.d));
y.domain(techan.scale.plot.ohlc(data, accessor).domain());
svg.append("g")
.datum(data)
.attr("class", "close")
.call(close);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Price ($)");
});
Working code here, this renders:
d3.json("/api/closedata/#equity.getId", function(error, data) {
var accessor = close.accessor();
data = data.map(function(d,i) {
console.log(d.high);
return {
date : parseDate(d.closeDate),
open : d.openPrice,
high : d.high,
low : d.low,
close : d.closePrice,
volume : d.volume
}
}).sort(function(a, b) { return d3.ascending(accessor.d(a), accessor.d(b)); });
x.domain(data.map(accessor.d));
y.domain(techan.scale.plot.ohlc(data, accessor).domain());
svg.append("g")
.datum(data)
.attr("class", "close")
.call(close);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Price ($)");
});
I you can stock your data in a json file, you have just to use the function
d3.json("file.json",callback);
after, you have to change the name of the attributes in the code (the attirbutes on his csv are probably differents to your attributes in json)
callback, is usually a function but i don't think you have any other change to do in your code.
Verify your json (use a website like http://jsonprettyprint.com/), test the code on your computer (http://bl.ocks.org/andredumas/af8674d57980790137a0) with the default csv to see if it's work. It it's work, change to use json (like i told you).

d3 x-axis label positioned below the x-axis

I'm trying to draw an x-axis label for one of my d3 graphs, and I'm having a lot of trouble positioning it below the axis itself. Here is my current code:
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("x", (width / 2))
.style("text-anchor", "middle")
.text("Users ordered by contribution");
And here is a screenshot of the problem. Is there a quick fix?
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("x", (width / 2))
.attr("y", height) //set your y attribute here
.style("text-anchor", "middle")
.text("Users ordered by contribution");
You can set your y position attribute for your svg text as per above

D3.js Plotting Multiple Data Sets from Separate Files

I'm trying to make a scatter plot with two sets of data from two tsv files. However, each one shares the x-axis with single scale. There are two y-axis each with their own scale. The graph I have right now will help visually.
Problem is, the 2nd data set (in orange) only plots partially as seen as a smudge at about 15,000 on the a-axis. it should really be a much larger line. Also, when I run this, sometimes the 2nd data set renders and the first does now. Not sure why this is happening..
Here are the two (likely) relevant blocks of code:
//1st data set
d3.tsv("datatest4.tsv", function(error, tsv1) {
tsv1.forEach(function(d) {
d.altit = +d.altit;
d.tmp = +d.tmp;
});
x.domain(d3.extent(tsv1, function(d) { return d.altit; })).nice();
y.domain(d3.extent(tsv1, function(d) { return d.tmp; })).nice();
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("Altitude (m)");
svg.append("g")
.attr("class", "y axis axis1")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end");
svg.selectAll(".dot")
.data(tsv1)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 1)
.attr("cx", function(d) { return x(d.altit); })
.attr("cy", function(d) { return y(d.tmp); })
.style("fill","steelblue");
});
and
//2nd data set
d3.tsv("datatest2.tsv", function(error, tsv2) {
tsv2.forEach(function(dd) {
dd.alti = +dd.alti;
dd.pressure = +dd.pressure;
});
x2.domain(d3.extent(tsv2, function(dd) { return dd.alti; })).nice();
y2.domain(d3.extent(tsv2, function(dd) { return dd.pressure; })).nice();
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis2)
.attr("x", width)
.attr("y", -6)
.text("Altitude (m)");
svg.append("g")
.attr("class", "y axis axis2")
.call(yAxis2)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end");
svg.selectAll(".dot")
.data(tsv2)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 1)
.attr("cx", function(dd) { return x2(dd.alti); })
.attr("cy", function(dd) { return y2(dd.pressure); })
.style("fill","orange");
});
The problem is that you're using the same selector, svg.selectAll(".dot"), for each dataset .data(tsv1) and .data(tsv2).
D3 thinks that the 2nd set is supposed to replace the first. You can fix it by assigning a unique class to each type of dot.
svg.selectAll(".blue.dot")// Select specifically blue dots
.data(tsv1)
.enter().append("circle")
.attr("class", "blue dot")// Assign two classes
...
svg.selectAll(".orange.dot")
.data(tsv2)
.enter().append("circle")
.attr("class", "orange dot")
...

Categories

Resources