1 2 package com.github.mikephil.charting.data; 3 4 import java.util.ArrayList; 5 import java.util.List; 6 7 /** 8 * The DataSet class represents one group or type of entries (Entry) in the 9 * Chart that belong together. It is designed to logically separate different 10 * groups of values inside the Chart (e.g. the values for a specific line in the 11 * LineChart, or the values of a specific group of bars in the BarChart). 12 * 13 * @author Philipp Jahoda 14 */ 15 public abstract class DataSet<T extends Entry> extends BaseDataSet<T> { 16 17 /** 18 * the entries that this DataSet represents / holds together 19 */ 20 protected List<T> mEntries; 21 22 /** 23 * maximum y-value in the value array 24 */ 25 protected float mYMax = -Float.MAX_VALUE; 26 27 /** 28 * minimum y-value in the value array 29 */ 30 protected float mYMin = Float.MAX_VALUE; 31 32 /** 33 * maximum x-value in the value array 34 */ 35 protected float mXMax = -Float.MAX_VALUE; 36 37 /** 38 * minimum x-value in the value array 39 */ 40 protected float mXMin = Float.MAX_VALUE; 41 42 43 /** 44 * Creates a new DataSet object with the given values (entries) it represents. Also, a 45 * label that describes the DataSet can be specified. The label can also be 46 * used to retrieve the DataSet from a ChartData object. 47 * 48 * @param entries 49 * @param label 50 */ DataSet(List<T> entries, String label)51 public DataSet(List<T> entries, String label) { 52 super(label); 53 this.mEntries = entries; 54 55 if (mEntries == null) 56 mEntries = new ArrayList<T>(); 57 58 calcMinMax(); 59 } 60 61 @Override calcMinMax()62 public void calcMinMax() { 63 64 mYMax = -Float.MAX_VALUE; 65 mYMin = Float.MAX_VALUE; 66 mXMax = -Float.MAX_VALUE; 67 mXMin = Float.MAX_VALUE; 68 69 if (mEntries == null || mEntries.isEmpty()) 70 return; 71 72 for (T e : mEntries) { 73 calcMinMax(e); 74 } 75 } 76 77 @Override calcMinMaxY(float fromX, float toX)78 public void calcMinMaxY(float fromX, float toX) { 79 mYMax = -Float.MAX_VALUE; 80 mYMin = Float.MAX_VALUE; 81 82 if (mEntries == null || mEntries.isEmpty()) 83 return; 84 85 int indexFrom = getEntryIndex(fromX, Float.NaN, Rounding.DOWN); 86 int indexTo = getEntryIndex(toX, Float.NaN, Rounding.UP); 87 88 if (indexTo < indexFrom) return; 89 90 for (int i = indexFrom; i <= indexTo; i++) { 91 92 // only recalculate y 93 calcMinMaxY(mEntries.get(i)); 94 } 95 } 96 97 /** 98 * Updates the min and max x and y value of this DataSet based on the given Entry. 99 * 100 * @param e 101 */ calcMinMax(T e)102 protected void calcMinMax(T e) { 103 104 if (e == null) 105 return; 106 107 calcMinMaxX(e); 108 109 calcMinMaxY(e); 110 } 111 calcMinMaxX(T e)112 protected void calcMinMaxX(T e) { 113 114 if (e.getX() < mXMin) 115 mXMin = e.getX(); 116 117 if (e.getX() > mXMax) 118 mXMax = e.getX(); 119 } 120 calcMinMaxY(T e)121 protected void calcMinMaxY(T e) { 122 123 if (e.getY() < mYMin) 124 mYMin = e.getY(); 125 126 if (e.getY() > mYMax) 127 mYMax = e.getY(); 128 } 129 130 @Override getEntryCount()131 public int getEntryCount() { 132 return mEntries.size(); 133 } 134 135 /** 136 * This method is deprecated. 137 * Use getEntries() instead. 138 * 139 * @return 140 */ 141 @Deprecated getValues()142 public List<T> getValues() { 143 return mEntries; 144 } 145 146 /** 147 * Returns the array of entries that this DataSet represents. 148 * 149 * @return 150 */ getEntries()151 public List<T> getEntries() { 152 return mEntries; 153 } 154 155 /** 156 * This method is deprecated. 157 * Use setEntries(...) instead. 158 * 159 * @param values 160 */ 161 @Deprecated setValues(List<T> values)162 public void setValues(List<T> values) { 163 setEntries(values); 164 } 165 166 /** 167 * Sets the array of entries that this DataSet represents, and calls notifyDataSetChanged() 168 * 169 * @return 170 */ setEntries(List<T> entries)171 public void setEntries(List<T> entries) { 172 mEntries = entries; 173 notifyDataSetChanged(); 174 } 175 176 /** 177 * Provides an exact copy of the DataSet this method is used on. 178 * 179 * @return 180 */ copy()181 public abstract DataSet<T> copy(); 182 183 /** 184 * 185 * @param dataSet 186 */ copy(DataSet dataSet)187 protected void copy(DataSet dataSet) { 188 super.copy(dataSet); 189 } 190 191 @Override toString()192 public String toString() { 193 StringBuffer buffer = new StringBuffer(); 194 buffer.append(toSimpleString()); 195 for (int i = 0; i < mEntries.size(); i++) { 196 buffer.append(mEntries.get(i).toString() + " "); 197 } 198 return buffer.toString(); 199 } 200 201 /** 202 * Returns a simple string representation of the DataSet with the type and 203 * the number of Entries. 204 * 205 * @return 206 */ toSimpleString()207 public String toSimpleString() { 208 StringBuffer buffer = new StringBuffer(); 209 buffer.append("DataSet, label: " + (getLabel() == null ? "" : getLabel()) + ", entries: " + mEntries.size() + 210 "\n"); 211 return buffer.toString(); 212 } 213 214 @Override getYMin()215 public float getYMin() { 216 return mYMin; 217 } 218 219 @Override getYMax()220 public float getYMax() { 221 return mYMax; 222 } 223 224 @Override getXMin()225 public float getXMin() { 226 return mXMin; 227 } 228 229 @Override getXMax()230 public float getXMax() { 231 return mXMax; 232 } 233 234 @Override addEntryOrdered(T e)235 public void addEntryOrdered(T e) { 236 237 if (e == null) 238 return; 239 240 if (mEntries == null) { 241 mEntries = new ArrayList<T>(); 242 } 243 244 calcMinMax(e); 245 246 if (mEntries.size() > 0 && mEntries.get(mEntries.size() - 1).getX() > e.getX()) { 247 int closestIndex = getEntryIndex(e.getX(), e.getY(), Rounding.UP); 248 mEntries.add(closestIndex, e); 249 } else { 250 mEntries.add(e); 251 } 252 } 253 254 @Override clear()255 public void clear() { 256 mEntries.clear(); 257 notifyDataSetChanged(); 258 } 259 260 @Override addEntry(T e)261 public boolean addEntry(T e) { 262 263 if (e == null) 264 return false; 265 266 List<T> values = getEntries(); 267 if (values == null) { 268 values = new ArrayList<>(); 269 } 270 271 calcMinMax(e); 272 273 // add the entry 274 return values.add(e); 275 } 276 277 @Override removeEntry(T e)278 public boolean removeEntry(T e) { 279 280 if (e == null) 281 return false; 282 283 if (mEntries == null) 284 return false; 285 286 // remove the entry 287 boolean removed = mEntries.remove(e); 288 289 if (removed) { 290 calcMinMax(); 291 } 292 293 return removed; 294 } 295 296 @Override getEntryIndex(Entry e)297 public int getEntryIndex(Entry e) { 298 return mEntries.indexOf(e); 299 } 300 301 @Override getEntryForXValue(float xValue, float closestToY, Rounding rounding)302 public T getEntryForXValue(float xValue, float closestToY, Rounding rounding) { 303 304 int index = getEntryIndex(xValue, closestToY, rounding); 305 if (index > -1) 306 return mEntries.get(index); 307 return null; 308 } 309 310 @Override getEntryForXValue(float xValue, float closestToY)311 public T getEntryForXValue(float xValue, float closestToY) { 312 return getEntryForXValue(xValue, closestToY, Rounding.CLOSEST); 313 } 314 315 @Override getEntryForIndex(int index)316 public T getEntryForIndex(int index) { 317 return mEntries.get(index); 318 } 319 320 @Override getEntryIndex(float xValue, float closestToY, Rounding rounding)321 public int getEntryIndex(float xValue, float closestToY, Rounding rounding) { 322 323 if (mEntries == null || mEntries.isEmpty()) 324 return -1; 325 326 int low = 0; 327 int high = mEntries.size() - 1; 328 int closest = high; 329 330 while (low < high) { 331 int m = (low + high) / 2; 332 333 final float d1 = mEntries.get(m).getX() - xValue, 334 d2 = mEntries.get(m + 1).getX() - xValue, 335 ad1 = Math.abs(d1), ad2 = Math.abs(d2); 336 337 if (ad2 < ad1) { 338 // [m + 1] is closer to xValue 339 // Search in an higher place 340 low = m + 1; 341 } else if (ad1 < ad2) { 342 // [m] is closer to xValue 343 // Search in a lower place 344 high = m; 345 } else { 346 // We have multiple sequential x-value with same distance 347 348 if (d1 >= 0.0) { 349 // Search in a lower place 350 high = m; 351 } else if (d1 < 0.0) { 352 // Search in an higher place 353 low = m + 1; 354 } 355 } 356 357 closest = high; 358 } 359 360 if (closest != -1) { 361 float closestXValue = mEntries.get(closest).getX(); 362 if (rounding == Rounding.UP) { 363 // If rounding up, and found x-value is lower than specified x, and we can go upper... 364 if (closestXValue < xValue && closest < mEntries.size() - 1) { 365 ++closest; 366 } 367 } else if (rounding == Rounding.DOWN) { 368 // If rounding down, and found x-value is upper than specified x, and we can go lower... 369 if (closestXValue > xValue && closest > 0) { 370 --closest; 371 } 372 } 373 374 // Search by closest to y-value 375 if (!Float.isNaN(closestToY)) { 376 while (closest > 0 && mEntries.get(closest - 1).getX() == closestXValue) 377 closest -= 1; 378 379 float closestYValue = mEntries.get(closest).getY(); 380 int closestYIndex = closest; 381 382 while (true) { 383 closest += 1; 384 if (closest >= mEntries.size()) 385 break; 386 387 final Entry value = mEntries.get(closest); 388 389 if (value.getX() != closestXValue) 390 break; 391 392 if (Math.abs(value.getY() - closestToY) <= Math.abs(closestYValue - closestToY)) { 393 closestYValue = closestToY; 394 closestYIndex = closest; 395 } 396 } 397 398 closest = closestYIndex; 399 } 400 } 401 402 return closest; 403 } 404 405 @Override getEntriesForXValue(float xValue)406 public List<T> getEntriesForXValue(float xValue) { 407 408 List<T> entries = new ArrayList<T>(); 409 410 int low = 0; 411 int high = mEntries.size() - 1; 412 413 while (low <= high) { 414 int m = (high + low) / 2; 415 T entry = mEntries.get(m); 416 417 // if we have a match 418 if (xValue == entry.getX()) { 419 while (m > 0 && mEntries.get(m - 1).getX() == xValue) 420 m--; 421 422 high = mEntries.size(); 423 424 // loop over all "equal" entries 425 for (; m < high; m++) { 426 entry = mEntries.get(m); 427 if (entry.getX() == xValue) { 428 entries.add(entry); 429 } else { 430 break; 431 } 432 } 433 434 break; 435 } else { 436 if (xValue > entry.getX()) 437 low = m + 1; 438 else 439 high = m - 1; 440 } 441 } 442 443 return entries; 444 } 445 446 /** 447 * Determines how to round DataSet index values for 448 * {@link DataSet#getEntryIndex(float, float, Rounding)} DataSet.getEntryIndex()} 449 * when an exact x-index is not found. 450 */ 451 public enum Rounding { 452 UP, 453 DOWN, 454 CLOSEST, 455 } 456 }