All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.codename1.charts.views.TimeChart Maven / Gradle / Ivy

There is a newer version: 7.0.164
Show newest version
/**
 * Copyright (C) 2009 - 2013 SC 4ViewSoft SRL
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.codename1.charts.views;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.codename1.charts.compat.Canvas;
import com.codename1.charts.compat.Paint;

import com.codename1.charts.models.XYMultipleSeriesDataset;
import com.codename1.charts.models.XYSeries;
import com.codename1.charts.renderers.XYMultipleSeriesRenderer;
import java.util.Calendar;
import java.util.TimeZone;


/**
 * The time chart rendering class.
 */
public class TimeChart extends LineChart {
    /**
     * The number of milliseconds in a minute.
     */
    private static final int MILLIS_TO_MINUTES = 60000;
    
    /**
     * This is missing from the Codename One Calendar object, but required by
     * TimeZone.getOffset()
     */
    private static final int ERA = 0;
    /**
  /** The constant to identify this chart type. */
  public static final String TYPE = "Time";
  /** The number of milliseconds in a day. */
  public static final long DAY = 24 * 60 * 60 * 1000;
  /** The date format pattern to be used in formatting the X axis labels. */
  private String mDateFormat;
  /** The starting point for labels. */
  private Double mStartPoint;

  TimeChart() {
  }

  /**
   * Builds a new time chart instance.
   * 
   * @param dataset the multiple series dataset
   * @param renderer the multiple series renderer
   */
  public TimeChart(XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer) {
    super(dataset, renderer);
  }

  /**
   * Returns the date format pattern to be used for formatting the X axis
   * labels.
   * 
   * @return the date format pattern for the X axis labels
   */
  public String getDateFormat() {
    return mDateFormat;
  }

  /**
   * Sets the date format pattern to be used for formatting the X axis labels.
   * 
   * @param format the date format pattern for the X axis labels. If null, an
   *          appropriate default format will be used.
   */
  public void setDateFormat(String format) {
    mDateFormat = format;
  }

  /**
   * The graphical representation of the labels on the X axis.
   * 
   * @param xLabels the X labels values
   * @param xTextLabelLocations the X text label locations
   * @param canvas the canvas to paint to
   * @param paint the paint to be used for drawing
   * @param left the left value of the labels area
   * @param top the top value of the labels area
   * @param bottom the bottom value of the labels area
   * @param xPixelsPerUnit the amount of pixels per one unit in the chart labels
   * @param minX the minimum value on the X axis in the chart
   * @param maxX the maximum value on the X axis in the chart
   */
  @Override
  protected void drawXLabels(List xLabels, Double[] xTextLabelLocations, Canvas canvas,
      Paint paint, int left, int top, int bottom, double xPixelsPerUnit, double minX, double maxX) {
    int length = xLabels.size();
    if (length > 0) {
      boolean showLabels = mRenderer.isShowLabels();
      boolean showGridY = mRenderer.isShowGridY();
      boolean showTickMarks = mRenderer.isShowTickMarks();
      DateFormat format = getDateFormat(xLabels.get(0), xLabels.get(length - 1));
      for (int i = 0; i < length; i++) {
        long label = Math.round(xLabels.get(i));
        float xLabel = (float) (left + xPixelsPerUnit * (label - minX));
        if (showLabels) {
          paint.setColor(mRenderer.getXLabelsColor());
          if (showTickMarks) {
            canvas.drawLine(xLabel, bottom, xLabel, bottom + mRenderer.getLabelsTextSize() / 3,
                paint);
          }
          drawText(canvas, format.format(new Date(label)), xLabel,
              bottom + mRenderer.getLabelsTextSize() * 4 / 3 + mRenderer.getXLabelsPadding(),
              paint, mRenderer.getXLabelsAngle());
        }
        if (showGridY) {
          paint.setColor(mRenderer.getGridColor(0));
          canvas.drawLine(xLabel, bottom, xLabel, top, paint);
        }
      }
    }
    drawXTextLabels(xTextLabelLocations, canvas, paint, true, left, top, bottom, xPixelsPerUnit,
        minX, maxX);
  }

