Mercurial > dive4elements > river
diff artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixWQCurveGenerator.java @ 5838:5aa05a7a34b7
Rename modules to more fitting names.
author | Sascha L. Teichmann <teichmann@intevation.de> |
---|---|
date | Thu, 25 Apr 2013 15:23:37 +0200 |
parents | flys-artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixWQCurveGenerator.java@bd047b71ab37 |
children | 4897a58c8746 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixWQCurveGenerator.java Thu Apr 25 15:23:37 2013 +0200 @@ -0,0 +1,620 @@ +package org.dive4elements.river.exports.fixings; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.annotations.XYTextAnnotation; +import org.jfree.chart.plot.Marker; +import org.jfree.chart.plot.ValueMarker; +import org.jfree.chart.title.TextTitle; +import org.jfree.data.xy.XYSeries; +import org.jfree.ui.RectangleAnchor; +import org.jfree.ui.RectangleInsets; +import org.jfree.ui.TextAnchor; +import org.w3c.dom.Document; + +import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; +import org.dive4elements.artifactdatabase.state.Facet; +import org.dive4elements.river.artifacts.FLYSArtifact; +import org.dive4elements.river.artifacts.StaticWKmsArtifact; +import org.dive4elements.river.artifacts.WINFOArtifact; +import org.dive4elements.river.artifacts.access.FixAnalysisAccess; +import org.dive4elements.river.artifacts.model.DateRange; +import org.dive4elements.river.artifacts.model.FacetTypes; +import org.dive4elements.river.artifacts.model.NamedDouble; +import org.dive4elements.river.artifacts.model.QWDDateRange; +import org.dive4elements.river.artifacts.model.WKms; +import org.dive4elements.river.artifacts.model.WQKms; +import org.dive4elements.river.artifacts.model.fixings.FixFunction; +import org.dive4elements.river.artifacts.model.fixings.FixWQCurveFacet; +import org.dive4elements.river.artifacts.model.fixings.QWD; +import org.dive4elements.river.artifacts.model.fixings.QWI; +import org.dive4elements.river.artifacts.resources.Resources; +import org.dive4elements.river.exports.ChartGenerator; +import org.dive4elements.river.exports.StyledSeriesBuilder; +import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation; +import org.dive4elements.river.jfree.FLYSAnnotation; +import org.dive4elements.river.jfree.JFreeUtil; +import org.dive4elements.river.jfree.StickyAxisAnnotation; +import org.dive4elements.river.jfree.StyledXYSeries; +import org.dive4elements.river.model.Gauge; +import org.dive4elements.river.model.River; +import org.dive4elements.river.utils.FLYSUtils; +import org.dive4elements.river.utils.ThemeUtil; + +/** + * Generator for WQ fixing charts. + * @author <a href="mailto:christian.lins@intevation.de">Christian Lins</a> + */ +public class FixWQCurveGenerator +extends FixChartGenerator +implements FacetTypes +{ + /** Private logger. */ + private static Logger logger = + Logger.getLogger(FixWQCurveGenerator.class); + + public static final String I18N_CHART_TITLE = + "chart.fixings.wq.title"; + + public static final String I18N_CHART_SUBTITLE = + "chart.fixings.wq.subtitle"; + + public static final String I18N_CHART_SUBTITLE1 = + "chart.fixings.wq.subtitle1"; + + public static final String I18N_XAXIS_LABEL = + "chart.fixings.wq.xaxis.label"; + + public static final String I18N_YAXIS_LABEL = + "chart.fixings.wq.yaxis.label"; + + public static final String I18N_CHART_TITLE_DEFAULT = + "Fixierungsanalyse"; + + public static final String I18N_XAXIS_LABEL_DEFAULT = + "Q [m\u00B3/s]"; + + public static final String I18N_YAXIS_LABEL_DEFAULT = + "W [NN + m]"; + + public static final double EPSILON = 0.001d; + + public static enum YAXIS { + W(0), + Q(1); + public int idx; + private YAXIS(int c) { + idx = c; + } + } + + + /** Needed to access data to create subtitle. */ + protected FLYSArtifact artifact; + + + @Override + public void doOut(ArtifactAndFacet aaf, Document doc, boolean visible) { + logger.debug("doOut: " + aaf.getFacetName()); + if (!prepareChartData(aaf, doc, visible)) { + logger.warn("Unknown facet, name " + aaf.getFacetName()); + } + } + + + /** Return true if data could be handled. */ + public boolean prepareChartData(ArtifactAndFacet aaf, Document doc, boolean visible) { + String name = aaf.getFacetName(); + + this.artifact = (FLYSArtifact)aaf.getArtifact(); + + if(name.startsWith(FIX_SECTOR_AVERAGE_WQ)) { + doSectorAverageOut(aaf, doc, visible); + } + else if(FIX_ANALYSIS_EVENTS_WQ.equals(name)) { + doAnalysisEventsOut(aaf, doc, visible); + } + else if(FIX_REFERENCE_EVENTS_WQ.equals(name)) { + doReferenceEventsOut(aaf, doc, visible); + } + else if(FIX_WQ_CURVE.equals(name)) { + doWQCurveOut(aaf, doc, visible); + } + else if(FIX_OUTLIER.equals(name)) { + doOutlierOut(aaf, doc, visible); + } + else if(QSECTOR.equals(name)) { + doQSectorOut(aaf, doc, visible); + } + else if(FIX_EVENTS.equals(name)) { + doEventsOut(aaf, doc, visible); + } + else if(/*STATIC_WKMS_INTERPOL.equals(name) ||*/ + STATIC_WKMS_MARKS.equals(name) || + STATIC_WKMS.equals(name) || + HEIGHTMARKS_POINTS.equals(name) ) { + doWAnnotations( + aaf.getData(context), + aaf, + doc, + visible); + } + else if (LONGITUDINAL_W.equals(name) || STATIC_WQ.equals(name) + || STATIC_WKMS_INTERPOL.equals(name)) { + doWQOut(aaf.getData(context), aaf, doc, visible); + } + else if (name.equals(DISCHARGE_CURVE)) { + doDischargeOut( + (WINFOArtifact) aaf.getArtifact(), + aaf.getData(context), + aaf.getFacetDescription(), + doc, + visible); + } + else if (FacetTypes.IS.MANUALPOINTS(aaf.getFacetName())) { + doPoints(aaf.getData(context), + aaf, + doc, visible, YAXIS.W.idx); + } + else { + return false; + } + return true; + } + + + /** Add sector average points to chart */ + protected void doSectorAverageOut(ArtifactAndFacet aaf, Document doc, boolean visible) { + logger.debug("doSectorAverageOut"); + + QWDDateRange qwdd = (QWDDateRange) aaf.getData(context); + QWD qwd = qwdd != null ? qwdd.getQWD() : null; + + if(qwd != null) { + addQWSeries(new QWD[] { qwd }, aaf, doc, visible); + } + else { + logger.debug("doSectorAverageOut: qwd == null"); + } + } + + /** Add analysis event points to chart */ + protected void doAnalysisEventsOut(ArtifactAndFacet aaf, Document doc, boolean visible) { + logger.debug("doAnalysisEventsOut"); + + QWD qwd = (QWD)aaf.getData(context); + if(qwd != null) { + XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), doc); + List<XYTextAnnotation> textAnnos = new ArrayList<XYTextAnnotation>(); + + DateFormat dateFormat = DateFormat.getDateInstance( + DateFormat.SHORT); + + series.add(qwd.getQ(), qwd.getW()); + + XYTextAnnotation anno = new CollisionFreeXYTextAnnotation( + dateFormat.format(qwd.getDate()), + qwd.getQ(), + qwd.getW()); + textAnnos.add(anno); + + addAxisSeries(series, 0, visible); + if(visible && ThemeUtil.parseShowPointLabel(doc)) { + FLYSAnnotation flysAnno = new FLYSAnnotation(null, null, null, doc); + flysAnno.setTextAnnotations(textAnnos); + addAnnotations(flysAnno); + } + } + else { + logger.debug("doAnalysisEventsOut: qwds == null"); + } + } + + + /** Add reference event points to chart */ + protected void doReferenceEventsOut(ArtifactAndFacet aaf, Document doc, boolean visible) { + logger.debug("doReferenceEventsOut"); + + QWI qwd = (QWI)aaf.getData(context); + if(qwd != null) { + XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), doc); + List<XYTextAnnotation> textAnnos = new ArrayList<XYTextAnnotation>(); + + DateFormat dateFormat = DateFormat.getDateInstance( + DateFormat.SHORT); + + series.add(qwd.getQ(), qwd.getW()); + + XYTextAnnotation anno = new CollisionFreeXYTextAnnotation( + dateFormat.format(qwd.getDate()), + qwd.getQ(), + qwd.getW()); + textAnnos.add(anno); + + addAxisSeries(series, 0, visible); + if(visible && ThemeUtil.parseShowPointLabel(doc)) { + FLYSAnnotation flysAnno = new FLYSAnnotation(null, null, null, doc); + flysAnno.setTextAnnotations(textAnnos); + addAnnotations(flysAnno); + } + } + else { + logger.debug("doReferenceEventsOut: qwds == null"); + } + } + + + private void addPointFromWQKms(WQKms wqkms, + String title, + Document theme, + boolean visible + ) { + XYSeries series = new StyledXYSeries(title, theme); + Double ckm = (Double) context.getContextValue(CURRENT_KM); + if (wqkms == null || wqkms.getKms().length == 0 || ckm == null) { + logger.info("addPointFromWQKms: No event data to show."); + return; + } + double[] kms = wqkms.getKms(); + for (int i = 0 ; i< kms.length; i++) { + if (Math.abs(kms[i] - ckm) <= EPSILON) { + series.add(wqkms.getQ(i), wqkms.getW(i)); + addAxisSeries(series, YAXIS.W.idx, visible); + return; + } + } + } + + protected void doEventsOut(ArtifactAndFacet aaf, Document doc, boolean visible) { + logger.debug("doEventsOut"); + // Find W/Q at km. + addPointFromWQKms((WQKms) aaf.getData(context), + aaf.getFacetDescription(), doc, visible); + } + + + protected void doWQCurveOut(ArtifactAndFacet aaf, Document doc, boolean visible) { + logger.debug("doWQCurveOut"); + + FixWQCurveFacet facet = (FixWQCurveFacet)aaf.getFacet(); + FixFunction func = (FixFunction)facet.getData( + aaf.getArtifact(), context); + + if (func == null) { + logger.warn("doWQCurveOut: Facet does not contain FixFunction"); + return; + } + + double maxQ = func.getMaxQ(); + + if (maxQ > 0) { + StyledXYSeries series = JFreeUtil.sampleFunction2D( + func.getFunction(), + doc, + aaf.getFacetDescription(), + 500, // number of samples + 0.0 , // start + maxQ); // end + + addAxisSeries(series, 0, visible); + } + else { + logger.warn("doWQCurveOut: maxQ <= 0"); + } + } + + protected void doOutlierOut(ArtifactAndFacet aaf, Document doc, boolean visible) { + logger.debug("doOutlierOut"); + + QWI[] qws = (QWI[])aaf.getData(context); + addQWSeries(qws, aaf, doc, visible); + } + + + /** Add markers for q sectors. */ + protected void doQSectorOut(ArtifactAndFacet aaf, Document theme, boolean visible) { + logger.debug("doQSectorOut"); + if (!visible) { + return; + } + + Object qsectorsObj = aaf.getData(context); + if (qsectorsObj == null || !(qsectorsObj instanceof List)) { + logger.warn("No QSectors coming from data."); + return; + } + + List<?> qsectorsList = (List<?>) qsectorsObj; + if (qsectorsList.size() == 0 || !(qsectorsList.get(0) instanceof NamedDouble)) { + logger.warn("No QSectors coming from data."); + return; + } + + @SuppressWarnings("unchecked") + List<NamedDouble> qsectors = (List<NamedDouble>) qsectorsList; + + for (NamedDouble qsector : qsectors) { + if (Double.isNaN(qsector.getValue())) { + continue; + } + Marker m = new ValueMarker(qsector.getValue()); + m.setPaint(Color.black); + + float[] dashes = ThemeUtil.parseLineStyle(theme); + int size = ThemeUtil.parseLineWidth(theme); + BasicStroke stroke; + if (dashes.length <= 1) { + stroke = new BasicStroke(size); + } + else { + stroke = new BasicStroke(size, + BasicStroke.CAP_BUTT, + BasicStroke.JOIN_ROUND, + 1.0f, + dashes, + 0.0f); + } + m.setStroke(stroke); + + if (ThemeUtil.parseShowLineLabel(theme)) { + m.setLabel(qsector.getName()); + m.setPaint(ThemeUtil.parseTextColor(theme)); + m.setLabelFont(ThemeUtil.parseTextFont(theme)); + } + Color paint = ThemeUtil.parseLineColorField(theme); + if (paint != null) { + m.setPaint(paint); + } + m.setLabelAnchor(RectangleAnchor.TOP_LEFT); + m.setLabelTextAnchor(TextAnchor.TOP_LEFT); + m.setLabelOffset(new RectangleInsets(5, 5, 10, 10)); + addDomainMarker(m); + } + } + + + /** + * Add W-Annotations to plot. + * @param wqkms actual data (double[][]). + * @param theme theme to use. + */ + protected void doWAnnotations( + Object wqkms, + ArtifactAndFacet aandf, + Document theme, + boolean visible + ) { + Facet facet = aandf.getFacet(); + + List<StickyAxisAnnotation> xy = new ArrayList<StickyAxisAnnotation>(); + if (wqkms instanceof double[][]) { + logger.debug("Got double[][]"); + double [][] data = (double [][]) wqkms; + for (int i = 0; i< data[0].length; i++) { + xy.add(new StickyAxisAnnotation(aandf.getFacetDescription(), + (float) data[1][i], StickyAxisAnnotation.SimpleAxis.Y_AXIS)); + } + + doAnnotations(new FLYSAnnotation(facet.getDescription(), xy), + aandf, theme, visible); + } + else { + // Assume its WKms. + logger.debug("Got WKms"); + WKms data = (WKms) wqkms; + + Double ckm = (Double) context.getContextValue(CURRENT_KM); + double location = (ckm != null) + ? ckm.doubleValue() + : getRange()[0]; + double w = StaticWKmsArtifact.getWAtKmLin(data, location); + xy.add(new StickyAxisAnnotation(aandf.getFacetDescription(), + (float) w, StickyAxisAnnotation.SimpleAxis.Y_AXIS)); + + doAnnotations(new FLYSAnnotation(facet.getDescription(), xy), + aandf, theme, visible); + } + } + + + /** + * Add series with discharge curve to diagram. + */ + protected void doDischargeOut( + WINFOArtifact artifact, + Object o, + String description, + Document theme, + boolean visible) + { + WQKms wqkms = (WQKms) o; + + String gaugeName = wqkms.getName(); + + River river = FLYSUtils.getRiver(artifact); + + if (river == null) { + logger.debug("no river found"); + return; + } + + Gauge gauge = river.determineGaugeByName(gaugeName); + + if (gauge == null) { + logger.debug("no gauge found"); + return; + } + + XYSeries series = new StyledXYSeries(description, theme); + StyledSeriesBuilder.addPointsQW(series, wqkms); + addAxisSeries(series, YAXIS.W.idx, visible); + } + + + /** + * Add WQ Data to plot. + * @param wqkms data as double[][] + */ + protected void doWQOut( + Object wqkms, + ArtifactAndFacet aaf, + Document theme, + boolean visible + ) { + logger.debug("FixWQCurveGenerator: doWQOut"); + if (wqkms instanceof WQKms) { + // TODO As in doEventsOut, the value-searching should + // be delivered by the facet already (instead of in the Generator). + logger.debug("FixWQCurveGenerator: doWQOut: WQKms"); + + addPointFromWQKms((WQKms) aaf.getData(context), aaf.getFacetDescription(), theme, visible); + } + else { + logger.debug("FixWQCurveGenerator: doWQOut: double[][]"); + double [][] data = (double [][]) wqkms; + + XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme); + StyledSeriesBuilder.addPoints(series, data, true); + + addAxisSeries(series, YAXIS.W.idx, visible); + } + } + + + protected void addQWSeries( + QWI [] qws, + ArtifactAndFacet aaf, + Document theme, + boolean visible + ) { + if (qws == null) { + return; + } + + XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme); + List<XYTextAnnotation> textAnnos = + new ArrayList<XYTextAnnotation>(qws.length); + + DateFormat dateFormat = DateFormat.getDateInstance( + DateFormat.SHORT); + + for (QWI qw: qws) { + series.add(qw.getQ(), qw.getW()); + + XYTextAnnotation anno = new CollisionFreeXYTextAnnotation( + dateFormat.format(qw.getDate()), + qw.getQ(), + qw.getW()); + textAnnos.add(anno); + } + + addAxisSeries(series, 0, visible); + if (visible && ThemeUtil.parseShowPointLabel(theme)) { + FLYSAnnotation flysAnno = + new FLYSAnnotation(null, null, null, theme); + flysAnno.setTextAnnotations(textAnnos); + addAnnotations(flysAnno); + } + } + + @Override + protected String getChartTitle() { + return Resources.format( + context.getMeta(), + I18N_CHART_TITLE, + I18N_CHART_TITLE_DEFAULT, + context.getContextValue(CURRENT_KM)); + } + + @Override + protected String getDefaultChartTitle() { + return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT); + } + + @Override + protected String getDefaultChartSubtitle() { + FixAnalysisAccess access = new FixAnalysisAccess(artifact, context); + DateRange dateRange = access.getDateRange(); + DateRange refRange = access.getReferencePeriod(); + + if (dateRange != null && refRange != null) { + return Resources.format( + context.getMeta(), + I18N_CHART_SUBTITLE, + "", + access.getRiver(), + dateRange.getFrom(), + dateRange.getTo(), + refRange.getFrom(), + refRange.getTo()); + } + + return null; + } + + @Override + protected void addSubtitles(JFreeChart chart) { + String defaultSubtitle = getDefaultChartSubtitle(); + + if (defaultSubtitle == null || defaultSubtitle.length() == 0) { + return; + } + + chart.addSubtitle(new TextTitle(defaultSubtitle)); + + StringBuilder buf = new StringBuilder(); + + // Add analysis periods as additional subtitle + FixAnalysisAccess access = new FixAnalysisAccess(artifact, context); + DateRange[] aperiods = access.getAnalysisPeriods(); + buf.append(msg("fix.analysis.periods")); + buf.append(": "); + for(int n = 0; n < aperiods.length; n++) { + buf.append( + Resources.format( + context.getMeta(), + I18N_CHART_SUBTITLE1, + "", + aperiods[n].getFrom(), + aperiods[n].getTo())); + if(n + 1 < aperiods.length) { + buf.append("; "); + } + } + + chart.addSubtitle(new TextTitle(buf.toString())); + } + + @Override + protected String getDefaultXAxisLabel() { + return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT); + } + + @Override + protected String getDefaultYAxisLabel(int pos) { + return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT); + } + + @Override + protected ChartGenerator.YAxisWalker getYAxisWalker() { + return new YAxisWalker() { + @Override + public int length() { + return YAXIS.values().length; + } + + @Override + public String getId(int idx) { + YAXIS[] yaxes = YAXIS.values(); + return yaxes[idx].toString(); + } + }; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :