• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 package com.github.mikephil.charting.data;
3 
4 import android.graphics.Typeface;
5 import android.util.Log;
6 
7 import com.github.mikephil.charting.components.YAxis.AxisDependency;
8 import com.github.mikephil.charting.formatter.IValueFormatter;
9 import com.github.mikephil.charting.highlight.Highlight;
10 import com.github.mikephil.charting.interfaces.datasets.IDataSet;
11 
12 import java.util.ArrayList;
13 import java.util.List;
14 
15 /**
16  * Class that holds all relevant data that represents the chart. That involves
17  * at least one (or more) DataSets, and an array of x-values.
18  *
19  * @author Philipp Jahoda
20  */
21 public abstract class ChartData<T extends IDataSet<? extends Entry>> {
22 
23     /**
24      * maximum y-value in the value array across all axes
25      */
26     protected float mYMax = -Float.MAX_VALUE;
27 
28     /**
29      * the minimum y-value in the value array across all axes
30      */
31     protected float mYMin = Float.MAX_VALUE;
32 
33     /**
34      * maximum x-value in the value array
35      */
36     protected float mXMax = -Float.MAX_VALUE;
37 
38     /**
39      * minimum x-value in the value array
40      */
41     protected float mXMin = Float.MAX_VALUE;
42 
43 
44     protected float mLeftAxisMax = -Float.MAX_VALUE;
45 
46     protected float mLeftAxisMin = Float.MAX_VALUE;
47 
48     protected float mRightAxisMax = -Float.MAX_VALUE;
49 
50     protected float mRightAxisMin = Float.MAX_VALUE;
51 
52     /**
53      * array that holds all DataSets the ChartData object represents
54      */
55     protected List<T> mDataSets;
56 
57     /**
58      * Default constructor.
59      */
ChartData()60     public ChartData() {
61         mDataSets = new ArrayList<T>();
62     }
63 
64     /**
65      * Constructor taking single or multiple DataSet objects.
66      *
67      * @param dataSets
68      */
ChartData(T... dataSets)69     public ChartData(T... dataSets) {
70         mDataSets = arrayToList(dataSets);
71         notifyDataChanged();
72     }
73 
74     /**
75      * Created because Arrays.asList(...) does not support modification.
76      *
77      * @param array
78      * @return
79      */
arrayToList(T[] array)80     private List<T> arrayToList(T[] array) {
81 
82         List<T> list = new ArrayList<>();
83 
84         for (T set : array) {
85             list.add(set);
86         }
87 
88         return list;
89     }
90 
91     /**
92      * constructor for chart data
93      *
94      * @param sets the dataset array
95      */
ChartData(List<T> sets)96     public ChartData(List<T> sets) {
97         this.mDataSets = sets;
98         notifyDataChanged();
99     }
100 
101     /**
102      * Call this method to let the ChartData know that the underlying data has
103      * changed. Calling this performs all necessary recalculations needed when
104      * the contained data has changed.
105      */
notifyDataChanged()106     public void notifyDataChanged() {
107         calcMinMax();
108     }
109 
110     /**
111      * Calc minimum and maximum y-values over all DataSets.
112      * Tell DataSets to recalculate their min and max y-values, this is only needed for autoScaleMinMax.
113      *
114      * @param fromX the x-value to start the calculation from
115      * @param toX   the x-value to which the calculation should be performed
116      */
calcMinMaxY(float fromX, float toX)117     public void calcMinMaxY(float fromX, float toX) {
118 
119         for (T set : mDataSets) {
120             set.calcMinMaxY(fromX, toX);
121         }
122 
123         // apply the new data
124         calcMinMax();
125     }
126 
127     /**
128      * Calc minimum and maximum values (both x and y) over all DataSets.
129      */
calcMinMax()130     protected void calcMinMax() {
131 
132         if (mDataSets == null)
133             return;
134 
135         mYMax = -Float.MAX_VALUE;
136         mYMin = Float.MAX_VALUE;
137         mXMax = -Float.MAX_VALUE;
138         mXMin = Float.MAX_VALUE;
139 
140         for (T set : mDataSets) {
141             calcMinMax(set);
142         }
143 
144         mLeftAxisMax = -Float.MAX_VALUE;
145         mLeftAxisMin = Float.MAX_VALUE;
146         mRightAxisMax = -Float.MAX_VALUE;
147         mRightAxisMin = Float.MAX_VALUE;
148 
149         // left axis
150         T firstLeft = getFirstLeft(mDataSets);
151 
152         if (firstLeft != null) {
153 
154             mLeftAxisMax = firstLeft.getYMax();
155             mLeftAxisMin = firstLeft.getYMin();
156 
157             for (T dataSet : mDataSets) {
158                 if (dataSet.getAxisDependency() == AxisDependency.LEFT) {
159                     if (dataSet.getYMin() < mLeftAxisMin)
160                         mLeftAxisMin = dataSet.getYMin();
161 
162                     if (dataSet.getYMax() > mLeftAxisMax)
163                         mLeftAxisMax = dataSet.getYMax();
164                 }
165             }
166         }
167 
168         // right axis
169         T firstRight = getFirstRight(mDataSets);
170 
171         if (firstRight != null) {
172 
173             mRightAxisMax = firstRight.getYMax();
174             mRightAxisMin = firstRight.getYMin();
175 
176             for (T dataSet : mDataSets) {
177                 if (dataSet.getAxisDependency() == AxisDependency.RIGHT) {
178                     if (dataSet.getYMin() < mRightAxisMin)
179                         mRightAxisMin = dataSet.getYMin();
180 
181                     if (dataSet.getYMax() > mRightAxisMax)
182                         mRightAxisMax = dataSet.getYMax();
183                 }
184             }
185         }
186     }
187 
188     /** ONLY GETTERS AND SETTERS BELOW THIS */
189 
190     /**
191      * returns the number of LineDataSets this object contains
192      *
193      * @return
194      */
getDataSetCount()195     public int getDataSetCount() {
196         if (mDataSets == null)
197             return 0;
198         return mDataSets.size();
199     }
200 
201     /**
202      * Returns the smallest y-value the data object contains.
203      *
204      * @return
205      */
getYMin()206     public float getYMin() {
207         return mYMin;
208     }
209 
210     /**
211      * Returns the minimum y-value for the specified axis.
212      *
213      * @param axis
214      * @return
215      */
getYMin(AxisDependency axis)216     public float getYMin(AxisDependency axis) {
217         if (axis == AxisDependency.LEFT) {
218 
219             if (mLeftAxisMin == Float.MAX_VALUE) {
220                 return mRightAxisMin;
221             } else
222                 return mLeftAxisMin;
223         } else {
224             if (mRightAxisMin == Float.MAX_VALUE) {
225                 return mLeftAxisMin;
226             } else
227                 return mRightAxisMin;
228         }
229     }
230 
231     /**
232      * Returns the greatest y-value the data object contains.
233      *
234      * @return
235      */
getYMax()236     public float getYMax() {
237         return mYMax;
238     }
239 
240     /**
241      * Returns the maximum y-value for the specified axis.
242      *
243      * @param axis
244      * @return
245      */
getYMax(AxisDependency axis)246     public float getYMax(AxisDependency axis) {
247         if (axis == AxisDependency.LEFT) {
248 
249             if (mLeftAxisMax == -Float.MAX_VALUE) {
250                 return mRightAxisMax;
251             } else
252                 return mLeftAxisMax;
253         } else {
254             if (mRightAxisMax == -Float.MAX_VALUE) {
255                 return mLeftAxisMax;
256             } else
257                 return mRightAxisMax;
258         }
259     }
260 
261     /**
262      * Returns the minimum x-value this data object contains.
263      *
264      * @return
265      */
getXMin()266     public float getXMin() {
267         return mXMin;
268     }
269 
270     /**
271      * Returns the maximum x-value this data object contains.
272      *
273      * @return
274      */
getXMax()275     public float getXMax() {
276         return mXMax;
277     }
278 
279     /**
280      * Returns all DataSet objects this ChartData object holds.
281      *
282      * @return
283      */
getDataSets()284     public List<T> getDataSets() {
285         return mDataSets;
286     }
287 
288     /**
289      * Retrieve the index of a DataSet with a specific label from the ChartData.
290      * Search can be case sensitive or not. IMPORTANT: This method does
291      * calculations at runtime, do not over-use in performance critical
292      * situations.
293      *
294      * @param dataSets   the DataSet array to search
295      * @param label
296      * @param ignorecase if true, the search is not case-sensitive
297      * @return
298      */
getDataSetIndexByLabel(List<T> dataSets, String label, boolean ignorecase)299     protected int getDataSetIndexByLabel(List<T> dataSets, String label,
300                                          boolean ignorecase) {
301 
302         if (ignorecase) {
303             for (int i = 0; i < dataSets.size(); i++)
304                 if (label.equalsIgnoreCase(dataSets.get(i).getLabel()))
305                     return i;
306         } else {
307             for (int i = 0; i < dataSets.size(); i++)
308                 if (label.equals(dataSets.get(i).getLabel()))
309                     return i;
310         }
311 
312         return -1;
313     }
314 
315     /**
316      * Returns the labels of all DataSets as a string array.
317      *
318      * @return
319      */
getDataSetLabels()320     public String[] getDataSetLabels() {
321 
322         String[] types = new String[mDataSets.size()];
323 
324         for (int i = 0; i < mDataSets.size(); i++) {
325             types[i] = mDataSets.get(i).getLabel();
326         }
327 
328         return types;
329     }
330 
331     /**
332      * Get the Entry for a corresponding highlight object
333      *
334      * @param highlight
335      * @return the entry that is highlighted
336      */
getEntryForHighlight(Highlight highlight)337     public Entry getEntryForHighlight(Highlight highlight) {
338         if (highlight.getDataSetIndex() >= mDataSets.size())
339             return null;
340         else {
341             return mDataSets.get(highlight.getDataSetIndex()).getEntryForXValue(highlight.getX(), highlight.getY());
342         }
343     }
344 
345     /**
346      * Returns the DataSet object with the given label. Search can be case
347      * sensitive or not. IMPORTANT: This method does calculations at runtime.
348      * Use with care in performance critical situations.
349      *
350      * @param label
351      * @param ignorecase
352      * @return
353      */
getDataSetByLabel(String label, boolean ignorecase)354     public T getDataSetByLabel(String label, boolean ignorecase) {
355 
356         int index = getDataSetIndexByLabel(mDataSets, label, ignorecase);
357 
358         if (index < 0 || index >= mDataSets.size())
359             return null;
360         else
361             return mDataSets.get(index);
362     }
363 
getDataSetByIndex(int index)364     public T getDataSetByIndex(int index) {
365 
366         if (mDataSets == null || index < 0 || index >= mDataSets.size())
367             return null;
368 
369         return mDataSets.get(index);
370     }
371 
372     /**
373      * Adds a DataSet dynamically.
374      *
375      * @param d
376      */
addDataSet(T d)377     public void addDataSet(T d) {
378 
379         if (d == null)
380             return;
381 
382         calcMinMax(d);
383 
384         mDataSets.add(d);
385     }
386 
387     /**
388      * Removes the given DataSet from this data object. Also recalculates all
389      * minimum and maximum values. Returns true if a DataSet was removed, false
390      * if no DataSet could be removed.
391      *
392      * @param d
393      */
removeDataSet(T d)394     public boolean removeDataSet(T d) {
395 
396         if (d == null)
397             return false;
398 
399         boolean removed = mDataSets.remove(d);
400 
401         // if a DataSet was removed
402         if (removed) {
403             notifyDataChanged();
404         }
405 
406         return removed;
407     }
408 
409     /**
410      * Removes the DataSet at the given index in the DataSet array from the data
411      * object. Also recalculates all minimum and maximum values. Returns true if
412      * a DataSet was removed, false if no DataSet could be removed.
413      *
414      * @param index
415      */
removeDataSet(int index)416     public boolean removeDataSet(int index) {
417 
418         if (index >= mDataSets.size() || index < 0)
419             return false;
420 
421         T set = mDataSets.get(index);
422         return removeDataSet(set);
423     }
424 
425     /**
426      * Adds an Entry to the DataSet at the specified index.
427      * Entries are added to the end of the list.
428      *
429      * @param e
430      * @param dataSetIndex
431      */
addEntry(Entry e, int dataSetIndex)432     public void addEntry(Entry e, int dataSetIndex) {
433 
434         if (mDataSets.size() > dataSetIndex && dataSetIndex >= 0) {
435 
436             IDataSet set = mDataSets.get(dataSetIndex);
437             // add the entry to the dataset
438             if (!set.addEntry(e))
439                 return;
440 
441             calcMinMax(e, set.getAxisDependency());
442 
443         } else {
444             Log.e("addEntry", "Cannot add Entry because dataSetIndex too high or too low.");
445         }
446     }
447 
448     /**
449      * Adjusts the current minimum and maximum values based on the provided Entry object.
450      *
451      * @param e
452      * @param axis
453      */
calcMinMax(Entry e, AxisDependency axis)454     protected void calcMinMax(Entry e, AxisDependency axis) {
455 
456         if (mYMax < e.getY())
457             mYMax = e.getY();
458         if (mYMin > e.getY())
459             mYMin = e.getY();
460 
461         if (mXMax < e.getX())
462             mXMax = e.getX();
463         if (mXMin > e.getX())
464             mXMin = e.getX();
465 
466         if (axis == AxisDependency.LEFT) {
467 
468             if (mLeftAxisMax < e.getY())
469                 mLeftAxisMax = e.getY();
470             if (mLeftAxisMin > e.getY())
471                 mLeftAxisMin = e.getY();
472         } else {
473             if (mRightAxisMax < e.getY())
474                 mRightAxisMax = e.getY();
475             if (mRightAxisMin > e.getY())
476                 mRightAxisMin = e.getY();
477         }
478     }
479 
480     /**
481      * Adjusts the minimum and maximum values based on the given DataSet.
482      *
483      * @param d
484      */
calcMinMax(T d)485     protected void calcMinMax(T d) {
486 
487         if (mYMax < d.getYMax())
488             mYMax = d.getYMax();
489         if (mYMin > d.getYMin())
490             mYMin = d.getYMin();
491 
492         if (mXMax < d.getXMax())
493             mXMax = d.getXMax();
494         if (mXMin > d.getXMin())
495             mXMin = d.getXMin();
496 
497         if (d.getAxisDependency() == AxisDependency.LEFT) {
498 
499             if (mLeftAxisMax < d.getYMax())
500                 mLeftAxisMax = d.getYMax();
501             if (mLeftAxisMin > d.getYMin())
502                 mLeftAxisMin = d.getYMin();
503         } else {
504             if (mRightAxisMax < d.getYMax())
505                 mRightAxisMax = d.getYMax();
506             if (mRightAxisMin > d.getYMin())
507                 mRightAxisMin = d.getYMin();
508         }
509     }
510 
511     /**
512      * Removes the given Entry object from the DataSet at the specified index.
513      *
514      * @param e
515      * @param dataSetIndex
516      */
removeEntry(Entry e, int dataSetIndex)517     public boolean removeEntry(Entry e, int dataSetIndex) {
518 
519         // entry null, outofbounds
520         if (e == null || dataSetIndex >= mDataSets.size())
521             return false;
522 
523         IDataSet set = mDataSets.get(dataSetIndex);
524 
525         if (set != null) {
526             // remove the entry from the dataset
527             boolean removed = set.removeEntry(e);
528 
529             if (removed) {
530                 notifyDataChanged();
531             }
532 
533             return removed;
534         } else
535             return false;
536     }
537 
538     /**
539      * Removes the Entry object closest to the given DataSet at the
540      * specified index. Returns true if an Entry was removed, false if no Entry
541      * was found that meets the specified requirements.
542      *
543      * @param xValue
544      * @param dataSetIndex
545      * @return
546      */
removeEntry(float xValue, int dataSetIndex)547     public boolean removeEntry(float xValue, int dataSetIndex) {
548 
549         if (dataSetIndex >= mDataSets.size())
550             return false;
551 
552         IDataSet dataSet = mDataSets.get(dataSetIndex);
553         Entry e = dataSet.getEntryForXValue(xValue, Float.NaN);
554 
555         if (e == null)
556             return false;
557 
558         return removeEntry(e, dataSetIndex);
559     }
560 
561     /**
562      * Returns the DataSet that contains the provided Entry, or null, if no
563      * DataSet contains this Entry.
564      *
565      * @param e
566      * @return
567      */
getDataSetForEntry(Entry e)568     public T getDataSetForEntry(Entry e) {
569 
570         if (e == null)
571             return null;
572 
573         for (int i = 0; i < mDataSets.size(); i++) {
574 
575             T set = mDataSets.get(i);
576 
577             for (int j = 0; j < set.getEntryCount(); j++) {
578                 if (e.equalTo(set.getEntryForXValue(e.getX(), e.getY())))
579                     return set;
580             }
581         }
582 
583         return null;
584     }
585 
586     /**
587      * Returns all colors used across all DataSet objects this object
588      * represents.
589      *
590      * @return
591      */
getColors()592     public int[] getColors() {
593 
594         if (mDataSets == null)
595             return null;
596 
597         int clrcnt = 0;
598 
599         for (int i = 0; i < mDataSets.size(); i++) {
600             clrcnt += mDataSets.get(i).getColors().size();
601         }
602 
603         int[] colors = new int[clrcnt];
604         int cnt = 0;
605 
606         for (int i = 0; i < mDataSets.size(); i++) {
607 
608             List<Integer> clrs = mDataSets.get(i).getColors();
609 
610             for (Integer clr : clrs) {
611                 colors[cnt] = clr;
612                 cnt++;
613             }
614         }
615 
616         return colors;
617     }
618 
619     /**
620      * Returns the index of the provided DataSet in the DataSet array of this data object, or -1 if it does not exist.
621      *
622      * @param dataSet
623      * @return
624      */
getIndexOfDataSet(T dataSet)625     public int getIndexOfDataSet(T dataSet) {
626         return mDataSets.indexOf(dataSet);
627     }
628 
629     /**
630      * Returns the first DataSet from the datasets-array that has it's dependency on the left axis.
631      * Returns null if no DataSet with left dependency could be found.
632      *
633      * @return
634      */
getFirstLeft(List<T> sets)635     protected T getFirstLeft(List<T> sets) {
636         for (T dataSet : sets) {
637             if (dataSet.getAxisDependency() == AxisDependency.LEFT)
638                 return dataSet;
639         }
640         return null;
641     }
642 
643     /**
644      * Returns the first DataSet from the datasets-array that has it's dependency on the right axis.
645      * Returns null if no DataSet with right dependency could be found.
646      *
647      * @return
648      */
getFirstRight(List<T> sets)649     public T getFirstRight(List<T> sets) {
650         for (T dataSet : sets) {
651             if (dataSet.getAxisDependency() == AxisDependency.RIGHT)
652                 return dataSet;
653         }
654         return null;
655     }
656 
657     /**
658      * Sets a custom IValueFormatter for all DataSets this data object contains.
659      *
660      * @param f
661      */
setValueFormatter(IValueFormatter f)662     public void setValueFormatter(IValueFormatter f) {
663         if (f == null)
664             return;
665         else {
666             for (IDataSet set : mDataSets) {
667                 set.setValueFormatter(f);
668             }
669         }
670     }
671 
672     /**
673      * Sets the color of the value-text (color in which the value-labels are
674      * drawn) for all DataSets this data object contains.
675      *
676      * @param color
677      */
setValueTextColor(int color)678     public void setValueTextColor(int color) {
679         for (IDataSet set : mDataSets) {
680             set.setValueTextColor(color);
681         }
682     }
683 
684     /**
685      * Sets the same list of value-colors for all DataSets this
686      * data object contains.
687      *
688      * @param colors
689      */
setValueTextColors(List<Integer> colors)690     public void setValueTextColors(List<Integer> colors) {
691         for (IDataSet set : mDataSets) {
692             set.setValueTextColors(colors);
693         }
694     }
695 
696     /**
697      * Sets the Typeface for all value-labels for all DataSets this data object
698      * contains.
699      *
700      * @param tf
701      */
setValueTypeface(Typeface tf)702     public void setValueTypeface(Typeface tf) {
703         for (IDataSet set : mDataSets) {
704             set.setValueTypeface(tf);
705         }
706     }
707 
708     /**
709      * Sets the size (in dp) of the value-text for all DataSets this data object
710      * contains.
711      *
712      * @param size
713      */
setValueTextSize(float size)714     public void setValueTextSize(float size) {
715         for (IDataSet set : mDataSets) {
716             set.setValueTextSize(size);
717         }
718     }
719 
720     /**
721      * Enables / disables drawing values (value-text) for all DataSets this data
722      * object contains.
723      *
724      * @param enabled
725      */
setDrawValues(boolean enabled)726     public void setDrawValues(boolean enabled) {
727         for (IDataSet set : mDataSets) {
728             set.setDrawValues(enabled);
729         }
730     }
731 
732     /**
733      * Enables / disables highlighting values for all DataSets this data object
734      * contains. If set to true, this means that values can
735      * be highlighted programmatically or by touch gesture.
736      */
setHighlightEnabled(boolean enabled)737     public void setHighlightEnabled(boolean enabled) {
738         for (IDataSet set : mDataSets) {
739             set.setHighlightEnabled(enabled);
740         }
741     }
742 
743     /**
744      * Returns true if highlighting of all underlying values is enabled, false
745      * if not.
746      *
747      * @return
748      */
isHighlightEnabled()749     public boolean isHighlightEnabled() {
750         for (IDataSet set : mDataSets) {
751             if (!set.isHighlightEnabled())
752                 return false;
753         }
754         return true;
755     }
756 
757     /**
758      * Clears this data object from all DataSets and removes all Entries. Don't
759      * forget to invalidate the chart after this.
760      */
clearValues()761     public void clearValues() {
762         if (mDataSets != null) {
763             mDataSets.clear();
764         }
765         notifyDataChanged();
766     }
767 
768     /**
769      * Checks if this data object contains the specified DataSet. Returns true
770      * if so, false if not.
771      *
772      * @param dataSet
773      * @return
774      */
contains(T dataSet)775     public boolean contains(T dataSet) {
776 
777         for (T set : mDataSets) {
778             if (set.equals(dataSet))
779                 return true;
780         }
781 
782         return false;
783     }
784 
785     /**
786      * Returns the total entry count across all DataSet objects this data object contains.
787      *
788      * @return
789      */
getEntryCount()790     public int getEntryCount() {
791 
792         int count = 0;
793 
794         for (T set : mDataSets) {
795             count += set.getEntryCount();
796         }
797 
798         return count;
799     }
800 
801     /**
802      * Returns the DataSet object with the maximum number of entries or null if there are no DataSets.
803      *
804      * @return
805      */
getMaxEntryCountSet()806     public T getMaxEntryCountSet() {
807 
808         if (mDataSets == null || mDataSets.isEmpty())
809             return null;
810 
811         T max = mDataSets.get(0);
812 
813         for (T set : mDataSets) {
814 
815             if (set.getEntryCount() > max.getEntryCount())
816                 max = set;
817         }
818 
819         return max;
820     }
821 }
822