ingo@359: package de.intevation.flys.exports; ingo@359: ingo@1690: import java.awt.Font; ingo@1690: ingo@359: import org.apache.log4j.Logger; ingo@359: ingo@375: import org.jfree.chart.JFreeChart; ingo@375: import org.jfree.chart.axis.NumberAxis; felix@1719: import org.jfree.chart.title.TextTitle; ingo@422: import org.jfree.chart.axis.ValueAxis; ingo@375: import org.jfree.chart.plot.XYPlot; ingo@720: import org.jfree.data.Range; ingo@369: import org.jfree.data.xy.XYSeries; ingo@364: ingo@359: import org.w3c.dom.Document; ingo@359: ingo@359: import de.intevation.artifacts.Artifact; ingo@359: ingo@695: import de.intevation.artifactdatabase.state.Facet; ingo@695: ingo@422: import de.intevation.flys.artifacts.FLYSArtifact; sascha@706: ingo@696: import de.intevation.flys.artifacts.model.FacetTypes; felix@1769: import de.intevation.flys.artifacts.model.WKms; ingo@364: import de.intevation.flys.artifacts.model.WQKms; ingo@364: ingo@1677: import de.intevation.flys.jfree.FLYSAnnotation; sascha@1688: felix@1944: import de.intevation.artifactdatabase.state.ArtifactAndFacet; felix@1944: ingo@1667: import de.intevation.flys.utils.FLYSUtils; sascha@1688: import de.intevation.flys.utils.DataUtil; ingo@359: felix@1035: felix@1035: /** ingo@359: * An OutGenerator that generates discharge curves. ingo@359: * ingo@359: * @author Ingo Weinzierl ingo@359: */ ingo@696: public class LongitudinalSectionGenerator ingo@696: extends XYChartGenerator ingo@696: implements FacetTypes ingo@696: { felix@1931: public static enum YAXIS { felix@1931: W(0), felix@1931: Q(1), felix@1931: D(2); felix@1931: protected int idx; felix@1931: private YAXIS(int c) { felix@1931: idx = c; felix@1931: } felix@1931: } felix@1931: felix@1037: /** The logger that is used in this generator. */ ingo@359: private static Logger logger = ingo@359: Logger.getLogger(LongitudinalSectionGenerator.class); ingo@359: felix@1700: /** Key to look up internationalized String for annotations label. */ felix@1041: public static final String I18N_ANNOTATIONS_LABEL = felix@1041: "chart.longitudinal.annotations.label"; felix@1041: felix@1701: /** felix@1701: * Key to look up internationalized String for LongitudinalSection diagrams felix@1701: * titles. felix@1701: */ felix@1701: public static final String I18N_CHART_TITLE = felix@1701: "chart.longitudinal.section.title"; felix@1701: felix@1701: /** felix@1701: * Key to look up internationalized String for LongitudinalSection diagrams felix@1701: * subtitles. felix@1701: */ felix@1701: public static final String I18N_CHART_SUBTITLE = felix@1701: "chart.longitudinal.section.subtitle"; felix@1701: felix@1701: public static final String I18N_XAXIS_LABEL = felix@1701: "chart.longitudinal.section.xaxis.label"; felix@1701: felix@1701: public static final String I18N_YAXIS_LABEL = felix@1701: "chart.longitudinal.section.yaxis.label"; felix@1701: felix@1701: public static final String I18N_2YAXIS_LABEL = felix@1701: "chart.longitudinal.section.yaxis.second.label"; felix@1701: felix@1701: public static final String I18N_CHART_TITLE_DEFAULT = "W-L\u00e4ngsschnitt"; felix@1701: public static final String I18N_XAXIS_LABEL_DEFAULT = "km"; felix@1701: public static final String I18N_YAXIS_LABEL_DEFAULT = "W [NN + m]"; felix@1701: public static final String I18N_2YAXIS_LABEL_DEFAULT = "Q [m\u00b3/s]"; felix@1701: felix@1700: /** Whether or not the plot is inverted (left-right). */ sascha@745: protected boolean inverted; sascha@745: felix@1041: ingo@359: public LongitudinalSectionGenerator() { ingo@359: super(); ingo@369: } ingo@369: ingo@369: sascha@745: public boolean isInverted() { sascha@745: return inverted; sascha@745: } sascha@745: felix@1700: sascha@745: public void setInverted(boolean inverted) { sascha@745: this.inverted = inverted; sascha@745: } ingo@369: felix@1700: felix@1700: /** felix@1700: * Get internationalized title for chart. felix@1700: */ felix@1700: public String getChartTitle() { felix@1701: return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT); felix@1700: } felix@1700: felix@1700: felix@1700: /** felix@1700: * Gets key to look up internationalized String for the charts subtitle. felix@1700: * @return key to look up translated subtitle. felix@1700: */ felix@1700: protected String getChartSubtitleKey() { felix@1701: return I18N_CHART_SUBTITLE; felix@1700: } felix@1700: felix@1700: felix@1700: /** felix@1700: * Add (internationalized) subtitle to chart. felix@1700: * @see getChartSubtitleKey felix@1700: */ ingo@414: @Override ingo@414: protected void addSubtitles(JFreeChart chart) { felix@1700: double[] dist = getRange(); felix@1719: ingo@414: Object[] args = new Object[] { ingo@414: getRiverName(), felix@1719: ingo@414: dist[0], felix@1719: ingo@414: dist[1] ingo@414: }; ingo@414: felix@1700: String subtitle = msg(getChartSubtitleKey(), "", args); ingo@414: chart.addSubtitle(new TextTitle(subtitle)); ingo@414: } ingo@414: ingo@414: felix@1700: /** felix@1700: * Get internationalized label for the x axis. felix@1700: */ ingo@369: protected String getXAxisLabel() { ingo@1667: FLYSArtifact flys = (FLYSArtifact) master; ingo@1667: ingo@1667: return msg( felix@1701: I18N_XAXIS_LABEL, felix@1701: I18N_XAXIS_LABEL_DEFAULT, ingo@1667: new Object[] { FLYSUtils.getRiver(flys).getName() }); ingo@369: } ingo@369: ingo@369: felix@1700: /** felix@1700: * Get internationalized label for the y axis. felix@1700: */ ingo@369: protected String getYAxisLabel() { ingo@1667: FLYSArtifact flys = (FLYSArtifact) master; ingo@1667: ingo@1673: String unit = FLYSUtils.getRiver(flys).getWstUnit().getName(); ingo@1667: ingo@1667: return msg( felix@1701: I18N_YAXIS_LABEL, felix@1701: I18N_YAXIS_LABEL_DEFAULT, ingo@1667: new Object[] { unit }); ingo@369: } ingo@369: ingo@369: felix@1700: /** felix@1931: * Create Axis for given index. felix@1931: * @return axis with according internationalized label. felix@1931: */ felix@1931: @Override felix@1931: protected NumberAxis createYAxis(int index) { felix@1931: Font labelFont = new Font("Tahoma", Font.BOLD, 14); felix@1931: String label = "default"; felix@1931: if (index == YAXIS.W.idx) { felix@1931: label = getYAxisLabel(); felix@1931: } felix@1931: else if (index == YAXIS.Q.idx) { felix@1931: label = msg(get2YAxisLabelKey(), get2YAxisDefaultLabel()); felix@1931: } felix@1931: else if (index == YAXIS.D.idx) { felix@1931: // TODO: diff label felix@1931: label = "TODO: diff"; felix@1931: } felix@1931: NumberAxis axis = new NumberAxis(label); felix@1941: // "Q" Axis shall include 0. felix@1941: if (index == YAXIS.Q.idx) { felix@1941: axis.setAutoRangeIncludesZero(true); felix@1941: } felix@1941: else { felix@1941: axis.setAutoRangeIncludesZero(false); felix@1941: } felix@1931: axis.setLabelFont(labelFont); felix@1931: return axis; felix@1931: } felix@1931: felix@1931: /** felix@1700: * Get default value for the second Y-Axis' label (if no translation was felix@1700: * found). felix@1700: */ felix@1700: protected String get2YAxisDefaultLabel() { felix@1701: return I18N_2YAXIS_LABEL_DEFAULT; felix@1700: } felix@1700: felix@1700: felix@1700: /** felix@1700: * Get key for internationalization of the second Y-Axis' label. felix@1700: */ felix@1700: protected String get2YAxisLabelKey() { felix@1701: return I18N_2YAXIS_LABEL; felix@1700: } felix@1700: felix@1700: felix@1700: /** felix@1931: * Trigger inversion. felix@1700: */ felix@1700: @Override ingo@375: protected void adjustAxes(XYPlot plot) { ingo@375: super.adjustAxes(plot); ingo@422: invertXAxis(plot.getDomainAxis()); ingo@422: } ingo@422: ingo@422: ingo@422: /** ingo@422: * This method inverts the x-axis based on the kilometer information of the ingo@422: * selected river. If the head of the river is at kilometer 0, the axis is ingo@422: * not inverted, otherwise it is. ingo@422: * ingo@422: * @param xaxis The domain axis. ingo@422: */ ingo@422: protected void invertXAxis(ValueAxis xaxis) { ingo@422: sascha@745: if (inverted) { ingo@1692: logger.debug("X-Axis.setInverted(true)"); ingo@422: xaxis.setInverted(true); ingo@422: } ingo@359: } ingo@359: ingo@359: felix@1769: /** felix@1769: * Produce output. felix@1769: * @param facet current facet. felix@1769: * @param attr theme for facet felix@1769: */ ingo@1684: public void doOut( felix@1944: ArtifactAndFacet artifactAndFacet, felix@1944: Document attr, felix@1944: boolean visible ingo@1684: ) { felix@1944: String name = artifactAndFacet.getFacetName(); ingo@359: ingo@695: logger.debug("LongitudinalSectionGenerator.doOut: " + name); ingo@695: ingo@695: if (name == null) { ingo@369: logger.error("No facet name for doOut(). No output generated!"); ingo@369: return; ingo@369: } ingo@369: felix@1944: Facet facet = artifactAndFacet.getFacet(); ingo@696: felix@1944: if (facet == null) { ingo@696: return; ingo@369: } ingo@696: ingo@696: if (name.equals(LONGITUDINAL_W)) { felix@1944: doWOut((WQKms) artifactAndFacet.getData(context), facet, attr, visible); ingo@696: } ingo@696: else if (name.equals(LONGITUDINAL_Q)) { felix@1944: doQOut((WQKms) artifactAndFacet.getData(context), facet, attr, visible); ingo@369: } felix@1028: else if (name.equals(LONGITUDINAL_ANNOTATION)) { felix@1944: doAnnotations((FLYSAnnotation) artifactAndFacet.getData(context), felix@1861: facet, attr, visible); felix@1028: } felix@1861: else if (name.equals(STATIC_WKMS) felix@1861: || name.equals(HEIGHTMARKS_POINTS) felix@1861: || name.equals(STATIC_WQKMS)) { felix@1944: doWOut((WKms) artifactAndFacet.getData(context), facet, attr, visible); felix@1769: } felix@1769: else if (name.equals(W_DIFFERENCES)) { felix@1769: doWDifferencesOut( felix@1944: (WKms) artifactAndFacet.getData(context), felix@1769: facet, felix@1769: attr, felix@1769: visible); felix@1769: } ingo@369: else { ingo@695: logger.warn("Unknown facet name: " + name); ingo@369: return; ingo@369: } ingo@369: } ingo@369: ingo@369: felix@1037: /** ingo@369: * Process the output for W facets in a longitudinal section curve. ingo@369: * ingo@369: * @param wqkms An array of WQKms values. ingo@1712: * @param facet The facet. This facet does NOT support any data objects. Use ingo@1712: * FLYSArtifact.getNativeFacet() instead to retrieve a Facet which supports ingo@1712: * data. ingo@924: * @param theme The theme that contains styling information. ingo@1712: * @param visible The visibility of the curve. ingo@369: */ ingo@1712: protected void doWOut( felix@1769: WKms wkms, ingo@1712: Facet facet, ingo@1712: Document theme, ingo@1712: boolean visible ingo@1712: ) { ingo@369: logger.debug("LongitudinalSectionGenerator.doWOut"); ingo@359: ingo@1712: XYSeries series = new StyledXYSeries(facet.getDescription(), theme); ingo@369: felix@1791: StyledSeriesBuilder.addPoints(series, wkms); ingo@696: felix@1931: addAxisSeries(series, YAXIS.W.idx, visible); sascha@745: felix@1769: if (wkms instanceof WQKms) { felix@1769: if (needInvertAxis((WQKms) wkms)) { felix@1769: setInverted(true); felix@1769: } felix@1769: } felix@1769: } felix@1769: felix@1769: felix@1769: /** felix@1769: * Add items to dataseries which describes the differences. felix@1769: */ felix@1769: protected void doWDifferencesOut( felix@1769: WKms wkms, felix@1769: Facet facet, felix@1769: Document theme, felix@1769: boolean visible felix@1769: ) { felix@1769: logger.debug("WDifferencesCurveGenerator.doWDifferencesOut"); felix@1769: if (wkms == null) { felix@1769: logger.warn("No data to add to WDifferencesChart."); felix@1769: return; felix@1769: } felix@1769: felix@1769: XYSeries series = new StyledXYSeries(facet.getDescription(), theme); felix@1769: felix@1769: if (logger.isDebugEnabled()) { felix@1769: if (wkms.size() > 0) { felix@1769: logger.debug("Generate series: " + series.getKey()); felix@1769: logger.debug("Start km: " + wkms.getKm(0)); felix@1791: logger.debug("End km: " + wkms.getKm(wkms.size() - 1)); felix@1791: logger.debug("Values : " + wkms.size()); felix@1769: } felix@1769: } felix@1769: felix@1791: StyledSeriesBuilder.addPoints(series, wkms); felix@1769: felix@1931: addAxisSeries(series, YAXIS.D.idx, visible); felix@1769: if (DataUtil.guessWaterIncreasing(wkms.allWs())) { sascha@745: setInverted(true); sascha@745: } ingo@369: } ingo@369: ingo@369: felix@1769: ingo@369: /** ingo@369: * Process the output for Q facets in a longitudinal section curve. ingo@369: * ingo@369: * @param wqkms An array of WQKms values. ingo@1712: * @param facet The facet. This facet does NOT support any data objects. Use ingo@1712: * FLYSArtifact.getNativeFacet() instead to retrieve a Facet which supports ingo@1712: * data. ingo@924: * @param theme The theme that contains styling information. ingo@1712: * @param visible The visibility of the curve. ingo@369: */ ingo@1712: protected void doQOut( ingo@1712: WQKms wqkms, ingo@1712: Facet facet, ingo@1712: Document theme, ingo@1712: boolean visible ingo@1712: ) { ingo@369: logger.debug("LongitudinalSectionGenerator.doQOut"); ingo@369: ingo@1712: XYSeries series = new StyledXYSeries(facet.getDescription(), theme); ingo@369: felix@1936: StyledSeriesBuilder.addPointsKmQ(series, wqkms); ingo@696: felix@1931: addAxisSeries(series, YAXIS.Q.idx, visible); sascha@745: ingo@1692: if (needInvertAxis(wqkms)) { ingo@1692: setInverted(true); ingo@1692: } ingo@1692: } ingo@1692: ingo@1692: ingo@1692: /** ingo@1692: * This method determines - taking JFreeCharts auto x value ordering into ingo@1692: * account - if the x axis need to be inverted. Waterlines in these charts ingo@1692: * should decrease. ingo@1692: * ingo@1692: * @param wqkms The data object that stores the x and y values used for this ingo@1692: * chart. ingo@1692: */ ingo@1692: public boolean needInvertAxis(WQKms wqkms) { ingo@1692: boolean wsUp = wqkms.guessWaterIncreasing(); ingo@1692: boolean kmUp = DataUtil.guessWaterIncreasing(wqkms.allKms()); ingo@1692: boolean inv = (wsUp && kmUp) || (!wsUp && !kmUp); ingo@1692: ingo@1692: int size = wqkms.size(); ingo@1692: ingo@1692: if (logger.isDebugEnabled()) { ingo@1692: logger.debug("Values : " + size); ingo@1692: if (size > 0) { ingo@1692: logger.debug("Start km: " + wqkms.getKm(0)); ingo@1692: logger.debug("End km: " + wqkms.getKm(size-1)); ingo@1692: } ingo@1692: logger.debug("wsUp: " + wsUp); ingo@1692: logger.debug("kmUp: " + kmUp); ingo@1692: logger.debug("inv: " + inv); ingo@1692: } ingo@1692: ingo@1692: return inv; ingo@369: } ingo@369: ingo@369: felix@1041: /** felix@1041: * Get name of series (displayed in legend). felix@1041: * @return name of the series. felix@1041: */ ingo@448: protected String getSeriesName(WQKms wqkms, String mode) { ingo@448: String name = wqkms.getName(); ingo@448: String prefix = name != null && name.indexOf(mode) >= 0 ? null : mode; ingo@448: ingo@448: return prefix != null && prefix.length() > 0 ingo@448: ? prefix + "(" + name +")" ingo@448: : name; ingo@359: } ingo@359: } ingo@359: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :