teichmann@5863: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde teichmann@5863: * Software engineering by Intevation GmbH teichmann@5863: * teichmann@5994: * This file is Free Software under the GNU AGPL (>=v3) teichmann@5863: * and comes with ABSOLUTELY NO WARRANTY! Check out the teichmann@5994: * documentation coming with Dive4Elements River for details. teichmann@5863: */ teichmann@5863: teichmann@5831: package org.dive4elements.river.exports; felix@1111: christian@3278: import java.awt.BasicStroke; christian@3278: import java.awt.Color; christian@3278: import java.awt.Paint; christian@3278: import java.awt.Stroke; christian@3278: import java.text.NumberFormat; christian@3278: import java.util.List; christian@3278: christian@3278: import org.apache.log4j.Logger; christian@3278: import org.jfree.chart.LegendItemCollection; christian@3278: import org.jfree.chart.annotations.XYBoxAnnotation; christian@3278: import org.jfree.chart.annotations.XYTextAnnotation; christian@3278: import org.jfree.chart.plot.XYPlot; christian@3278: import org.jfree.data.xy.XYSeries; christian@3278: teichmann@5831: import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; teichmann@5831: import org.dive4elements.artifacts.DataProvider; teichmann@5867: import org.dive4elements.river.artifacts.D4EArtifact; teichmann@5831: import org.dive4elements.river.artifacts.geom.Lines; teichmann@5831: import org.dive4elements.river.artifacts.model.CrossSectionFacet; teichmann@5831: import org.dive4elements.river.artifacts.model.FacetTypes; teichmann@5831: import org.dive4elements.river.artifacts.model.HYKFactory; teichmann@5831: import org.dive4elements.river.artifacts.resources.Resources; teichmann@5864: import org.dive4elements.river.jfree.RiverAnnotation; teichmann@5831: import org.dive4elements.river.jfree.StyledXYSeries; teichmann@5831: import org.dive4elements.river.model.FastCrossSectionLine; teichmann@5831: import org.dive4elements.river.themes.LineStyle; teichmann@5831: import org.dive4elements.river.themes.TextStyle; teichmann@6905: import org.dive4elements.river.themes.ThemeDocument; teichmann@5865: import org.dive4elements.river.utils.RiverUtils; teichmann@5831: import org.dive4elements.river.utils.Formatter; christian@3771: sascha@2120: felix@1111: /** felix@1111: * An OutGenerator that generates cross section graphs. felix@1111: */ felix@1111: public class CrossSectionGenerator felix@2138: extends LongitudinalSectionGenerator felix@1111: implements FacetTypes felix@1111: { felix@1111: /** The logger that is used in this generator. */ felix@1111: private static Logger logger = christian@3771: Logger.getLogger(CrossSectionGenerator.class); felix@1111: felix@1111: public static final String I18N_CHART_TITLE = christian@3771: "chart.cross_section.title"; felix@1111: felix@1111: public static final String I18N_CHART_SUBTITLE = christian@3771: "chart.cross_section.subtitle"; felix@1111: felix@1111: public static final String I18N_XAXIS_LABEL = christian@3771: "chart.cross_section.xaxis.label"; felix@1111: felix@1111: public static final String I18N_YAXIS_LABEL = christian@3771: "chart.cross_section.yaxis.label"; felix@1111: felix@1125: public static final String I18N_CHART_TITLE_DEFAULT = "Querprofildiagramm"; felix@1125: public static final String I18N_XAXIS_LABEL_DEFAULT = "Abstand [m]"; felix@1125: public static final String I18N_YAXIS_LABEL_DEFAULT = "W [NN + m]"; felix@1111: felix@1111: felix@1111: /** Trivial Constructor. */ felix@1111: public CrossSectionGenerator() { felix@1111: super(); felix@1111: } felix@1111: felix@1111: ingo@2052: @Override ingo@2052: protected YAxisWalker getYAxisWalker() { ingo@2052: return new YAxisWalker() { ingo@2052: @Override ingo@2052: public int length() { ingo@2052: return 1; ingo@2052: } ingo@2052: ingo@2052: /** Get identifier for this index. */ ingo@2052: @Override ingo@2052: public String getId(int idx) { ingo@2052: return "W"; ingo@2052: } ingo@2052: }; ingo@2052: } ingo@2052: ingo@2052: felix@1125: /** felix@1125: * Get localized chart title. felix@1125: */ ingo@2048: @Override felix@2104: public String getDefaultChartTitle() { felix@1141: Object[] i18n_msg_args = new Object[] { christian@3771: getRiverName() felix@1141: }; felix@1141: return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT, i18n_msg_args); felix@1111: } felix@1111: felix@1111: felix@2138: /** Always return default subtitle. */ felix@1111: @Override ingo@1989: protected String getChartSubtitle() { ingo@2048: // XXX NOTE: overriding this method disables ChartSettings subtitle! raimund@2167: // The default implementation of this method in ChartGenerator returns raimund@2167: // the subtitle changed via the chart settings dialog. This method raimund@2167: // always returns the subtitle containing river and km, NEVER the raimund@2167: // ChartSettings subtitle! ingo@2048: return getDefaultChartSubtitle(); ingo@2048: } ingo@2048: ingo@2048: felix@2138: /** Get Charts default subtitle. */ ingo@2048: @Override ingo@2048: protected String getDefaultChartSubtitle() { felix@2042: List providers = christian@3771: context.getDataProvider(CrossSectionFacet.BLACKBOARD_CS_MASTER_DATA); felix@2042: double km = 0d; felix@2042: if (providers.size() > 0) { sascha@2120: FastCrossSectionLine csl = (FastCrossSectionLine) providers.get(0). christian@3771: provideData(CrossSectionFacet.BLACKBOARD_CS_MASTER_DATA, christian@3771: null, context); christian@3602: km = csl == null ? -1 : csl.getKm(); felix@2042: } felix@1111: felix@1111: Object[] args = new Object[] { christian@3771: getRiverName(), christian@3771: km felix@1111: }; felix@1111: christian@3771: logger.debug("Locale: " + Resources.getLocale(context.getMeta())); christian@3771: ingo@1989: return msg(I18N_CHART_SUBTITLE, "", args); ingo@1989: } ingo@1989: ingo@1989: christian@3278: /** Get color for hyk zones by their type (which is the name). */ christian@3278: protected Paint colorForHYKZone(String zoneName) { christian@3278: if (zoneName.startsWith("R")) { christian@3278: // Brownish. christian@3278: return new Color(153, 60, 0); christian@3278: } christian@3278: else if (zoneName.startsWith("V")) { christian@3278: // Greenish. christian@3278: return new Color(0, 255, 0); christian@3278: } christian@3278: else if (zoneName.startsWith("B")) { christian@3278: // Grayish. christian@3278: return new Color(128, 128, 128); christian@3278: } christian@3278: else if (zoneName.startsWith("H")) { christian@3278: // Blueish. christian@3278: return new Color(0, 0, 255); christian@3278: } christian@3278: else { christian@3278: // Default. christian@3278: logger.debug("Unknown zone type found."); christian@3278: return new Color(255, 0, 0); christian@3278: } christian@3278: } sascha@3280: christian@3278: @Override christian@3278: protected void addAnnotationsToRenderer(XYPlot plot) { christian@3278: super.addAnnotationsToRenderer(plot); sascha@3280: christian@3278: // Paints for the boxes/lines. christian@3278: Stroke basicStroke = new BasicStroke(1.0f); christian@3278: teichmann@4047: // XXX: DEAD CODE // Paint linePaint = new Color(255, 0,0,60); christian@3278: Paint fillPaint = new Color(0, 255,0,60); christian@3278: Paint tranPaint = new Color(0, 0,0, 0); christian@3278: christian@3278: // OPTMIMIZE: Pre-calculate positions christian@3278: ChartArea area = new ChartArea( christian@3771: plot.getDomainAxis(0).getRange(), christian@3771: plot.getRangeAxis().getRange()); sascha@3280: teichmann@5864: for(RiverAnnotation fa : this.annotations) { sascha@3280: christian@3278: // Access text styling, if any. teichmann@6905: ThemeDocument theme = fa.getTheme(); christian@3464: TextStyle textStyle = null; teichmann@4047: // XXX: DEAD CODE // LineStyle lineStyle = null; sascha@3280: christian@3278: // Get Themeing information and add legend item. christian@3278: if (theme != null) { teichmann@6906: textStyle = theme.parseComplexTextStyle(); teichmann@4047: // XXX: DEAD CODE // lineStyle = themeAccess.parseLineStyle(); christian@3278: if (fa.getLabel() != null) { christian@3278: LegendItemCollection lic = new LegendItemCollection(); christian@3278: LegendItemCollection old = plot.getFixedLegendItems(); christian@3278: lic.add(createLegendItem(theme, fa.getLabel())); christian@3278: // (Re-)Add prior legend entries. christian@3278: if (old != null) { christian@3278: old.addAll(lic); christian@3278: } christian@3278: else { christian@3278: old = lic; christian@3278: } christian@3278: plot.setFixedLegendItems(old); christian@3278: } christian@3278: } sascha@3280: christian@3278: // Hyks. christian@3278: for (HYKFactory.Zone zone: fa.getBoxes()) { christian@3278: // For each zone, create a box to fill with color, a box to draw christian@3278: // the lines and a text to display the type. christian@3278: fillPaint = colorForHYKZone(zone.getName()); sascha@3280: christian@3278: XYBoxAnnotation boxA = new XYBoxAnnotation(zone.getFrom(), area.atGround(), christian@3771: zone.getTo(), area.ofGround(0.03f), basicStroke, tranPaint, fillPaint); christian@3278: XYBoxAnnotation boxB = new XYBoxAnnotation(zone.getFrom(), area.atGround(), christian@3771: zone.getTo(), area.atTop(), basicStroke, fillPaint, tranPaint); sascha@3280: christian@3278: XYTextAnnotation tex = new XYTextAnnotation(zone.getName(), felix@4653: zone.getFrom() + (zone.getTo() - zone.getFrom()) / 2.0d, christian@3771: area.ofGround(0.015f)); christian@3278: if (textStyle != null) { christian@3278: textStyle.apply(tex); christian@3278: } sascha@3280: christian@3278: plot.getRenderer().addAnnotation(boxA, org.jfree.ui.Layer.BACKGROUND); christian@3278: plot.getRenderer().addAnnotation(boxB, org.jfree.ui.Layer.BACKGROUND); christian@3278: plot.getRenderer().addAnnotation(tex, org.jfree.ui.Layer.BACKGROUND); christian@3278: } christian@3278: } christian@3278: } felix@1111: ingo@2051: @Override ingo@2051: protected String getDefaultXAxisLabel() { felix@1111: return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT); felix@1111: } felix@1111: felix@1111: ingo@2051: @Override ingo@2051: protected String getDefaultYAxisLabel(int pos) { teichmann@5867: D4EArtifact flys = (D4EArtifact) master; felix@5130: teichmann@5865: String unit = RiverUtils.getRiver(flys).getWstUnit().getName(); felix@5130: felix@5130: return msg(I18N_YAXIS_LABEL, felix@5130: I18N_YAXIS_LABEL_DEFAULT, felix@5130: new Object[] { unit }); felix@1111: } felix@1111: felix@1111: felix@1111: /** felix@1116: * Let one facet do its job. felix@1116: */ christian@3278: @Override ingo@1684: public void doOut( christian@3771: ArtifactAndFacet artifactFacet, teichmann@6905: ThemeDocument attr, christian@3771: boolean visible christian@3771: ) { felix@1944: String name = artifactFacet.getFacetName(); felix@1111: felix@1111: logger.debug("CrossSectionGenerator.doOut: " + name); felix@1111: felix@1111: if (name == null) { felix@1111: logger.error("No facet name for doOut(). No output generated!"); felix@1111: return; felix@1111: } felix@1111: felix@1111: if (name.equals(CROSS_SECTION)) { ingo@1684: doCrossSectionOut( christian@3771: artifactFacet.getData(context), christian@3771: artifactFacet.getFacetDescription(), christian@3771: attr, christian@3771: visible); felix@1111: } felix@1122: else if (name.equals(CROSS_SECTION_WATER_LINE)) { ingo@1684: doCrossSectionWaterLineOut( christian@3771: artifactFacet.getData(context), christian@3771: artifactFacet.getFacetDescription(), christian@3771: attr, christian@3771: visible); felix@1122: } felix@2020: else if (FacetTypes.IS.AREA(name)) { felix@2006: doArea(artifactFacet.getData(context), christian@3771: artifactFacet, christian@3771: attr, christian@3771: visible); felix@2006: } felix@2138: else if (name.equals(HYK)) { felix@2138: doHyk(artifactFacet.getData(context), christian@3771: artifactFacet.getFacetDescription(), christian@3771: attr, christian@3771: visible); felix@2138: } felix@3198: else if (FacetTypes.IS.MANUALLINE(name)) { felix@3198: doCrossSectionWaterLineOut( christian@3771: artifactFacet.getData(context), christian@3771: artifactFacet.getFacetDescription(), christian@3771: attr, christian@3771: visible); felix@3198: } felix@2206: else if (FacetTypes.IS.MANUALPOINTS(name)) { felix@2206: doPoints(artifactFacet.getData(context), christian@3771: artifactFacet, christian@3771: attr, visible, YAXIS.W.idx); felix@2206: } felix@1111: else { felix@1111: logger.warn("CrossSection.doOut: Unknown facet name: " + name); felix@1111: return; felix@1111: } felix@1111: } felix@1111: felix@2020: felix@2104: /** Look up the axis identifier for a given facet type. */ christian@3278: @Override felix@2104: public int axisIdxForFacet(String facetName) { felix@2104: // TODO Where to add thid axis too. felix@2104: return 0; felix@2006: } felix@1111: felix@2020: felix@1111: /** felix@1122: * Do cross sections waterline out. felix@1122: * felix@1141: * @param seriesName name of the data (line) to display in legend. felix@1122: * @param theme Theme for the data series. felix@1122: */ ingo@1684: protected void doCrossSectionWaterLineOut( christian@3771: Object o, christian@3771: String seriesName, teichmann@6905: ThemeDocument theme, christian@3771: boolean visible christian@3771: ) { felix@1122: logger.debug("CrossSectionGenerator.doCrossSectionWaterLineOut"); felix@1141: felix@2652: Lines.LineData lines = (Lines.LineData) o; felix@2728: // DO NOT SORT DATA! This destroys the gaps indicated by NaNs. felix@2652: StyledXYSeries series = new StyledXYSeries(seriesName, false, theme); felix@1122: teichmann@6905: if (!theme.parseShowLineLabel()) { felix@3228: series.setLabel(""); felix@3228: } teichmann@6905: if (theme.parseShowWidth()) { christian@3278: NumberFormat nf = Formatter.getMeterFormat(this.context); felix@3228: String labelAdd = "b=" + nf.format(lines.width) + "m"; sascha@3453: if (series.getLabel().length() == 0) { christian@3771: series.setLabel(labelAdd); felix@3228: } felix@3228: else { felix@3228: series.setLabel(series.getLabel() + ", " + labelAdd); felix@3228: } felix@2663: } teichmann@6905: if (theme.parseShowLevel() && lines.points.length > 1 christian@3278: && lines.points[1].length > 0) { christian@3278: NumberFormat nf = Formatter.getMeterFormat(this.context); teichmann@5867: D4EArtifact flys = (D4EArtifact) master; felix@5130: teichmann@5865: String unit = RiverUtils.getRiver(flys).getWstUnit().getName(); felix@5130: felix@5130: String labelAdd = "W=" + nf.format(lines.points[1][0]) + unit; sascha@3453: if (series.getLabel().length() == 0) { christian@3771: series.setLabel(labelAdd); felix@3228: } felix@3228: else { felix@3228: series.setLabel(series.getLabel() + ", " + labelAdd); felix@3228: } felix@2663: } teichmann@6905: if (theme.parseShowMiddleHeight() && lines.width != 0) { christian@3278: NumberFormat nf = Formatter.getMeterFormat(this.context); felix@3640: String labelAdd = "T=" + nf.format(lines.area / lines.width) + "m"; christian@3771: // : " + lines.area + "/" + lines.width); sascha@3453: if (series.getLabel().length() == 0) { christian@3771: series.setLabel(labelAdd); felix@3228: } felix@3228: else { felix@3228: series.setLabel(series.getLabel() + ", " + labelAdd); felix@3228: } felix@2674: } felix@2728: felix@2685: StyledSeriesBuilder.addPoints(series, lines.points, false); felix@1791: felix@1931: addAxisSeries(series, 0, visible); felix@1122: } felix@1122: felix@1122: felix@2138: /** Add HYK-Annotations (colorize and label some areas, draw lines. */ felix@2138: protected void doHyk( christian@3771: Object o, christian@3771: String seriesName, teichmann@6905: ThemeDocument theme, christian@3771: boolean visible christian@3771: ) { felix@2138: logger.debug("CrossSectionGenerator.doHyk"); felix@2138: felix@2138: List zones = (List) o; felix@2138: sascha@3555: if (zones == null || zones.isEmpty()) { ingo@2789: logger.warn("CrossSectionGenerator.doHYK: empty zone list received."); ingo@2789: return; felix@2138: } felix@2138: felix@2152: // Actual Styling is done in XYChartGenerator. felix@2772: if (visible) { teichmann@5864: addAnnotations(new RiverAnnotation(seriesName, null, zones, theme)); felix@2772: } felix@2138: } felix@2138: felix@2138: felix@1122: /** felix@1116: * Do cross sections out. felix@1111: * felix@1141: * @param seriesName name of the data (line) to display in legend. felix@1116: * @param theme Theme for the data series. felix@1111: */ ingo@1684: protected void doCrossSectionOut( christian@3771: Object o, christian@3771: String seriesName, teichmann@6905: ThemeDocument theme, christian@3771: boolean visible christian@3771: ) { felix@1116: logger.debug("CrossSectionGenerator.doCrossSectionOut"); felix@1111: felix@1141: XYSeries series = new StyledXYSeries(seriesName, theme); felix@1122: felix@2685: StyledSeriesBuilder.addPoints(series, (double [][]) o, false); felix@1791: felix@1931: addAxisSeries(series, 0, visible); felix@1111: } raimund@2167: raimund@2167: raimund@2167: /** raimund@2167: * Creates a new ChartSection. raimund@2167: * felix@3619: * Overridden to prevent inclusion of subtitle. felix@3619: * raimund@2167: * @return a new ChartSection. raimund@2167: */ raimund@2167: @Override raimund@2167: protected ChartSection buildChartSection() { raimund@2167: ChartSection chartSection = new ChartSection(); raimund@2167: chartSection.setTitle(getChartTitle()); felix@3613: chartSection.setDisplayGrid(isGridVisible()); felix@3619: chartSection.setDisplayLogo(showLogo()); felix@3619: chartSection.setLogoVPlacement(logoVPlace()); felix@3619: chartSection.setLogoHPlacement(logoHPlace()); raimund@2167: return chartSection; raimund@2167: } felix@1111: } felix@1111: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :