D3 Trend Line Domain - d3.js

I draw a line chart using D3 with some data from our database, I got some data for the entire year to calculate what would be our trendline (taking some values for 8am, 12m, 4pm and 9pm), I'm drawing this in the chart with path and values for each X (time).
Now my problem is the domain of the trendline is of course bigger than our current values (lets say its 2 pm and my trendline will always go to 9 pm). The closes I got was setting the trendline's domain to my current data domain, which returns this:
Test1
xTrend.domain(d3.extent(trendData, function (d) { return d.date; }));
How can I cut it so it doesn't go beyond the SVGs width? I tried setting the width attribute and it doesn't work, so my guess is it has something to do with the domain.
If I set the trendline's domain to its data, I get this:
Test2
xTrend.domain(d3.extent(data, function (d) { return d.date; }));
PS: While we are on this, if anyone can point me on how I could see if my line is above-below my trendline it would be great ;)
Update:
Thanks to #lhoworko
I added this
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
And this to my line path
.attr("clip-path", "url(#clip)")

Take a look at d3 clip paths. It will make sure the line beyond the end of the chart isn't displayed.
Take a look here.
https://developer.mozilla.org/en-US/docs/Web/SVG/Element/clipPath

Related

DimpleJS barchart styling columns

I'm basically using a modified version of : http://dimplejs.org/advanced_examples_viewer.html?id=advanced_bar_labels .
I'd like to be able to add for each value a border on the left as high as the value (with a specific color for that border).
I'm not really sure where to start for adding that.
Any ideas?
Thanks.
More details : This is what I'd like to obtain : https://dl.dropboxusercontent.com/u/2227188/Image%202.png - the border on the left is the issue. (jsfiddle.net/mkzTk/5/ this what I currently have which is pretty much what's in the example - I don't know where to start really for adding a border)
You could append a rectangle after drawing for each element of the series as follows:
mySeries.afterDraw = function (s, d) {
var shape = d3.select(s);
svg.append("rect")
.attr("x", shape.attr("x"))
.attr("y", shape.attr("y"))
.attr("height", shape.attr("height"))
.attr("width", "10px")
.style("fill", shape.style("stroke"))
.style("pointer-events", "none");
};
The example you mention already uses the afterDraw function so just add the contents above to the existing method for labelling.
It looks nice, here's an example:
http://jsbin.com/lorin/9/edit?js,output#J:L20
I would set up each bar + edge pair as its own group based on a certain data point, and then append two rect elements to that group. Differences in color can be used to give them their distinctive colors.
Your code would look something like this:
var monthBars = d3.selectAll('.monthBar') //These will be for each chart
.data(allMyData, idFunction) //Assign and key your data
.enter()
.append('g')
.classed('monthBar', true);
.each(function(d){
var taskGroups = d3.select(this).selectAll('.taskGroup')
.data(d.dataForThisMonth, taskIdFn)
.enter()
.append('g')
.classed('.taskGroup', true);
.attr('transform', ...) //Define the x and y positioning for the group
taskGroups.append('rect')
//Make this the 'body' rect with the text in it
taskGroups.append('rect')
//Make this the edge rect
})

dc.js accessing a chart other that it's own in postRender

