view artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/AbstractSInfoProcessor.java @ 9205:3dae6b78e1da

inundationDuration/floodDuration multiple columns+chartLines refactoring
author gernotbelger
date Mon, 02 Jul 2018 19:01:09 +0200
parents 4f411c6ee3ae
children 0fc9c82e744e
line wrap: on
line source
/* Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
 * Software engineering by
 *  Björnsen Beratende Ingenieure GmbH
 *  Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
 *
 * 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.artifacts.sinfo.common;

import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
import org.dive4elements.artifactdatabase.state.Facet;
import org.dive4elements.artifacts.Artifact;
import org.dive4elements.artifacts.CallContext;
import org.dive4elements.river.artifacts.D4EArtifact;
import org.dive4elements.river.artifacts.access.RiverAccess;
import org.dive4elements.river.artifacts.common.AbstractCalculationResult;
import org.dive4elements.river.artifacts.common.IResultType;
import org.dive4elements.river.artifacts.context.RiverContext;
import org.dive4elements.river.artifacts.math.MovingAverage;
import org.dive4elements.river.artifacts.model.ZoomScale;
import org.dive4elements.river.artifacts.resources.Resources;
import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
import org.dive4elements.river.exports.DiagramGenerator;
import org.dive4elements.river.exports.StyledSeriesBuilder;
import org.dive4elements.river.exports.process.DefaultProcessor;
import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
import org.dive4elements.river.jfree.StyledXYSeries;
import org.dive4elements.river.themes.ThemeDocument;

/**
 * Abstraction for some processor implementation within S-INFO. Probably this abstraction could also be used for other
 * cases as well.
 *
 * @author Gernot Belger
 *
 */
public abstract class AbstractSInfoProcessor extends DefaultProcessor {

    protected static final double GAP_DISTANCE = 0.101;

    private final static Logger log = Logger.getLogger(AbstractSInfoProcessor.class);

    private String yAxisLabel;

    private final Set<String> handled_facet_types;

    private final String i18n_axis_label;

    public AbstractSInfoProcessor(final String i18n_axis_label, final Set<String> handled_facet_types) {
        this.i18n_axis_label = i18n_axis_label;
        this.handled_facet_types = handled_facet_types;
    }

