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

Drawing CubicLineChart based on NSDates #38

Closed
caloon opened this issue Apr 20, 2015 · 11 comments
Closed

Drawing CubicLineChart based on NSDates #38

caloon opened this issue Apr 20, 2015 · 11 comments

Comments

@caloon
Copy link

caloon commented Apr 20, 2015

Hi there,

first of all - thanks for providing this framework. It's exactly what I've been looking for for years.

A problem I am currently facing is that the cubic line charts in my app are currently drawn based on the amount of values in the Set/Array (i.e. if there are 5 values to draw in a rect which is 200px wide, the x-distance between the graph points is 40px).
However, in many use cases, drawing the chart based on NSDates makes a lot of sense. If you for example think about stock prices: a specific stock price is always associated to a specific date & time.

Maybe I've just overseen this option.
Can this already be done with ios-charts? If not, are there plans to implement this option?

@danielgindi
Copy link
Collaborator

There are discussions about supporting this directly-
But still it can be done virtually, as mapping values to dates in your data
provider. It's not optimal, but can do the trick.
As you can map days or months to x-values, and can convert a date to a
decimal value, it's really easy to implement. And you can supply your own
formatters for just about anything :)

@caloon
Copy link
Author

caloon commented Apr 26, 2015

In case anybody faces the same issue: I solved it by using the timestamp of the (chronologically) first value in the chart as zero and using the seconds between the first value and each of the following values as xIndex like that:

yVals.append(ChartDataEntry(value: [some float], xIndex: [time interval between a value and the first value] * count / [time interval between first and last value]))

I am currently working on the x-Axis description (corresponding dates). These are still ordered by count and not by timestamp. More on that as soon.

@caloon
Copy link
Author

caloon commented Apr 26, 2015

Update on the x-Axis description (bit hacky, though): I simply add an "independent" date value for each float value to display in the chart.

let timeInterval = Int(dateOfFirstMeasurement.timeIntervalSince1970) + Int([loop iterator] * timeIntervalTotal / [count of float values to display])
xVals.append(dateFormatter.stringFromDate(NSDate(timeIntervalSince1970: NSTimeInterval(timeInterval))))

@FranzBusch
Copy link

I am also currently trying to find a good solution for the NSDate mapping. My solution at the moment is to add all xValues at the beginning for the TimeInterval to Show and later map the data xValue to the shown TimeInterval.

  • (void)addDataSetForSymptom:(Symptom *)symptom
    {
    NSArray *entries = [self.dataEntryStore entriesSince:self.startDate until:self.endDate withSymptom:symptom];
    NSMutableArray *values = [NSMutableArray array];

    for (DataEntry _entry in entries)
    {
    ChartDataEntry *de = [[ChartDataEntry alloc] initWithValue:[entry.level floatValue]_100 xIndex:[self xValueForEntry:entry]];
    [values addObject:de];
    }

    LineChartDataSet *set = [[LineChartDataSet alloc] initWithYVals:values];
    set.lineWidth = 2.5f;
    set.circleRadius = 5.0;
    [set setColor:ChartColorTemplates.vordiplom[0]];
    [set setCircleColor:ChartColorTemplates.vordiplom[0]];
    [set setCircleHoleColor: Rgb2UIColor(51, 51, 51)];
    set.drawValuesEnabled = NO;

    [self.chartData addDataSet:set];
    self.lineChart.data = self.chartData;
    }

  • (void)fillXValues
    {
    NSTimeInterval ti = [self.endDate timeIntervalSinceDate:self.startDate]/60;

    for (int i = 0; i < ti ; i ++)
    {
    [self.chartData addXValue:[@(i) stringValue]];
    }
    }

  • (NSInteger)xValueForEntry:(DataEntry *)entry
    {
    return [self.endDate timeIntervalSinceDate:entry.date]/60;
    }

My problem is, if I want to display the data of a whole year, I have to add too many xValues. Is your solution doing the same trick caloon?

@danielgindi
Copy link
Collaborator

365 values are not a lot :-)

On Mon, Apr 27, 2015 at 5:21 PM, FranzBusch notifications@github.com
wrote:

I am also currently trying to find a good solution for the NSDate mapping.
My solution at the moment is to add all xValues at the beginning for the
TimeInterval to Show and later map the data xValue to the shown
TimeInterval.

