tim@65: /** tim@65: * Title: ChartFactory, $Header: /share/gdi/SDI-Suite/Repository/projekte/BSH-GDI/genericViewer/src/main/java/de/conterra/bsh/gdi/gnviewer/output/chart/ChartFactory.java,v 1.8 2007/12/21 12:31:15 blume Exp $ tim@65: * Source: $Source: /share/gdi/SDI-Suite/Repository/projekte/BSH-GDI/genericViewer/src/main/java/de/conterra/bsh/gdi/gnviewer/output/chart/ChartFactory.java,v $ tim@65: * created by: Stefan Blume (blume) tim@65: * erstellt am: 06.12.2007 tim@65: * Copyright: con terra GmbH, 2005 tim@65: * tim@65: * modified by: $Author: blume $ tim@65: * modified on: $Date: 2007/12/21 12:31:15 $ tim@65: * Version: $Revision: 1.8 $ tim@65: * TAG: $Name: $ tim@65: * locked from: $Locker: $ tim@65: * CVS State: $State: Exp $ tim@65: * Project: $ProjectName$ tim@65: */ tim@65: package de.intevation.gnv.chart; tim@65: tim@65: import java.awt.Color; tim@65: import java.awt.Font; tim@171: import java.awt.Transparency; tim@65: import java.awt.image.BufferedImage; tim@65: import java.io.IOException; tim@67: import java.io.OutputStream; tim@67: import java.util.Collection; tim@65: import java.util.Date; tim@67: import java.util.Iterator; tim@65: tim@65: import org.apache.log4j.Logger; tim@65: import org.jfree.chart.JFreeChart; tim@65: import org.jfree.chart.axis.AxisLocation; tim@65: import org.jfree.chart.axis.DateAxis; tim@65: import org.jfree.chart.axis.NumberAxis; tim@65: import org.jfree.chart.axis.NumberTickUnit; tim@65: import org.jfree.chart.encoders.KeypointPNGEncoderAdapter; tim@65: import org.jfree.chart.plot.PlotOrientation; tim@65: import org.jfree.chart.plot.XYPlot; tim@65: import org.jfree.chart.renderer.xy.StandardXYItemRenderer; tim@65: import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; tim@65: import org.jfree.data.time.Minute; tim@65: import org.jfree.data.time.TimeSeries; tim@65: import org.jfree.data.time.TimeSeriesCollection; tim@65: import org.jfree.data.xy.XYDataset; tim@65: import org.jfree.ui.RectangleInsets; tim@65: tim@67: import de.intevation.gnv.chart.exception.TechnicalChartException; tim@67: import de.intevation.gnv.geobackend.base.Result; tim@335: import de.intevation.gnv.state.describedata.KeyValueDescibeData; tim@217: import de.intevation.gnv.timeseries.gap.TimeGap; tim@65: tim@65: /** tim@65: * The class ChartFactory fulfills the following purposes: tim@65: *
    tim@65: *
  1. tim@65: *
tim@65: * tim@65: * @author blume tim@65: * @version 1.0 tim@65: * @serial 1.0 tim@65: * @see tim@65: * @since 06.12.2007 17:25:59 tim@65: */ tim@65: public class ChartFactory { tim@65: tim@171: /** tim@171: * Default Logging instance tim@171: */ tim@171: private static Logger sLogger = Logger.getLogger(ChartFactory.class); tim@171: private static boolean sDebug = sLogger.isDebugEnabled(); tim@225: tim@225: private final static long NOTIMEGAP = Long.MAX_VALUE - 1000; tim@225: // Minus 1000 damit es bei Additionen keinen Überlauf gibt tim@65: tim@171: public synchronized void createSimpleTimeSeriesChart( tim@171: ChartLabels pLabels, tim@171: ChartStyle pStyle, tim@171: Collection parameters, tim@171: Collection measurements, tim@171: OutputStream outputStream, tim@217: Collection resultSet, tim@217: Collection timeGaps) tim@171: throws IOException, tim@171: TechnicalChartException { tim@171: if (sDebug) tim@171: sLogger.debug("createSimpleTimeSeriesChart()"); tim@171: int lLowerLevel = Integer.MIN_VALUE; tim@171: int lUpperLevel = Integer.MAX_VALUE; tim@171: if (pStyle.isUseUpperDataLevel() tim@171: && pStyle.getUpperLevel() < Integer.MAX_VALUE) { tim@171: lUpperLevel = pStyle.getUpperLevel(); tim@171: } tim@171: if (pStyle.isUseLowerDataLevel() tim@171: && pStyle.getLowerLevel() > Integer.MIN_VALUE) { tim@171: lLowerLevel = pStyle.getLowerLevel(); tim@171: } tim@171: if (sDebug) tim@171: sLogger.debug(" vor createDataset()"); tim@171: XYDataset lSet = this.createDataset(resultSet, lUpperLevel, tim@217: lLowerLevel, parameters, measurements,timeGaps); tim@171: if (sDebug) tim@171: sLogger.debug(" nach createDataset()"); tim@171: final Color[] color = { Color.black, Color.red, Color.green, Color.blue }; tim@171: DateAxis domain = new DateAxis(pLabels.getDomainAxisLabel()); tim@65: NumberAxis axis; tim@65: StandardXYItemRenderer renderer = new StandardXYItemRenderer(); tim@171: XYPlot plot = new XYPlot(); tim@171: // Global settings tim@65: tim@171: plot.setOrientation(PlotOrientation.VERTICAL); tim@171: plot.setBackgroundPaint(Color.lightGray); tim@171: plot.setDomainGridlinePaint(Color.white); tim@171: plot.setRangeGridlinePaint(Color.white); tim@171: plot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0)); tim@171: // plot.getRangeAxis().setFixedDimension(10.0); tim@171: plot.setDomainAxis(domain); tim@171: plot.setDomainAxisLocation(AxisLocation.BOTTOM_OR_LEFT); tim@171: if (parameters.size() == 1) { tim@171: KeyValueDescibeData parameter = parameters.iterator().next(); tim@171: axis = new NumberAxis(parameter.getValue()); tim@171: if (parameter.getValue().contains("richtung")) { tim@171: NumberAxis axis1 = new NumberAxis((parameter tim@171: .getValue()));// ,new Range(0.0,360.0)); tim@171: axis1.setTickUnit(new NumberTickUnit(30.0)); tim@171: axis1.setUpperBound(360.0); tim@171: axis1.setLowerBound(0.0); tim@171: // axis1.setDisplayRange(0.0,360.0); tim@171: plot.setRangeAxis(axis1); tim@171: } else { tim@171: axis.setFixedDimension(10.0); tim@171: axis.setAutoRangeIncludesZero(false); tim@171: plot.setRangeAxis(axis); tim@65: } tim@171: axis.configure(); tim@171: plot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT); tim@171: plot.setRenderer(renderer); tim@171: plot.setDataset(lSet); tim@171: } else { tim@171: // Individual settings for different parameters tim@171: for (int i = 0; i < lSet.getSeriesCount(); i++) { tim@65: tim@171: plot.setDataset(i, getDataset((TimeSeriesCollection) lSet, i)); tim@171: Color mColor = color[i % color.length]; // zyklische Farbvergabe tim@171: mColor = color[0]; tim@171: // if ( pParameterId.length==1){ tim@65: tim@171: if (((String) lSet.getSeriesKey(i)).contains("richtung")) { tim@171: NumberAxis axis1 = new NumberAxis(((String) lSet tim@171: .getSeriesKey(i)));// ,new Range(0.0,360.0)); tim@171: axis1.setTickUnit(new NumberTickUnit(30.0)); tim@171: // axis1.setDisplayRange(0.0,360.0); tim@171: axis1.setLabelPaint(mColor); tim@171: axis1.setTickLabelPaint(mColor); tim@171: axis1.setUpperBound(360.0); tim@171: axis1.setLowerBound(0.0); tim@171: plot.setRangeAxis(i, axis1); tim@65: tim@171: } else { tim@171: axis = new NumberAxis((String) lSet.getSeriesKey(i)); tim@171: axis.setFixedDimension(10.0); tim@171: axis.setAutoRangeIncludesZero(false); tim@171: axis.setLabelPaint(mColor); tim@171: axis.setTickLabelPaint(mColor); tim@171: plot.setRangeAxis(i, axis); tim@171: axis.configure(); tim@171: } tim@171: if (i % 2 != 0) tim@171: plot.setRangeAxisLocation(i, AxisLocation.BOTTOM_OR_RIGHT); tim@171: else tim@171: plot.setRangeAxisLocation(i, AxisLocation.BOTTOM_OR_LEFT); tim@171: plot.mapDatasetToRangeAxis(i, i); tim@171: // } tim@171: renderer = new StandardXYItemRenderer(); tim@171: renderer.setSeriesPaint(i, mColor); tim@171: // renderer.setSeriesStroke(i,stroke[j]); tim@171: plot.setRenderer(i, renderer); tim@171: } tim@171: } tim@171: JFreeChart chart = new JFreeChart(pLabels.getTitle(), new Font( tim@171: "SansSerif", Font.BOLD, 24), plot, true); tim@65: tim@171: setStyle(chart, pStyle); tim@171: configureRenderingOptions(chart); tim@171: if (sDebug) tim@171: sLogger.debug(" vor encodeChart()"); tim@65: tim@171: encodeChart(chart, pStyle, outputStream); tim@171: } tim@171: tim@171: private static XYDataset getDataset(TimeSeriesCollection T, int pIndex) { // throws tim@171: // TechnicalChartException{ tim@171: // if (T.getSeriesCount() < pIndex) throw TechnicalChartException(); tim@171: TimeSeriesCollection TSC = new TimeSeriesCollection(); tim@171: TSC.addSeries(T.getSeries(pIndex)); tim@171: return TSC; tim@171: } tim@171: tim@171: private void configureRenderingOptions(JFreeChart pJfreechart) { tim@171: org.jfree.chart.renderer.xy.XYItemRenderer xyitemrenderer = ((XYPlot) pJfreechart tim@171: .getPlot()).getRenderer(); tim@171: if (xyitemrenderer instanceof XYLineAndShapeRenderer) { tim@171: XYLineAndShapeRenderer xylineandshaperenderer = (XYLineAndShapeRenderer) xyitemrenderer; tim@171: xylineandshaperenderer.setBaseShapesVisible(true); tim@171: xylineandshaperenderer.setBaseShapesFilled(true); tim@171: } tim@171: } tim@171: tim@171: private void setStyle(JFreeChart pJfreechart, ChartStyle pStyle) { tim@171: if (sDebug) tim@171: sLogger.debug("setStyle()"); tim@171: pJfreechart.setBackgroundPaint(pStyle.getCanvasColor()); tim@171: XYPlot xyplot = (XYPlot) pJfreechart.getPlot(); tim@171: xyplot.setBackgroundPaint(pStyle.getPlotBackgroundColor()); tim@171: xyplot.setDomainGridlinePaint(pStyle.getDomainGridlineColor()); tim@171: xyplot.setRangeGridlinePaint(pStyle.getRangeGridlineColor()); tim@171: tim@171: Insets lOffsets = pStyle.getAxisOffset(); tim@171: RectangleInsets lRectangleInsets = new RectangleInsets(lOffsets.mUpper, tim@171: lOffsets.mLeft, lOffsets.mLower, lOffsets.mRight); tim@171: xyplot.setAxisOffset(lRectangleInsets); tim@171: xyplot.setDomainCrosshairVisible(pStyle.isDomainCrosshairVisible()); tim@171: xyplot.setRangeCrosshairVisible(pStyle.isRangeCrosshairVisible()); tim@171: tim@171: } tim@171: tim@171: private TimeSeries createTimeSeries(String pTimeSeriesName, tim@171: Collection resultSet, tim@171: int lUpperCut, int lLowerCut, tim@217: int pStart, int pEnd, long maxGap) tim@171: throws TechnicalChartException { tim@171: if (sDebug) tim@171: sLogger.debug("createTimeSeries()"); tim@171: Result lRow0, lRow1; tim@171: tim@171: Date lDate = null, lDate0 = null; tim@171: tim@171: TimeSeries lTimeseries = new TimeSeries(pTimeSeriesName, tim@171: org.jfree.data.time.Minute.class); tim@171: try { tim@217: long lDateDiff = 0; tim@171: double lValue = 0; tim@217: tim@171: int i = 0; tim@171: Iterator resultIterator = resultSet.iterator(); tim@171: while (resultIterator.hasNext()) { tim@171: Result lRow = resultIterator.next(); tim@171: if (i >= pStart + 1 && i <= pEnd) { tim@171: if (i == pStart + 1) { tim@171: lRow0 = lRow;// tim@171: lDate = lRow0.getDate("XORDINATE"); tim@171: lDate0 = lDate; tim@171: lValue = lRow0.getDouble("YORDINATE"); tim@171: if (lValue > lLowerCut && lValue < lUpperCut) { tim@171: // lTimeseries.addOrUpdate(new Minute(lDate), tim@171: // lValue); tim@171: lTimeseries.add(new Minute(lDate), lValue); tim@171: } tim@171: } tim@171: // for (int i = pStart+1; i <= pEnd; i++) { tim@171: lRow1 = lRow; tim@171: lDate = lRow1.getDate("XORDINATE"); tim@171: lValue = lRow1.getDouble("YORDINATE"); tim@171: lDateDiff = lDate.getTime() - lDate0.getTime(); tim@171: if (lDateDiff > maxGap) { tim@171: // add 1 minute in millisecs to left hand side Date tim@171: // and insert Dummy to break line tim@171: lDate0.setTime((lDate0.getTime() + 60000)); tim@171: lTimeseries.addOrUpdate(new Minute(lDate0), null); tim@171: lTimeseries.addOrUpdate(new Minute(lDate), lValue); tim@171: // lTimeseries.add(new Minute(lDate0), null); tim@171: } else if (lDateDiff == 0) { tim@171: if (sDebug) tim@171: sLogger.debug("Datediff: " + lDateDiff tim@171: + " bei index : " + i + " Datum : " tim@171: + lDate + " " + lDate0); tim@171: } else if (lValue > lLowerCut && lValue < lUpperCut) { tim@171: lTimeseries.addOrUpdate(new Minute(lDate), lValue); tim@171: // lTimeseries.add(new Minute(lDate), lValue); tim@171: } tim@171: lRow0 = lRow1; tim@171: lDate0 = lDate; tim@171: } else if (i > pEnd) { tim@171: return lTimeseries; tim@171: } tim@171: i++; tim@171: } tim@171: tim@171: } catch (OutOfMemoryError e) { tim@171: sLogger.error(e.getMessage(), e); tim@171: return lTimeseries; tim@171: tim@171: } catch (Exception e) { // TechnicalChartException tim@171: sLogger.error(e.getMessage(), e); tim@171: } finally { tim@171: } tim@171: tim@171: return lTimeseries; tim@171: } tim@171: tim@171: private XYDataset createDataset(Collection resultSet, tim@171: int lUpperCut, int lLowerCut, tim@171: Collection parameters, tim@217: Collection measurements, tim@217: Collection timeGaps) tim@171: throws TechnicalChartException { tim@171: tim@171: TimeSeriesCollection lTimeSeriesCollection = new TimeSeriesCollection(); tim@171: try { tim@171: Date dStart = null, dEnd = null; tim@171: String break1, break2, break3; tim@171: int mStart = 0; tim@171: int mEnd = 0; tim@171: tim@171: Iterator resultIterator = resultSet.iterator(); tim@171: if (resultIterator.hasNext()) { tim@171: Result row = resultIterator.next(); tim@171: tim@171: break1 = row.getString("GROUP1"); // 2 tim@171: break2 = row.getString("GROUP2"); // 3 tim@171: break3 = row.getString("GROUP3"); // 4 tim@171: dStart = row.getDate("XORDINATE"); tim@171: int i = 1; tim@217: Integer gapID = -1; tim@171: while (resultIterator.hasNext()) { tim@171: row = resultIterator.next(); tim@171: if (!break1.equals(row.getString("GROUP1")) tim@171: || !break2.equals(row.getString("GROUP2")) tim@171: || !break3.equals(row.getString("GROUP3"))) { tim@171: String mTimeSeriesName = findValueTitle(parameters, tim@171: break1) tim@171: + " " tim@171: + findValueTitle(measurements, tim@171: break2) + "m"; tim@217: long maxGap = this.calculateMaxGap(dStart, dEnd,mStart, tim@217: mEnd, gapID.intValue(), tim@217: timeGaps); tim@217: tim@217: tim@171: lTimeSeriesCollection.addSeries(createTimeSeries( tim@171: mTimeSeriesName, resultSet, lUpperCut, tim@217: lLowerCut, mStart, mEnd,maxGap)); tim@171: mStart = i; tim@171: dStart = row.getDate("XORDINATE"); tim@171: break1 = row.getString("GROUP1"); tim@171: break2 = row.getString("GROUP2"); // 3 tim@171: break3 = row.getString("GROUP3"); // 4 tim@171: tim@171: } tim@171: mEnd = i; tim@171: dEnd = row.getDate("XORDINATE"); tim@217: gapID = row.getInteger("GAPID"); tim@171: i = i + 1; tim@171: } tim@171: tim@171: String mTimeSeriesName = findValueTitle(parameters, break1) tim@171: + " " tim@171: + findValueTitle(measurements, break2) tim@171: + "m"; tim@217: long maxGap = this.calculateMaxGap(dStart, dEnd,mStart, tim@217: mEnd,gapID.intValue(), tim@217: timeGaps); tim@171: lTimeSeriesCollection.addSeries(createTimeSeries( tim@171: mTimeSeriesName, resultSet, lUpperCut, lLowerCut, tim@217: mStart, mEnd, maxGap)); tim@193: }else{ tim@217: // Es sind keine Daten vorhanden: Es wird eine Zeitserie eingefügt. tim@193: lTimeSeriesCollection.addSeries(createTimeSeries( tim@193: "", resultSet, lUpperCut, lLowerCut, tim@217: mStart, mEnd, 999999)); tim@171: } tim@171: } catch (Exception e) { tim@171: sLogger.error(e.getMessage(), e); tim@171: } finally { tim@171: } tim@171: return lTimeSeriesCollection; tim@171: } tim@171: tim@217: /** tim@217: * @param dStart tim@217: * @param dEnd tim@217: */ tim@217: private long calculateMaxGap(Date dStart, Date dEnd, int pStart , tim@217: int pEnd, int gapID, tim@217: Collection timeGaps) { tim@217: tim@217: // umgesetzt nach issue 45 tim@217: // Handle Gaps > 0.5% timeserieslength, tim@217: // i.e do not draw here tim@217: long maxGap = (dEnd.getTime() - dStart.getTime()) / 200; tim@217: // 0,5 prozent der Länge tim@217: long timeInterval = this.getTimeGapValue(dStart, dEnd, pStart, tim@217: pEnd, gapID, timeGaps); tim@217: tim@217: if (maxGap < timeInterval){ tim@217: maxGap = timeInterval + 10; tim@217: } tim@217: // if (maxGap < (dEnd.getTime() - dStart.getTime()) tim@217: // / (pEnd - pStart)) tim@217: // maxGap = (dEnd.getTime() - dStart.getTime()) tim@217: // / (pEnd - pStart) + 1000; tim@217: tim@217: return maxGap; tim@217: } tim@217: tim@217: private long getTimeGapValue(Date dStart, Date dEnd, tim@217: int pStart ,int pEnd, tim@217: int gapID, Collection timeGaps){ tim@217: long gap = 0; tim@217: tim@217: if (gapID < 0 || gapID >= 99){ tim@221: tim@221: if (gapID == -1){ // Mesh tim@225: gap = NOTIMEGAP; // Es gibt keine Lücken in Netzen. tim@221: }else if (pEnd-pStart < 60){ tim@221: gap = (3/(pEnd-pStart)) * (dEnd.getTime() - dStart.getTime()); tim@221: } tim@217: }else{ tim@217: tim@217: Iterator it = timeGaps.iterator(); tim@217: tim@217: while (it.hasNext()){ tim@217: TimeGap tempTimeGap = it.next(); tim@217: if (tempTimeGap.getKey() == gapID){ tim@217: int gapValue = tempTimeGap.getValue(); tim@217: String unit = tempTimeGap.getUnit(); tim@217: tim@217: if (unit.equals(TimeGap.TIME_UNIT_MINUTE)){ tim@217: gap = gapValue * TimeGap.MINUTE_IN_MILLIS; tim@217: }else if (unit.equals(TimeGap.TIME_UNIT_HOUR)){ tim@217: gap = gapValue * TimeGap.HOUR_IN_MILLIS; tim@217: }else if (unit.equals(TimeGap.TIME_UNIT_DAY)){ tim@217: gap = gapValue * TimeGap.DAY_IN_MILLIS; tim@217: }else if (unit.equals(TimeGap.TIME_UNIT_WEEK)){ tim@217: gap = gapValue * TimeGap.WEEK_IN_MILLIS; tim@217: }else if (unit.equals(TimeGap.TIME_UNIT_MONTH)){ tim@217: // TODO wie soll das laufen tim@217: gap = gapValue * (TimeGap.DAY_IN_MILLIS *30); tim@217: }else if (unit.equals(TimeGap.TIME_UNIT_YEAR)){ tim@217: // TODO wie soll das laufen für schaltjahre tim@217: gap = gapValue * (TimeGap.DAY_IN_MILLIS *365); tim@217: } tim@217: break; tim@217: } tim@217: } tim@217: } tim@217: tim@217: return gap; tim@217: } tim@217: tim@171: private void encodeChart(JFreeChart pChart, ChartStyle pStyle, tim@171: OutputStream outputStream) throws IOException { tim@171: if (sDebug) tim@171: sLogger.debug("encodeChart()"); tim@171: KeypointPNGEncoderAdapter lEncoder = new KeypointPNGEncoderAdapter(); tim@171: lEncoder.setEncodingAlpha(true); tim@171: tim@171: int lWidth = (int) pStyle.getChartSize().getWidth(); tim@171: int lHeight = (int) pStyle.getChartSize().getHeight(); tim@171: tim@171: BufferedImage lImage = pChart.createBufferedImage(lWidth, lHeight, tim@171: Transparency.BITMASK, null); tim@171: tim@171: lEncoder.encode(lImage, outputStream); tim@171: tim@171: } tim@171: tim@171: private String findValueTitle(Collection values, tim@171: String pMmtId) { tim@171: int id = 0; tim@171: try { tim@82: id = Integer.parseInt(pMmtId); tim@82: } catch (NumberFormatException e) { tim@171: sLogger.warn(e, e); tim@82: return pMmtId; tim@82: } tim@171: tim@82: Iterator it = values.iterator(); tim@171: while (it.hasNext()) { tim@171: KeyValueDescibeData data = it.next(); tim@171: if (id == Integer.parseInt(data.getKey())) { tim@171: return data.getValue(); tim@171: } tim@171: } tim@171: return ""; tim@65: } tim@65: }