Mercurial > dive4elements > river
view artifacts/src/main/java/org/dive4elements/river/exports/DischargeCurveGenerator.java @ 9323:86d2cbfe7f7f
bundu bezugswst excel metadaten
author | gernotbelger |
---|---|
date | Fri, 27 Jul 2018 13:29:34 +0200 |
parents | 740d65e4aa14 |
children | 9b8e8fc1f408 |
line wrap: on
line source
/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde * Software engineering by Intevation GmbH * * This file is Free Software under the GNU AGPL (>=v3) * and comes with ABSOLUTELY NO WARRANTY! Check out the * documentation coming with Dive4Elements River for details. */ package org.dive4elements.river.exports; import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; import org.dive4elements.artifactdatabase.state.State; import org.dive4elements.artifacts.CallContext; import org.dive4elements.river.artifacts.D4EArtifact; import org.dive4elements.river.artifacts.GaugeDischargeCurveArtifact; import org.dive4elements.river.artifacts.access.RiverAccess; import org.dive4elements.river.artifacts.model.FacetTypes; import org.dive4elements.river.artifacts.model.WQKms; import org.dive4elements.river.exports.process.MiscDischargeProcessor; import org.dive4elements.river.jfree.Bounds; import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation; import org.dive4elements.river.jfree.DoubleBounds; import org.dive4elements.river.jfree.RiverAnnotation; 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.themes.ThemeDocument; import org.jfree.chart.annotations.XYTextAnnotation; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.XYPlot; import org.jfree.data.Range; import org.jfree.data.xy.XYSeries; /** * An OutGenerator that generates discharge curves. * * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> */ public class DischargeCurveGenerator extends XYChartGenerator implements FacetTypes { /** Beware, in this implementation, the W axis is also in cm! */ public static enum YAXIS { WCm(0), W(1); protected int idx; private YAXIS(final int c) { this.idx = c; } } /** The log used in this generator. */ private static Logger log = Logger.getLogger(DischargeCurveGenerator.class); public static final String I18N_CHART_TITLE = "chart.discharge.curve.title"; public static final String I18N_CHART_SUBTITLE = "chart.discharge.curve.subtitle"; public static final String I18N_XAXIS_LABEL = "common.export.csv.header.q"; public static final String I18N_YAXIS_LABEL = "chart.discharge.curve.yaxis.label"; public static final String I18N_CHART_TITLE_DEFAULT = "Abflusskurven"; public static final String I18N_XAXIS_LABEL_DEFAULT = "Q [m\u00b3/s]"; public static final String I18N_YAXIS_LABEL_DEFAULT = "W [cm]"; /** * Returns the PNP (Datum) of gauge, if at gauge, 0 otherwise. */ public static double getCurrentGaugeDatum(final double km, final D4EArtifact artifact, final double tolerance) { // Look if there is a gauge at chosen km: // Get gauge which is defined for km final Gauge gauge = new RiverAccess(artifact).getRiver().determineGaugeAtStation(km); if (gauge == null) { log.error("No Gauge could be found at station " + km + "!"); return 0d; } double subtractPNP = 0d; // Compare to km. if (Math.abs(km - gauge.getStation().doubleValue()) < tolerance) { subtractPNP = gauge.getDatum().doubleValue(); } return subtractPNP; } /** Get the current Gauge datum with default distance tolerance. */ public double getCurrentGaugeDatum() { return getCurrentGaugeDatum(getRange()[0], (D4EArtifact) getMaster(), 1e-4); } /** Overriden to show second axis also if no visible data present. */ @Override protected void adjustAxes(final XYPlot plot) { super.adjustAxes(plot); // XXX Hacking around that there were two axes shown in official Gauge // Discharge, the one from the WINFO module. // This should be made unecessary in a Q Diagram refactoring with // decent inheritance. if (getMaster() instanceof GaugeDischargeCurveArtifact) { final GaugeDischargeCurveArtifact myMaster = (GaugeDischargeCurveArtifact) getMaster(); final State state = myMaster.getCurrentState(getContext()); if (GaugeDischargeCurveArtifact.STATIC_STATE_NAME.equals(state.getID())) { return; } } // End Hack if (getCurrentGaugeDatum() != 0d) { // Show the W[*m] axis even if there is no data. plot.setRangeAxis(1, createYAxis(YAXIS.W.idx)); syncWAxisRanges(); } } protected void syncWAxisRanges() { // Syncronizes the ranges of both W Axes to make sure // that the Data matches for both axes. Bounds boundsInMGauge = getYBounds(YAXIS.W.idx); Bounds boundsInCM = getYBounds(YAXIS.WCm.idx); if (boundsInMGauge == null || boundsInCM == null) { // One axis does not exist. Nothing to sync return; } // XXX Q-Symetry: I am assuming here that there can only // be a fixed Range for WinM as this is currently the only // thing that is configureable. final Range fixedWinMRange = getRangeForAxisFromSettings(getYAxisWalker().getId(YAXIS.W.idx)); // The combination of Range and Bounds is crazy.. if (fixedWinMRange != null) { boundsInMGauge = new DoubleBounds(fixedWinMRange.getLowerBound(), fixedWinMRange.getUpperBound()); } log.debug("Syncing Axis Bounds. Bounds W: " + boundsInMGauge.toString() + " Bounds Wcm: " + boundsInCM.toString()); final double datum = getCurrentGaugeDatum(); // Convert boundsInMGauge to Datum+cm final double convertedLower = ((Double) boundsInMGauge.getLower() - datum) * 100; final double convertedUpper = ((Double) boundsInMGauge.getUpper() - datum) * 100; final Bounds convertedBounds = new DoubleBounds(convertedLower, convertedUpper); // Now combine both Ranges boundsInCM = boundsInCM.combine(convertedBounds); // Recalculate absolute bounds boundsInMGauge = new DoubleBounds((Double) boundsInCM.getLower() / 100d + datum, (Double) boundsInCM.getUpper() / 100d + datum); // Set the new combined bounds setYBounds(YAXIS.W.idx, boundsInMGauge); setYBounds(YAXIS.WCm.idx, boundsInCM); log.debug("Synced Bounds W: " + boundsInMGauge.toString() + " Bounds Wcm: " + boundsInCM.toString()); } public DischargeCurveGenerator() { super(); } @Override protected YAxisWalker getYAxisWalker() { return new YAxisWalker() { @Override public int length() { return YAXIS.values().length; } @Override public String getId(final int idx) { final YAXIS[] yaxes = YAXIS.values(); return yaxes[idx].toString(); } }; } /** * Returns always null to suppress subtitles. */ @Override protected String getDefaultChartTitle(final CallContext context) { return null; } @Override protected String getDefaultXAxisLabel(final CallContext context) { return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT); } @Override protected String getDefaultYAxisLabel(final int pos) { return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT); } /* TODO is this one really needed? */ @Override protected boolean zoomX(final XYPlot plot, final ValueAxis axis, final Bounds bounds, final Range x) { final boolean zoomin = super.zoom(plot, axis, bounds, x); if (!zoomin) { axis.setLowerBound(0d); } return zoomin; } /** Translate River annotations if a gauge. */ public void translateRiverAnnotation(final RiverAnnotation riverAnnotation) { if (getCurrentGaugeDatum() == 0d) { return; } log.debug("Translate some river annotation."); final double translate = getCurrentGaugeDatum(); final double factor = 100d; for (final StickyAxisAnnotation annotation : riverAnnotation.getAxisTextAnnotations()) { if (!annotation.atX()) { annotation.setPos((annotation.getPos() - translate) * factor); } } for (final XYTextAnnotation annotation : riverAnnotation.getTextAnnotations()) { annotation.setY((annotation.getY() - translate) * factor); } } @Override public void doOut(final ArtifactAndFacet artifactFacet, final ThemeDocument theme, final boolean visible) { final String name = artifactFacet.getFacetName(); log.debug("DischargeCurveGenerator.doOut: " + name); final CallContext context = getContext(); final MiscDischargeProcessor dProcessor = new MiscDischargeProcessor(getRange()[0]); if (dProcessor.canHandle(name)) { // In Base DischargeCurveGenerator, always at gauge, use WCm axis. dProcessor.doOut(this, artifactFacet, theme, visible, YAXIS.WCm.idx); } else if (name.equals(DISCHARGE_CURVE) || name.equals(GAUGE_DISCHARGE_CURVE)) { doDischargeOut((D4EArtifact) artifactFacet.getArtifact(), artifactFacet.getData(context), artifactFacet.getFacetDescription(), theme, visible); } else if (FacetTypes.IS.MANUALPOINTS(name)) { doPoints(artifactFacet.getData(context), artifactFacet, theme, visible, YAXIS.W.idx); } else if (STATIC_WQ.equals(name)) { doWQOut(artifactFacet.getData(context), artifactFacet, theme, visible); } else { log.warn("DischargeCurveGenerator.doOut: Unknown facet name: " + name); return; } } /** * Add series with discharge curve to diagram. */ protected void doDischargeOut(final D4EArtifact artifact, final Object o, final String description, final ThemeDocument theme, final boolean visible) { log.debug("DischargeCurveGenerator.doDischargeOut"); final WQKms wqkms = (WQKms) o; final String gaugeName = wqkms.getName(); final River river = new RiverAccess(artifact).getRiver(); if (river == null) { log.debug("no river found"); return; } final Gauge gauge = river.determineGaugeByName(gaugeName); if (gauge == null) { log.debug("no gauge found"); return; } final XYSeries series = new StyledXYSeries(description, theme); StyledSeriesBuilder.addPointsQW(series, wqkms); addAxisSeries(series, YAXIS.W.idx, visible); } /** * Add W/Q-Series to plot. * * @param wqkms * actual data * @param theme * theme to use. */ protected void doQOut(final Object wqkms, final ArtifactAndFacet aaf, final ThemeDocument theme, final boolean visible) { log.debug("DischargeCurveGenerator: doQOut (add W/Q data)."); final XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme); StyledSeriesBuilder.addPointsQW(series, (WQKms) wqkms); addAxisSeries(series, YAXIS.W.idx, visible); } /** Add a point annotation at given x and y coordinates. */ protected void addPointTextAnnotation(final String title, final double x, final double y, final ThemeDocument theme) { final List<XYTextAnnotation> textAnnos = new ArrayList<>(); final XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(title, x, y); textAnnos.add(anno); final RiverAnnotation flysAnno = new RiverAnnotation(null, null, null, theme); flysAnno.setTextAnnotations(textAnnos); addAnnotations(flysAnno); } /** * Return true if all values in data[0] are smaller than zero * (in imported data they are set to -1 symbolically). * Return false if data is null or empty */ private static boolean hasNoDischarge(final double[][] data) { if (data == null || data.length == 0) { return false; } final double[] qs = data[0]; for (final double q : qs) { if (q > 0d) { return false; } } return true; } /** * Add WQ Data to plot. * * @param wq * data as double[][] */ protected void doWQOut(final Object wq, final ArtifactAndFacet aaf, final ThemeDocument theme, final boolean visible) { log.debug("DischargeCurveGenerator: doWQOut"); final double[][] data = (double[][]) wq; final String title = aaf.getFacetDescription(); final double translate = getCurrentGaugeDatum(); // If no Q values (i.e. all -1) found, add annotations. if (hasNoDischarge(data)) { final List<StickyAxisAnnotation> xy = new ArrayList<>(); for (double y : data[1]) { if (translate != 0d) { y = (y - translate) * 100d; } xy.add(new StickyAxisAnnotation(title, (float) y, StickyAxisAnnotation.SimpleAxis.Y_AXIS)); } doAnnotations(new RiverAnnotation(title, xy), aaf, theme, visible); return; } // Otherwise add points. final XYSeries series = new StyledXYSeries(title, theme); if (translate != 0d) { StyledSeriesBuilder.addPointsQW(series, data, -translate, 100d); addAxisSeries(series, YAXIS.W.idx, visible); } else { StyledSeriesBuilder.addPoints(series, data, true); addAxisSeries(series, YAXIS.W.idx, visible); } if (visible && theme.parseShowPointLabel() && data != null && data.length != 0) { final double[] xs = data[0]; final double[] ys = data[1]; for (int i = 0; i < xs.length; i++) { final double x = xs[i]; double y = ys[i]; if (translate != 0d) { y = (y - translate) * 100d; } addPointTextAnnotation(title, x, y, theme); } } } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :