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.fixings;
christian@3055:
christian@3770: import java.awt.BasicStroke;
christian@3770: import java.awt.Color;
christian@3770: import java.text.DateFormat;
christian@3770: import java.util.ArrayList;
christian@3770: import java.util.List;
christian@3770:
christian@3770: import org.apache.log4j.Logger;
christian@3770: import org.jfree.chart.JFreeChart;
christian@3770: import org.jfree.chart.annotations.XYTextAnnotation;
christian@3770: import org.jfree.chart.plot.Marker;
christian@3770: import org.jfree.chart.plot.ValueMarker;
felix@6559: import org.jfree.chart.plot.XYPlot;
christian@3770: import org.jfree.chart.title.TextTitle;
christian@3770: import org.jfree.data.xy.XYSeries;
christian@3770: import org.jfree.ui.RectangleAnchor;
christian@3911: import org.jfree.ui.RectangleInsets;
christian@3770: import org.jfree.ui.TextAnchor;
christian@3770:
teichmann@5831: import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
teichmann@5831: import org.dive4elements.artifactdatabase.state.Facet;
teichmann@5867: import org.dive4elements.river.artifacts.D4EArtifact;
teichmann@5831: import org.dive4elements.river.artifacts.StaticWKmsArtifact;
teichmann@5831: import org.dive4elements.river.artifacts.WINFOArtifact;
teichmann@5831: import org.dive4elements.river.artifacts.access.FixAnalysisAccess;
teichmann@5831: import org.dive4elements.river.artifacts.model.DateRange;
teichmann@5831: import org.dive4elements.river.artifacts.model.FacetTypes;
teichmann@5831: import org.dive4elements.river.artifacts.model.NamedDouble;
teichmann@5831: import org.dive4elements.river.artifacts.model.QWDDateRange;
teichmann@5831: import org.dive4elements.river.artifacts.model.WKms;
teichmann@5831: import org.dive4elements.river.artifacts.model.WQKms;
teichmann@5831: import org.dive4elements.river.artifacts.model.fixings.FixFunction;
teichmann@5831: import org.dive4elements.river.artifacts.model.fixings.FixWQCurveFacet;
teichmann@5831: import org.dive4elements.river.artifacts.model.fixings.QWD;
teichmann@5831: import org.dive4elements.river.artifacts.model.fixings.QWI;
teichmann@5831: import org.dive4elements.river.artifacts.resources.Resources;
teichmann@5831: import org.dive4elements.river.exports.ChartGenerator;
felix@6467: import org.dive4elements.river.exports.DischargeCurveGenerator;
teichmann@5831: import org.dive4elements.river.exports.StyledSeriesBuilder;
teichmann@5831: import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation;
teichmann@5864: import org.dive4elements.river.jfree.RiverAnnotation;
teichmann@5831: import org.dive4elements.river.jfree.JFreeUtil;
teichmann@5831: import org.dive4elements.river.jfree.StickyAxisAnnotation;
teichmann@5831: import org.dive4elements.river.jfree.StyledXYSeries;
teichmann@5831: import org.dive4elements.river.model.Gauge;
teichmann@5831: import org.dive4elements.river.model.River;
teichmann@6905: import org.dive4elements.river.themes.ThemeDocument;
teichmann@5865: import org.dive4elements.river.utils.RiverUtils;
sascha@7542: import org.dive4elements.river.java2d.ShapeUtils;
christian@3055:
aheinecke@7691: import org.dive4elements.river.jfree.Bounds;
aheinecke@7691: import org.dive4elements.river.jfree.DoubleBounds;
aheinecke@7691:
aheinecke@7691: import org.jfree.data.Range;
aheinecke@7691:
christian@3254: /**
christian@3286: * Generator for WQ fixing charts.
christian@3254: * @author Christian Lins
christian@3254: */
sascha@3076: public class FixWQCurveGenerator
sascha@3215: extends FixChartGenerator
sascha@3215: implements FacetTypes
christian@3055: {
teichmann@8202: /** Private log. */
teichmann@8202: private static Logger log =
christian@3770: Logger.getLogger(FixWQCurveGenerator.class);
christian@3055:
christian@3055: public static final String I18N_CHART_TITLE =
christian@3770: "chart.fixings.wq.title";
christian@3055:
christian@3055: public static final String I18N_CHART_SUBTITLE =
christian@3770: "chart.fixings.wq.subtitle";
christian@3055:
christian@3410: public static final String I18N_CHART_SUBTITLE1 =
christian@3770: "chart.fixings.wq.subtitle1";
christian@3410:
christian@3055: public static final String I18N_XAXIS_LABEL =
christian@3770: "chart.fixings.wq.xaxis.label";
christian@3055:
christian@3055: public static final String I18N_YAXIS_LABEL =
christian@3770: "chart.fixings.wq.yaxis.label";
christian@3055:
christian@3055: public static final String I18N_CHART_TITLE_DEFAULT =
christian@3770: "Fixierungsanalyse";
christian@3055:
christian@3055: public static final String I18N_XAXIS_LABEL_DEFAULT =
christian@3770: "Q [m\u00B3/s]";
christian@3055:
christian@3055: public static final String I18N_YAXIS_LABEL_DEFAULT =
christian@3770: "W [NN + m]";
christian@3055:
felix@5739: public static final double EPSILON = 0.001d;
felix@5739:
christian@3055: public static enum YAXIS {
felix@6467: WCm(0),
felix@6467: W(1);
christian@3055: public int idx;
christian@3055: private YAXIS(int c) {
christian@3055: idx = c;
christian@3055: }
christian@3055: }
christian@3055:
christian@3406:
felix@4147: /** Needed to access data to create subtitle. */
teichmann@5867: protected D4EArtifact artifact;
christian@3406:
felix@6559: /** Returns value != 0 if the current km is not at a gauge. */
felix@6467: public double getCurrentGaugeDatum() {
teichmann@7525: Object ckm = context.getContextValue(CURRENT_KM);
teichmann@7525: if (ckm != null) {
felix@6467: return DischargeCurveGenerator.getCurrentGaugeDatum(
teichmann@7525: (Double) ckm,
felix@6531: (D4EArtifact) getMaster(), 1e-4);
felix@6467: }
teichmann@7525: return 0d;
felix@6467: }
felix@6467:
felix@6559: /** Overriden to show second axis also if no visible data present. */
felix@6559: @Override
felix@6559: protected void adjustAxes(XYPlot plot) {
felix@6559: super.adjustAxes(plot);
felix@6559: if (getCurrentGaugeDatum() != 0d) {
felix@6559: // Show the W[*m] axis even if there is no data.
felix@6559: plot.setRangeAxis(1, createYAxis(YAXIS.W.idx));
aheinecke@7691: syncWAxisRanges();
felix@6559: }
felix@6559: }
felix@6559:
aheinecke@7691: // XXX This is a copy of DischargeCurveGenerator syncWAxisRanges
aheinecke@7691: // even without fancy Q Symetry this class should inherit
aheinecke@7691: // from there..
aheinecke@7691: protected void syncWAxisRanges() {
aheinecke@7691: // Syncronizes the ranges of both W Axes to make sure
aheinecke@7691: // that the Data matches for both axes.
aheinecke@7691: Bounds boundsInMGauge = getYBounds(YAXIS.W.idx);
aheinecke@7691: Bounds boundsInCM = getYBounds(YAXIS.WCm.idx);
aheinecke@7691:
aheinecke@7691: // XXX Q-Symetry: I am assuming here that there can only
aheinecke@7691: // be a fixed Range for WinM as this is currently the only
aheinecke@7691: // thing that is configureable.
aheinecke@7691: Range fixedWinMRange = getRangeForAxisFromSettings(
aheinecke@7691: getYAxisWalker().getId(YAXIS.W.idx));
aheinecke@7691:
aheinecke@7691: // The combination of Range and Bounds is crazy..
aheinecke@7691: if (fixedWinMRange != null) {
aheinecke@7691: boundsInMGauge = new DoubleBounds(fixedWinMRange.getLowerBound(),
aheinecke@7691: fixedWinMRange.getUpperBound());
aheinecke@7691: }
aheinecke@7691:
teichmann@8202: log.debug("Syncing Axis Bounds. Bounds W: " + boundsInMGauge.toString() +
aheinecke@7691: " Bounds Wcm: " + boundsInCM.toString());
aheinecke@7691:
aheinecke@7691: double datum = getCurrentGaugeDatum();
aheinecke@7691:
aheinecke@7691: // Convert boundsInMGauge to Datum+cm
aheinecke@7691: double convertedLower = ((Double)boundsInMGauge.getLower() - datum) * 100;
aheinecke@7691: double convertedUpper = ((Double)boundsInMGauge.getUpper() - datum) * 100;
aheinecke@7691: Bounds convertedBounds = new DoubleBounds(convertedLower, convertedUpper);
aheinecke@7691:
aheinecke@7691: // Now combine both Ranges
aheinecke@7691: boundsInCM = boundsInCM.combine(convertedBounds);
aheinecke@7691:
aheinecke@7691: // Recalculate absolute bounds
aheinecke@7691: boundsInMGauge = new DoubleBounds((Double)boundsInCM.getLower() / 100d + datum,
aheinecke@7691: (Double)boundsInCM.getUpper() / 100d + datum);
aheinecke@7691:
aheinecke@7691: // Set the new combined bounds
aheinecke@7691: setYBounds(YAXIS.W.idx, boundsInMGauge);
aheinecke@7691: setYBounds(YAXIS.WCm.idx, boundsInCM);
teichmann@8202: log.debug("Synced Bounds W: " + boundsInMGauge.toString() +
aheinecke@7691: " Bounds Wcm: " + boundsInCM.toString());
aheinecke@7691: }
aheinecke@7691:
christian@3055: @Override
teichmann@6905: public void doOut(ArtifactAndFacet aaf, ThemeDocument doc, boolean visible) {
teichmann@8202: log.debug("doOut: " + aaf.getFacetName());
felix@4361: if (!prepareChartData(aaf, doc, visible)) {
teichmann@8202: log.warn("Unknown facet, name " + aaf.getFacetName());
felix@4361: }
felix@4361: }
felix@4361:
felix@6472: /**
felix@6472: * Return true if data could be handled,
felix@6472: * to be overridden to add more handled data.
felix@6472: */
teichmann@6905: public boolean prepareChartData(ArtifactAndFacet aaf, ThemeDocument doc, boolean visible) {
christian@3056: String name = aaf.getFacetName();
christian@3056:
felix@6426: this.artifact = (D4EArtifact) aaf.getArtifact();
christian@3406:
christian@3133: if(name.startsWith(FIX_SECTOR_AVERAGE_WQ)) {
christian@3056: doSectorAverageOut(aaf, doc, visible);
christian@3062: }
christian@3062: else if(FIX_ANALYSIS_EVENTS_WQ.equals(name)) {
christian@3056: doAnalysisEventsOut(aaf, doc, visible);
christian@3062: }
teichmann@7525: else if(FIX_REFERENCE_EVENTS_WQ.equals(name)
teichmann@7525: || FIX_EVENTS.equals(name)) {
christian@3056: doReferenceEventsOut(aaf, doc, visible);
christian@3062: }
christian@3062: else if(FIX_WQ_CURVE.equals(name)) {
christian@3056: doWQCurveOut(aaf, doc, visible);
christian@3062: }
christian@3062: else if(FIX_OUTLIER.equals(name)) {
christian@3056: doOutlierOut(aaf, doc, visible);
christian@3062: }
felix@3578: else if(QSECTOR.equals(name)) {
felix@3578: doQSectorOut(aaf, doc, visible);
felix@3578: }
teichmann@7525: /*
felix@5731: else if(FIX_EVENTS.equals(name)) {
felix@5731: doEventsOut(aaf, doc, visible);
felix@5731: }
teichmann@7525: */
felix@4143: else if(/*STATIC_WKMS_INTERPOL.equals(name) ||*/
felix@4143: STATIC_WKMS_MARKS.equals(name) ||
felix@3587: STATIC_WKMS.equals(name) ||
felix@3587: HEIGHTMARKS_POINTS.equals(name) ) {
felix@3587: doWAnnotations(
felix@3587: aaf.getData(context),
felix@3587: aaf,
felix@3587: doc,
felix@3587: visible);
felix@3587: }
felix@4143: else if (LONGITUDINAL_W.equals(name) || STATIC_WQ.equals(name)
felix@6492: || STATIC_WKMS_INTERPOL.equals(name)
felix@6492: || FIX_WQ_LS.equals(name)) {
felix@3585: doWQOut(aaf.getData(context), aaf, doc, visible);
felix@3585: }
felix@3588: else if (name.equals(DISCHARGE_CURVE)) {
teichmann@8202: log.debug("diso " + name);
felix@3588: doDischargeOut(
christian@3770: (WINFOArtifact) aaf.getArtifact(),
christian@3770: aaf.getData(context),
christian@3770: aaf.getFacetDescription(),
christian@3770: doc,
christian@3770: visible);
christian@3770: }
rrenkert@5978: else if (name.equals(MAINVALUES_W) || name.equals(MAINVALUES_Q)) {
felix@6527: RiverAnnotation mainValues = (RiverAnnotation) aaf.getData(context);
rrenkert@5978: doAnnotations(
felix@6527: mainValues,
rrenkert@5978: aaf,
rrenkert@5978: doc,
rrenkert@5978: visible);
rrenkert@5978: }
christian@3770: else if (FacetTypes.IS.MANUALPOINTS(aaf.getFacetName())) {
christian@3770: doPoints(aaf.getData(context),
christian@3770: aaf,
christian@3770: doc, visible, YAXIS.W.idx);
felix@3588: }
christian@3062: else {
felix@4361: return false;
christian@3055: }
felix@4361: return true;
christian@3055: }
christian@3055:
felix@4361:
felix@6426: /** Add sector average points to chart. */
teichmann@6905: protected void doSectorAverageOut(ArtifactAndFacet aaf, ThemeDocument doc, boolean visible) {
teichmann@8202: log.debug("doSectorAverageOut");
christian@3055:
ingo@3551: QWDDateRange qwdd = (QWDDateRange) aaf.getData(context);
ingo@3551: QWD qwd = qwdd != null ? qwdd.getQWD() : null;
ingo@3551:
christian@3132: if(qwd != null) {
christian@3194: addQWSeries(new QWD[] { qwd }, aaf, doc, visible);
christian@3062: }
christian@3133: else {
teichmann@8202: log.debug("doSectorAverageOut: qwd == null");
christian@3133: }
christian@3055: }
christian@3055:
felix@6426: /** Add analysis event points to chart. */
teichmann@7527: protected void doAnalysisEventsOut(
teichmann@7527: ArtifactAndFacet aaf,
teichmann@7527: ThemeDocument doc,
teichmann@7527: boolean visible
teichmann@7527: ) {
teichmann@8202: log.debug("doAnalysisEventsOut");
christian@3055:
raimund@3610: QWD qwd = (QWD)aaf.getData(context);
raimund@3610:
teichmann@7527: if (qwd == null) {
teichmann@8202: log.debug("doAnalysisEventsOut: qwd == null");
teichmann@7527: return;
teichmann@7527: }
raimund@3610:
teichmann@7527: double gaugeDatum = getCurrentGaugeDatum();
teichmann@7527: boolean atGauge = gaugeDatum != 0d;
teichmann@7527:
teichmann@7527: double factor = atGauge ? 100d : 1d;
teichmann@7527:
teichmann@7527: double w = factor*(qwd.getW()-gaugeDatum);
teichmann@7527:
teichmann@7545: // Force empty symbol.
teichmann@7545: if (qwd.getInterpolated()) {
teichmann@7545: doc = new ThemeDocument(doc); // prevent potential side effects.
teichmann@7545: doc.setValue(ThemeDocument.USE_FILL_PAINT, "true");
teichmann@7545: }
teichmann@7545:
sascha@7542: XYSeries series = new StyledXYSeries(
sascha@7542: aaf.getFacetDescription(),
sascha@7542: doc,
sascha@7542: qwd.getInterpolated()
sascha@7542: ? ShapeUtils.INTERPOLATED_SHAPE
sascha@7542: : ShapeUtils.MEASURED_SHAPE);
teichmann@7527:
sascha@7542: series.add(qwd.getQ(), w);
teichmann@7527:
teichmann@7527: addAxisSeries(series, atGauge ? YAXIS.WCm.idx : YAXIS.W.idx, visible);
teichmann@7527:
teichmann@7527: if (visible && doc.parseShowPointLabel()) {
sascha@7542:
sascha@7542: List textAnnos = new ArrayList();
sascha@7542:
sascha@7542: DateFormat dateFormat = DateFormat.getDateInstance(
sascha@7542: DateFormat.SHORT);
sascha@7542: XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
sascha@7542: dateFormat.format(qwd.getDate()),
sascha@7542: qwd.getQ(),
sascha@7542: w);
sascha@7542: textAnnos.add(anno);
sascha@7542:
teichmann@7527: RiverAnnotation flysAnno = new RiverAnnotation(null, null, null, doc);
teichmann@7527: flysAnno.setTextAnnotations(textAnnos);
teichmann@7527: addAnnotations(flysAnno);
christian@3095: }
christian@3055: }
christian@3055:
felix@4323:
felix@6426: /** Add reference event points to chart. */
teichmann@6905: protected void doReferenceEventsOut(ArtifactAndFacet aaf, ThemeDocument doc, boolean visible) {
teichmann@8202: log.debug("doReferenceEventsOut");
sascha@3110:
christian@3770: QWI qwd = (QWI)aaf.getData(context);
teichmann@6868: if (qwd == null) {
teichmann@8202: log.debug("doReferenceEventsOut: qwds == null");
teichmann@6868: return;
teichmann@6868: }
raimund@3610:
teichmann@7545: // Force empty symbol.
teichmann@7545: if (qwd.getInterpolated()) {
teichmann@7545: doc = new ThemeDocument(doc); // prevent potential side effects.
teichmann@7545: doc.setValue(ThemeDocument.USE_FILL_PAINT, "true");
teichmann@7545: }
teichmann@7545:
sascha@7542: XYSeries series = new StyledXYSeries(
sascha@7542: aaf.getFacetDescription(),
sascha@7542: false, true, doc,
sascha@7542: qwd.getInterpolated()
sascha@7542: ? ShapeUtils.INTERPOLATED_SHAPE
sascha@7542: : ShapeUtils.MEASURED_SHAPE);
teichmann@6868:
felix@7258: double gaugeDatum = getCurrentGaugeDatum();
teichmann@6868:
sascha@7542: boolean atGauge = gaugeDatum != 0d;
sascha@7542:
sascha@7542: double factor = atGauge ? 100d : 1d;
sascha@7542: double w = factor*(qwd.getW()-gaugeDatum);
sascha@7542:
sascha@7542: series.add(qwd.getQ(), w, false);
sascha@7542:
sascha@7542: if (visible && doc.parseShowPointLabel()) {
sascha@7542: DateFormat dateFormat = DateFormat.getDateInstance(
sascha@7542: DateFormat.SHORT);
sascha@7542:
sascha@7542: XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
teichmann@6868: dateFormat.format(qwd.getDate()),
teichmann@6868: qwd.getQ(),
sascha@7542: w);
teichmann@6868:
sascha@7542: List textAnnos = new ArrayList();
sascha@7542: textAnnos.add(anno);
teichmann@6868: RiverAnnotation flysAnno = new RiverAnnotation(null, null, null, doc);
teichmann@6868: flysAnno.setTextAnnotations(textAnnos);
teichmann@6868: addAnnotations(flysAnno);
raimund@3610: }
felix@7258:
sascha@7542: addAxisSeries(series, atGauge ? YAXIS.WCm.idx : YAXIS.W.idx, visible);
christian@3055: }
christian@3055:
felix@5740:
felix@5740: private void addPointFromWQKms(WQKms wqkms,
teichmann@6905: String title,
teichmann@6905: ThemeDocument theme,
teichmann@6905: boolean visible
felix@5740: ) {
felix@5740: XYSeries series = new StyledXYSeries(title, theme);
felix@5731: Double ckm = (Double) context.getContextValue(CURRENT_KM);
felix@5739: if (wqkms == null || wqkms.getKms().length == 0 || ckm == null) {
teichmann@8202: log.info("addPointFromWQKms: No event data to show.");
felix@5739: return;
felix@5739: }
felix@5731: double[] kms = wqkms.getKms();
felix@7260: double gaugeDatum = getCurrentGaugeDatum();
felix@7260: double factor = (gaugeDatum == 0d) ? 1d : 100d;
felix@5731: for (int i = 0 ; i< kms.length; i++) {
felix@5739: if (Math.abs(kms[i] - ckm) <= EPSILON) {
teichmann@6876: series.add(wqkms.getQ(i), wqkms.getW(i), false);
felix@5731: addAxisSeries(series, YAXIS.W.idx, visible);
teichmann@6905: if(visible && theme.parseShowPointLabel()) {
felix@6542: List textAnnos = new ArrayList();
felix@6542: XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
felix@6542: title,
felix@6542: wqkms.getQ(i),
felix@7260: factor*(wqkms.getW(i)-gaugeDatum));
felix@6542: textAnnos.add(anno);
felix@6542: RiverAnnotation flysAnno = new RiverAnnotation(null, null, null, theme);
felix@6542: flysAnno.setTextAnnotations(textAnnos);
felix@6542: addAnnotations(flysAnno);
felix@6542: }
felix@5731: return;
felix@5731: }
felix@5731: }
felix@5731: }
felix@4323:
teichmann@6905: protected void doEventsOut(ArtifactAndFacet aaf, ThemeDocument doc, boolean visible) {
teichmann@8202: log.debug("doEventsOut");
felix@5740: // Find W/Q at km.
felix@5740: addPointFromWQKms((WQKms) aaf.getData(context),
felix@5740: aaf.getFacetDescription(), doc, visible);
felix@5740: }
felix@5740:
felix@5740:
teichmann@6905: protected void doWQCurveOut(ArtifactAndFacet aaf, ThemeDocument doc, boolean visible) {
teichmann@8202: log.debug("doWQCurveOut");
christian@3055:
christian@3081: FixWQCurveFacet facet = (FixWQCurveFacet)aaf.getFacet();
christian@3081: FixFunction func = (FixFunction)facet.getData(
christian@3770: aaf.getArtifact(), context);
sascha@3073:
sascha@3073: if (func == null) {
teichmann@8202: log.warn("doWQCurveOut: Facet does not contain FixFunction");
sascha@3073: return;
sascha@3073: }
sascha@3110:
christian@3081: double maxQ = func.getMaxQ();
sascha@3073:
sascha@3215: if (maxQ > 0) {
christian@3192: StyledXYSeries series = JFreeUtil.sampleFunction2D(
christian@3770: func.getFunction(),
christian@3770: doc,
christian@3770: aaf.getFacetDescription(),
christian@3770: 500, // number of samples
christian@3770: 0.0 , // start
christian@3770: maxQ); // end
christian@3155:
felix@6474: double gaugeDatum = getCurrentGaugeDatum();
felix@6474:
felix@6474: if (gaugeDatum == 0d) {
felix@6474: addAxisSeries(series, YAXIS.W.idx, visible);
felix@6474: }
felix@6474: else {
felix@6530: StyledXYSeries series2 = JFreeUtil.sampleFunction2D(
felix@6530: func.getFunction(),
felix@6530: doc,
felix@6530: aaf.getFacetDescription(),
felix@6530: 500, // number of samples
felix@6530: 0.0 , // start
felix@6530: maxQ); // end
felix@6530: addAxisSeries(series2, YAXIS.W.idx, false);
felix@6474: // Use second axis at cm if at gauge.
teichmann@7525: for (int i = 0, N = series.getItemCount(); i < N; i++) {
teichmann@7525: series.updateByIndex(
teichmann@7525: i, new Double(100d*(series.getY(i).doubleValue()-gaugeDatum)));
felix@6474: }
felix@6474: addAxisSeries(series, YAXIS.WCm.idx, visible);
felix@6474: }
christian@3055: }
christian@3062: else {
teichmann@8202: log.warn("doWQCurveOut: maxQ <= 0");
christian@3062: }
christian@3055: }
christian@3055:
teichmann@6905: protected void doOutlierOut(ArtifactAndFacet aaf, ThemeDocument doc, boolean visible) {
teichmann@8202: log.debug("doOutlierOut");
christian@3055:
sascha@3729: QWI[] qws = (QWI[])aaf.getData(context);
christian@3062: addQWSeries(qws, aaf, doc, visible);
christian@3062: }
christian@3062:
felix@3578:
felix@3578: /** Add markers for q sectors. */
teichmann@6905: protected void doQSectorOut(ArtifactAndFacet aaf, ThemeDocument theme, boolean visible) {
teichmann@8202: log.debug("doQSectorOut");
felix@3578: if (!visible) {
felix@3578: return;
felix@3578: }
felix@3578:
christian@3770: Object qsectorsObj = aaf.getData(context);
christian@3770: if (qsectorsObj == null || !(qsectorsObj instanceof List)) {
teichmann@8202: log.warn("No QSectors coming from data.");
felix@3578: return;
felix@3578: }
christian@3770:
christian@3770: List> qsectorsList = (List>) qsectorsObj;
christian@3770: if (qsectorsList.size() == 0 || !(qsectorsList.get(0) instanceof NamedDouble)) {
teichmann@8202: log.warn("No QSectors coming from data.");
christian@3770: return;
christian@3770: }
christian@3770:
christian@3770: @SuppressWarnings("unchecked")
christian@3770: List qsectors = (List) qsectorsList;
christian@3770:
felix@3578: for (NamedDouble qsector : qsectors) {
felix@3578: if (Double.isNaN(qsector.getValue())) {
felix@3578: continue;
felix@3578: }
felix@3578: Marker m = new ValueMarker(qsector.getValue());
felix@3578: m.setPaint(Color.black);
felix@3578:
teichmann@6905: float[] dashes = theme.parseLineStyle();
teichmann@6905: int size = theme.parseLineWidth();
sascha@3593: BasicStroke stroke;
felix@3578: if (dashes.length <= 1) {
sascha@3593: stroke = new BasicStroke(size);
felix@3578: }
felix@3578: else {
sascha@3593: stroke = new BasicStroke(size,
christian@3770: BasicStroke.CAP_BUTT,
christian@3770: BasicStroke.JOIN_ROUND,
christian@3770: 1.0f,
christian@3770: dashes,
christian@3770: 0.0f);
sascha@3593: }
felix@3578: m.setStroke(stroke);
felix@3578:
teichmann@6905: if (theme.parseShowLineLabel()) {
felix@3578: m.setLabel(qsector.getName());
teichmann@6905: m.setPaint(theme.parseTextColor());
teichmann@6905: m.setLabelFont(theme.parseTextFont());
felix@3578: }
teichmann@6905: Color paint = theme.parseLineColorField();
felix@3578: if (paint != null) {
felix@3578: m.setPaint(paint);
felix@3578: }
felix@3578: m.setLabelAnchor(RectangleAnchor.TOP_LEFT);
felix@3578: m.setLabelTextAnchor(TextAnchor.TOP_LEFT);
christian@3911: m.setLabelOffset(new RectangleInsets(5, 5, 10, 10));
felix@3578: addDomainMarker(m);
felix@3578: }
felix@3578: }
felix@3578:
felix@3596:
felix@3596: /**
felix@3587: * Add W-Annotations to plot.
felix@3587: * @param wqkms actual data (double[][]).
felix@3587: * @param theme theme to use.
felix@3587: */
felix@3587: protected void doWAnnotations(
teichmann@6905: Object wqkms,
christian@3770: ArtifactAndFacet aandf,
teichmann@6905: ThemeDocument theme,
teichmann@6905: boolean visible
christian@3770: ) {
felix@3587: Facet facet = aandf.getFacet();
felix@3587:
felix@3587: List xy = new ArrayList();
felix@3587: if (wqkms instanceof double[][]) {
teichmann@8202: log.debug("Got double[][]");
felix@3587: double [][] data = (double [][]) wqkms;
felix@3587: for (int i = 0; i< data[0].length; i++) {
felix@3587: xy.add(new StickyAxisAnnotation(aandf.getFacetDescription(),
christian@3770: (float) data[1][i], StickyAxisAnnotation.SimpleAxis.Y_AXIS));
felix@3587: }
felix@3587:
teichmann@5864: doAnnotations(new RiverAnnotation(facet.getDescription(), xy),
christian@3770: aandf, theme, visible);
felix@3587: }
felix@3587: else {
felix@3587: // Assume its WKms.
teichmann@8202: log.debug("Got WKms");
felix@3587: WKms data = (WKms) wqkms;
felix@3596:
felix@3596: Double ckm = (Double) context.getContextValue(CURRENT_KM);
felix@3587: double location = (ckm != null)
christian@3770: ? ckm.doubleValue()
felix@4147: : getRange()[0];
felix@4147: double w = StaticWKmsArtifact.getWAtKmLin(data, location);
felix@4147: xy.add(new StickyAxisAnnotation(aandf.getFacetDescription(),
felix@4147: (float) w, StickyAxisAnnotation.SimpleAxis.Y_AXIS));
felix@3587:
teichmann@5864: doAnnotations(new RiverAnnotation(facet.getDescription(), xy),
felix@4147: aandf, theme, visible);
felix@3587: }
felix@3587: }
felix@3587:
felix@3587:
felix@3585: /**
felix@3588: * Add series with discharge curve to diagram.
felix@3588: */
felix@3588: protected void doDischargeOut(
christian@3770: WINFOArtifact artifact,
christian@3770: Object o,
christian@3770: String description,
teichmann@6905: ThemeDocument theme,
christian@3770: boolean visible)
felix@3588: {
felix@3588: WQKms wqkms = (WQKms) o;
felix@3588:
felix@3588: String gaugeName = wqkms.getName();
felix@3588:
teichmann@5865: River river = RiverUtils.getRiver(artifact);
felix@3588:
felix@3588: if (river == null) {
teichmann@8202: log.debug("no river found");
felix@3588: return;
felix@3588: }
felix@3588:
felix@3588: Gauge gauge = river.determineGaugeByName(gaugeName);
felix@3588:
felix@3588: if (gauge == null) {
teichmann@8202: log.debug("no gauge found");
felix@3588: return;
felix@3588: }
felix@3588:
felix@3588: XYSeries series = new StyledXYSeries(description, theme);
felix@7258:
felix@7258: double gaugeDatum = getCurrentGaugeDatum();
felix@7258:
felix@7258: if (true || gaugeDatum == 0d) {
felix@7258: StyledSeriesBuilder.addPointsQW(series, wqkms);
felix@7258: addAxisSeries(series, YAXIS.W.idx, visible);
felix@7258: }
felix@7258: else {
felix@7258: XYSeries series2 = new StyledXYSeries(description, theme);
felix@7258: StyledSeriesBuilder.addPointsQW(series2, wqkms);
felix@7258: addAxisSeries(series2, YAXIS.W.idx, false);
felix@7258:
felix@7258: // Use second axis...
felix@7258: StyledSeriesBuilder.addPointsQW(series, wqkms, -gaugeDatum, 100d);
felix@7258: addAxisSeries(series, YAXIS.WCm.idx, visible);
felix@7258: }
felix@3588: }
felix@3588:
felix@3596:
felix@3588: /**
felix@3585: * Add WQ Data to plot.
felix@3585: * @param wqkms data as double[][]
felix@3585: */
felix@3585: protected void doWQOut(
christian@3770: Object wqkms,
christian@3770: ArtifactAndFacet aaf,
teichmann@6905: ThemeDocument theme,
christian@3770: boolean visible
christian@3770: ) {
teichmann@8202: log.debug("FixWQCurveGenerator: doWQOut");
felix@3596: if (wqkms instanceof WQKms) {
felix@5739: // TODO As in doEventsOut, the value-searching should
felix@5739: // be delivered by the facet already (instead of in the Generator).
teichmann@8202: log.debug("FixWQCurveGenerator: doWQOut: WQKms");
felix@3585:
felix@5740: addPointFromWQKms((WQKms) aaf.getData(context), aaf.getFacetDescription(), theme, visible);
felix@3596: }
felix@3596: else {
teichmann@8202: log.debug("FixWQCurveGenerator: doWQOut: double[][]");
felix@3596: double [][] data = (double [][]) wqkms;
felix@3596:
teichmann@6876: XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), false, true, theme);
felix@3596: StyledSeriesBuilder.addPoints(series, data, true);
felix@3596:
felix@3596: addAxisSeries(series, YAXIS.W.idx, visible);
felix@3596: }
felix@3585: }
felix@3585:
felix@3596:
sascha@3729: protected void addQWSeries(
christian@3770: QWI [] qws,
christian@3770: ArtifactAndFacet aaf,
teichmann@6905: ThemeDocument theme,
christian@3770: boolean visible
christian@3770: ) {
sascha@3729: if (qws == null) {
sascha@3729: return;
sascha@3729: }
christian@3194:
teichmann@6876: XYSeries series = new StyledXYSeries(
teichmann@6876: aaf.getFacetDescription(),
teichmann@6876: false, true,
teichmann@6876: theme);
teichmann@6876:
sascha@3729: List textAnnos =
christian@3770: new ArrayList(qws.length);
christian@3062:
sascha@3729: DateFormat dateFormat = DateFormat.getDateInstance(
christian@3770: DateFormat.SHORT);
sascha@3729:
felix@7259: double gaugeDatum = getCurrentGaugeDatum();
felix@7259: double factor = (gaugeDatum == 0d) ? 1d : 100d;
sascha@3729: for (QWI qw: qws) {
felix@7259: series.add(qw.getQ(), factor*(qw.getW()-gaugeDatum), false);
sascha@3729:
sascha@3729: XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
christian@3770: dateFormat.format(qw.getDate()),
christian@3770: qw.getQ(),
felix@7259: factor*(qw.getW()-gaugeDatum));
sascha@3729: textAnnos.add(anno);
sascha@3729: }
sascha@3729:
felix@7259: if (gaugeDatum == 0d) {
felix@7259: addAxisSeries(series, YAXIS.W.idx, visible);
felix@7259: }
felix@7259: else {
felix@7259: addAxisSeries(series, YAXIS.WCm.idx, visible);
felix@7259: }
teichmann@7270: if (visible && theme != null && theme.parseShowPointLabel()) {
teichmann@5864: RiverAnnotation flysAnno =
teichmann@5864: new RiverAnnotation(null, null, null, theme);
sascha@3729: flysAnno.setTextAnnotations(textAnnos);
sascha@3729: addAnnotations(flysAnno);
christian@3055: }
christian@3055: }
christian@3055:
christian@3055: @Override
christian@3406: protected String getChartTitle() {
christian@3406: return Resources.format(
christian@3406: context.getMeta(),
christian@3406: I18N_CHART_TITLE,
christian@3406: I18N_CHART_TITLE_DEFAULT,
christian@3406: context.getContextValue(CURRENT_KM));
christian@3406: }
christian@3406:
christian@3406: @Override
christian@3055: protected String getDefaultChartTitle() {
christian@3055: return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
christian@3055: }
christian@3055:
christian@3055: @Override
christian@3406: protected String getDefaultChartSubtitle() {
teichmann@6101: FixAnalysisAccess access = new FixAnalysisAccess(artifact);
christian@3409: DateRange dateRange = access.getDateRange();
christian@3409: DateRange refRange = access.getReferencePeriod();
ingo@3466:
ingo@3466: if (dateRange != null && refRange != null) {
ingo@3466: return Resources.format(
ingo@3466: context.getMeta(),
ingo@3466: I18N_CHART_SUBTITLE,
ingo@3466: "",
felix@7261: access.getRiverName(),
ingo@3466: dateRange.getFrom(),
ingo@3466: dateRange.getTo(),
ingo@3466: refRange.getFrom(),
ingo@3466: refRange.getTo());
ingo@3466: }
ingo@3466:
ingo@3466: return null;
christian@3406: }
christian@3406:
christian@3406: @Override
christian@3410: protected void addSubtitles(JFreeChart chart) {
ingo@3466: String defaultSubtitle = getDefaultChartSubtitle();
ingo@3466:
ingo@3466: if (defaultSubtitle == null || defaultSubtitle.length() == 0) {
ingo@3466: return;
ingo@3466: }
ingo@3466:
ingo@3466: chart.addSubtitle(new TextTitle(defaultSubtitle));
ingo@3466:
christian@3410: StringBuilder buf = new StringBuilder();
christian@3410:
christian@3410: // Add analysis periods as additional subtitle
teichmann@6101: FixAnalysisAccess access = new FixAnalysisAccess(artifact);
christian@3410: DateRange[] aperiods = access.getAnalysisPeriods();
christian@3410: buf.append(msg("fix.analysis.periods"));
christian@3410: buf.append(": ");
christian@3410: for(int n = 0; n < aperiods.length; n++) {
christian@3410: buf.append(
christian@3410: Resources.format(
christian@3410: context.getMeta(),
christian@3410: I18N_CHART_SUBTITLE1,
christian@3410: "",
christian@3410: aperiods[n].getFrom(),
christian@3410: aperiods[n].getTo()));
christian@3410: if(n + 1 < aperiods.length) {
christian@3410: buf.append("; ");
christian@3410: }
christian@3410: }
christian@3410:
christian@3410: chart.addSubtitle(new TextTitle(buf.toString()));
christian@3410: }
christian@3410:
christian@3410: @Override
christian@3055: protected String getDefaultXAxisLabel() {
christian@3055: return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
christian@3055: }
christian@3055:
christian@3055: @Override
christian@3055: protected String getDefaultYAxisLabel(int pos) {
felix@6467: D4EArtifact flys = (D4EArtifact) master;
felix@6467:
teichmann@7525: String unit = pos == 0
teichmann@7525: ? "cm"
teichmann@7525: : RiverUtils.getRiver(flys).getWstUnit().getName();
felix@6467:
felix@6467: return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT, new Object[] { unit });
christian@3055: }
christian@3055:
christian@3055: @Override
christian@3055: protected ChartGenerator.YAxisWalker getYAxisWalker() {
christian@3055: return new YAxisWalker() {
christian@3055: @Override
christian@3055: public int length() {
christian@3055: return YAXIS.values().length;
christian@3055: }
christian@3055:
christian@3055: @Override
christian@3770: public String getId(int idx) {
christian@3055: YAXIS[] yaxes = YAXIS.values();
christian@3055: return yaxes[idx].toString();
christian@3055: }
christian@3055: };
sascha@3076: }
christian@3055: }
christian@3055: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :