How to set dynamic minorTickInterval according to the datarange in Highstock? - highcharts

I used configured Highstock's "tickPositioner" to set ticks dynamically for different ranges of data. For example, if the range is 1 hour max => 1 tick every 10 minutes. Here is some code of the function I put in tickPositioner (which is in xAxis config):
if (xDataRange <= oneHour) {
// If range is 1 hour max => 1 tick every 10 minutes
increment = oneMinute*10;
positions.info.unitName = "minute";
} else if (xDataRange > oneHour && xDataRange <= oneDay) {
// If range is between 1 hour and 1 day => 1 tick every hour
increment = oneHour;
positions.info.unitName = "hour";
} else { ... }
Here is an illustrating fiddle : http://jsfiddle.net/E6GHC/124/
(I know that the range choices are not the best here.)
The QUESTION: I would like to do the same with the minorTicks.
As you can see in the Fiddle, when you click on the month button the ticks are positionned each week and minorTicks are each day. But this is static configuration (minorTickInterval: oneDay).
I have some ideas and I have tried them and nothing seems to work out..
So if someone have any suggestion ? I would be veryyy thankful.
The perfect thing would have been to be have to set/update the minorTickInterval in tickPositioner function.

Related

jQuery UI Timepicker off by 1 minute

I'm maintaining an application which uses the jQuery UI Timepicker and trying to troubleshoot a bug.
Basically, this is how to re-create it:
There are 2 timepickers, start time and end time
1. on the start-time, select 6:00am (from the hour area)
2. on the end-time, select 12:00pm (from the hour area)
3. go back to the start-time and select 00 from the minute area
Then some code runs to determine the elapsed time in minutes. That code is:
function setDuration() {
var $startTime = $(".field.times input[name$='StartTime']");
var $endTime = $(".field.times input[name$='EndTime']");
var $durationHours = $(".field.duration input[name$='DurationHours']");
var $durationMinutes = $(".field.duration input[name$='DurationMinutes']");
if ($startTime.val() != "" && $endTime.val() != "") {
var startTime = $startTime.timepicker ? $startTime.timepicker('getTimeAsDate') : Date.parse('01 jan 01 ' + $startTime.val(), "hh:mm tt");
var endTime = $endTime.timepicker ? $endTime.timepicker('getTimeAsDate') : Date.parse('01 jan 01 ' + $endTime.val(), "hh:mm tt");
var duration = (endTime - startTime) / 1000 / 60;
//check whether spanning multiple days
if (startTime > endTime) {
duration = duration + (60 * 24);
}
$durationHours.val(Math.floor(duration / 60));
$durationMinutes.val(duration % 60);
}
}
I have found that after completing steps 1 and 2, everything is fine.
But, if I complete step 3, the endTime local variable is assigned a date object which has a time component of 11:59 and not 12:00.
Off by 1 minute.
If I then go to the End Time picker and select 00 from the minute area, as I did with the Start Time picker, the elapsed time rights itself again.
As an interesting observation when debugging, both timepickers are 1 minute behind after steps 1 and 2. That is, they are at 5:59 and 11:59. The elapsed time is correct, as the difference is important. But as a said, as soon as a user selects 00 from the hours area, it seems to "right itself" for that picker and the difference is out by 1 minute (until the same thing is done for the other picker).
Javascript dates are hard! Help would be great.
Yeah, figured it out. I guess it is an opinionated component which holds the opinion that the user must select a minutes figure from the minutes area. Because if they don't, the minutes will be -1.
Fair enough.
To fix this bug, in the onSelect handler, I wrote some code similar to this:
$('#timepicker').timepicker({
onSelect: function (time, inst) {
if (inst.minutes < 0)
inst.minutes = 0;
}
});
In our app, if they don't select '00' or any other minutes, it will be presumed to be 0.

Highcharts - Custom X axis format

I'm wondering if it's possible to define a custom ordering or format for the xAxis in highcharts. My dataset has a date time which would be used for the xAxis however my client has specified that it should show the 'Period' on the xAxis. A period is specified as a 30 minute slice - meaning there are 48 periods in a day.
The range of the data is from a period on the previous day to the current day with that period. For example 06/03/2017 Period 11 (10:00) to 07/03/2017 Period 11, the xAxis should look like so:
Currently I've attempted to do this by fiddling with the actual dateTime of each point, so that it is set to Y-m-d H:i:{Period} and then using the dateFormat to only show seconds on the xAxis. However this leaves a gap in data between 2017-03-06 23:59:{Period 48} and 2017-03-07 00:00:{Period 11}
Assuming your series-data is arranged as pairs of [<timestamp in milliseconds>, <value>] you can leave your data alone and simple do calculations and presentation of the label.
To show the labels as we want we use xAxis.labels.formatter (and possibly tickInterval to space them as we want). For example:
xAxis: {
type: 'datetime',
labels: {
formatter: function() {
return createLabelFromTimestamp(this.value);
}
},
tickInterval: 1800000
}
The calculation of the period number (as described in question) can be done for example like this:
function createLabelFromTimestamp(timestamp) {
var hms = timestamp % (1800000 * 48);
var period = hms / 1800000;
// if 0-th period, show date instead
if(period == 0) {
var date = new Date(timestamp);
return date.toDateString();
}
// otherwise show period number
else {
return period;
}
}
We simply modulo away the day and divide to find what 30-minute segment we are in.
See this JSFiddle example of it in action.

UI Slider Time of Day Beyond Midnight?

