How does d3.js decides how many and which ticks to show? - svg

On my chart, I have a time scale xAxis. I want user can do zoom in, zoom out operations to switch among month view(display ticks of one month), week view( display ticks of a week, Mon, Tue, Wed ...), day view( display ticks of 24 hours).
When I call the zoom behavior on svg. My chart is stretching and condensing nicely. But the ticks on the xAxis are changing "gradually" which is not what I want. E.g. when I zoom in at a certain scale, the ticks are [May 3, May 5, May 7 ......], the ticks D3 decides randomly seem to be skipping one day. And it will happen when user zoom further into hours as well.
What I want is: Assume that user is initially on the month view(which displays ticks of one month), then user keeps zooming in till to a certain scale, the ticks of xAxis will turn into 7 days( Mon, Tue, Wed ...... ), if user keeps zooming in, at a certain scale, the ticks turn into hours. I don't want d3 to decide ticks randomly.
How can I achieve that?

For each zoom scale you should set axis.ticks() and axis.tickFormat() explicitly.
When zoom event occurs you can call something like this:
adjustTimeAxis: function() {
switch(this.getZoomState()) {
case 'longDays':
this.axis
.ticks(d3.time.days, 1)
.tickFormat(function(d) { return d3.time.format('%a %e')(d); })
.tickSubdivide(false);
break;
case 'shortDays':
this.axis
.ticks(d3.time.days, 1)
.tickFormat(function(d) { return d3.time.format('%e')(d); })
.tickSubdivide(false);
break;
case 'weeks':
this.axis
.ticks(d3.time.mondays, 1)
.tickFormat(null)
.tickSubdivide(6);
break;
default: // months
this.axis
.ticks(d3.time.months, 1)
.tickFormat(null)
.tickSubdivide(1);
}
} // adjustTimeAxis
In this code function this.getZoomState returns one of the string values ['longDays', 'shortDays', 'weeks'] depending on zoom scale.
You can see how this code works here: http://bl.ocks.org/oluckyman/6199145

Related

noUISlider - Any way to place labels within the connect segments

I have a noUiSlider with several handles to allow specifying several contiguous date periods (example = Feb to Apr, May to July, and Aug to Sept). Ideally I would like to have labels that appear centered on the connect divisions to describe what each period relates to (ex. "Current Period", "Next Period"). I was thinking I could do this by setting a centered background image on the noUi-connect divisions.
However, the noUi-connect divisions use transform (translate/scale) styling which results in my background images being scaled which I do not want.
I also thought maybe I could revise the javascript to generate an outer division around each nonUi-connect division, and I would apply the background onto the outer division instead - but I was unable to get the background from the outer division to appear.
Any other ways I could accomplish this? The only other thing I can think of it to have floating divisions defined outside of the noUiSlider object which I would need to reposition whenever I detect changes in the handle positions.
You can add an element outside of the connects and absolutely position it.
A quick version for a slider with two handles (showing the value for the first handle):
var origin = slider.querySelector('.noUi-connects');
var node = document.createElement('div');
node.style.textAlign = 'center';
node.style.position = 'absolute';
node.style.zIndex = '10';
node.style.fontSize = '10px';
origin.appendChild(node);
slider.noUiSlider.on('update', function(values, handle, unencoded, tap, positions) {
node.style.left = positions[0] + '%';
node.style.right = (100 - positions[1]) + '%';
node.innerText = values[0];
});
Just realized another approach is to set the innerHtml of the specific noUi-connect divisions to my label values. Simpler than playing with background images.
But the transform styling still affects the labels, so the end result is not better. Maybe I can load the innerHtml with an inner division that somehow ignores the transform settings but I haven't figure out how to do that yet. transform: none does not make any difference.

Daily subsets of Annual Data

I am trying to optimize an energy production process in a year, based on the hourly resolutions. The purchased electricity price from the grid has two different values based on the two times of a day; between 07:00-18:00 the price is 10, between 18:00-07:00 the price is 5.
The time sets are:
P_el electricityprice /t7*t18 10, t19*t6 5, ....../
t time /t1*t8760/
How can I create the P_el automatically, so that I don't need to write different sets by hand until the 8760th hour?
I think what you want to do here is make p_el a parameter, not a set. The following should work. (Do check that I got the timing exactly right)
set t "time" /t1*t8760/;
parameter hour(t) "hour of the day from 1 to 24";
parameter p_el(t) "electricity price";
hour(t) = mod(ord(t), 24);
p_el(t) = 5;
p_el(t)$(hour(t) >= 7 and hour(t) < 18) = 10;

How to render end ticks always using d3 svg axis

