Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stacked Bar Chart Using TimeIntervalSince1970 X-Axis Values With Bars And Their Data Placed Every Day Don't Align Properly With X-Axis values #3675

Open
1 task done
jimtreats opened this issue Oct 2, 2018 · 1 comment

Comments

@jimtreats
Copy link

jimtreats commented Oct 2, 2018

What did you do?

I'm trying to make a stacked bar chart that can display a stacked bar for every day in a range of dates. I've followed the line chart example in the sample app which used timeintervalsince1970 for the axis, with an hourly granularity of 3600 seconds. In my case I want daily granularity so have tried using 24 * 60 * 60 seconds as the granularity. I've written my own DateValueFormatter which takes timeIntervalSince1970 and displays the dd/mm/yy date string using a dateformattter. I've also been debugging the actual x axis values by using the default axis value formatter to display the actual timeIntervalSince1970 values. Beyond this as a comparison I've duplicated the basic graph functionality but added a simple mode that just uses x integer values of 0-9 to see how this graph performs.

What did you expect to happen?

I expect the bars to appear aligned against the appropriate x axis labels for each day, whether this be the raw x timeIntervalSince1970 values of my dataset that I add to the graph, or the formatted date strings from those same timeIntervalSince1970. Also the alternative simplistic 0-9 integer x axis graph I also expect to behave consistently with the bars appearing in line with their actual data elements

What happened instead?

Unfortunately, what did occur is that no matter what I do I cannot get the bars to align with the actual values that I'm setting in my data. Some of my bars are cropped off the edges of the graph and their alignment with the labels is off completely and even drifts more out of sync as you look along the x-axis. The alternative simplistic 0-9 integer x axis graph behaves properly with the bars appearing in line and spaced appropriately from the y-axis origin (although its still a slightly narrower gap to the y-axis than between the bars themselves) as you can see here

image

However the Use Dates version of the app displaying the raw timeIntervalSince1970 x values on the x axis shows the misalignment with my actual data values

image

Here you can see that the first bar on the far left of the graph is cropped and also you can see that the actual bars don't line up with the data values that I've actually specified, who's x values were in this case

StackedChartTests[63665:3878935] dataEntry=x=1538521200.0;
StackedChartTests[63665:3878935] dataEntry=x=1538607600.0
StackedChartTests[63665:3878935] dataEntry=x=1538694000.0;
StackedChartTests[63665:3878935] dataEntry=x=1538780400.0;
StackedChartTests[63665:3878935] dataEntry=x=1538866800.0;
StackedChartTests[63665:3878935] dataEntry=x=1538953200.0;

As can be seen the actual x axis values being displayed are NOT the dataEntry x values of my data, rather they are rounded up values that go up in 100000 second steps rather than my granularity that is set and required of 24 * 60 * 60 = 86400 seconds.

I did a bit of debugging and tracked down some of this rounding up occurring within AxisRenderBase's computeAxisValues() function. There seems to be no way to configure the axis to leave my granularity intact unless I hardcode some hacks in there to undermine the normalisation that is performed and just ensure that the interval is assigned to my axis granularity.

        // Normalize interval
        let intervalMagnitude = pow(10.0, Double(Int(log10(interval)))).roundedToNextSignficant()
        let intervalSigDigit = Int(interval / intervalMagnitude)
        if intervalSigDigit > 5
        {
            // Use one order of magnitude higher, to avoid intervals like 0.9 or 90
            // if it's 0.0 after floor(), we use the old value
            interval = floor(10.0 * intervalMagnitude) == 0.0 ? interval : floor(10.0 * intervalMagnitude)
        }
      
        // hack!
        interval = axis.granularity

Doing this hack does let the graph line up better (though still seems slightly offset from the bars being perfectly centred) though I'm still missing the left and right edges of the bar with my default data and axis setups.

image

You can see that the first axis is slightly offset from the actual y axis which looks a little messy, when compared to the simple 0-9 x-axis version which cleanly puts its axis lines in the right place and displays all its bars.

I'm not clear on anything else I can do to fix this and am considering a workaround that just uses 0-n x axis integer values and somehow looks up the appropriate date to display for this when rendering the axis labels. Ideally though I'd be able to use timeIntervalSince1970 directly from my actual underlining data, just how the test app does albeit with the smaller hourly seconds resolution rather than the daily seconds resolution i require.

I can upload a sample project after tidying it up, but thought I'd start the bug report as perhaps it won't be needed.

Relevant code stubs for the actual chart axis configuration

    let xAxis = barChartView.xAxis
    xAxis.labelPosition = .bottom
    xAxis.labelFont = UIFont.preferredFont(forTextStyle: .caption1)
    
    let secondsInADay = (24.0*60.0*60.0);
    
    if (useDatesSwitch.isOn)
    {
      xAxis.valueFormatter = DefaultAxisValueFormatter(formatter: NumberFormatter())
      //xAxis.valueFormatter = DateValueFormatter() // disabled
      xAxis.granularity = secondsInADay; // xAxis is done in seconds since 1970.. granularity should be a single day in seconds
      xAxis.centerAxisLabelsEnabled = false
    }

...
      xAxis.axisMinimum = dataSet.values.first!.x
      xAxis.axisMaximum = dataSet.values.last!.x

...
      data.barWidth = secondsInADay*0.85

Charts Environment

Charts version/Branch/Commit Number: v3.1.1 (obtained via carthage in my main app, source copied over into my test project
Xcode version:9.4
Swift version:4.1
Platform(s) running Charts: iOS Simulator
macOS version running Xcode:10.13.6

Demo Project

Demo project is available here

@jimtreats jimtreats changed the title Stacked Bar Chart Using TimeIntervalsSince1970 With Bars And Their Data Every Day On X-Axis Don't Align Properly With X-Axis values Stacked Bar Chart Using TimeIntervalSince1970 X-Axis Values With Bars And Their Data Placed Every Day Don't Align Properly With X-Axis values Oct 3, 2018
@liuxuan30
Copy link
Member

liuxuan30 commented Oct 24, 2018

did you change the logic?
image
it shouldn't be drawn if it is out of bound; that's why I am asking.

but generally, center x axis label is working well from what I remember. What you mentioned seems a wrong setup, so it's not aligned. I need you to check on your side:

  1. use default bar width;
  2. call notifyDataSetChanged after you set any chart property that needs recalculation.
  3. use ChartsDemo to test your data.
  4. be careful with xAxis.centerAxisLabelsEnabled = false if you know what it does.

besides, you can check renderAxisLabels to see if you add any offset. I'm sorry that I don't have time to read what you wrote; time is short.

If you sure it's a bug, pelase provide code that we can reproduce it with ChartsDemo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants