/** * * The MIT License * * Copyright (c) 2011 the original author or authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.googlecode.charts4j; import static com.googlecode.charts4j.collect.Preconditions.*; import java.util.Collections; import java.util.List; import java.util.Map; import com.googlecode.charts4j.collect.ImmutableList; import com.googlecode.charts4j.collect.Lists; import com.googlecode.charts4j.collect.Maps; import com.googlecode.charts4j.parameters.AxisTypes; /** * Abstract type that is common to all charts with axes. * * @author Julien Chastang (julien.c.chastang at gmail dot com) */ public abstract class AbstractAxisChart extends AbstractGraphChart implements GridChart { /** List of X axis labels. **/ private final List<AxisLabelsImpl> xAxisLabels = Lists.newLinkedList(); /** List of Y axis labels. **/ private final List<AxisLabelsImpl> yAxisLabels = Lists.newLinkedList(); /** List of top axis labels. **/ private final List<AxisLabelsImpl> topAxisLabels = Lists.newLinkedList(); /** List of right axis labels. **/ private final List<AxisLabelsImpl> rightAxisLabels = Lists.newLinkedList(); /** List of free markers. **/ private final List<FreeMarker> freeMarkers = Lists.newLinkedList(); /** * Line style for grid. For internal purposes only. Thickness field is * ignored. */ private LineStyle gridLineStyle; /** X axis step size for the grid. **/ private double xAxisStepSize; /** Y axis step size for the grid. **/ private double yAxisStepSize; /** * Abstract Axis chart constructor. */ AbstractAxisChart() { super(); } /** * Add X axis labels. * * @param axisLabels * x axis labels. Cannot be null. axisLabels parameter is * defensively copied. * * @see AxisLabelsFactory */ public final void addXAxisLabels(final AxisLabels axisLabels) { checkNotNull(axisLabels, "axisLabel cannnot be null"); xAxisLabels.add((AxisLabelsImpl) axisLabels.klone()); } /** * Add Y axis information. * * @param axisLabels * y axis information. Cannot be null. axisLabel parameter is * defensively copied. * * @see AxisLabelsFactory */ public final void addYAxisLabels(final AxisLabels axisLabels) { checkNotNull(axisLabels, "axisLabel cannnot be null"); yAxisLabels.add((AxisLabelsImpl) axisLabels.klone()); } /** * Add Top axis information. * * @param axisLabels * top axis information. Cannot be null. axisLabel parameter is * defensively copied. * * @see AxisLabelsFactory */ public final void addTopAxisLabels(final AxisLabels axisLabels) { checkNotNull(axisLabels, "axisLabel cannnot be null"); topAxisLabels.add((AxisLabelsImpl) axisLabels.klone()); } /** * Add Right axis information. * * @param axisLabels * right axis information. Cannot be null. axisLabels parameter * is defensively copied. * * @see AxisLabelsFactory */ public final void addRightAxisLabels(final AxisLabels axisLabels) { checkNotNull(axisLabels, "axisLabels cannnot be null"); rightAxisLabels.add((AxisLabelsImpl) axisLabels.klone()); } /** * Add a shape or text marker at any position in this chart. As is true for * the rest of the API, specify the position between 0 and 100 along the x * and y axes. * * @param marker * The shape or text marker that will be displayed in the chart. * Cannot be null. * @param xPos * The x position of the text. xPos >= 0 and <= 100. * @param yPos * The y position of the text. yPos >= 0 and <= 100. */ public final void addMarker(final Marker marker, final double xPos, final double yPos) { checkNotNull(marker, "marker cannnot be null"); checkArgument(xPos >= 0 && xPos <= 100, "xPos must be >= 0 and <= 100: %s", xPos); checkArgument(yPos >= 0 && yPos <= 100, "yPos must be >= 0 and <= 100: %s", yPos); freeMarkers.add(new FreeMarker(marker, xPos, yPos)); } /** * {@inheritDoc} */ public final void setGrid(final double xAxisStepSize, final double yAxisStepSize, final int lengthOfLineSegment, final int lengthOfBlankSegment) { checkArgument(xAxisStepSize > 0, "xAxisStepSize must be positive: %s", xAxisStepSize); checkArgument(yAxisStepSize > 0, "yAxisStepSize must be positive: %s", yAxisStepSize); checkArgument(lengthOfLineSegment >= 0, "lengthOfLineSegment must be 0 or positive: %s", lengthOfLineSegment); checkArgument(lengthOfBlankSegment >= 0, "lengthOfBlankSegment must be 0 or positive: %s", lengthOfBlankSegment); this.xAxisStepSize = xAxisStepSize; this.yAxisStepSize = yAxisStepSize; // Line thickness is ignored. LineStyle just happened to be a convenient // container. gridLineStyle = LineStyle.newLineStyle(1, lengthOfLineSegment, lengthOfBlankSegment); } /** * {@inheritDoc} */ @Override protected void prepareData() { super.prepareData(); if (gridLineStyle != null) { parameterManager.setGridLineParameter(xAxisStepSize, yAxisStepSize, gridLineStyle.getLengthOfLineSegment(), gridLineStyle.getLengthOfBlankSegment()); } for (FreeMarker marker : freeMarkers) { parameterManager.addFreeMarker(marker.marker, marker.xPos, marker.yPos); } if (!xAxisLabels.isEmpty() || !yAxisLabels.isEmpty() || !topAxisLabels.isEmpty() || !rightAxisLabels.isEmpty()) { final Map<AxisTypes, List<AxisLabelsImpl>> axisTypeMap = Maps.newEnumMap(AxisTypes.class); axisTypeMap.put(AxisTypes.RIGHT_Y_AXIS, rightAxisLabels); axisTypeMap.put(AxisTypes.TOP_X_AXIS, topAxisLabels); axisTypeMap.put(AxisTypes.LEFT_Y_AXIS, yAxisLabels); axisTypeMap.put(AxisTypes.BOTTOM_X_AXIS, xAxisLabels); int axisIndex = 0; for (Map.Entry<AxisTypes, List<AxisLabelsImpl>> entry : axisTypeMap.entrySet()) { for (AxisLabelsImpl axisLabel : entry.getValue()) { parameterManager.addAxisTypes(entry.getKey()); // if there are labels but no positions if (axisLabel.getPositions().isEmpty() && !axisLabel.getLabels().isEmpty()) { parameterManager.addAxisLabels(axisIndex, axisLabel.getLabels()); } // if there are labels and positions else if (!axisLabel.getPositions().isEmpty() && !axisLabel.getLabels().isEmpty()) { parameterManager.addAxisLabels(axisIndex, axisLabel.getLabels()); parameterManager.addAxisLabelPosition(axisIndex, axisLabel.getPositions()); parameterManager.addAxisRange(axisIndex, Data.MIN_VALUE, Data.MAX_VALUE, Double.NaN); } // if there are positions but no labels else if (!axisLabel.getPositions().isEmpty() && axisLabel.getLabels().isEmpty()) { final List<Double> sortedpositions = convertToSortedDoubleList(axisLabel.getPositions()); parameterManager.addAxisLabelPosition(axisIndex, axisLabel.getPositions()); parameterManager.addAxisRange(axisIndex, sortedpositions.get(0), sortedpositions.get(sortedpositions.size() - 1), Double.NaN); } else if (axisLabel.getRange() != null) { parameterManager.addAxisRange(axisIndex, axisLabel.getRange().getMin(), axisLabel.getRange().getMax(), axisLabel.getRange().getInterval()); } if (axisLabel.getAxisStyle() != null) { //For an explanation of this peculiar code see the comment at AxisStyle.correctAxisStyle. parameterManager.addAxisStyle(axisIndex, getClass().equals(PrivateRadarChart.class) ? axisLabel.getAxisStyle() : AxisStyle.correctAxisStyle(axisLabel.getAxisStyle())); if (axisLabel.getAxisStyle().getTickMarkLength() != null) { parameterManager.addTickMarkLength(axisIndex, axisLabel.getAxisStyle().getTickMarkLength()); } } axisIndex++; } } } } /** * A method to convert a number list to a double list. * * @param positions * the axis positions list. * @return a double list containing positions. */ private static List<Double> convertToSortedDoubleList(final ImmutableList<? extends Number> positions) { final List<Double> doubleList = Lists.newLinkedList(); for (Number number : positions) { doubleList.add(number.doubleValue()); } Collections.sort(doubleList); return doubleList; } /** * Private static inner class to encapsulate a FreeMarker. */ private static class FreeMarker { /** The marker. */ private final Marker marker; /** The x pos. */ private final double xPos; /** The y pos. */ private final double yPos; /** * Instantiates a new free marker. * * @param marker * the marker * @param xPos * the x pos * @param yPos * the y pos */ private FreeMarker(final Marker marker, final double xPos, final double yPos) { this.marker = marker; this.xPos = xPos; this.yPos = yPos; } } }