ingo@1115: /* ingo@1115: * Copyright (c) 2010 by Intevation GmbH ingo@1115: * ingo@1115: * This program is free software under the LGPL (>=v2.1) ingo@1115: * Read the file LGPL.txt coming with the software for details ingo@1115: * or visit http://www.gnu.org/licenses/ if it does not exist. ingo@1115: */ ingo@1115: sascha@446: package de.intevation.gnv.chart; sascha@446: sascha@521: import de.intevation.gnv.jfreechart.PolygonDataset; sascha@521: import de.intevation.gnv.jfreechart.PolygonPlot; sascha@521: import de.intevation.gnv.jfreechart.PolygonRenderer; sascha@521: import de.intevation.gnv.jfreechart.PolygonSeries; sascha@446: sascha@521: import de.intevation.gnv.math.AttributedXYColumns; sascha@521: sascha@521: import de.intevation.gnv.raster.Palette; sascha@450: sascha@446: import java.awt.Color; sascha@446: import java.awt.Paint; sascha@446: sascha@521: import java.text.NumberFormat; sascha@446: sascha@521: import java.util.HashSet; sascha@521: import java.util.Locale; sascha@521: import java.util.Map; sascha@446: sascha@446: import org.jfree.chart.JFreeChart; sascha@446: sascha@521: import org.jfree.chart.axis.NumberAxis; sascha@521: import org.jfree.chart.axis.SymbolAxis; sascha@521: import org.jfree.chart.axis.ValueAxis; sascha@521: sascha@446: import org.jfree.chart.plot.PlotOrientation; sascha@446: sascha@521: import org.jfree.chart.renderer.LookupPaintScale; sascha@446: sascha@446: import org.jfree.chart.title.PaintScaleLegend; ingo@492: import org.jfree.chart.title.TextTitle; sascha@446: ingo@841: import org.jfree.data.Range; ingo@841: sascha@446: import org.jfree.ui.RectangleEdge; sascha@446: import org.jfree.ui.RectangleInsets; sascha@446: sascha@446: /** ingo@817: * This class represents a 2D chart containing polygon data. ingo@817: * ingo@767: * @author Ingo Weinzierl ingo@767: * @author Sascha L. Teichmann sascha@446: */ sascha@446: public class VerticalCrossSectionChart sascha@446: implements Chart sascha@446: { ingo@767: /** ingo@767: * Lookup class for retrieving a color which corresponds to a specific ingo@767: * integer value. ingo@767: */ sascha@446: public static final class PalettePaintLookup sascha@446: implements PolygonRenderer.PaintLookup sascha@446: { ingo@767: /** ingo@767: * Object storing information about value ranges and its coresponding ingo@767: * colors and descriptions. ingo@767: */ sascha@521: private Palette palette; ingo@767: ingo@767: /** ingo@767: * Map containing some special Paint like ground fillcolor. ingo@767: */ sascha@521: private Map special; sascha@446: ingo@767: /** ingo@767: * Constructor ingo@767: * ingo@767: * @param palette See {@link #palette} ingo@767: */ sascha@446: public PalettePaintLookup(Palette palette) { sascha@521: this(palette, null); sascha@521: } sascha@521: ingo@767: /** ingo@767: * Constructor ingo@767: * ingo@767: * @param palette See {@link #palette} ingo@767: * @param special See {@link #special} ingo@767: */ sascha@521: public PalettePaintLookup( sascha@778: Palette palette, sascha@521: Map special sascha@521: ) { sascha@446: this.palette = palette; sascha@521: this.special = special; sascha@446: } sascha@446: ingo@767: /** ingo@767: * @param index Index of a Paint ingo@767: * ingo@767: * @return Paint object for a given index. ingo@767: */ sascha@446: public Paint getPaint(int index) { sascha@521: if (special != null) { sascha@521: Paint paint = special.get(index); sascha@521: if (paint != null) { sascha@521: return paint; sascha@521: } sascha@521: } sascha@446: return index < 0 sascha@446: ? Color.black sascha@446: : palette.getColor(index); sascha@446: } sascha@446: } // class PalettePaintLookup sascha@446: ingo@767: /** sascha@778: * This class is used to turn labels which represent a number into a ingo@767: * specific format. ingo@767: */ sascha@450: public static class LocalizedLabelGenerator sascha@450: extends PolygonRenderer.DefaultLabelGenerator sascha@450: { ingo@767: /** ingo@767: * NumberFormat which is used to turn a number into a ingo@767: * specific format. ingo@767: */ sascha@450: protected NumberFormat format; sascha@450: ingo@767: /** ingo@767: * Constructor ingo@767: */ sascha@450: public LocalizedLabelGenerator() { sascha@450: } sascha@450: ingo@767: /** sascha@778: * Constructor ingo@767: * ingo@767: * @param format See {@link #format} ingo@767: */ sascha@450: public LocalizedLabelGenerator(NumberFormat format) { sascha@450: this.format = format; sascha@450: } sascha@450: ingo@767: /** ingo@767: * If label is a Number, it is turned into a format ingo@767: * specified by {@link #format}. ingo@767: * ingo@767: * @param label Label to format. ingo@767: * ingo@767: * @return String representation of label. ingo@767: */ ingo@767: @Override sascha@450: protected String toString(Object label) { sascha@450: return label instanceof Number sascha@450: ? format.format(((Number)label).doubleValue()) sascha@450: : super.toString(label); sascha@450: } sascha@450: } // class LocalizedLabelGenerator sascha@450: ingo@841: public static double MARGIN_TOP = 0.05d; ingo@842: public static double MARGIN_BOTTOM = 0.00d; ingo@842: public static double MARGIN_LEFT = 0.00d; ingo@841: public static double MARGIN_RIGHT = 0.05d; ingo@841: ingo@767: /** ingo@767: * JFreeChart object stored at this place after chart creation. ingo@767: */ sascha@446: protected JFreeChart chart; sascha@446: ingo@767: /** ingo@767: * Stores {@link de.intevation.gnv.jfreechart.PolygonDataset} which is used ingo@767: * to create a vertical cross chart. ingo@767: */ sascha@446: protected AttributedXYColumns columns; ingo@767: ingo@767: /** ingo@767: * Map which contains colors to fill polygons draw by this chart. ingo@767: */ sascha@521: protected Map special; ingo@767: ingo@767: /** ingo@767: * Object used to map value intervals to specific colors and descriptions. ingo@767: */ sascha@446: protected Palette palette; ingo@767: ingo@767: /** ingo@767: * Locale object used for i18n support. ingo@767: */ sascha@446: protected Locale locale; ingo@767: ingo@767: /** ingo@767: * Labels for decorating the chart. ingo@767: */ ingo@492: protected ChartLabels labels; sascha@446: ingo@767: /** ingo@767: * Default Constructor ingo@767: */ sascha@446: public VerticalCrossSectionChart() { sascha@446: } sascha@446: ingo@767: /** ingo@767: * Constructor for VerticalCrossSectionChart creation. ingo@767: * ingo@767: * @param columns See {@link #columns} ingo@767: * @param palette See {@link #palette} ingo@767: * @param locale See {@link #locale} ingo@767: * @param labels See {@link #labels} ingo@767: */ sascha@446: public VerticalCrossSectionChart( sascha@446: AttributedXYColumns columns, sascha@446: Palette palette, ingo@492: Locale locale, ingo@492: ChartLabels labels sascha@446: ) { sascha@521: this(columns, palette, null, locale, labels); sascha@521: } sascha@521: ingo@767: /** ingo@767: * Constructor for VerticalCrossSectionChart creation. ingo@767: * ingo@767: * @param columns See {@link #columns} ingo@767: * @param palette See {@link #palette} ingo@767: * @param special See {@link #special} ingo@767: * @param locale See {@link #locale} ingo@767: * @param labels See {@link #labels} ingo@767: */ sascha@521: public VerticalCrossSectionChart( sascha@521: AttributedXYColumns columns, sascha@521: Palette palette, sascha@521: Map special, sascha@521: Locale locale, sascha@521: ChartLabels labels sascha@521: ) { sascha@446: this.columns = columns; sascha@446: this.palette = palette; sascha@521: this.special = special; sascha@446: this.locale = locale; ingo@492: this.labels = labels; sascha@446: } sascha@446: ingo@767: /** ingo@767: * This method is used to create a JFreeChart from this object. A 2D plot is ingo@767: * drawn and a legend panel is created containing each value range. ingo@767: * ingo@767: * @return JFreeChart object ingo@767: */ sascha@446: protected JFreeChart createChart() { sascha@446: ingo@492: boolean legendB = false; ingo@492: boolean tooltips = false; ingo@492: boolean urls = false; sascha@446: sascha@446: PlotOrientation po = PlotOrientation.HORIZONTAL; sascha@446: PolygonDataset data = columns.getPolygonDataset(); sascha@446: sascha@449: HashSet usedColors = new HashSet(); sascha@449: sascha@449: for (int i = data.getSeriesCount()-1; i >= 0; --i) { sascha@449: PolygonSeries ps = data.getSeries(i); sascha@449: Integer fill = (Integer)ps.getAttribute("fill"); sascha@521: if (fill != null sascha@521: && (special != null && !special.containsKey(fill))) { sascha@449: usedColors.add(fill); sascha@449: } sascha@449: } sascha@449: sascha@450: NumberFormat format = NumberFormat.getInstance(locale); sascha@450: format.setMinimumFractionDigits(0); sascha@450: format.setMaximumFractionDigits(2); sascha@450: sascha@446: PolygonRenderer renderer = new PolygonRenderer( sascha@521: new PalettePaintLookup(palette, special), sascha@450: new LocalizedLabelGenerator(format)); sascha@446: ingo@492: ValueAxis domainAxis = new NumberAxis(this.labels.getDomainAxisLabel()); ingo@492: ValueAxis rangeAxis = new NumberAxis(this.labels.getRangeAxisLabel()); sascha@446: sascha@446: PolygonPlot plot = new PolygonPlot( sascha@446: data, sascha@446: renderer, sascha@446: domainAxis, sascha@446: rangeAxis, sascha@446: null); sascha@446: ingo@534: plot.setOutlinePaint(Color.WHITE); ingo@534: sascha@449: String [] labels = new String[usedColors.size()]; sascha@449: sascha@446: int colors = palette.getSize(); sascha@446: LookupPaintScale lookupPaint = sascha@449: new LookupPaintScale(-0.5d, labels.length-0.5d, Color.white); sascha@446: sascha@446: Color color = null; sascha@446: sascha@449: for (int i = 0, j = labels.length-1; i < colors && j >= 0; i++) { sascha@449: if (usedColors.contains(i)) { sascha@449: Palette.Entry entry = palette.getEntryByIndex(i); sascha@449: color = entry.getColor(); sascha@449: labels[j] = entry.getDescription(); sascha@449: lookupPaint.add(j-0.5d, color); sascha@449: --j; sascha@449: } sascha@446: } sascha@446: sascha@446: JFreeChart chart = new JFreeChart( ingo@492: this.labels.getTitle(), sascha@446: JFreeChart.DEFAULT_TITLE_FONT, sascha@446: plot, sascha@446: legendB); sascha@446: sascha@446: chart.removeLegend(); ingo@492: chart.addSubtitle(new TextTitle(this.labels.getSubtitle())); sascha@446: ingo@492: SymbolAxis scale = new SymbolAxis(this.labels.getParameterName(), labels); sascha@449: scale.setRange(-1.5d, labels.length+0.5d); sascha@446: scale.setGridBandsVisible(false); sascha@446: scale.setPlot(plot); sascha@446: sascha@446: PaintScaleLegend legend = new PaintScaleLegend( sascha@446: lookupPaint, scale); sascha@446: legend.setMargin(new RectangleInsets(3d, 10d, 3d, 10d)); sascha@446: legend.setPosition(RectangleEdge.LEFT); sascha@446: legend.setAxisOffset(5d); sascha@446: sascha@446: chart.addSubtitle(legend); sascha@446: ingo@841: // XXX Workaround, because Axes labels are cut at the ingo@841: // left/right/top/bottom edge. The following lines add a white border ingo@841: // between data area and plot border. ingo@842: // see http://www.jfree.org/phpBB2/viewtopic.php?f=3&t=22177&start=0&hilit=axis+labels+cut ingo@841: ValueAxis xAxis = plot.getDomainAxis(); ingo@841: Range xRange = xAxis.getRange(); ingo@841: xRange = Range.expand(xRange, MARGIN_LEFT, MARGIN_RIGHT); ingo@841: xAxis.setRange(xRange); ingo@841: plot.setDomainAxis(xAxis); ingo@841: ingo@841: ValueAxis yAxis = plot.getRangeAxis(); ingo@841: Range yRange = yAxis.getRange(); ingo@841: yRange = Range.expand(yRange, MARGIN_BOTTOM, MARGIN_TOP); ingo@841: yAxis.setRange(yRange); ingo@841: plot.setRangeAxis(yAxis); ingo@841: ingo@842: chart.setPadding(new RectangleInsets(10d, 10d, 10d, 10d)); ingo@842: sascha@446: return chart; sascha@446: } sascha@446: ingo@767: /** ingo@767: * @see de.intevation.gnv.chart.Chart#generateChart() ingo@767: */ sascha@446: public JFreeChart generateChart() { sascha@446: if (chart == null) { sascha@446: chart = createChart(); sascha@446: } ingo@527: sascha@446: return chart; sascha@446: } ingo@527: ingo@767: /** ingo@767: * Set the background paint of {@link #chart}. sascha@803: * @param paint ingo@767: */ ingo@527: public void setBackgroundPaint(Paint paint) { ingo@527: chart.setBackgroundPaint(paint); ingo@527: } sascha@446: } sascha@446: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :