package com.github.mikephil.charting.charts;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.components.YAxis.AxisDependency;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.RadarData;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.renderer.RadarChartRenderer;
import com.github.mikephil.charting.renderer.XAxisRendererRadarChart;
import com.github.mikephil.charting.renderer.YAxisRendererRadarChart;
import com.github.mikephil.charting.utils.Utils;
/**
* Implementation of the RadarChart, a "spidernet"-like chart. It works best
* when displaying 5-10 entries per DataSet.
*
* @author Philipp Jahoda
*/
public class RadarChart extends PieRadarChartBase<RadarData> {
/**
* width of the main web lines
*/
private float mWebLineWidth = 2.5f;
/**
* width of the inner web lines
*/
private float mInnerWebLineWidth = 1.5f;
/**
* color for the main web lines
*/
private int mWebColor = Color.rgb(122, 122, 122);
/**
* color for the inner web
*/
private int mWebColorInner = Color.rgb(122, 122, 122);
/**
* transparency the grid is drawn with (0-255)
*/
private int mWebAlpha = 150;
/**
* flag indicating if the web lines should be drawn or not
*/
private boolean mDrawWeb = true;
/**
* modulus that determines how many labels and web-lines are skipped before the next is drawn
*/
private int mSkipWebLineCount = 1;
/**
* the object reprsenting the y-axis labels
*/
private YAxis mYAxis;
/**
* the object representing the x-axis labels
*/
private XAxis mXAxis;
protected YAxisRendererRadarChart mYAxisRenderer;
protected XAxisRendererRadarChart mXAxisRenderer;
public RadarChart(Context context) {
super(context);
}
public RadarChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RadarChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void init() {
super.init();
mYAxis = new YAxis(AxisDependency.LEFT);
mXAxis = new XAxis();
mXAxis.setSpaceBetweenLabels(0);
mWebLineWidth = Utils.convertDpToPixel(1.5f);
mInnerWebLineWidth = Utils.convertDpToPixel(0.75f);
mRenderer = new RadarChartRenderer(this, mAnimator, mViewPortHandler);
mYAxisRenderer = new YAxisRendererRadarChart(mViewPortHandler, mYAxis, this);
mXAxisRenderer = new XAxisRendererRadarChart(mViewPortHandler, mXAxis, this);
}
@Override
protected void calcMinMax() {
super.calcMinMax();
float minLeft = mData.getYMin(AxisDependency.LEFT);
float maxLeft = mData.getYMax(AxisDependency.LEFT);
mXChartMax = mData.getXVals().size() - 1;
mDeltaX = Math.abs(mXChartMax - mXChartMin);
float leftRange = Math.abs(maxLeft - (mYAxis.isStartAtZeroEnabled() ? 0 : minLeft));
float topSpaceLeft = leftRange / 100f * mYAxis.getSpaceTop();
float bottomSpaceLeft = leftRange / 100f * mYAxis.getSpaceBottom();
mXChartMax = mData.getXVals().size() - 1;
mDeltaX = Math.abs(mXChartMax - mXChartMin);
if (mYAxis.isStartAtZeroEnabled()) {
if (minLeft < 0.f && maxLeft < 0.f) {
// If the values are all negative, let's stay in the negative zone
mYAxis.mAxisMinimum = Math.min(0.f, !Float.isNaN(mYAxis.getAxisMinValue()) ? mYAxis.getAxisMinValue() : (minLeft - bottomSpaceLeft));
mYAxis.mAxisMaximum = 0.f;
} else if (minLeft >= 0.0) {
// We have positive values only, stay in the positive zone
mYAxis.mAxisMinimum = 0.f;
mYAxis.mAxisMaximum = Math.max(0.f, !Float.isNaN(mYAxis.getAxisMaxValue()) ? mYAxis.getAxisMaxValue() : (maxLeft + topSpaceLeft));
} else {
// Stick the minimum to 0.0 or less, and maximum to 0.0 or more (startAtZero for negative/positive at the same time)
mYAxis.mAxisMinimum = Math.min(0.f, !Float.isNaN(mYAxis.getAxisMinValue()) ? mYAxis.getAxisMinValue() : (minLeft - bottomSpaceLeft));
mYAxis.mAxisMaximum = Math.max(0.f, !Float.isNaN(mYAxis.getAxisMaxValue()) ? mYAxis.getAxisMaxValue() : (maxLeft + topSpaceLeft));
}
} else {
// Use the values as they are
mYAxis.mAxisMinimum = !Float.isNaN(mYAxis.getAxisMinValue()) ? mYAxis.getAxisMinValue() : (minLeft - bottomSpaceLeft);
mYAxis.mAxisMaximum = !Float.isNaN(mYAxis.getAxisMaxValue()) ? mYAxis.getAxisMaxValue() : (maxLeft + topSpaceLeft);
}
mYAxis.mAxisRange = Math.abs(mYAxis.mAxisMaximum - mYAxis.mAxisMinimum);
}
@Override
protected float[] getMarkerPosition(Entry e, Highlight highlight) {
float angle = getSliceAngle() * e.getXIndex() + getRotationAngle();
float val = e.getVal() * getFactor();
PointF c = getCenterOffsets();
PointF p = new PointF((float) (c.x + val * Math.cos(Math.toRadians(angle))),
(float) (c.y + val * Math.sin(Math.toRadians(angle))));
return new float[]{
p.x, p.y
};
}
@Override
public void notifyDataSetChanged() {
if (mDataNotSet)
return;
calcMinMax();
// if (mYAxis.needsDefaultFormatter()) {
// mYAxis.setValueFormatter(mDefaultFormatter);
// }
mYAxisRenderer.computeAxis(mYAxis.mAxisMinimum, mYAxis.mAxisMaximum);
mXAxisRenderer.computeAxis(mData.getXValAverageLength(), mData.getXVals());
if (mLegend != null && !mLegend.isLegendCustom())
mLegendRenderer.computeLegend(mData);
calculateOffsets();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mDataNotSet)
return;
mXAxisRenderer.renderAxisLabels(canvas);
if (mDrawWeb)
mRenderer.drawExtras(canvas);
mYAxisRenderer.renderLimitLines(canvas);
mRenderer.drawData(canvas);
if (valuesToHighlight())
mRenderer.drawHighlighted(canvas, mIndicesToHighlight);
mYAxisRenderer.renderAxisLabels(canvas);
mRenderer.drawValues(canvas);
mLegendRenderer.renderLegend(canvas);
drawDescription(canvas);
drawMarkers(canvas);
}
/**
* Returns the factor that is needed to transform values into pixels.
*
* @return
*/
public float getFactor() {
RectF content = mViewPortHandler.getContentRect();
return (float) Math.min(content.width() / 2f, content.height() / 2f)
/ mYAxis.mAxisRange;
}
/**
* Returns the angle that each slice in the radar chart occupies.
*
* @return
*/
public float getSliceAngle() {
return 360f / (float) mData.getXValCount();
}
@Override
public int getIndexForAngle(float angle) {
// take the current angle of the chart into consideration
float a = Utils.getNormalizedAngle(angle - getRotationAngle());
float sliceangle = getSliceAngle();
for (int i = 0; i < mData.getXValCount(); i++) {
if (sliceangle * (i + 1) - sliceangle / 2f > a)
return i;
}
return 0;
}
/**
* Returns the object that represents all y-labels of the RadarChart.
*
* @return
*/
public YAxis getYAxis() {
return mYAxis;
}
/**
* Returns the object that represents all x-labels that are placed around
* the RadarChart.
*
* @return
*/
public XAxis getXAxis() {
return mXAxis;
}
/**
* Sets the width of the web lines that come from the center.
*
* @param width
*/
public void setWebLineWidth(float width) {
mWebLineWidth = Utils.convertDpToPixel(width);
}
public float getWebLineWidth() {
return mWebLineWidth;
}
/**
* Sets the width of the web lines that are in between the lines coming from
* the center.
*
* @param width
*/
public void setWebLineWidthInner(float width) {
mInnerWebLineWidth = Utils.convertDpToPixel(width);
}
public float getWebLineWidthInner() {
return mInnerWebLineWidth;
}
/**
* Sets the transparency (alpha) value for all web lines, default: 150, 255
* = 100% opaque, 0 = 100% transparent
*
* @param alpha
*/
public void setWebAlpha(int alpha) {
mWebAlpha = alpha;
}
/**
* Returns the alpha value for all web lines.
*
* @return
*/
public int getWebAlpha() {
return mWebAlpha;
}
/**
* Sets the color for the web lines that come from the center. Don't forget
* to use getResources().getColor(...) when loading a color from the
* resources. Default: Color.rgb(122, 122, 122)
*
* @param color
*/
public void setWebColor(int color) {
mWebColor = color;
}
public int getWebColor() {
return mWebColor;
}
/**
* Sets the color for the web lines in between the lines that come from the
* center. Don't forget to use getResources().getColor(...) when loading a
* color from the resources. Default: Color.rgb(122, 122, 122)
*
* @param color
*/
public void setWebColorInner(int color) {
mWebColorInner = color;
}
public int getWebColorInner() {
return mWebColorInner;
}
/**
* If set to true, drawing the web is enabled, if set to false, drawing the
* whole web is disabled. Default: true
*
* @param enabled
*/
public void setDrawWeb(boolean enabled) {
mDrawWeb = enabled;
}
/**
* Sets the number of web-lines that should be skipped on chart web before the
* next one is drawn. This targets the lines that come from the center of the RadarChart.
*
* @param count if count = 1 -> 1 line is skipped in between
*/
public void setSkipWebLineCount(int count) {
mSkipWebLineCount = Math.max(0, count);
}
/**
* Returns the modulus that is used for skipping web-lines.
*
* @return
*/
public int getSkipWebLineCount() {
return mSkipWebLineCount;
}
@Override
protected float getRequiredLegendOffset() {
return mLegendRenderer.getLabelPaint().getTextSize() * 4.f;
}
@Override
protected float getRequiredBaseOffset() {
return mXAxis.isEnabled() && mXAxis.isDrawLabelsEnabled() ? mXAxis.mLabelWidth : Utils.convertDpToPixel(10f);
}
@Override
public float getRadius() {
RectF content = mViewPortHandler.getContentRect();
return Math.min(content.width() / 2f, content.height() / 2f);
}
/**
* Returns the maximum value this chart can display on it's y-axis.
*/
public float getYChartMax() {
return mYAxis.mAxisMaximum;
}
/**
* Returns the minimum value this chart can display on it's y-axis.
*/
public float getYChartMin() {
return mYAxis.mAxisMinimum;
}
/**
* Returns the range of y-values this chart can display.
*
* @return
*/
public float getYRange() {
return mYAxis.mAxisRange;
}
}