package org.ohmage.charts;
import org.achartengine.chart.BubbleChart;
import org.achartengine.model.XYMultipleSeriesDataset;
import org.achartengine.model.XYValueSeries;
import org.ohmage.Utilities;
import org.ohmage.charts.HistogramBase.CleanRenderer;
import org.ohmage.loader.PromptFeedbackLoader.FeedbackItem;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.format.DateUtils;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
/**
* This is a bubble histogram which can show days on the x-axis and a value on the y-axis.
* @author cketcham
*
*/
public class HistogramBubble extends BubbleChart {
private static final long MAX_DAYS = 10;
transient private HistogramBase mBase;
/**
* Construct a new Histogram object with the given values.
*
* @param context
* @param renderer A renderer can be specified
* @param data is a list of FeedbackItems
*/
public HistogramBubble(Context context, CleanRenderer renderer, List<FeedbackItem> data) {
super(buildDataSet(data), (renderer != null ? renderer : new CleanRenderer()));
mBase = new HistogramBase(this);
mBase.fitData();
mBase.setDateFormat("MMM d");
}
public HistogramBubble(Context context, List<FeedbackItem> values) {
this(context, null, values);
}
public HistogramBubble(Context context, CleanRenderer renderer, List<FeedbackItem> data, int color) {
this(context, renderer, data);
getRenderer().getSeriesRendererAt(0).setColor(context.getResources().getColor(color));
}
/**
* This has the same functionality as the super class, except it calls getDateLabel
* instead of just getLabel which will format the label as a date
* {@inheritDoc}
*/
@Override
protected void drawXLabels(List<Double> xLabels, Double[] xTextLabelLocations, Canvas canvas,
Paint paint, int left, int top, int bottom, double xPixelsPerUnit, double minX,double maxX) {
mBase.drawXLabels(xLabels, xTextLabelLocations, canvas, paint, left, top, bottom, xPixelsPerUnit, minX, maxX);
}
/**
* Builds a dataset with dates as the x value and the value as the y value.
* It expects exactly one number for each day. values[0] will be interpreted
* as today. values[N] will be interpreted as N days ago.
*
* @param data
* @return
*/
private static XYMultipleSeriesDataset buildDataSet(List<FeedbackItem> data) {
XYMultipleSeriesDataset dataSet = new XYMultipleSeriesDataset();
XYValueSeries series = new XYValueSeries("");
HashMap<Integer, HashMap<Integer, Integer>> distribution = new HashMap<Integer, HashMap<Integer, Integer>>();
Calendar calendar = Calendar.getInstance();
Utilities.clearTime(calendar);
calendar.add(Calendar.DATE, 1);
long today = calendar.getTimeInMillis();
for(FeedbackItem item : data) {
if(item.time > today - DateUtils.DAY_IN_MILLIS * MAX_DAYS) {
calendar.setTimeInMillis(item.time);
Utilities.clearTime(calendar);
int idx = (int) ((today - calendar.getTimeInMillis()) / DateUtils.DAY_IN_MILLIS) - 1;
HashMap<Integer,Integer> dayData = distribution.get(-idx);
if(dayData == null) {
dayData = new HashMap<Integer,Integer>();
distribution.put(-idx, dayData);
}
Integer i = dayData.get(item.value.intValue());
if(i == null)
i = 0;
dayData.put(item.value.intValue(), i+1);
}
}
for(Integer day : distribution.keySet()) {
HashMap<Integer, Integer> dayDist = distribution.get(day);
for(Integer key : dayDist.keySet()) {
series.add(day, key, dayDist.get(key));
}
}
series.add(0, 0, 0);
series.add(-MAX_DAYS + 1, 0, 0);
dataSet.addSeries(series);
return dataSet;
}
}