I am not sure how the tick placement works with d3 svg axis. I tried playing with the ticks, tickSize options but essentially I get the start and end ticks "sometimes" with random data plotted
http://plnkr.co/edit/i5DBgfWzr2sa8Yugg2A8?p=preview
(Please ignore the angularjs aspect. The part that I am interested in is the following in d3)
var make_x_axis = function () {
return d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(5)
.tickFormat("")
.tickPadding(8);
};
var make_y_axis = function () {
return d3.svg.axis()
.scale(y)
.ticks(6)
.orient("left")
.tickSize(10,10)
.tickPadding(8);
};
How do I always get the start and end ticks rendered for both X and Y axis? I clearly always get the start and end tick lines on both X and y axis (Unsure how that is happening - I do not see a .tick element at that location in the chrome inspector) but the labels are missing. I can find the first and last tick and render a text with the tick label info but clearly I cannot always do it because sometime the axis gives me the start ticks and sometime it does not.
Any help is appreciated.
Thank you
I am not sure I fully understand your question, if you want to remove the ending x-axis ticks, you should set
d3js V3
.outerTickSize(0),
d3js V4 - V6
tickSizeOuter(0)
If you want to customize the ending ticks, you may use
.tickValues(y.ticks(5).concat( y.domain() ));
Have a look at my JSFiddle for a demonstration.

jQuery Flot: Change xaxis when zooming

Is there a way I can get the x-axis to switch from showing hour, to e.g. week days, months .. when zooming out?
Currently my x-axis is configured as such:
xaxis: {
mode: "time",
minTickSize: [1, "second"],
timeformat: "%H:%M:%S",
}
My default the graph looks nice, but when I zoom out enough times, the labels on the xaxis just display "00:00". How can I change the timeformat so that the date is included also? E.g. Tue 27 00:00 or similar.
Here's an example of when the graph is zoomed out a lot (obviously I need to remote some datapoints to make it look smoother..)
You can add a date using the standard specifiers, like %Y-%m-%d. A full list can be found in the API docs under the Time Series Data section.
To get the format to update based on the range, i.e. show HMS when zoomed in but YMD when zoomed out, is trickier. You'll need to listen for the 'plotzoom' event and check the range to see whether the format needs to change. If so, use getOptions() to retrieve and update the plot's options, then call setupGrid & draw to redraw the plot using the new format.
I solved this by making a custom tickFormatter for the X-axis:
In your xaxis options put:
xaxis: {
mode: "time",
tickFormatter: customXAxisFormatter,
...
}
Then your customXAxisFormatter could be eg. :
function customXAxisFormatter(val, axis)
{
var d = new Date(val);
// If time difference is more than 24 hours
if ((axis.max - axis.min) > (24*3600*1000))
return d.strftime("%a<br>%H:%M");
else
return d.strftime("%H:%M");
}
I hope this helps :)

How do I tell FLOT to NOT use 0 as the default minimum for the Y axis in a bar chart?

I am using the FLOT charting library to plot calories consumed over time. I would like the y-axis to use autoscaling (to just show values that are +/- the min and max in the dataset).
However, the chart always uses 0 as the minimum for my bar chart rather than autoscaling the y-axis values. Here is my code:
function DrawCalorieChart(calorieData) {
plot = $.plot($("#CalorieHistoryChart"), [calorieData],
{
series: {
bars: { show: true, barWidth: 0.8, align: "center" }
},
legend: {
show: false
},
xaxis: { tickDecimals: 0, tickSize: 1, mode: "categories", tickLength: 0 },
yaxis: { autoscaleMargin: 0.025 },
grid: { labelMargin: 10 }
});
}
I can manually set a minimum value but the problem is that I don't know what the minimum might be after new data is entered. I have tried removing the yaxis spec altogether, setting an autoscaleMargin (as shown) and setting the min to null. But nothing works - the minimum is either a static value or zero! Any help would be greatly appreciated.
Currently there's no way to do this within the library's own API. You'll need to write some JS to iterate over your values and find a min/max manually. This could be done either outside your plot/redraw calls, or in a processRawData hook, which might work better if you're constantly adding values and redrawing.
Addressing this is actually an active project that I expect will be merged into the Github master branch within the next 2-3 weeks. So depending on whether you're able to wait, and willing to use less-stable code, that's another option.
Current answer after the change mentioned in DNS' answer has been implemented:
Set zero: false in the bar options.
From the official documentation:
Area and bar charts normally start from zero, regardless of the data's range. This is because they convey information through size, and starting from a different value would distort their meaning. In cases where the fill is purely for decorative purposes, however, "zero" allows you to override this behavior. It defaults to true for filled lines and bars; setting it to false tells the series to use the same automatic scaling as an un-filled line.

Resources