I have 2 charts on a page (A and B) and wish for some custom behavior to be added to chart B when a brush filter on chart A is performed.
I thought I could achieve this by doing something like;
charta.on('postRender', function(){
...
d3.selectAll('#chartb svg')
.data(nested, function(d){ return d.key})
.enter()
.append("g")
.attr("class", "aclass")... more code....
But this #chartb selector doesn't seem to work - when I inspect the DOM it has appended the <g> attributes to the <html> element and not the svg element I wanted to append to.
Is what I am trying to achieve possible?
If you are just adding stuff to the other chart, something like this should be possible. I don't think you will be able to select the generated items of the other chart and then apply a d3 join to it, because it is already joined.
I believe the problem with the code above is that d3.select is what you use to choose the context for a join, and d3.selectAll is what you use to actually make the data join. See
http://bost.ocks.org/mike/join/
So your code is trying to join to the chart and svg elements, which would have the effect you are describing. Instead you'll want to d3.select the svg and then d3.selectAll the elements you want to add - even though they don't exist yet! Yes, it's kind of a mind-bender; take a look at the above and the linked articles to get a better idea of it.
Note: there are dc convenience methods on the chart object which will execute the selects in the right context.
I got this working in the end by replacing the .enter() with repeated calls to datum() instead. A bit of a hack, but it works; If anyone can suggest a more d3ish way of acheiving this, I would be very grateful.
var svg = chart.svg();
nested.forEach(function(withValues) {
_(withValues.values).filter(function(d){return d.value < threshold}).forEach(function(timesMatchingThreshold){
svg.datum(timesMatchingThreshold)
.append("rect")
.style("opacity", 0.6)
.attr("transform", "translate(" + 30 + ", " + (-30) + ")")
.attr("class", "belowThreshold")
.attr("x", function(d) {return x(d.date)})
.attr("y", function(d) {return 200 - y(d.value)})
.attr("width", 3)
.attr("height", function(d) {return y(d.value)});
});

Add a custom tick / marker for d3.time.scale() in a dc.js seriesChart

I would like to add a marker for a specific date for a date axis. Please see the line chart below.
My xAxis is drawn by this function, where dateMin and dateMax can be set by the user through the front end (or a brush, etc.).
d3.time.scale.utc().domain([dateMin,dateMax]);
This means the tickValues are calculated automatically.
Now, there is a certain fixed date for our data where there is a cutoff. For example, we may have consolidated figures up to Jan. 31st 2014, and then projected data from February 1st 2014 onwards.
What I need to do is make it visually clear at what date the cutoff point is. I have manually drawn a red vertical line at the date into the JPG below. But how do I do this programatically with d3?
One caveat is that the user might choose a date range (using the brush, etc.) which does not include the cutoff date (say, Jan 1st 2014 to Jan 20th, 2014). In this case, no line should be drawn.
If possible, it would be even better if the actual lines of the line chart would look different from the cutoff date onwards. They could be dotted instead of solid, or their colours could be less saturated (.brighter ?), to make visually clear that the underlying data is not consolidated yet.
Thanks for any hints you can give me.
Sorry I can't post images to StackOverflow yet, hence I uploaded the example here:
Trying out code from the answers
Using the code below, the line and label get drawn, but not at the given x value (cutoffDate), but too "early" on the time scale, approximately on the evening of 2014-01-29.
var cutoffDate = new Date("2014-02-01T00:00:00Z");
seriesChart.svg().append("svg:line")
.attr("x1", xScale(cutoffDate))
.attr("x2", xScale(cutoffDate))
.attr("y1", yScale.range()[1])
.attr("y2", yScale.range()[0])
.style("stroke", "rgb(225,0,0)")
.style("stroke-width", "1");
seriesChart.svg()
.append("text")
.attr("text-anchor", "start")
.attr("x", xScale(cutoffDate))
.attr("y", 80)
.text("Projected data");
See the result here:
http://i.imgur.com/0PXKFup.jpg
In my original question, I didn't mention I am using seriesChart from dc.js:
seriesChart API docs
I suppose this does something with the xScale when it composes the seriesChart so setting a value on the xScale later on will result in a shifted display. Will investigate further.
Update: x position fixed
The correct way to append svg elements to a dc.js chart is not to use
chart.svg().append()
but
chart.chartBodyG().append()
This fixes the position offset for custom elements added to the chart. Using this in combination with Lars' answer works.
This would be difficult to achieve with one axis, but easy with separate axes. First, for the dividing line, you can use code like this:
svg.append("line")
.attr("x1", xScale(cutoffDate))
.attr("x2", xScale(cutoffDate))
.attr("y1", yScale.range()[0])
.attr("y2", yScale.range()[1]);
svg.append("text").attr("x", xScale(cutoffDate) + 10).attr("y", yCoord)
.text("projected");
To have different styles, use two different axes:
var xScale1 = d3.time.scale().domain([..., cutoffDate]).range([0, 100]),
xScale2 = d3.time.scale().domain([cutoffDate, ...]).range([100, 200]);
svg.append("g").attr("class", "before")
.call(d3.svg.axis().scale(xScale1));
svg.append("g").attr("class", "after")
.attr("transform", "translate(" + xScale1.range()[1] + ",0)")
.call(d3.svg.axis().scale(xScale2));
Once appended, you can style the axes using the classes or by selecting the individual DOM elements. This means you end up with two scales -- to make using them for computing coordinates easier, you could wrap them:
var xScale = function(x) { return x < cutoffDate ? xScale1(x) : xScale2(x); };
The exact best way to implement this will depend on your specific application, but the above should give you a rough guide how to do it.

d3.js bar chart with pos & neg bars (win/loss) for each record

I want to make a bar chart in d3.js that has both positive and negative bars for each item or row, like this:
it's somewhat like the google finance "Sector Summary" chart (http://google.com/finance)
Can anyone point me to a d3.js example of this kind of chart? I am aware of the "bar chart with negative values" example here: http://bl.ocks.org/mbostock/2368837
If there is a relatively easy way to explain how to modify that example to accomplish what I want, that could work too.
Thanks!
Here is what I could do:
The basis is to have two values per item. To simplify things we can say that all values have to be positive, the first one will be displayed on the right, the second one on the left.
From the example you provided, we will just plot the second value we add:
data = [
{name: "A",value: 1,value2: 2},
...
]
We will also fix the domain (you can write a function to do it nicely afterwards):
x.domain([-10,10])
Finally, we will draw the second bars (on the left):
svg.selectAll(".bar2")
.data(data)
.enter().append("rect")
.attr("class", "bar2")
.attr("x", function (d) {
return x(Math.min(0, -d.value2));
})
.attr("y", function (d) {
return y(d.name);
})
.attr("width", function (d) {
return Math.abs(x(-d.value2) - x(0));
})
.attr("height", y.rangeBand());
This code is just copy paste from the example where I replaced d.value by -d.value1 and .bar by .bar2 for the class.
You will also have to modify the x-axis format for having "75, 50, 25, 0, 25, 50, 75".
jsFiddle: http://jsfiddle.net/chrisJamesC/vgZ6E/

Display x axis properly on stacked bar chart

Hey so I'm having difficulty with the positioning of my stacked bar chart. It's showing up, I'm just having difficulty declaring it's x axis. Here is the jsfiddle: http://jsfiddle.net/E2HST/
var xTimeScale = d3.time.scale().
domain([new Date(data[0].date), d3.time.day.offset(new Date(data[data.length - 1].date), 1)])
.range([0, width]);
is obviously part of the problem, I pulled code and have unfortunately fallen into the trap of not fully understanding it.
var bars = svg.selectAll(".bar")
.data(data).enter()
.append("g")
.attr("class","bar")
.attr("transform", function(d){
return "translate("+xTimeScale(d.date)+",0)"
})
I've tried swapping in d.year for d.date seeing as there is no more d.date but it's throwing up errors. Any help would be greatly appreciated.
The simple answer is that the objects in your data array do not have a date key, so your xTimeScale domain is undefined. An easy way to correct this would be to create this key for each data item:
data.forEach( function(d) { d.date = new Date(d.year, 0, 1); });
Which creates the date as January 1st of the year variable. This basically solves your x-axis problem.
I would, however, suggest that your data would be better suited to using an ordinal scale, since you only have yearly data.
A couple of other small things:
Your x-axis definition has way too many ticks, consider changing this
Consider adding css styles for .axis elements, to improve readability
An updated fiddle with these slight changes is at http://jsfiddle.net/E2HST/1/

Resources