(void)addDataSetForSymptom:(Symptom *)symptom
{
NSArray *entries = [self.dataEntryStore entriesSince:self.startDate
until:self.endDate withSymptom:symptom];
NSMutableArray *values = [NSMutableArray array];

for (DataEntry

_entry in entries) { ChartDataEntry *de = [[ChartDataEntry alloc]
initWithValue:[entry.level floatValue]_100 xIndex:[self
xValueForEntry:entry]];
[values addObject:de];
}

LineChartDataSet *set = [[LineChartDataSet alloc]
initWithYVals:values];
set.lineWidth = 2.5f;
set.circleRadius = 5.0;
[set setColor:ChartColorTemplates.vordiplom[0]];
[set setCircleColor:ChartColorTemplates.vordiplom[0]];
[set setCircleHoleColor: Rgb2UIColor(51, 51, 51)];
set.drawValuesEnabled = NO;

[self.chartData addDataSet:set];
self.lineChart.data = self.chartData;
}
-

(void)fillXValues
{
NSTimeInterval ti = [self.endDate
timeIntervalSinceDate:self.startDate]/60;

for (int i = 0; i < ti ; i ++)
{
[self.chartData addXValue:[@(i) stringValue]];
}
}
-

(NSInteger)xValueForEntry:(DataEntry *)entry
{
return [self.endDate timeIntervalSinceDate:entry.date]/60;
}

My problem is, if I want to display the data of a whole year, I have to
add too many xValues. Is your solution doing the same trick caloon?


Reply to this email directly or view it on GitHub
#38 (comment)
.

@FranzBusch
Copy link

365 is no problem;)

But if I want to cover every minute of one month, I have to handle about 43.000 values.

@danielgindi
Copy link
Collaborator

Man you shouldn't do that. You need to run an averaging filter on your data

‏בתאריך יום שני, 27 באפריל 2015, FranzBusch notifications@github.com כתב:

365 is no problem;)

But if I want to cover every minute of one month, I have to handle about
43.000 values.


Reply to this email directly or view it on GitHub
#38 (comment)
.

@FranzBusch
Copy link

You are probably right about that. I was just trying to experiment with the time it needs to display..

Another question I think the xAxisLabelModulus is not working properly.

ChartXAxis *xAxis = self.lineChart.xAxis;
xAxis.labelFont = [UIFont systemFontOfSize:12.f];
xAxis.labelPosition = XAxisLabelPositionBottom;
xAxis.labelTextColor = UIColor.whiteColor;
xAxis.axisLabelModulus = 24;

After this setUp code the chart still doesn't apply the modulus. Is it not possible to set a fixed modulus?

@caloon
Copy link
Author

caloon commented Apr 27, 2015

Follow up on your first comment @FranzBusch: my app does not contain too much data (e.g. no stock prices) and I only tried it with about 70-80 xValues. Averaging the data should be the right way to go. There won't be "enough space" to display 43000 values on your iOS device anyway.

I also planned to use an averaging filter. If values are "too close to each other" in x-direction, the cubic line will make it look like the chart is not ordered correctly, with graphs going backwards. @danielgindi: Is there an averaging mechanism built into ios-charts?

@danielgindi
Copy link
Collaborator

Actually there is a filtering mechanism with one filter implemented- a very
specific averaging algorithm suited for charts.
But it is disabled right now because of structural changes in the
MPAndroidChart library. It will be re-enabled soon again, I hope!

‏בתאריך יום שני, 27 באפריל 2015, Josef notifications@github.com כתב:

Follow up on your first comment @FranzBusch
https://github.com/FranzBusch: my app does not contain to much data
(e.g. no stock prices) and I only tried it with about 70-80 xValues.

Averaging the data should be the right way to go. There presumably won't
be "enough space" on your iOS device to display 43000 values anyway.

I also planned to use an averaging filter. If values are "to close to each
other" in x-direction, the cubic line will make it look like the chart is
not ordered correctly. @danielgindi https://github.com/danielgindi: Is
there an averaging mechanism already built into ios-charts?


Reply to this email directly or view it on GitHub
#38 (comment)
.

@caloon
Copy link
Author

caloon commented May 5, 2015

Follow up - my first code sample was not guide as it caused the line chart to vanish (cp. issue #63). Instead of the xIndices, yVals should be an approximation.

The right way to go (sorry but its difficult to describe) would be to approximate each xIndex with a yVal:

  • detect which two yVals are the closest to an xIndex (e.g. via for loop)
  • calculate a relative yVal for the xIndex: (yVal(smaller than xIndex) * xDistance(between yVal greater than xIndex and xIndex) + yVal(greater than xIndex) * xDistance(between yVal smaller than xIndex and xIndex)) / xDistance(between yVal smaller than xIndex and yVal greater than xIndex)

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

No branches or pull requests

3 participants