mzimmerm / flutter_charts

Charts Library for Flutter, written in Dart with Flutter.
Other
250 stars 42 forks source link

Time support in line chart x axis #3

Open MichaelRFairhurst opened 6 years ago

MichaelRFairhurst commented 6 years ago

Hey Milan,

just gonna share a few things I saw I wish this library had. First up: I'm doing a line chart of values over time...is there a way to make this take a List<DateTime> and get time formatting on a line chart?

mzimmerm commented 6 years ago

That is most welcome. The List x values or similar and a formatter for it, is a requirement another user (Jason, issue number 1) also had (his is a bit more extensive, but I think this would suffice for him. I started on a bit over-engineered effort on that but I think I will just sneak this in the current version.

Would it work if we have (for now):

1) the ability to set xLabels still as List , but client can put there stringified integers (representing millisecond offset) and 2) we add a xLabelsFormatter function as a member to ChartData, that is used to convert the xLabels to the actual value shown on the chart

Would that work?

Another major issue we have now is there is no nice default way to "skip" some x labels display if they become too crowded. My original idea was to engineer this into a smart layouter, but again that seems overcomplicated for now - maybe we can add a member maxXLabels that would control to show only a subset of labels. Based on looking at your app, I suspect you need something like that as well, am I right?

Thanks.

MichaelRFairhurst commented 6 years ago

What I am doing now is mapping the dates into seconds from the start time, and then into strings, and then removing repeated values.

So: [10:30:18:123, 10:30:18:456, 10:30:789, 10:31:19:123, 10:31:19:123] becomes [18, 18, 18, 19, 19] and then ['18', '', '', '19', ''].

I guess my initial reaction over the API is that I wanted to pass in a start time/end time. However, I'm not sure that's actually a good API.

It was easy enough for me to get the dates themselves, so then I just toStringd all of them, and then I noticed that they collided with each other. It might be nice to angle the text when it is long, like this I probably could have kept it like that.

What I did next was turn them into integer offsets, but then [1, 1, 1, 1, 2, 2, 2, ...] looked dumb. I could have made them fractional, but then they'd write over each other again.

So I probably should have done something like make them fractional and keep every fifth label (out of 20). But then I was worried about getting weird values out of it [1, 2,3, 5.5, 7.7].

I guess my point here is I'm not sure which API would be best. For me, a startTime/endTime and a formatter probably would have been best. But at this point its not a problem, and I'm not sure if that's useful to others. Next most useful would likely have been either spaced labels or diagonal labels.

Mostly I think what I'm reporting is that the API "felt wrong," passing in strings and such. It felt like a bar chart API for the line chart. Not sure if such feedback is useful.

Regarding your suggestion, I'm not sure it solves any underlying issues. It would be pretty easy for me, if I had the string millis, to date format them myself rather than pass in a formatter. But, I suppose it might make the API feel better, like it was designed with dates in mind.

A quick brain dump, I did a lot of analytics at my last job, and we had everything defined in terms of metrics and dimensions. In this case, time is a valid dimension type, as is enum or string. Other common dimension types include integers (ie country code) or int ranges (for instance, "age 18-25"). In some systems a dimension can be a full object (ie User).

A line chart renders a single metric against a single dimension, and could be defined like so:

class LineChart<M, D> {
  List<M> metricData;
  List<D> dimensionData;
}

In this case, you'd probably want a Stringifier<D> and a Doublifier<M>.

Then in this case, I would do

new LineChart<double, DateTime>(points, listOfDates);

And once the line chart is stacked, usually that is another dimension. Which would then require a new API

new StackedLineChart<double, String, DateTime>(points, legend, listOfDates);

Just a brain dump.

mzimmerm commented 6 years ago

Thanks Michael.

For the more futures part: This is interesting; your measure and dimension sounds great. If I understand the way I understand :) it is somewhat in-line with a proposal I started to write up for myself last weekend, although I took a bit different approach. The difference is I was thinking to define an InternalChartData class in terms of the primitives a chart needs to handle: On the independent axis (x) either Ordinal (Comparable interface, not honoring distances) or Measured data (interface for data that honor distances). Right now the chart cannot handle data with a measure (distance) on the X axis - it cannot display x values [1,2,20,200] spaced honoring the distances. Also I collapsed the primitives to doubles in the InternalChartData, and thought if client need to input data in another format, e.g. ChartData<Date, Number>, or any generic data that are either Comparable or Measured, client would provide the converter, although this package would provide some common converters. Or maybe there could be one generic converter from ChartData<M, D> to InternalChartData as long as M and D are at least Comparable or Measured .. or something along those lines.

As to the more concrete short term: It sounds like my suggestion would not help you much, if I understand you are already doing the most within the current limitations of the charts. But it would help you if

1) Labels can be tilted 2) Labels can be skipped if too crowded. (BTW, can you "poor man" achieve this by setting the label to an empty string?)

Both are on my list. My original thought was to incorporate both into an improved chart layouter (iterative layouter). But that will take time. So due to the usefulness, I will do 1) soon, and maybe 2) as well. I will probably need 1) pretty soon myself.

As a final note, the original goal of Flutter Chart has been to keep it minimalistic, although now I am getting urges to move away from minimalistic and abstract/extend, but it does take time and thinking, and these discussions are much appreciated. Thanks

mzimmerm commented 2 years ago

@MichaelRFairhurst First of all, thanks of your detail and thoughtful comments and suggestion. I am not at all sure my comment is still relevant, and if you are still using the flutter_charts library.

I just re-read your issue description. I should have responded earlier, I am sorry about that. As of version version 0.1.8 (2018-06-20), flutter_charts has the ability to tilt labels and skip labels via the DefaultIterativeLabelLayoutStrategy. Please take a look at the relevant section in

https://pub.dev/packages/flutter_charts/changelog

Please let me know if this helps any.

Anyway, I am working on this library sparingly, but changes are being made. I also plan to address items such as special data and time oriented axes, but not sure when that will be.

Thanks