sascha@422: package de.intevation.gnv.jfreechart; sascha@422: sascha@422: import java.awt.AlphaComposite; sascha@422: import java.awt.Composite; sascha@422: import java.awt.Graphics2D; sascha@422: import java.awt.Shape; sascha@422: sascha@422: import java.awt.geom.Point2D; sascha@422: import java.awt.geom.Rectangle2D; sascha@422: sascha@422: import java.util.HashMap; sascha@422: import java.util.Iterator; sascha@422: import java.util.Map; sascha@422: sascha@422: import org.jfree.chart.axis.Axis; sascha@422: import org.jfree.chart.axis.AxisCollection; sascha@422: import org.jfree.chart.axis.AxisLocation; sascha@422: import org.jfree.chart.axis.AxisSpace; sascha@422: import org.jfree.chart.axis.AxisState; sascha@422: import org.jfree.chart.axis.ValueAxis; sascha@422: sascha@422: import org.jfree.chart.plot.Plot; sascha@779: import org.jfree.chart.plot.PlotOrientation; sascha@422: import org.jfree.chart.plot.PlotRenderingInfo; sascha@422: import org.jfree.chart.plot.PlotState; sascha@422: sascha@422: import org.jfree.data.Range; sascha@422: sascha@422: import org.jfree.ui.RectangleEdge; sascha@422: import org.jfree.ui.RectangleInsets; sascha@422: sascha@422: import org.jfree.util.ObjectList; sascha@422: sascha@422: /** ingo@795: * A class for plotting polygons into a 2D chart. This plot makes use of ingo@795: * PolygonRenderer. sascha@803: * sascha@780: * @author Ingo Weinzierl sascha@422: */ sascha@422: // TODO implement cloneable sascha@422: public class PolygonPlot sascha@422: extends Plot sascha@422: { ingo@795: /** ingo@795: * ingo@795: */ sascha@422: public static final String PLOT_TYPE = "PolygonPlot"; sascha@422: ingo@795: /** ingo@795: * ingo@795: */ sascha@422: public static final PlotOrientation DEFAULT_PLOT_ORIENTATION = sascha@422: PlotOrientation.VERTICAL; sascha@422: sascha@422: private PolygonDataset dataset; sascha@422: private transient PolygonRenderer renderer; sascha@422: sascha@422: private PlotOrientation orientation; sascha@422: sascha@422: private RectangleInsets axisOffset; sascha@422: sascha@422: private ObjectList domainAxisLocation; sascha@422: private ObjectList rangeAxisLocation; sascha@422: private ObjectList domainAxes; sascha@422: private ObjectList rangeAxes; sascha@422: sascha@422: ingo@795: /** ingo@795: * Constructs a new PolygonPlot with a dataset and a renderer. ingo@795: * ingo@795: * @param dataset Dataset containing polygons. ingo@795: * @param renderer The renderer used to draw polygons. ingo@795: */ sascha@422: public PolygonPlot(PolygonDataset dataset, PolygonRenderer renderer) { sascha@422: this(dataset, renderer, null, null, PlotOrientation.HORIZONTAL); sascha@422: } sascha@422: sascha@422: ingo@795: /** ingo@795: * ingo@795: * @param dataset Dataset containing polygons. ingo@795: * @param renderer The renderer used to draw polygons. ingo@795: * @param orientation The orientation used for this plot. ingo@795: */ sascha@422: public PolygonPlot( sascha@422: PolygonDataset dataset, sascha@422: PolygonRenderer renderer, sascha@422: PlotOrientation orientation sascha@422: ) { sascha@422: this(dataset, renderer, null, null, orientation); sascha@422: } sascha@422: sascha@422: ingo@795: /** ingo@795: * ingo@795: * @param dataset Dataset containing polygons. ingo@795: * @param renderer The renderer used to draw polygons. ingo@795: * @param domainAxis The x axis. ingo@795: * @param rangeAxis The y axis. ingo@795: * @param orientation The orientation used for this plot. ingo@795: */ sascha@422: public PolygonPlot( sascha@422: PolygonDataset dataset, sascha@422: PolygonRenderer renderer, sascha@422: ValueAxis domainAxis, sascha@422: ValueAxis rangeAxis, sascha@422: PlotOrientation orientation sascha@422: ) { sascha@422: super(); sascha@422: sascha@422: this.dataset = dataset; sascha@422: this.renderer = renderer; sascha@422: this.domainAxes = new ObjectList(); sascha@422: this.rangeAxes = new ObjectList(); sascha@422: this.domainAxisLocation = new ObjectList(); sascha@422: this.rangeAxisLocation = new ObjectList(); sascha@422: this.axisOffset = RectangleInsets.ZERO_INSETS; sascha@422: sascha@422: if (orientation != null) sascha@422: this.orientation = orientation; sascha@422: else sascha@422: this.orientation = DEFAULT_PLOT_ORIENTATION; sascha@422: sascha@422: if (domainAxis != null) { sascha@422: this.domainAxes.set(0, domainAxis); sascha@422: domainAxis.setPlot(this); sascha@422: } sascha@422: domainAxisLocation.set(0, AxisLocation.BOTTOM_OR_LEFT); sascha@422: sascha@422: if (rangeAxis != null) { sascha@422: this.rangeAxes.set(0, rangeAxis); sascha@422: rangeAxis.setPlot(this); sascha@422: } sascha@422: rangeAxisLocation.set(0, AxisLocation.BOTTOM_OR_LEFT); sascha@422: sascha@422: configureDomainAxis(); sascha@422: configureRangeAxis(); sascha@422: } sascha@422: sascha@422: ingo@795: /** ingo@795: * ingo@795: */ sascha@422: public void configureDomainAxis() { sascha@422: // we just have 1 dataset sascha@422: Range domainAxisRange = getDataset().getDomainBounds(); sascha@422: sascha@422: for (int i = 0; i < domainAxes.size(); i++) { sascha@422: ValueAxis axis = (ValueAxis) domainAxes.get(i); sascha@422: sascha@422: if (axis != null) { sascha@422: axis.configure(); sascha@422: axis.setRange(domainAxisRange); sascha@422: } sascha@422: } sascha@422: } sascha@422: ingo@795: /** ingo@795: * ingo@795: * @return ingo@795: */ sascha@450: public ValueAxis getDomainAxis() { sascha@450: return getDomainAxis(0); sascha@450: } sascha@450: ingo@795: /** ingo@795: * ingo@795: * @param index ingo@795: * @return ingo@795: */ sascha@450: public ValueAxis getDomainAxis(int index) { sascha@450: return index < domainAxes.size() sascha@450: ? (ValueAxis)domainAxes.get(index) sascha@450: : null; sascha@450: } sascha@450: ingo@795: /** ingo@795: * ingo@795: * @return ingo@795: */ sascha@450: public ValueAxis getRangeAxis() { sascha@450: return getRangeAxis(0); sascha@450: } sascha@450: ingo@795: /** ingo@795: * ingo@795: * @param index ingo@795: * @return ingo@795: */ sascha@450: public ValueAxis getRangeAxis(int index) { sascha@450: return index < rangeAxes.size() sascha@450: ? (ValueAxis)rangeAxes.get(index) sascha@450: : null; sascha@450: } sascha@422: ingo@795: /** ingo@795: * ingo@795: */ sascha@422: public void configureRangeAxis() { sascha@422: // we just have 1 dataset sascha@422: Range rangeAxisRange = getDataset().getRangeBounds(); sascha@422: sascha@422: for (int i = 0; i < rangeAxes.size(); i++) { sascha@422: ValueAxis axis = (ValueAxis) rangeAxes.get(i); sascha@422: sascha@422: if (axis != null) { sascha@422: axis.configure(); sascha@422: axis.setRange(rangeAxisRange); sascha@422: } sascha@422: } sascha@422: } sascha@422: sascha@422: ingo@795: /** ingo@795: * ingo@795: * @return ingo@795: */ sascha@422: public PolygonDataset getDataset(){ sascha@422: return this.dataset; sascha@422: } sascha@422: sascha@422: ingo@795: /** ingo@795: * ingo@795: * @return ingo@795: */ sascha@422: public String getPlotType() { sascha@422: return PLOT_TYPE; sascha@422: } sascha@422: sascha@422: ingo@795: /** ingo@795: * ingo@795: * @param dataset ingo@795: */ sascha@422: public void setDataset(PolygonDataset dataset) { sascha@422: this.dataset = dataset; sascha@422: } sascha@422: sascha@422: ingo@795: /** ingo@795: * This is the major method to draw the into a given Graphic2D object. ingo@795: * ingo@795: * @param g2 Graphics object where the plot is drawn into. ingo@795: * @param area The bounds for drawing this plot. ingo@795: * @param anchor An anchor point. ingo@795: * @param parentState The plot state. ingo@795: * @param info ingo@795: */ sascha@422: public void draw( sascha@422: Graphics2D g2, sascha@422: Rectangle2D area, sascha@422: Point2D anchor, sascha@422: PlotState parentState, sascha@422: PlotRenderingInfo info sascha@422: ) { sascha@422: Graphics2D savedG2 = g2; sascha@422: Rectangle2D savedDataArea = area; sascha@422: sascha@422: if (info != null) { sascha@422: info.setPlotArea(area); sascha@422: info.setDataArea(area); sascha@422: } sascha@422: sascha@422: AxisSpace space = calculateAxisSpace(g2, area); sascha@422: Rectangle2D dataArea = space.shrink(area, null); sascha@422: sascha@422: // draw background and outline sascha@422: drawBackground(g2, area); sascha@422: drawOutline(g2, area); sascha@422: sascha@422: Shape savedClip = g2.getClip(); sascha@422: g2.clip(area); sascha@422: sascha@422: Composite originalComposite = g2.getComposite(); sascha@422: g2.setComposite(AlphaComposite.getInstance( sascha@422: AlphaComposite.SRC_OVER, sascha@422: getForegroundAlpha() sascha@422: )); sascha@778: sascha@422: // draw axis sascha@422: drawAxes(g2, area, dataArea, info); sascha@422: sascha@422: if (!isEmptyOrNull(dataset)) { sascha@422: // draw data sascha@422: drawPolygons(savedG2, dataArea, info); sascha@450: drawLabels(savedG2, dataArea, info); sascha@422: } sascha@422: sascha@422: g2.setClip(savedClip); sascha@422: g2.setComposite(originalComposite); sascha@422: } sascha@422: sascha@422: ingo@795: /** ingo@795: * Method to draw the axis for this plot. sascha@803: * ingo@795: * @param g2 ingo@795: * @param plotArea ingo@795: * @param dataArea ingo@795: * @param plotState ingo@795: */ sascha@422: private void drawAxes( sascha@422: Graphics2D g2, sascha@422: Rectangle2D plotArea, sascha@422: Rectangle2D dataArea, sascha@422: PlotRenderingInfo plotState sascha@422: ) { sascha@422: AxisCollection axisCollection = new AxisCollection(); sascha@422: sascha@422: for (int i = 0; i < domainAxes.size(); i++) { sascha@422: ValueAxis axis = (ValueAxis) domainAxes.get(i); sascha@422: if (axis != null) sascha@422: axisCollection.add(axis, getDomainAxisEdge(i)); sascha@422: } sascha@422: sascha@422: for (int i = 0; i < rangeAxes.size(); i++) { sascha@422: ValueAxis axis = (ValueAxis) rangeAxes.get(i); sascha@422: if (axis != null) sascha@422: axisCollection.add(axis, getRangeAxisEdge(i)); sascha@422: } sascha@422: sascha@422: Map axisStateMap = new HashMap(); sascha@422: sascha@422: // draw the top axes sascha@422: double cursor = dataArea.getMinY() - this.axisOffset.calculateTopOutset( sascha@422: dataArea.getHeight()); sascha@422: Iterator iterator = axisCollection.getAxesAtTop().iterator(); sascha@422: while (iterator.hasNext()) { sascha@422: ValueAxis axis = (ValueAxis) iterator.next(); sascha@422: AxisState info = axis.draw(g2, cursor, plotArea, dataArea, sascha@422: RectangleEdge.TOP, plotState); sascha@422: cursor = info.getCursor(); sascha@422: axisStateMap.put(axis, info); sascha@422: } sascha@422: sascha@422: // draw the bottom axes sascha@422: cursor = dataArea.getMaxY() sascha@422: + this.axisOffset.calculateBottomOutset(dataArea.getHeight()); sascha@422: iterator = axisCollection.getAxesAtBottom().iterator(); sascha@422: while (iterator.hasNext()) { sascha@422: ValueAxis axis = (ValueAxis) iterator.next(); sascha@422: AxisState info = axis.draw(g2, cursor, plotArea, dataArea, sascha@422: RectangleEdge.BOTTOM, plotState); sascha@422: cursor = info.getCursor(); sascha@422: axisStateMap.put(axis, info); sascha@422: } sascha@422: sascha@422: // draw the left axes sascha@422: cursor = dataArea.getMinX() sascha@422: - this.axisOffset.calculateLeftOutset(dataArea.getWidth()); sascha@422: iterator = axisCollection.getAxesAtLeft().iterator(); sascha@422: while (iterator.hasNext()) { sascha@422: ValueAxis axis = (ValueAxis) iterator.next(); sascha@422: AxisState info = axis.draw(g2, cursor, plotArea, dataArea, sascha@422: RectangleEdge.LEFT, plotState); sascha@422: cursor = info.getCursor(); sascha@422: axisStateMap.put(axis, info); sascha@422: } sascha@422: sascha@422: // draw the right axes sascha@422: cursor = dataArea.getMaxX() sascha@422: + this.axisOffset.calculateRightOutset(dataArea.getWidth()); sascha@422: iterator = axisCollection.getAxesAtRight().iterator(); sascha@422: while (iterator.hasNext()) { sascha@422: ValueAxis axis = (ValueAxis) iterator.next(); sascha@422: AxisState info = axis.draw(g2, cursor, plotArea, dataArea, sascha@422: RectangleEdge.RIGHT, plotState); sascha@422: cursor = info.getCursor(); sascha@422: axisStateMap.put(axis, info); sascha@422: } sascha@422: } sascha@422: sascha@422: ingo@795: /** ingo@795: * Put some labels at data items into the plot. Uses PolygonRenderer to do ingo@795: * this job. ingo@795: * ingo@795: * @param g2 ingo@795: * @param area ingo@795: * @param info ingo@795: */ sascha@450: private void drawLabels( sascha@450: Graphics2D g2, sascha@450: Rectangle2D area, sascha@450: PlotRenderingInfo info sascha@450: ) { sascha@450: renderer.drawLabels(g2, this, area, dataset); sascha@450: } sascha@450: ingo@795: ingo@795: /** ingo@795: * Plot the polygons. Uses PolygonRenderer to do this job. sascha@803: * ingo@795: * @param g2 ingo@795: * @param area ingo@795: * @param info ingo@795: */ sascha@422: private void drawPolygons( sascha@422: Graphics2D g2, sascha@422: Rectangle2D area, sascha@422: PlotRenderingInfo info sascha@422: ) { sascha@451: renderer.drawPolygons(g2, this, area, dataset); sascha@422: } sascha@422: sascha@422: ingo@795: /** ingo@795: * ingo@795: * @param g2 ingo@795: * @param plotArea ingo@795: * @return ingo@795: */ sascha@422: private AxisSpace calculateAxisSpace(Graphics2D g2, Rectangle2D plotArea) { sascha@422: AxisSpace space = new AxisSpace(); sascha@422: space = calculateRangeAxisSpace(g2, plotArea, space); sascha@422: Rectangle2D tmpPlotArea = space.shrink(plotArea, null); sascha@422: space = calculateDomainAxisSpace(g2, plotArea, space); sascha@422: sascha@422: return space; sascha@422: } sascha@422: sascha@422: ingo@795: /** sascha@803: * ingo@795: * @param g2 ingo@795: * @param plotArea ingo@795: * @param space ingo@795: * @return ingo@795: */ sascha@422: private AxisSpace calculateDomainAxisSpace( sascha@422: Graphics2D g2, sascha@422: Rectangle2D plotArea, sascha@422: AxisSpace space sascha@422: ) { sascha@422: if (space == null) sascha@422: space = new AxisSpace(); sascha@422: sascha@422: for (int i = 0; i < domainAxes.size(); i++) { sascha@422: Axis axis = (Axis) domainAxes.get(i); sascha@422: sascha@422: if (axis != null) { sascha@422: RectangleEdge edge = getDomainAxisEdge(i); sascha@422: space = axis.reserveSpace(g2, this, plotArea, edge, space); sascha@422: } sascha@422: } sascha@422: sascha@422: return space; sascha@422: } sascha@422: sascha@422: ingo@795: /** ingo@795: * ingo@795: * @param g2 ingo@795: * @param plotArea ingo@795: * @param space ingo@795: * @return ingo@795: */ sascha@422: private AxisSpace calculateRangeAxisSpace( sascha@422: Graphics2D g2, sascha@422: Rectangle2D plotArea, sascha@422: AxisSpace space sascha@422: ) { sascha@422: if (space == null) sascha@422: space = new AxisSpace(); sascha@422: sascha@422: for (int i = 0; i < rangeAxes.size(); i++) { sascha@422: Axis axis = (Axis) rangeAxes.get(i); sascha@422: sascha@422: if (axis != null) { sascha@422: RectangleEdge edge = getRangeAxisEdge(i); sascha@422: space = axis.reserveSpace(g2, this, plotArea, edge, space); sascha@422: } sascha@422: } sascha@422: sascha@422: return space; sascha@422: } sascha@422: ingo@795: ingo@795: /** ingo@795: * ingo@795: * @return ingo@795: */ sascha@450: public RectangleEdge getDomainAxisEdge() { sascha@422: return Plot.resolveDomainAxisLocation( sascha@422: getDomainAxisLocation(), orientation sascha@422: ); sascha@422: } sascha@422: sascha@422: ingo@795: /** ingo@795: * ingo@795: * @param idx ingo@795: * @return ingo@795: */ sascha@450: public RectangleEdge getDomainAxisEdge(int idx) { sascha@422: AxisLocation location = getDomainAxisLocation(idx); sascha@422: RectangleEdge result = Plot.resolveDomainAxisLocation( sascha@422: location, orientation sascha@422: ); sascha@422: sascha@422: if (result == null) sascha@422: result = RectangleEdge.opposite(getDomainAxisEdge()); sascha@422: sascha@422: return result; sascha@422: } sascha@422: sascha@422: ingo@795: /** ingo@795: * ingo@795: * @return ingo@795: */ sascha@450: public RectangleEdge getRangeAxisEdge() { sascha@422: return Plot.resolveRangeAxisLocation( sascha@422: getRangeAxisLocation(), orientation sascha@422: ); sascha@422: } sascha@422: sascha@422: ingo@795: /** ingo@795: * ingo@795: * @param idx ingo@795: * @return ingo@795: */ sascha@450: public RectangleEdge getRangeAxisEdge(int idx) { sascha@422: AxisLocation location = getRangeAxisLocation(idx); sascha@422: RectangleEdge result = Plot.resolveRangeAxisLocation( sascha@422: location, sascha@422: orientation sascha@422: ); sascha@422: sascha@422: if (result == null) sascha@422: result = RectangleEdge.opposite(getRangeAxisEdge()); sascha@422: sascha@422: return result; sascha@422: } sascha@422: sascha@422: ingo@795: /** ingo@795: * ingo@795: * @return ingo@795: */ sascha@422: public AxisLocation getDomainAxisLocation() { sascha@422: return (AxisLocation) domainAxisLocation.get(0); sascha@422: } sascha@422: sascha@422: ingo@795: /** ingo@795: * ingo@795: * @param idx ingo@795: * @return ingo@795: */ sascha@422: public AxisLocation getDomainAxisLocation(int idx) { sascha@422: if (idx < domainAxisLocation.size()) sascha@422: return (AxisLocation) domainAxisLocation.get(idx); sascha@422: sascha@422: return null; sascha@422: } sascha@422: sascha@422: ingo@795: /** ingo@795: * ingo@795: * @return ingo@795: */ sascha@422: public AxisLocation getRangeAxisLocation() { sascha@422: return (AxisLocation) rangeAxisLocation.get(0); sascha@422: } sascha@422: sascha@422: ingo@795: /** ingo@795: * ingo@795: * @param idx ingo@795: * @return ingo@795: */ sascha@422: public AxisLocation getRangeAxisLocation(int idx) { sascha@422: if (idx < rangeAxisLocation.size()) sascha@422: return (AxisLocation) rangeAxisLocation.get(idx); sascha@422: sascha@422: return null; sascha@422: } sascha@422: sascha@422: ingo@795: /** sascha@803: * ingo@795: * @param dataset ingo@795: * @return true, if dataset is null or if it does not contain any ingo@795: * PolygonSeries, otherwise false. ingo@795: */ sascha@422: private boolean isEmptyOrNull(PolygonDataset dataset) { sascha@422: if (dataset != null) { sascha@422: int seriesCount = dataset.getSeriesCount(); sascha@422: for (int s = 0; s < seriesCount; s++) { sascha@422: PolygonSeries series = dataset.getSeries(s); sascha@422: if (series.getItemCount() > 0) { sascha@422: return false; sascha@422: } sascha@422: } sascha@422: } sascha@422: return true; sascha@422: } sascha@422: } ingo@795: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :