ingo@2074: package de.intevation.flys.jfree; ingo@2074: ingo@2591: import java.awt.Color; ingo@2074: import java.awt.Graphics2D; ingo@2591: import java.awt.Paint; ingo@2074: import java.awt.Shape; ingo@2074: import java.awt.geom.Rectangle2D; ingo@2074: import java.util.HashMap; ingo@2074: import java.util.Map; ingo@2074: ingo@2074: import org.apache.log4j.Logger; ingo@2074: ingo@2074: import org.jfree.chart.axis.ValueAxis; ingo@2074: import org.jfree.chart.entity.EntityCollection; ingo@2074: import org.jfree.chart.plot.CrosshairState; ingo@2074: import org.jfree.chart.plot.PlotOrientation; ingo@2074: import org.jfree.chart.plot.XYPlot; ingo@2074: import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; ingo@2074: import org.jfree.data.xy.XYDataset; ingo@2074: import org.jfree.ui.RectangleEdge; ingo@2074: import org.jfree.util.BooleanList; ingo@2074: import org.jfree.util.ShapeUtilities; ingo@2074: felix@2642: /** felix@2642: * Renderer with additional functionality. felix@2642: */ ingo@2074: public class EnhancedLineAndShapeRenderer extends XYLineAndShapeRenderer { ingo@2074: ingo@2074: private static final Logger logger = ingo@2074: Logger.getLogger(EnhancedLineAndShapeRenderer.class); ingo@2074: ingo@2074: protected BooleanList isMinimumShapeVisible; ingo@2074: protected BooleanList isMaximumShapeVisible; ingo@2074: ingo@2074: protected Map seriesMinimum; felix@2644: protected Map seriesMinimumX; ingo@2074: protected Map seriesMaximum; ingo@2074: ingo@2074: ingo@2074: public EnhancedLineAndShapeRenderer(boolean lines, boolean shapes) { ingo@2074: super(lines, shapes); ingo@2074: this.isMinimumShapeVisible = new BooleanList(); ingo@2074: this.isMaximumShapeVisible = new BooleanList(); ingo@2074: this.seriesMinimum = new HashMap(); ingo@2074: this.seriesMaximum = new HashMap(); felix@2644: this.seriesMinimumX = new HashMap(); ingo@2074: } ingo@2074: ingo@2074: ingo@2074: public boolean getItemShapeVisible(XYDataset dataset, int series, int item){ ingo@2074: if (super.getItemShapeVisible(series, item)) { ingo@2074: return true; ingo@2074: } ingo@2074: ingo@2074: if (isMinimumShapeVisible(series) && isMinimum(dataset, series, item)) { ingo@2074: return true; ingo@2074: } ingo@2074: ingo@2074: if (isMaximumShapeVisible(series) && isMaximum(dataset, series, item)) { ingo@2074: return true; ingo@2074: } ingo@2074: ingo@2074: return false; ingo@2074: } ingo@2074: ingo@2074: ingo@2591: public Shape getMaximumShape(int series, int column) { ingo@2591: return new Rectangle2D.Double(-5d, -5d, 10d, 10d); ingo@2591: } ingo@2591: ingo@2591: ingo@2591: public Shape getMinimumShape(int series, int column) { ingo@2591: return new Rectangle2D.Double(-5d, -5d, 10d, 10d); ingo@2591: } ingo@2591: ingo@2591: ingo@2591: public Paint getMaximumFillPaint(int series, int column) { ingo@2591: Paint p = getItemPaint(series, column); ingo@2591: ingo@2591: if (p instanceof Color) { ingo@2591: Color c = (Color) p; ingo@2591: Color b = c; ingo@2591: ingo@2591: for (int i = 0; i < 2; i++) { ingo@2591: b = b.darker(); ingo@2591: } ingo@2591: ingo@2591: return b; ingo@2591: } ingo@2591: ingo@2591: logger.warn("Item paint is no instance of Color!"); ingo@2591: return p; ingo@2591: } ingo@2591: ingo@2591: ingo@2591: public Paint getMinimumFillPaint(int series, int column) { ingo@2591: Paint p = getItemPaint(series, column); ingo@2591: ingo@2591: if (p instanceof Color) { ingo@2591: Color c = (Color) p; ingo@2591: Color b = c; ingo@2591: ingo@2591: for (int i = 0; i < 2; i++) { ingo@2591: b = b.darker(); ingo@2591: } ingo@2591: ingo@2591: return b; ingo@2591: } ingo@2591: ingo@2591: logger.warn("Item paint is no instance of Color!"); ingo@2591: return p; ingo@2591: } ingo@2591: ingo@2591: ingo@2074: /** ingo@2074: * Overrides XYLineAndShapeRenderer.drawSecondaryPass() to call an adapted ingo@2074: * method getItemShapeVisible() which now takes an XYDataset. So, 99% of ingo@2074: * code equal the code in XYLineAndShapeRenderer. ingo@2074: */ ingo@2074: @Override ingo@2074: protected void drawSecondaryPass( ingo@2074: Graphics2D g2, ingo@2074: XYPlot plot, ingo@2074: XYDataset dataset, ingo@2074: int pass, ingo@2074: int series, ingo@2074: int item, ingo@2074: ValueAxis domainAxis, ingo@2074: Rectangle2D dataArea, ingo@2074: ValueAxis rangeAxis, ingo@2074: CrosshairState crosshairState, ingo@2074: EntityCollection entities ingo@2074: ) { ingo@2074: Shape entityArea = null; ingo@2074: ingo@2074: // get the data point... ingo@2074: double x1 = dataset.getXValue(series, item); ingo@2074: double y1 = dataset.getYValue(series, item); ingo@2074: if (Double.isNaN(y1) || Double.isNaN(x1)) { ingo@2074: return; ingo@2074: } ingo@2074: ingo@2074: PlotOrientation orientation = plot.getOrientation(); ingo@2074: RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); ingo@2074: RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); ingo@2074: double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation); ingo@2074: double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation); ingo@2074: ingo@2074: if (getItemShapeVisible(dataset, series, item)) { ingo@2591: Shape shape = null; ingo@2591: felix@2644: // OPTIMIZE: instead of calculating minimum and maximum for every felix@2644: // point, calculate it just once (assume that dataset felix@2644: // content does not change during rendering). felix@2644: // NOTE: Above OPTIMIZE might already be fulfilled to most extend. ingo@2591: boolean isMinimum = isMinimumShapeVisible(series) ingo@2591: && isMinimum(dataset, series, item); ingo@2591: ingo@2591: boolean isMaximum = isMaximumShapeVisible(series) ingo@2591: && isMaximum(dataset, series, item); ingo@2591: ingo@2591: if (isMinimum) { ingo@2591: logger.debug("Create a Minimum shape."); ingo@2591: shape = getMinimumShape(series, item); ingo@2591: } ingo@2591: else if (isMaximum) { ingo@2591: logger.debug("Create a Maximum shape."); ingo@2591: shape = getMaximumShape(series, item); ingo@2591: } ingo@2591: else { ingo@2591: shape = getItemShape(series, item); ingo@2591: } ingo@2591: ingo@2074: if (orientation == PlotOrientation.HORIZONTAL) { ingo@2074: shape = ShapeUtilities.createTranslatedShape(shape, transY1, ingo@2074: transX1); ingo@2074: } ingo@2074: else if (orientation == PlotOrientation.VERTICAL) { ingo@2074: shape = ShapeUtilities.createTranslatedShape(shape, transX1, ingo@2074: transY1); ingo@2074: } ingo@2074: entityArea = shape; ingo@2074: if (shape.intersects(dataArea)) { ingo@2074: if (getItemShapeFilled(series, item)) { ingo@2074: if (getUseFillPaint()) { ingo@2074: g2.setPaint(getItemFillPaint(series, item)); ingo@2074: } ingo@2074: else { ingo@2074: g2.setPaint(getItemPaint(series, item)); ingo@2074: } ingo@2074: g2.fill(shape); ingo@2074: } ingo@2074: if (getDrawOutlines()) { ingo@2074: if (getUseOutlinePaint()) { ingo@2074: g2.setPaint(getItemOutlinePaint(series, item)); ingo@2074: } ingo@2074: else { ingo@2074: g2.setPaint(getItemPaint(series, item)); ingo@2074: } ingo@2074: g2.setStroke(getItemOutlineStroke(series, item)); ingo@2074: g2.draw(shape); ingo@2074: } ingo@2591: ingo@2591: if (isMinimum) { ingo@2591: g2.setPaint(getMinimumFillPaint(series, item)); ingo@2591: g2.fill(shape); ingo@2591: g2.setPaint(getItemOutlinePaint(series, item)); ingo@2591: g2.setStroke(getItemOutlineStroke(series, item)); ingo@2591: g2.draw(shape); ingo@2591: } ingo@2591: else if (isMaximum) { ingo@2591: g2.setPaint(getMaximumFillPaint(series, item)); ingo@2591: g2.fill(shape); ingo@2591: g2.setPaint(getItemOutlinePaint(series, item)); ingo@2591: g2.setStroke(getItemOutlineStroke(series, item)); ingo@2591: g2.draw(shape); ingo@2591: } ingo@2074: } felix@2642: // TODO labeling of waterlevels could happen here, too. felix@2644: } // if (getItemShapeVisible(dataset, series, item)) ingo@2074: ingo@2074: double xx = transX1; ingo@2074: double yy = transY1; ingo@2074: if (orientation == PlotOrientation.HORIZONTAL) { ingo@2074: xx = transY1; ingo@2074: yy = transX1; ingo@2074: } ingo@2074: ingo@2074: // draw the item label if there is one... ingo@2074: if (isItemLabelVisible(series, item)) { ingo@2074: drawItemLabel(g2, orientation, dataset, series, item, xx, yy, ingo@2074: (y1 < 0.0)); ingo@2074: } ingo@2074: felix@2644: boolean doWaterlevelLabel = false; felix@2644: if (isMinimumX (dataset, series, item)) { felix@2644: String waterlevelLabel = "label"; felix@2644: // TODO Force water of some German rivers to flow direction mountains. felix@2644: g2.drawString("waterlevel label", (float)xx, (float)yy-3f); felix@2644: } felix@2644: ingo@2074: int domainAxisIndex = plot.getDomainAxisIndex(domainAxis); ingo@2074: int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis); ingo@2074: updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex, ingo@2074: rangeAxisIndex, transX1, transY1, orientation); ingo@2074: ingo@2074: // add an entity for the item, but only if it falls within the data ingo@2074: // area... ingo@2074: if (entities != null && isPointInRect(dataArea, xx, yy)) { ingo@2074: addEntity(entities, entityArea, dataset, series, item, xx, yy); ingo@2074: } ingo@2074: } ingo@2074: ingo@2074: ingo@2074: public void setIsMinimumShapeVisisble(int series, boolean isVisible) { ingo@2074: this.isMinimumShapeVisible.setBoolean(series, isVisible); ingo@2074: } ingo@2074: ingo@2074: ingo@2074: public boolean isMinimumShapeVisible(int series) { ingo@2241: if (this.isMinimumShapeVisible.size() <= series) { ingo@2241: return false; ingo@2241: } ingo@2241: ingo@2074: return isMinimumShapeVisible.getBoolean(series); ingo@2074: } ingo@2074: ingo@2074: ingo@2074: public void setIsMaximumShapeVisible(int series, boolean isVisible) { ingo@2074: this.isMaximumShapeVisible.setBoolean(series, isVisible); ingo@2074: } ingo@2074: ingo@2074: ingo@2074: public boolean isMaximumShapeVisible(int series) { ingo@2241: if (this.isMaximumShapeVisible.size() <= series) { ingo@2241: return false; ingo@2241: } ingo@2241: ingo@2074: return isMaximumShapeVisible.getBoolean(series); ingo@2074: } ingo@2074: ingo@2074: felix@2644: public boolean isMinimumX(XYDataset dataset, int series, int item) { felix@2644: return dataset.getXValue(series, item) == getMinimumX(dataset, series); felix@2644: } felix@2644: felix@2644: public double getMinimumX(XYDataset dataset, int series) { felix@2644: Integer key = Integer.valueOf(series); felix@2644: Double old = seriesMinimumX.get(key); felix@2644: felix@2644: if (old != null) { felix@2644: return old.doubleValue(); felix@2644: } felix@2644: felix@2644: logger.debug("Compute minimum of Series: " + series); felix@2644: felix@2644: double min = Double.MAX_VALUE; felix@2644: felix@2644: for (int i = 0, n = dataset.getItemCount(series); i < n; i++) { felix@2644: double tmpValue = dataset.getXValue(series, i); felix@2644: felix@2644: if (tmpValue < min) { felix@2644: min = tmpValue; felix@2644: } felix@2644: } felix@2644: felix@2644: seriesMinimumX.put(key, Double.valueOf(min)); felix@2644: felix@2644: return min; felix@2644: } felix@2644: ingo@2074: public boolean isMinimum(XYDataset dataset, int series, int item) { ingo@2074: return dataset.getYValue(series, item) == getMinimum(dataset, series); ingo@2074: } ingo@2074: ingo@2074: ingo@2074: public double getMinimum(XYDataset dataset, int series) { ingo@2074: Integer key = Integer.valueOf(series); ingo@2075: Double old = seriesMinimum.get(key); ingo@2074: ingo@2074: if (old != null) { ingo@2074: return old.doubleValue(); ingo@2074: } ingo@2074: ingo@2074: logger.debug("Compute minimum of Series: " + series); ingo@2074: ingo@2074: double min = Double.MAX_VALUE; ingo@2074: ingo@2074: for (int i = 0, n = dataset.getItemCount(series); i < n; i++) { ingo@2074: double tmpValue = dataset.getYValue(series, i); ingo@2074: ingo@2074: if (tmpValue < min) { ingo@2074: min = tmpValue; ingo@2074: } ingo@2074: } ingo@2074: ingo@2074: seriesMinimum.put(key, Double.valueOf(min)); ingo@2074: ingo@2074: return min; ingo@2074: } ingo@2074: ingo@2074: ingo@2074: public boolean isMaximum(XYDataset dataset, int series, int item) { ingo@2074: return dataset.getYValue(series, item) == getMaximum(dataset, series); ingo@2074: } ingo@2074: ingo@2074: felix@2644: // ingo@2074: public double getMaximum(XYDataset dataset, int series) { ingo@2074: Integer key = Integer.valueOf(series); ingo@2075: Double old = seriesMaximum.get(key); ingo@2074: ingo@2074: if (old != null) { ingo@2074: return old.doubleValue(); ingo@2074: } ingo@2074: ingo@2074: logger.debug("Compute maximum of Series: " + series); ingo@2074: ingo@2074: double max = -Double.MAX_VALUE; ingo@2074: ingo@2074: for (int i = 0, n = dataset.getItemCount(series); i < n; i++) { ingo@2074: double tmpValue = dataset.getYValue(series, i); ingo@2074: ingo@2074: if (tmpValue > max) { ingo@2074: max = tmpValue; ingo@2074: } ingo@2074: } ingo@2074: ingo@2074: seriesMaximum.put(key, Double.valueOf(max)); ingo@2074: ingo@2074: return max; ingo@2074: } ingo@2074: } ingo@2074: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :