ingo@2074: package de.intevation.flys.jfree;
ingo@2074: 
ingo@2591: import java.awt.Color;
christian@3464: import java.awt.Font;
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: 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;
felix@2653: import org.jfree.data.xy.XYSeries;
felix@2649: import org.jfree.data.xy.XYSeriesCollection;
christian@3464: import org.jfree.text.TextUtilities;
ingo@2074: import org.jfree.ui.RectangleEdge;
felix@3023: import org.jfree.ui.TextAnchor;
ingo@2074: import org.jfree.util.BooleanList;
ingo@2074: import org.jfree.util.ShapeUtilities;
ingo@2074: 
felix@2642: /**
felix@2647:  * Renderer with additional the additional functionality of renderering minima
felix@2647:  * and/or maxima of dataseries contained in datasets.
felix@2642:  */
ingo@2074: public class EnhancedLineAndShapeRenderer extends XYLineAndShapeRenderer {
ingo@2074: 
christian@3464:     /**
christian@3464:      *
christian@3464:      */
christian@3464:     private static final long serialVersionUID = 1L;
christian@3464: 
felix@2647:     /** Own logger. */
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;
felix@2648:     protected BooleanList showLineLabel;
ingo@2074: 
ingo@2074:     protected Map<Integer, Double> seriesMinimum;
felix@2644:     protected Map<Integer, Double> seriesMinimumX;
ingo@2074:     protected Map<Integer, Double> seriesMaximum;
ingo@2074: 
felix@2656:     protected Map<Integer, Font> lineLabelFonts;
felix@2660:     protected Map<Integer, Color> lineLabelTextColors;
felix@2660:     protected BooleanList showLineLabelBG;
felix@2660:     protected Map<Integer, Color> lineLabelBGColors;
felix@2656: 
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();
felix@2648:         this.showLineLabel         = new BooleanList();
felix@2660:         this.showLineLabelBG       = new BooleanList();
ingo@2074:         this.seriesMinimum         = new HashMap<Integer, Double>();
ingo@2074:         this.seriesMaximum         = new HashMap<Integer, Double>();
felix@2644:         this.seriesMinimumX        = new HashMap<Integer, Double>();
felix@2656:         this.lineLabelFonts        = new HashMap<Integer, Font>();
felix@2660:         this.lineLabelTextColors   = new HashMap<Integer, Color>();
felix@2660:         this.lineLabelBGColors     = new HashMap<Integer, Color>();
felix@2660:     }
felix@2660: 
felix@2660: 
felix@3015:     /**
felix@3015:      * Draw a background-box of a text to render.
felix@3015:      * @param g2 graphics device to use
felix@3015:      * @param text text to draw
felix@3015:      * @param textX x-position for text
felix@3015:      * @param textY y-position for text
felix@3015:      * @param bgColor color to fill box with.
felix@3015:      */
felix@3015:     public static void drawTextBox(Graphics2D g2,
felix@3015:         String text, float textX, float textY, Color bgColor
felix@3015:     ) {
felix@2660:         Rectangle2D hotspotBox = g2.getFontMetrics().getStringBounds(text, g2);
felix@2660:         float w = (float) hotspotBox.getWidth(), h = (float) hotspotBox.getHeight();
felix@2660:         hotspotBox.setRect(textX, textY-h, w, h);
felix@2660:         Color oldColor = g2.getColor();
felix@2660:         g2.setColor(bgColor);
felix@2660:         g2.fill(hotspotBox);
felix@2660:         g2.setColor(oldColor);
ingo@2074:     }
ingo@2074: 
ingo@2074: 
felix@3023:     /**
felix@3023:      * Whether or not a specific item in a series (maybe the maxima) should
felix@3023:      * be rendered with shape.
felix@3023:      */
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: 
felix@3023:     /**
felix@3023:      * Rectangle used to draw maximums shape.
felix@3023:      */
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: 
felix@3023:     /**
felix@3023:      * Rectangle used to draw minimums shape.
felix@3023:      */
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: 
felix@3395:     /** Get fill paint for the maximum indicators. */
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: 
felix@3395:     /** Get fill paint for the minimum indicators. */
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@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: 
felix@3395:         // 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@2649:         // Draw label of line.
felix@3395:         if (dataset instanceof XYSeriesCollection
felix@3395:             && isShowLineLabel(series)
felix@3395:             && isMinimumX (dataset, series, item)
felix@3395:             ) {
felix@2653:             XYSeries xYSeries = ((XYSeriesCollection) dataset).getSeries(series);
felix@2653:             String waterlevelLabel = (xYSeries instanceof HasLabel)
felix@2653:                 ? ((HasLabel)xYSeries).getLabel()
felix@2653:                 : xYSeries.getKey().toString();
felix@2644:             // TODO Force water of some German rivers to flow direction mountains.
felix@3023: 
felix@2659:             Font oldFont = g2.getFont();
felix@2660: 
felix@2660:             Color oldColor = g2.getColor();
felix@2659:             g2.setFont(this.getLineLabelFont(series));
felix@2660:             g2.setColor(this.getLineLabelTextColor(series));
felix@2660:             g2.setBackground(Color.black);
felix@2660: 
felix@3625:             // Try to always display label if the data is visible.
felix@3625:             if (!isPointInRect(dataArea, xx, yy)) {
felix@3625:                 // Move into the data area.
felix@3625:                 xx = Math.max(xx, dataArea.getMinX());
felix@3625:                 xx = Math.min(xx, dataArea.getMaxX());
felix@3625:                 yy = Math.max(yy, dataArea.getMinY());
felix@3625:                 yy = Math.min(yy, dataArea.getMaxY());
felix@3625:             }
felix@3625: 
felix@3023:             // Move to right until no collisions exist anymore
felix@3023:             Shape hotspot = TextUtilities.calculateRotatedStringBounds(
felix@3395:                 waterlevelLabel, g2, (float)xx, (float)yy-3f,
felix@3395:                 TextAnchor.CENTER_LEFT,
felix@3023:                 0f, TextAnchor.CENTER_LEFT);
felix@3395:             while (JFreeUtil.collides(hotspot, entities,
felix@3395:                                       CollisionFreeLineLabelEntity.class)) {
felix@3023:                 xx += 5f;
felix@3023:                 hotspot = TextUtilities.calculateRotatedStringBounds(
felix@3023:                     waterlevelLabel, g2, (float)xx, (float)yy-3f, TextAnchor.CENTER_LEFT,
felix@3023:                     0f, TextAnchor.CENTER_LEFT);
felix@3023:             }
felix@3023: 
felix@3023:             // Register to avoid collissions.
felix@3023:             entities.add(new CollisionFreeLineLabelEntity(hotspot,
felix@3023:                 1, "", ""));
felix@3023: 
felix@2662:             // Fill background.
felix@2662:             if (isShowLineLabelBG(series)) {
felix@2662:                 drawTextBox(g2, waterlevelLabel, (float)xx, (float)yy-3f,
felix@2662:                     getLineLabelBGColor(series));
felix@2662:             }
felix@2660: 
felix@2649:             g2.drawString(waterlevelLabel, (float)xx, (float)yy-3f);
felix@2660: 
felix@2659:             g2.setFont(oldFont);
felix@2660:             g2.setColor(oldColor);
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: 
felix@3395:         // 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: 
felix@3023:     /**
felix@3023:      * Sets whether or not the minimum should be rendered with shape.
felix@3023:      */
ingo@2074:     public void setIsMinimumShapeVisisble(int series, boolean isVisible) {
ingo@2074:         this.isMinimumShapeVisible.setBoolean(series, isVisible);
ingo@2074:     }
ingo@2074: 
ingo@2074: 
felix@3395:     /**
felix@3395:      * Whether or not the minimum should be rendered with shape.
felix@3395:      */
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: 
felix@3395:     /**
felix@3395:      * Sets whether or not the maximum should be rendered with shape.
felix@3395:      */
ingo@2074:     public void setIsMaximumShapeVisible(int series, boolean isVisible) {
ingo@2074:         this.isMaximumShapeVisible.setBoolean(series, isVisible);
ingo@2074:     }
ingo@2074: 
ingo@2074: 
felix@3395:     /**
felix@3395:      * Whether or not the maximum should be rendered with shape.
felix@3395:      */
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: 
felix@2648:     /** Whether or not a label should be shown for series. */
felix@2648:     public boolean isShowLineLabel(int series) {
felix@2648:         if (this.showLineLabel.size() <= series) {
felix@2648:             return false;
felix@2648:         }
felix@2648: 
felix@2648:         return showLineLabel.getBoolean(series);
felix@2648:     }
felix@2648: 
felix@2648: 
felix@2648:     /** Sets whether or not a label should be shown for series. */
felix@2648:     public void setShowLineLabel(boolean showLineLabel, int series) {
felix@2648:         this.showLineLabel.setBoolean(series, showLineLabel);
felix@2648:     }
felix@2648: 
felix@3395: 
felix@2660:     /** Whether or not a label should be shown for series. */
felix@2660:     public boolean isShowLineLabelBG(int series) {
felix@2660:         if (this.showLineLabelBG.size() <= series) {
felix@2660:             return false;
felix@2660:         }
felix@2660: 
felix@2660:         return showLineLabelBG.getBoolean(series);
felix@2660:     }
felix@2660: 
felix@3395: 
felix@2660:     public void setShowLineLabelBG(int series, boolean doShow) {
felix@2660:         this.showLineLabelBG.setBoolean(series, doShow);
felix@2660:     }
felix@2660: 
felix@2660:     public Color getLineLabelBGColor(int series) {
felix@2660:         if (this.lineLabelBGColors.size() <= series) {
felix@2660:             return null;
felix@2660:         }
felix@2660: 
felix@2660:         return this.lineLabelBGColors.get(series);
felix@2660:     }
felix@2660: 
felix@2660:     public void setLineLabelBGColor(int series, Color color) {
felix@2660:         this.lineLabelBGColors.put(series, color);
felix@2660:     }
felix@2660: 
felix@2660:     public Color getLineLabelTextColor(int series) {
felix@2660:         if (this.lineLabelTextColors.size() <= series) {
felix@2660:             return null;
felix@2660:         }
felix@2660: 
felix@2660:         return this.lineLabelTextColors.get(series);
felix@2660:     }
felix@2660: 
felix@2660:     public void setLineLabelTextColor(int series, Color color) {
felix@2660:         this.lineLabelTextColors.put(series, color);
felix@2660:     }
felix@2660: 
felix@2656:     public void setLineLabelFont(Font font, int series) {
felix@2656:         this.lineLabelFonts.put(series, font);
felix@2656:     }
felix@2656: 
felix@2656:     public Font getLineLabelFont(int series) {
felix@2656:         return this.lineLabelFonts.get(series);
felix@2656:     }
felix@2656: 
ingo@2074: 
felix@2647:     /**
felix@2647:      * True if the given item of given dataset has the smallest
felix@2647:      * X value within this set.
felix@2647:      */
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@2647: 
felix@2647:     /**
felix@2647:      * Get Minimum X Value of a given series in a dataset.
felix@2647:      * The value is stored for later use if queried the first time.
felix@2647:      */
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: 
felix@2647: 
felix@2647:     /**
felix@2647:      * True if the given item of given dataset has the smallest
felix@2647:      * Y value within this set.
felix@2647:      */
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: 
felix@2647:     /**
felix@2647:      * Get Minimum Y Value of a given series in a dataset.
felix@2647:      * The value is stored for later use if queried the first time.
felix@2647:      */
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: 
felix@2647:     /**
felix@2647:      * True if the given item of given dataset has the biggest
felix@2647:      * Y value within this set.
felix@2647:      */
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@2647:     /**
felix@2647:      * Get maximum Y Value of a given series in a dataset.
felix@2647:      * The value is stored for later use if queried the first time.
felix@2647:      */
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 :