    @Override
    public final void doOut(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme, final boolean visible) {
        try {
            this.yAxisLabel = generateSeries(generator, bundle, theme, visible);
        }
        catch (final Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    protected static final AbstractCalculationResult getResult(final DiagramGenerator generator, final ArtifactAndFacet bundle) {
        final CallContext context = generator.getContext();
        final AbstractCalculationResult data = (AbstractCalculationResult) bundle.getData(context);
        if (data == null) {
            // Check has been here before so we keep it for security reasons
            // this should never happen though.
            final String facetName = bundle.getFacetName();
            throw new IllegalStateException("Data is null for facet: " + facetName);
        }

        return data;
    }

    protected static final int getDataIndex(final ArtifactAndFacet bundle) {
        return ((SInfoResultFacet) bundle.getFacet()).getDataIndex();
    }

    /**
     * @return The axis label
     */
    protected abstract String generateSeries(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme, final boolean visible);

    protected final String buildSeriesForType(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme, final boolean visible,
            final IResultType resultType, final Double gapDistance) {

        final AbstractCalculationResult data = getResult(generator, bundle);

        final double[][] points = data.getStationPoints(resultType);

        return buildSeriesForType(points, generator, bundle, theme, visible, gapDistance);
    }

    protected final String buildSeriesForType(final double[][] points, final DiagramGenerator generator, final ArtifactAndFacet bundle,
            final ThemeDocument theme, final boolean visible, final Double gapDistance) {
        final CallContext context = generator.getContext();
        final Map<String, String> metaData = bundle.getFacet().getMetaData();

        final Artifact artifact = bundle.getArtifact();

        final StyledXYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme);
        series.putMetaData(metaData, artifact, context);

        final String facetName = bundle.getFacetName();

        final double[][] filteredPoints = filterPoints(points, context, artifact, facetName);

        if (gapDistance == null)
            StyledSeriesBuilder.addPoints(series, filteredPoints, true);
        else
            StyledSeriesBuilder.addPoints(series, filteredPoints, true, gapDistance);

        generator.addAxisSeries(series, getAxisName(), visible);

        return metaData.get("Y");
    }

    // protected final String buildSeriesForType1(final DiagramGenerator generator, final ArtifactAndFacet bundle, final
    // ThemeDocument theme,
    // final boolean visible, final IResultType resultType, final Double gapDistance) {
    // final CallContext context = generator.getContext();
    // final Map<String, String> metaData = bundle.getFacet().getMetaData();
    //
    // final Artifact artifact = bundle.getArtifact();
    //
    // final StyledXYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme);
    // series.putMetaData(metaData, artifact, context);
    //
    // final String facetName = bundle.getFacetName();
    //
    // final AbstractCalculationResult data = (AbstractCalculationResult) bundle.getData(context);
    // if (data == null) {
    // // Check has been here before so we keep it for security reasons
    // // this should never happen though.
    // throw new IllegalStateException("Data is null for facet: " + facetName);
    // }
    //
    // final double[][] points = generatePoints(context, artifact, data, facetName, resultType,
    // bundle.getFacet().getIndex());
    //
    // if (gapDistance == null)
    // StyledSeriesBuilder.addPoints(series, points, true);
    // else
    // StyledSeriesBuilder.addPoints(series, points, true, gapDistance);
    //
    // generator.addAxisSeries(series, getAxisName(), visible);
    //
    // return metaData.get("Y");
    // }

    protected final String buildSeriesForTkh(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme,
            final boolean visible) {

        final AbstractTkhCalculationResult data = (AbstractTkhCalculationResult) getResult(generator, bundle);

        final StyledXYSeries seriesUp = new StyledXYSeries(bundle.getFacetDescription(), theme);
        final double[][] pointsUp = data.getTkhUpPoints();
        StyledSeriesBuilder.addPoints(seriesUp, pointsUp, true);

        // REMARK: we add " " because the description is misused as id, which must be unique.
        final StyledXYSeries seriesDown = new StyledXYSeries(bundle.getFacetDescription() + " ", theme);
        final double[][] pointsDown = data.getTkhDownPoints();
        StyledSeriesBuilder.addPoints(seriesDown, pointsDown, true);

        final StyledAreaSeriesCollection area = new StyledAreaSeriesCollection(theme);
        area.setMode(StyledAreaSeriesCollection.FILL_MODE.BETWEEN);
        area.addSeries(seriesUp);
        area.addSeries(seriesDown);

        generator.addAreaSeries(area, getAxisName(), visible);

        return null;
    }

    private Double findRadius(final CallContext context, final Artifact artifact) {
        final Double start = (Double) context.getContextValue("startkm");
        final Double end = (Double) context.getContextValue("endkm");

        if (start == null || end == null)
            return null;

        final RiverContext fc = (RiverContext) context.globalContext();
        final ZoomScale scales = (ZoomScale) fc.get("zoomscale");
        final RiverAccess access = new RiverAccess((D4EArtifact) artifact);
        final String river = access.getRiverName();

        return scales.getRadius(river, start, end);
    }

    private double[][] filterPoints(final double[][] points, final CallContext context, final Artifact artifact, final String facetName) {

        if (facetName.endsWith(".filtered")) {
            final Double radius = findRadius(context, artifact);
            return movingAverage(radius, points);
        }

        return points;
    }

    // private double[][] generatePoints(final CallContext context, final Artifact artifact, final AbstractCalculationResult
    // data, final String facetName,
    // final IResultType resultType, final int index) {
    //
    // final double[][] points = data.getStationPoints(resultType, index);
    // if (facetName.endsWith(".filtered")) {
    // final Double radius = findRadius(context, artifact);
    // return movingAverage(radius, points);
    // }
    //
    // return points;
    // }

    private double[][] movingAverage(final Double radius, final double[][] points) {

        if (radius == null)
            return points;

        return MovingAverage.weighted(points, radius);
    }

    @Override
    public final boolean canHandle(final String facettype) {
        return this.handled_facet_types.contains(facettype);
    }

    @Override
    public final String getAxisLabel(final DiagramGenerator generator) {
        if (this.yAxisLabel != null && !this.yAxisLabel.isEmpty()) {
            // REMARK/UNINTENDED: yAxisLabel may also be a resolved message (side-effect of StyledXYSeries#putMetadata),
            // and cannot be resolved, so we need to give the resolved value as default
            // TODO: In other implementations (i.e. FlowVelocityProcessor), an explicit (German) default label is given here,
            // probably the English version will also show German (CHECK)
            return generator.msg(this.yAxisLabel, this.yAxisLabel);
        }
        return generator.msg(this.i18n_axis_label, "MISSING");
    }

    protected static final Facet createFacet(final CallContext context, final String hash, final String id, final AbstractCalculationResult result,
            final int resultIndex, final String axisLabel, final String facetId, final String description) {
        return createFacet(context, hash, id, result, resultIndex, -1, axisLabel, facetId, description);
    }

    protected static final Facet createFacet(final CallContext context, final String hash, final String id, final AbstractCalculationResult result,
            final int resultIndex, final int dataIndex, final String axisLabel, final String facetId, final String description) {
        final String facetFlowDepthFilteredDescription = Resources.getMsg(context.getMeta(), description, description, result.getLabel());
        return new SInfoResultFacet(resultIndex, dataIndex, facetId, facetFlowDepthFilteredDescription, axisLabel, ComputeType.ADVANCE, id, hash);
    }
}

http://dive4elements.wald.intevation.org