So this is a unique situation. I have a double slider I made using swift to make a time range picker like the one Kayak has. Unfortunately mine needs to be a time range between 00:00 all the way to 04:00 the following morning.
It was easy to get it from 00:00 to 23:59 using a scale of 86340 seconds for my slider control. I simply plug that into this little function and out pops the correct range on both ends/knobs:
func getTimeStringFromSeconds(seconds: Double) -> String {
let dcFormatter = NSDateComponentsFormatter()
dcFormatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehavior.Pad
dcFormatter.allowedUnits = [NSCalendarUnit.Hour, NSCalendarUnit.Minute]
dcFormatter.unitsStyle = NSDateComponentsFormatterUnitsStyle.Positional
return dcFormatter.stringFromTimeInterval(seconds)!
}
As you can see in the screenshot above though I have 100740 seconds instead as the scale. How can I get it to go beyond 23:59, then reset to 00:00 and go into the next day? I can make it go to 28:00 as seen above which would technically be 4am but I want it to start over and show 04:00, not 28:00. What's a good solution for this?
Just as a note, my only solution so far was a sort of hack to make the label say 0:00 by resetting the seconds to 0.00 once it goes over 86340. The scale is still 100740 but there's some math like the following to make the label say otherwise without messing with the value of the knob (upperValue):
if upperKnobLayer.highlighted {
upperValue += valueDelta
upperValue = max(min(maximumValue, upperValue), lowerValue)
var upperDouble = Double(round(upperValue))
if upperDouble > 86340.00 {
let newValue = upperDouble - 86340
upperDouble = 0.00 + newValue
}
upperTime = getTimeStringFromSeconds(upperDouble)
}
It would help to post the implementation of your picker but generally you want the sliders value type to be NSTimeIntervals. Then you can have a reference date which will be midnight 00:00 of today. You can use NSDate(timeIntervalSince:referenceDate) to then get an NSDate representing any date since that reference date which can be formatted accordingly and display to the screen. The added benefit of this way is that the actual day of the date will be correct if your timeInterval makes it spill over to the next day
How about normalizing to a day's worth of seconds.
return dcFormatter.stringFromTimeInterval(seconds % 86340)!

Setting Maximum Data Points for Range Selector and Interval

I've got a chart that has a large number of data points. I'm setting the extremes right now so it only shows a smaller number - about 100 or so at a time. The issue I have is I don't want to disable the range selector or the manual input for dates, but currently the user can expand the range selector to include the entire data set. Is there any way to limit this so they can only grab, at most, X number of points?
I don't want to use data grouping either, I have different data for different data groups, so it's important the grouping does not change. Thanks in advance for any pointers on this one!
Unfortunately you can't disable range selector for such purpose, but you can overwrite extremes after user change them, see:
api.highcharts.com/highstock#xAxis.events.afterSetExtremes So for example, if user takes range for 4 days and you want max for 2 days - grab one of min or max, and add/remove two days in extremes (call chart.xAxis.setExtremes() with new extremes). Of course, it will call another afterSetExtremes, so it would be infinite loop, however now extremes will be proper (two days), so it wouldn't call another setExtremes().
Here's a working example of using afterSetExtremes (like Dexter describes) to create a maximum range from the range selector :
http://jsfiddle.net/B7vCR/3/
xAxis: {
minRange:6 * 30 * 24 * 3600 * 1000,
events: {
afterSetExtremes: function(e) {
var maxDistance = 10 * 30 * 24 * 3600 * 1000; //8 months time
var xaxis = this;
if ((e.max - e.min) > maxDistance) {
var min = e.max - maxDistance;
var max = e.max;
window.setTimeout(function() {
xaxis.setExtremes(min, max);
}, 1);
}
}
}
}
From this post
http://forum.highcharts.com/viewtopic.php?f=12&t=21741

How to get rid of the vacant weekend in the candlestick chart

I've read some articles about the weekend problem with teechart and the suggestions are the foolowing steps:
TChart1.Series(0).XValues.DateTime = False
Use continuous numbers as Xvalues
replace the X label with datetime.
The problem is: I draw the real time candlesticks according to the date,hour and minute and I must keep TChart1.Series(0).XValues.DateTime = True
So are there any ways to get rid of the null weekend (not only the weekend, even some time in a day as from 5:15 to 6:00 need to be removed) so that the candlesticks are shown continuously (the Xvalues must keep datetime style all the time)?
thanks.
(BTW, I use Teechart2011 Eval & VB6)
TeeChart draws each point according to its X and Y values in the associated axes.
You can work with Break Axis tool to obtain a discontinuous effect in an axis, but the easiest way to draw the points continuously in the horizontal axis is having continuous X values. So I'd suggest you to use the TChart1.Series(i).asCandle.AddCandle XVal, OpenVal, HighVal, LowVal, CloseVal, Label, clTeeColor function, being:
XVal: TChart1.Series(i).Count, instead of the date retrieved from the system
Label: the date retrieved from the system, converted to string
If this doesn't solve the problem because you still think you must have DateTime XValues, please explain why.
Taking your snipped of code from here, I've created a simple example that seems to work fine for me here. Find it below:
Private Sub Form_Load()
Dim month, day, year, hour, minute, second, nums As Integer
Dim tmpDate As Date
month = 6
day = 7
year = 2012
hour = 6
minute = 15
second = 0
nums = 10
TChart1.Aspect.View3D = False
TChart1.AddSeries scCandle
TChart1.Axis.Bottom.Labels.Angle = 90
For i = 1 To nums
tmpDate = (DateValue(day & "," & month & "," & year) + TimeValue(hour & ":" & minute & ":" & second))
If Not IsWeekend(tmpDate) Then
With TChart1.Series(0)
.asCandle.AddCandle .Count, 5, 15, 0, 10, tmpDate, clTeeColor
End With
End If
day = day + 1
Next i
End Sub
Public Function IsWeekend(InputDate As Date) As Boolean
Select Case Weekday(InputDate)
Case vbSaturday, vbSunday
IsWeekend = True
Case Else
IsWeekend = False
End Select
End Function

Resources