  /**
   * Returns the date format pattern to be used, based on the date range.
   * 
   * @param start the start date in milliseconds
   * @param end the end date in milliseconds
   * @return the date format
   */
  private DateFormat getDateFormat(double start, double end) {
    if (mDateFormat != null) {
      SimpleDateFormat format = null;
      try {
        format = new SimpleDateFormat(mDateFormat);
        return format;
      } catch (Exception e) {
        // do nothing here
      }
    }
    DateFormat format = SimpleDateFormat.getDateInstance(SimpleDateFormat.MEDIUM);
    double diff = end - start;
    if (diff > DAY && diff < 5 * DAY) {
      format = SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.SHORT);
    } else if (diff < DAY) {
      format = SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM);
    }
    return format;
  }

  /**
   * Returns the chart type identifier.
   * 
   * @return the chart type
   */
  public String getChartType() {
    return TYPE;
  }

  @Override
  protected List getXLabels(double min, double max, int count) {
    final List result = new ArrayList();
    if (!mRenderer.isXRoundedLabels()) {
      if (mDataset.getSeriesCount() > 0) {
        XYSeries series = mDataset.getSeriesAt(0);
        int length = series.getItemCount();
        int intervalLength = 0;
        int startIndex = -1;
        for (int i = 0; i < length; i++) {
          double value = series.getX(i);
          if (min <= value && value <= max) {
            intervalLength++;
            if (startIndex < 0) {
              startIndex = i;
            }
          }
        }
        if (intervalLength < count) {
          for (int i = startIndex; i < startIndex + intervalLength; i++) {
            result.add(series.getX(i));
          }
        } else {
          float step = (float) intervalLength / count;
          int intervalCount = 0;
          for (int i = 0; i < length && intervalCount < count; i++) {
            double value = series.getX(Math.round(i * step));
            if (min <= value && value <= max) {
              result.add(value);
              intervalCount++;
            }
          }
        }
        return result;
      } else {
        return super.getXLabels(min, max, count);
      }
    }
    if (mStartPoint == null) {
        TimeZone  tz = TimeZone.getDefault();
        Calendar cal = Calendar.getInstance(tz);
        cal.setTime(new Date(Math.round(min)));
        
        
        int offset = getDSTOffset(cal);
      mStartPoint = min - (min % DAY) + DAY + offset * 60 * 60 * 1000;
    }
    if (count > 25) {
      count = 25;
    }

    final double cycleMath = (max - min) / count;
    if (cycleMath <= 0) {
      return result;
    }
    double cycle = DAY;

    if (cycleMath <= DAY) {
      while (cycleMath < cycle / 2) {
        cycle = cycle / 2;
      }
    } else {
      while (cycleMath > cycle) {
        cycle = cycle * 2;
      }
    }

    double val = mStartPoint - Math.floor((mStartPoint - min) / cycle) * cycle;
    int i = 0;
    while (val < max && i++ <= count) {
      result.add(val);
      val += cycle;
    }

    return result;
  }
  
  /**
     * Determine the number of minutes to adjust the date for local DST. This
     * should provide a historically correct value, also accounting for changes
     * in GMT offset. See TimeZone javadoc for more details.
     *
     * @param source
     * @return
     */
    int getDSTOffset(Calendar source) {
        TimeZone localTimezone = Calendar.getInstance().getTimeZone();
        int rawOffset = localTimezone.getRawOffset() / MILLIS_TO_MINUTES;
        return getOffsetInMinutes(source, localTimezone) - rawOffset;
    }

    /**
     * Get the offset from GMT for a given timezone.
     *
     * @param source
     * @param timezone
     * @return
     */
    int getOffsetInMinutes(Calendar source, TimeZone timezone) {
        return timezone.getOffset(source.get(ERA), source.get(Calendar.YEAR), source.get(Calendar.MONTH),
                source.get(Calendar.DAY_OF_MONTH), source.get(Calendar.DAY_OF_WEEK), source.get(Calendar.MILLISECOND))
                / MILLIS_TO_MINUTES;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy