tom@8858: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde tom@8858: * Software engineering by Intevation GmbH tom@8858: * tom@8858: * This file is Free Software under the GNU AGPL (>=v3) tom@8858: * and comes with ABSOLUTELY NO WARRANTY! Check out the tom@8858: * documentation coming with Dive4Elements River for details. tom@8858: */ tom@8858: rrenkert@8206: package org.dive4elements.river.exports.process; rrenkert@8206: rrenkert@8206: import java.awt.BasicStroke; rrenkert@8206: import java.awt.Color; rrenkert@8206: import java.text.DateFormat; rrenkert@8206: import java.util.ArrayList; rrenkert@8206: import java.util.List; rrenkert@8206: rrenkert@8206: import org.apache.log4j.Logger; rrenkert@8206: import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; rrenkert@8354: import org.dive4elements.artifacts.CallMeta; rrenkert@8354: import org.dive4elements.river.artifacts.D4EArtifact; rrenkert@8354: import org.dive4elements.river.artifacts.access.RiverAccess; rrenkert@8206: import org.dive4elements.river.artifacts.model.FacetTypes; rrenkert@8206: import org.dive4elements.river.artifacts.model.NamedDouble; rrenkert@8206: import org.dive4elements.river.artifacts.model.QWDDateRange; rrenkert@8206: import org.dive4elements.river.artifacts.model.WQKms; rrenkert@8206: import org.dive4elements.river.artifacts.model.fixings.FixFunction; rrenkert@8206: import org.dive4elements.river.artifacts.model.fixings.FixWQCurveFacet; rrenkert@8206: import org.dive4elements.river.artifacts.model.fixings.QWD; rrenkert@8354: import org.dive4elements.river.artifacts.resources.Resources; rrenkert@8206: import org.dive4elements.river.exports.DiagramGenerator; rrenkert@8206: import org.dive4elements.river.exports.StyledSeriesBuilder; gernotbelger@9325: import org.dive4elements.river.exports.fixings.FixWQCurveGenerator; rrenkert@8206: import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation; rrenkert@8206: import org.dive4elements.river.jfree.JFreeUtil; rrenkert@8206: import org.dive4elements.river.jfree.RiverAnnotation; rrenkert@8206: import org.dive4elements.river.jfree.StickyAxisAnnotation; rrenkert@8206: import org.dive4elements.river.jfree.StyledXYSeries; rrenkert@8206: import org.dive4elements.river.themes.ThemeDocument; rrenkert@8206: import org.jfree.chart.annotations.XYTextAnnotation; rrenkert@8206: import org.jfree.chart.plot.Marker; rrenkert@8206: import org.jfree.chart.plot.ValueMarker; rrenkert@8206: import org.jfree.data.xy.XYSeries; rrenkert@8206: import org.jfree.ui.RectangleAnchor; rrenkert@8206: import org.jfree.ui.RectangleInsets; rrenkert@8206: import org.jfree.ui.TextAnchor; rrenkert@8206: tom@8364: import static org.dive4elements.river.exports.injector.InjectorConstants.CURRENT_KM; rrenkert@8206: rrenkert@8206: public class FixWQProcessor rrenkert@8206: extends DefaultProcessor rrenkert@8206: implements FacetTypes rrenkert@8206: { tom@8366: private static Logger log = Logger.getLogger(FixWQProcessor.class); rrenkert@8206: rrenkert@8354: private String I18N_AXIS_LABEL = "chart.discharge.curve.yaxis.label"; rrenkert@8354: rrenkert@8206: public FixWQProcessor() { rrenkert@8206: } rrenkert@8206: rrenkert@8206: @Override rrenkert@8206: public void doOut( rrenkert@8206: DiagramGenerator generator, rrenkert@8206: ArtifactAndFacet bundle, rrenkert@8206: ThemeDocument theme, rrenkert@8206: boolean visible rrenkert@8206: ) { tom@8364: // TODO: Simplify this processor and move general facets/data to rrenkert@8206: // MiscDischargeProcessor or something... rrenkert@8206: String facetType = bundle.getFacetName(); tom@8856: log.debug("facet: " + facetType tom@8856: + " name: " + bundle.getFacetDescription()); rrenkert@8206: if(facetType.startsWith(FIX_SECTOR_AVERAGE_WQ)) { rrenkert@8206: doSectorAverageOut(generator, bundle, theme, visible); rrenkert@8206: } rrenkert@8206: else if(FIX_ANALYSIS_EVENTS_WQ.equals(facetType) rrenkert@8206: || FIX_REFERENCE_EVENTS_WQ.equals(facetType) rrenkert@8206: || FIX_EVENTS.equals(facetType)) { rrenkert@8206: doEventsOut(generator, bundle, theme, visible); rrenkert@8206: } rrenkert@8206: else if(FIX_WQ_CURVE.equals(facetType)) { rrenkert@8206: doWQCurveOut(generator, bundle, theme, visible); rrenkert@8206: } rrenkert@8206: else if(QSECTOR.equals(facetType)) { rrenkert@8206: doQSectorOut(generator, bundle, theme, visible); rrenkert@8206: } rrenkert@8206: else if(STATIC_WKMS_MARKS.equals(facetType) || rrenkert@8206: STATIC_WKMS.equals(facetType) || rrenkert@8206: HEIGHTMARKS_POINTS.equals(facetType) ) { rrenkert@8206: doWAnnotations(generator, bundle, theme, visible); rrenkert@8206: } rrenkert@8206: else if (LONGITUDINAL_W.equals(facetType) rrenkert@8206: || STATIC_WKMS_INTERPOL.equals(facetType) rrenkert@8206: || FIX_WQ_LS.equals(facetType)) { rrenkert@8206: doWQOut(generator, bundle, theme, visible); rrenkert@8206: } tom@8387: rrenkert@8206: } rrenkert@8206: rrenkert@8206: /** Add sector average points to chart. */ gernotbelger@9360: private void doSectorAverageOut( rrenkert@8206: DiagramGenerator generator, rrenkert@8206: ArtifactAndFacet bundle, rrenkert@8206: ThemeDocument theme, rrenkert@8206: boolean visible rrenkert@8206: ) { tom@8366: log.debug("doSectorAverageOut"); tom@8856: QWDDateRange qwdd = (QWDDateRange)bundle.getData( gernotbelger@9123: generator.getContext()); rrenkert@8206: QWD qwd = qwdd != null ? qwdd.getQWD() : null; rrenkert@8206: rrenkert@8206: if(qwd != null) { gernotbelger@9556: XYSeries series = new StyledXYSeries(bundle.getFacetName(), rrenkert@8206: bundle.getFacetDescription(), rrenkert@8206: false, true, rrenkert@8206: theme); rrenkert@8206: DateFormat dateFormat = DateFormat.getDateInstance( rrenkert@8206: DateFormat.SHORT); rrenkert@8206: rrenkert@8206: series.add(qwd.getQ(), qwd.getW(), false); rrenkert@8206: rrenkert@8206: XYTextAnnotation anno = new CollisionFreeXYTextAnnotation( rrenkert@8206: dateFormat.format(qwd.getDate()), rrenkert@8206: qwd.getQ(), rrenkert@8206: qwd.getW()); gernotbelger@9348: List annos = new ArrayList<>(); rrenkert@8206: annos.add(anno); rrenkert@8206: generator.addAxisSeries(series, axisName, visible); rrenkert@8206: rrenkert@8206: if (visible && theme != null && theme.parseShowPointLabel()) { rrenkert@8206: RiverAnnotation flysAnno = rrenkert@8206: new RiverAnnotation(null, null, null, theme); rrenkert@8206: flysAnno.setTextAnnotations(annos); rrenkert@8206: generator.addAnnotations(flysAnno); rrenkert@8206: } rrenkert@8206: } rrenkert@8206: else { tom@8366: log.debug("doSectorAverageOut: qwd == null"); rrenkert@8206: } rrenkert@8206: } rrenkert@8206: rrenkert@8206: rrenkert@8206: /** Add analysis event points to chart. */ gernotbelger@9360: private void doEventsOut( rrenkert@8206: DiagramGenerator generator, rrenkert@8206: ArtifactAndFacet bundle, rrenkert@8206: ThemeDocument theme, rrenkert@8206: boolean visible rrenkert@8206: ) { tom@8366: log.debug("doAnalysisEventsOut"); rrenkert@8206: gernotbelger@9123: QWD qwd = (QWD)bundle.getData(generator.getContext()); rrenkert@8206: rrenkert@8206: if (qwd == null) { tom@8366: log.debug("doAnalysisEventsOut: qwd == null"); rrenkert@8206: return; rrenkert@8206: } rrenkert@8206: gernotbelger@9325: // prevent potential side effects gernotbelger@9325: final ThemeDocument themeInterpolated = FixWQCurveGenerator.configureThemeInterpolated(theme, qwd); gernotbelger@9360: final XYSeries series = FixWQCurveGenerator.createQWDSeries(generator.getContext().getMeta(), bundle, themeInterpolated, qwd); rrenkert@8206: rrenkert@8206: generator.addAxisSeries(series, axisName, visible); rrenkert@8206: gernotbelger@9325: if (visible && themeInterpolated.parseShowPointLabel()) { rrenkert@8206: gernotbelger@9348: final List textAnnos = new ArrayList<>(); rrenkert@8206: rrenkert@8206: DateFormat dateFormat = DateFormat.getDateInstance( rrenkert@8206: DateFormat.SHORT); rrenkert@8206: XYTextAnnotation anno = new CollisionFreeXYTextAnnotation( rrenkert@8206: dateFormat.format(qwd.getDate()), rrenkert@8206: qwd.getQ(), rrenkert@8206: qwd.getW()); rrenkert@8206: textAnnos.add(anno); rrenkert@8206: gernotbelger@9348: RiverAnnotation flysAnno = new RiverAnnotation(null, null, null, themeInterpolated); rrenkert@8206: flysAnno.setTextAnnotations(textAnnos); rrenkert@8206: generator.addAnnotations(flysAnno); rrenkert@8206: } rrenkert@8206: } rrenkert@8206: gernotbelger@9360: private void doWQCurveOut( rrenkert@8206: DiagramGenerator generator, rrenkert@8206: ArtifactAndFacet bundle, rrenkert@8206: ThemeDocument theme, rrenkert@8206: boolean visible rrenkert@8206: ) { tom@8366: log.debug("doWQCurveOut"); rrenkert@8206: rrenkert@8206: FixWQCurveFacet facet = (FixWQCurveFacet)bundle.getFacet(); rrenkert@8206: FixFunction func = (FixFunction)facet.getData( gernotbelger@9123: bundle.getArtifact(), generator.getContext()); rrenkert@8206: rrenkert@8206: if (func == null) { tom@8366: log.warn("doWQCurveOut: Facet does not contain FixFunction"); rrenkert@8206: return; rrenkert@8206: } rrenkert@8206: rrenkert@8206: double maxQ = func.getMaxQ(); rrenkert@8206: rrenkert@8206: if (maxQ > 0) { rrenkert@8206: StyledXYSeries series = JFreeUtil.sampleFunction2D( gernotbelger@9556: bundle.getFacetName(), rrenkert@8206: func.getFunction(), rrenkert@8206: theme, rrenkert@8206: bundle.getFacetDescription(), rrenkert@8206: 500, // number of samples rrenkert@8206: 0.0 , // start rrenkert@8206: maxQ); // end tom@8375: tom@8375: generator.addAxisSeries(series, axisName, visible); rrenkert@8206: } rrenkert@8206: else { tom@8366: log.warn("doWQCurveOut: maxQ <= 0"); rrenkert@8206: } rrenkert@8206: } rrenkert@8206: rrenkert@8206: /** Add markers for q sectors. */ rrenkert@8206: protected void doQSectorOut( rrenkert@8206: DiagramGenerator generator, rrenkert@8206: ArtifactAndFacet bundle, rrenkert@8206: ThemeDocument theme, rrenkert@8206: boolean visible rrenkert@8206: ) { tom@8366: log.debug("doQSectorOut"); rrenkert@8206: if (!visible) { rrenkert@8206: return; rrenkert@8206: } rrenkert@8206: gernotbelger@9123: Object qsectorsObj = bundle.getData(generator.getContext()); rrenkert@8206: if (qsectorsObj == null || !(qsectorsObj instanceof List)) { tom@8366: log.warn("No QSectors coming from data."); rrenkert@8206: return; rrenkert@8206: } rrenkert@8206: rrenkert@8206: List qsectorsList = (List) qsectorsObj; tom@8856: if (qsectorsList.size() == 0 tom@8856: || !(qsectorsList.get(0) instanceof NamedDouble) tom@8856: ) { tom@8366: log.warn("No QSectors coming from data."); rrenkert@8206: return; rrenkert@8206: } rrenkert@8206: rrenkert@8206: @SuppressWarnings("unchecked") rrenkert@8206: List qsectors = (List) qsectorsList; rrenkert@8206: rrenkert@8206: for (NamedDouble qsector : qsectors) { rrenkert@8206: if (Double.isNaN(qsector.getValue())) { rrenkert@8206: continue; rrenkert@8206: } rrenkert@8206: Marker m = new ValueMarker(qsector.getValue()); rrenkert@8206: m.setPaint(Color.black); rrenkert@8206: rrenkert@8206: float[] dashes = theme.parseLineStyle(); rrenkert@8206: int size = theme.parseLineWidth(); rrenkert@8206: BasicStroke stroke; rrenkert@8206: if (dashes.length <= 1) { rrenkert@8206: stroke = new BasicStroke(size); rrenkert@8206: } rrenkert@8206: else { rrenkert@8206: stroke = new BasicStroke(size, rrenkert@8206: BasicStroke.CAP_BUTT, rrenkert@8206: BasicStroke.JOIN_ROUND, rrenkert@8206: 1.0f, rrenkert@8206: dashes, rrenkert@8206: 0.0f); rrenkert@8206: } rrenkert@8206: m.setStroke(stroke); rrenkert@8206: rrenkert@8206: if (theme.parseShowLineLabel()) { rrenkert@8206: m.setLabel(qsector.getName()); rrenkert@8206: m.setPaint(theme.parseTextColor()); rrenkert@8206: m.setLabelFont(theme.parseTextFont()); rrenkert@8206: } rrenkert@8206: Color paint = theme.parseLineColorField(); rrenkert@8206: if (paint != null) { rrenkert@8206: m.setPaint(paint); rrenkert@8206: } rrenkert@8206: m.setLabelAnchor(RectangleAnchor.TOP_LEFT); rrenkert@8206: m.setLabelTextAnchor(TextAnchor.TOP_LEFT); rrenkert@8206: m.setLabelOffset(new RectangleInsets(5, 5, 10, 10)); rrenkert@8206: generator.addDomainMarker(m); rrenkert@8206: } rrenkert@8206: } rrenkert@8206: rrenkert@8206: /** rrenkert@8206: * Add W-Annotations to plot. rrenkert@8206: * @param wqkms actual data (double[][]). rrenkert@8206: * @param theme theme to use. rrenkert@8206: */ rrenkert@8206: protected void doWAnnotations( rrenkert@8206: DiagramGenerator generator, rrenkert@8206: ArtifactAndFacet bundle, rrenkert@8206: ThemeDocument theme, rrenkert@8206: boolean visible rrenkert@8206: ) { gernotbelger@9123: Object data = bundle.getData(generator.getContext()); gernotbelger@9348: List xy = new ArrayList<>(); rrenkert@8206: if (data instanceof double[][]) { tom@8366: log.debug("Got double[][]"); rrenkert@8206: double [][] values = (double [][]) data; rrenkert@8206: for (int i = 0; i< values[0].length; i++) { tom@8856: xy.add(new StickyAxisAnnotation( tom@8856: bundle.getFacetDescription(), tom@8856: (float) values[1][i], tom@8856: StickyAxisAnnotation.SimpleAxis.Y_AXIS)); rrenkert@8206: } rrenkert@8206: rrenkert@8206: if (visible) { rrenkert@8206: generator.addAnnotations( rrenkert@8206: new RiverAnnotation( rrenkert@8206: bundle.getFacetDescription(), xy, null, theme)); rrenkert@8206: } rrenkert@8206: } rrenkert@8206: else { rrenkert@8206: // Assume its WKms. tom@8366: log.debug("Got WKms"); rrenkert@8206: /* TODO rrenkert@8206: WKms wkms = (WKms) data; rrenkert@8206: rrenkert@8206: Double ckm = gernotbelger@9123: (Double)generator.getContext().getContextValue( tom@8856: FixChartGenerator.CURRENT_KM); rrenkert@8206: double location = (ckm != null) rrenkert@8206: ? ckm.doubleValue() rrenkert@8206: : getRange()[0]; rrenkert@8206: double w = StaticWKmsArtifact.getWAtKmLin(data, location); rrenkert@8206: xy.add(new StickyAxisAnnotation(aandf.getFacetDescription(), rrenkert@8206: (float) w, StickyAxisAnnotation.SimpleAxis.Y_AXIS)); rrenkert@8206: rrenkert@8206: doAnnotations(new RiverAnnotation(facet.getDescription(), xy), rrenkert@8206: aandf, theme, visible);*/ rrenkert@8206: } rrenkert@8206: } rrenkert@8206: rrenkert@8206: /** rrenkert@8206: * Add WQ Data to plot. rrenkert@8206: * @param wqkms data as double[][] rrenkert@8206: */ rrenkert@8206: protected void doWQOut( rrenkert@8206: DiagramGenerator generator, rrenkert@8206: ArtifactAndFacet bundle, rrenkert@8206: ThemeDocument theme, rrenkert@8206: boolean visible rrenkert@8206: ) { gernotbelger@9123: Object data = bundle.getData(generator.getContext()); rrenkert@8206: if (data instanceof WQKms) { rrenkert@8206: WQKms wqkms = (WQKms)data; rrenkert@8206: // TODO As in doEventsOut, the value-searching should tom@8364: // be delivered by the facet already gernotbelger@9556: XYSeries series = new StyledXYSeries(bundle.getFacetName(), tom@8856: bundle.getFacetDescription(), theme); gernotbelger@9123: Double ckm = (Double) generator.getContext() tom@8364: .getContextValue(CURRENT_KM); tom@8364: gernotbelger@9348: if (wqkms.getKms().length == 0 || ckm == null) { tom@8366: log.info("addPointFromWQKms: No event data to show."); rrenkert@8206: return; rrenkert@8206: } tom@8364: rrenkert@8206: double[] kms = wqkms.getKms(); rrenkert@8206: for (int i = 0 ; i< kms.length; i++) { andre@8472: /* We use a tolerance of 1m here to find a hit. andre@8472: * Probably to avoid some rounding errors. */ andre@8472: if (Math.abs(kms[i] - ckm) <= 0.001) { rrenkert@8206: series.add(wqkms.getQ(i), wqkms.getW(i), false); rrenkert@8206: generator.addAxisSeries(series, axisName, visible); rrenkert@8206: if(visible && theme.parseShowPointLabel()) { gernotbelger@9348: List textAnnos = new ArrayList<>(); tom@8856: XYTextAnnotation anno = tom@8856: new CollisionFreeXYTextAnnotation( tom@8856: bundle.getFacetDescription(), tom@8856: wqkms.getQ(i), tom@8856: wqkms.getW(i)); rrenkert@8206: textAnnos.add(anno); tom@8856: RiverAnnotation flysAnno = tom@8856: new RiverAnnotation(null, null, null, theme); rrenkert@8206: flysAnno.setTextAnnotations(textAnnos); rrenkert@8206: generator.addAnnotations(flysAnno); rrenkert@8206: } rrenkert@8206: return; rrenkert@8206: } rrenkert@8206: } rrenkert@8206: } rrenkert@8206: else { tom@8366: log.debug("FixWQCurveGenerator: doWQOut: double[][]"); rrenkert@8206: double [][] values = (double [][]) data; rrenkert@8206: gernotbelger@9556: XYSeries series = new StyledXYSeries(bundle.getFacetName(), tom@8856: bundle.getFacetDescription(), false, true, theme); rrenkert@8206: StyledSeriesBuilder.addPoints(series, values, true); rrenkert@8206: rrenkert@8206: generator.addAxisSeries(series, axisName, visible); rrenkert@8206: } rrenkert@8206: } rrenkert@8206: rrenkert@8206: @Override rrenkert@8206: public boolean canHandle(String facettype) { rrenkert@8206: return facettype.startsWith(FIX_SECTOR_AVERAGE_WQ) rrenkert@8206: || FIX_ANALYSIS_EVENTS_WQ.equals(facettype) rrenkert@8206: || FIX_REFERENCE_EVENTS_WQ.equals(facettype) rrenkert@8206: || FIX_EVENTS.equals(facettype) rrenkert@8206: || FIX_WQ_CURVE.equals(facettype) rrenkert@8206: || QSECTOR.equals(facettype) rrenkert@8206: || STATIC_WKMS_MARKS.equals(facettype) rrenkert@8206: || STATIC_WKMS.equals(facettype) rrenkert@8206: || HEIGHTMARKS_POINTS.equals(facettype) rrenkert@8206: || LONGITUDINAL_W.equals(facettype) rrenkert@8206: || STATIC_WKMS_INTERPOL.equals(facettype) rrenkert@8206: || FIX_WQ_LS.equals(facettype); rrenkert@8206: } rrenkert@8354: rrenkert@8354: @Override rrenkert@8354: public String getAxisLabel(DiagramGenerator generator) { gernotbelger@9123: CallMeta meta = generator.getContext().getMeta(); rrenkert@8354: tom@8856: RiverAccess access = new RiverAccess((D4EArtifact)generator tom@8856: .getMaster()); rrenkert@8354: String unit = access.getRiver().getWstUnit().getName(); rrenkert@8354: return Resources.getMsg( rrenkert@8354: meta, rrenkert@8354: I18N_AXIS_LABEL, rrenkert@8354: new Object[] { unit }); rrenkert@8354: } rrenkert@8206: }