What are the units of d3.js zoom.translate vector? - javascript

I am trying to use the following code taken from this example to pan my time-series line-chart sideways, as new data is added to the graph:
d3.select("path")
.datum(globalData)
.attr("class", "line")
.attr("d", valueline(globalData))
.transition()
.ease("linear")
.attr("transform", "translate(" + x(-dx) + ")");
But this does't work - my line disappears. I am trying to understand what the units of the translate vector need to be for this to work. In my case dx would be the difference between the first and last x-values of the new data.

The units used in translate are screen pixels in the default coordinate system.

Related

D3 chart zooms the plot but not the axis - callback.apply is not a function

I've got a chart based on this tutorial
It's working very all apart from that when I zoom, the plot works but not the x-axis.
The problem lies on my zoom function
const zoomed = (event) => {
xScale.range(
[margin.left, width - margin.right].map((d) =>
event.transform.applyX(d)
)
);
svg
.selectAll(".bars rect")
.attr("x", (d) => xScale(d.name))
.attr("width", xScale.bandwidth());
svg.selectAll(".x-axis").call(xAxis);
};
The last line svg.selectAll(".x-axis").call(xAxis); throws the error: callback.apply is not a function.
if I log svg.selectAll(".x-axis") to the console I get the corresponding nodes with the call method attached so I don't know why's not working. I'm using d3 7.6.1 so this method might not work in the same way but after researching, I couldn't find an answer.
Here's a sandbox
When you zoom, you want to recreate the x scale inside the g.x-axis element. To do that you need to call an axis generator on that element like that:
axisGenerator(d3.selectAll(".x-axis"))
which is equivalent to execute:
d3.selectAll(".x-axis").call(axisGenerator)
In your code the parameter to call() is not an axis generator. The xAxis variable is a d3 selection. So you first need to create an axis generator:
const xAxis = d3.axisBottom(xScale).tickSizeOuter(0);
And then initialize the x axis by creating a group element and call the axis generator on it:
svg.append("g")
.attr("transform", `translate(0, ${height - margin.bottom})`)
.attr("class", "x-axis")
.call(xAxis)
In your zoomed function, you can then update the x axis like that:
svg.selectAll(".x-axis").call(xAxis);

d3 .ticks() .tickValues() not updating in chart

I have seen others with this question (What does it take for .ticks() to work?) but I still have not found a clear solution. My dataset is quite large and the x-axis tick labels (simple strings) are overlapping. I would like to set a maximum of maybe 10 ticks on the chart. A snippet of my code is below.
// Add the x Axis
svg.append("g")
.attr("transform", "translate(0," + (chart_height - margin["bottom"]) + ")")
.call(d3.axisBottom(x)
.ticks(5),
)
.selectAll("text")
.attr("y", (y(d3.extent(y_values)[0]) - y(0) + 30))
.style("font-family", "PT Sans")
.style("font-size", "14px");
I am referencing this: https://github.com/d3/d3-axis/blob/master/README.md#axis_ticks but no matter what I do, my number of ticks never changes. I tried both .ticks() and .tickValues().
Am I maybe adding .ticks() to wrong portion of code? I'm super new to d3.

d3.js Making the node size proportional to it's degree by using d.weight causing a error

I have a fully functional force directed graph. Now I am trying to have the node size i.e the radius of the nodes proportional to there degrees.
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(d3GraphData.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r",function(d) {return d.weight * 2;})
This causes all the nodes to disappear and I get a error stating
Error: <circle> attribute r: Expected length, "NaN".
This is still my first project in d3.js. Is there more complexity involved in making the node radius proportional to the degree.
Below is the fiddle
I looked at your code and the degree information is not included in your nodes:
group:1
id:"10000000500874"
index:252
type:"Agent"
visible:true
vx:-0.06967413905945731
vy:0.08562878588481856
x:179.01501937845038
y:424.4658956406365
You will first need to compute the degree, store it as a new property and then change the radius accordingly.

brush on rotated lines using d3 to create zoom effect

I am working on this plnkr. I have three lines at angle 30, 45 and 60. I want to apply a brush on these lines so that when the chart is brushed the lines get redrawn at where it crossed the brushed rectangle with appropriate the values on the axis. Any help or hint to solve this problem is greatly appreciated.
EDIT: If you have different solutions to draw the rotated lines and brush on top of them it is welcomed too. Please help.
var ga = d3.select("svg")
.append("g")
.attr("class", "a axis")
.attr("transform", "translate(" + margin.left + "," + (height + margin.top) + ")")
.selectAll("g")
.data([30, 45, 60])
.enter()
.append("g")
.attr("class", "rotatedlines")
.attr("transform", function(d) { return "rotate(" + -d + ")"; })
.attr("stroke-width", 1)
.attr("stroke", "black")
.attr("stroke-dasharray", "5,5");
To explain my solution:
The fundamental steps to take are as follows:
update the domains of the x and y scales to the brush extent
redraw the axes
compute the scale factor and translation for the lines
scale and translate the line containers accordingly
reset the brush
Note that steps 3 and 4 are only necessary because you're not using the scales to draw everything -- a better approach would be to define two points for each line as the data that's bound to the elements and then use the scales to redraw. This would make the code simpler.
With your approach it's still possible though. In order to facilitate it, I've made a few modifications to your code -- in particular, I've cleaned up the various nested g elements with different translations and defined the lines through their x1, x2, y1, y2 attributes rather than through translation of the containers. Both of these changes make the functionality you want easier to implement as only a single transformation takes places that doesn't need to consider multiple other transformations. I've also nested the lines in multiple g elements so that they can be scaled and translated more easily.
The brush handler function now looks like this:
// update scales, redraw axes
var extent = brush.extent();
x.domain(brush.empty() ? x2.domain() : [ extent[0][0], extent[1][0] ]);
y.domain(brush.empty() ? y2.domain() : [ extent[0][1], extent[1][1] ]);
xAxisG.call(xAxis);
yAxisG.call(yAxis);
This code should be fairly self-explanatory -- the domains of the scales are updated according to the current extent of the brush and the axes are redrawn.
// compute and apply scaling and transformation of the g elements containing the lines
var sx = (x2.domain()[1] - x2.domain()[0])/(x.domain()[1] - x.domain()[0]),
sy = (y2.domain()[1] - y2.domain()[0])/(y.domain()[1] - y.domain()[0]),
dx = -x2(x.domain()[0]) - x2.range()[0],
dy = -y2(y.domain()[1]) - y2.range()[1];
d3.selectAll("g.container")
.attr("transform", "translate(" + [sx * dx, sy * dy] + ")scale(" + [sx, sy] + ")");
This is the tricky part -- based on the new domains of the scales, we need to compute the scale and translation for the lines. The scaling factors are simply the ratio of the old extent to the new extent (note that I have made copies of the scales that are not modified), i.e. a number greater than 1. The translation determines the shift of the (0,0) coordinate and is computed through the difference of the old (0,0) coordinate (I get this from the range of the original scales) and the position of the new domain origin according to the original scales.
When applying the translation and scale at the same time, we need to multiply the offsets with the scaling factors.
// reset brush
brush.clear();
d3.select(".brush").call(brush);
Finally, we clear the brush and reset it to get rid of the grey rectangle.
Complete demo here.
You can access the brush extent via d3.event.target.extent(). The flow for drawing the scale is this:
Set scale
Set axis
Draw axis
As soon as the brush is done, you have to modify the scale and then re-draw the axis according to the current x and y domain. Is that what you meant?
I cleaned up the code a bit and made a little demonstration: http://plnkr.co/edit/epKbXbcBR2MiwUOMlU5A?p=preview

Can I create a Javascript chart from this table?

I just started programming with Highcharts, and wonder if it's possible to get any sort of line chart based on this table: http://www2.nve.no/h/hd/plotreal/Q/0027.00025.000/knekkpunkt.html
The table updates each half hour, and I want to program a line chart that updates accordingly.
My problem is that I have no idea if this is possible. The table data are remote, on a server I don't have access to. The data is public and open, though.
Would be grateful for some help!
One great open source project you'd want to check out is D3.js. It's a feature-rich library for generating complex visualizations (using SVG elements and the DOM) from raw data. Here is a demo I made using a snippet of your data that is powered by D3:
http://jsfiddle.net/2s6Y3/
//Define two functions that will 'map' x- and y-values into their respective axis domain
var x_scale_fn = d3.scale.linear().domain([Math.min.apply(null, times), Math.max.apply(null, times)]).range([0, width]);
var y_scale_fn = d3.scale.linear().domain([Math.max.apply(null, levels), Math.min.apply(null, levels)]).range([0, height]);
//Create a 'line' object that can read our records and turn them into x-y coordinates
var line = d3.svg.line()
.x_scale_fn(function(d, i) { return x(times[i]); })
.y_scale_fn(function(d, i) { return y(levels[i]); });
//Create a new SVG element (our chart) and give it some basic attributes
var graph = d3.select(".chart").append("svg:svg")
.attr("width", width + margins[1] + margins[3])
.attr("height", height + margins[0] + margins[2])
.append("svg:g")
.attr("transform", "translate(" + margins[3] + ", " + margins[0] + ")");
//Create our chart axes
var x_axis = d3.svg.axis().scale(x),
y_axis = d3.svg.axis().scale(y).orient("left");
//Render the x-axis
graph.append("svg:g")
.attr("transform", "translate(0," + height + ")")
.attr("class", "x axis")
.call(x_axis)
.selectAll("text")
.attr("transform", function(d) { return "rotate(45deg)"; });
//Render the y-axs
graph.append("svg:g")
.attr("transform", "translate(-25,0)")
.attr("class", "y axis")
.call(y_axis);
//Render our data (line) onto the chart
graph.append("svg:path").attr("d", line(data));
If you want to create a project that offers a persistent, up-to-date visualization of the water data, you'll need to set up a scraper that will periodically read data from the page and format it to be used by D3 (i.e. - JSON). This could be done with a number of different tools, but one suggestion would be using Python and urllib2.
This will be possible, but not fun at all. That data is all one big preformatted string. Getting access to a table of those values will make things way easier for you. Otherwise, you'll need to look into writing some RegEx to parse that monster node's text.
Your link seems to not be working.
If you are talking about an HTML table, take a look at the demo here:
http://www.highcharts.com/demo/column-parsed
Thanks for all the help, this made me realize that my skills doesn't add up to what it takes to get this working.

Categories

Resources