view artifacts/src/main/java/org/dive4elements/river/artifacts/states/WDifferencesState.java @ 8881:6b93a2498e06

Slightly better abstraction for extraction waterlevels via datacage
author gernotbelger
date Fri, 09 Feb 2018 16:11:34 +0100
parents 23264d1a528f
children 3f49835a00c3
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.artifacts.states;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import org.dive4elements.artifactdatabase.state.Facet;
import org.dive4elements.artifactdatabase.state.FacetActivity;
import org.dive4elements.artifactdatabase.data.StateData;

import org.dive4elements.artifacts.CallContext;
import org.dive4elements.artifacts.Artifact;
import org.dive4elements.river.artifacts.access.RangeAccess;
import org.dive4elements.river.artifacts.ChartArtifact;
import org.dive4elements.river.artifacts.D4EArtifact;
import org.dive4elements.river.artifacts.FixationArtifact;
import org.dive4elements.river.artifacts.MINFOArtifact;
import org.dive4elements.river.artifacts.StaticWKmsArtifact;
import org.dive4elements.river.artifacts.StaticWQKmsArtifact;
import org.dive4elements.river.artifacts.WINFOArtifact;

import org.dive4elements.river.artifacts.math.WKmsOperation;

import org.dive4elements.river.artifacts.model.CalculationResult;
import org.dive4elements.river.artifacts.model.DataFacet;
import org.dive4elements.river.artifacts.model.DifferenceCurveFacet;
import org.dive4elements.river.artifacts.model.DifferenceCurveFilterFacet;
import org.dive4elements.river.artifacts.model.EmptyFacet;
import org.dive4elements.river.artifacts.model.FacetTypes;
import org.dive4elements.river.artifacts.model.WKms;
import org.dive4elements.river.artifacts.model.WQKms;

import org.dive4elements.river.artifacts.model.fixings.FixRealizingResult;
import org.dive4elements.river.artifacts.resources.Resources;

import org.dive4elements.river.utils.RiverUtils;
import org.dive4elements.river.backend.utils.StringUtil;

/** State of a WINFOArtifact to get differences of data of other artifacts. */
public class WDifferencesState
extends      DefaultState
implements   FacetTypes
{
    /** The log that is used in this state. */
    private static Logger log = Logger.getLogger(WDifferencesState.class);

    private static final String I18N_DIFFERENCES_FACET_NAME =
        "facet.w_differences";
    private static final String I18N_DIFFERENCES_FACET_NAME_RAW =
        "facet.w_differences.raw";

    static {
        // Active/deactivate facets.
        FacetActivity.Registry.getInstance().register(
            "winfo",
            new FacetActivity() {
                @Override
                public Boolean isInitialActive(
                    Artifact artifact,
                    Facet    facet,
                    String   output
                ) {
                    String name = facet.getName();

                    if (name.equals(FacetTypes.W_DIFFERENCES)) {
                        return Boolean.FALSE;
                    }
                    return Boolean.TRUE;
                }
            });
    }

    /** Specify to display nothing (this is kind of a "final" state). */
    @Override
    protected String getUIProvider() {
        return "noinput";
    }


    @Override
    public boolean validate(Artifact artifact)
    throws IllegalArgumentException
    {
        D4EArtifact flys = (D4EArtifact) artifact;
        if (artifact instanceof ChartArtifact) {
            return true;
        }

        StateData data = flys.getData("diffids");

        if (data == null) {
            throw new IllegalArgumentException("diffids is empty");
        }

        // TODO: Also validate format.

        return true;
    }


    /**
     * Access the data (wkms) of an artifact, coded in mingle.
     */
    // FIXME: meanwhile used by several places outside this context; refactor into separate helper class to access waterlevels
    // FIXME: use org.dive4elements.river.artifacts.states.WaterlevelFetcher instead
    public WKms getWKms(
        String mingle,
        CallContext context,
        double from,
        double to
    ) {
        log.debug("WDifferencesState.getWKms " + mingle);
        String[] def  = mingle.split(";");
        String   uuid = def[0];
        String   name = def[1];
        int      idx  = Integer.parseInt(def[2]);
        D4EArtifact d4eArtifact = RiverUtils.getArtifact(
            uuid,
            context);

        if (d4eArtifact == null) {
            log.warn("One of the artifacts (1) for diff calculation "
                + "could not be loaded");
            return null;
        }

        WKms retval = null;
        if (d4eArtifact instanceof StaticWKmsArtifact) {
            StaticWKmsArtifact staticWKms = (StaticWKmsArtifact) d4eArtifact;
            log.debug("WDifferencesState obtain data from StaticWKms");
            WKms wkms = staticWKms.getWKms(idx, from, to);
            if (wkms == null) {
                log.error("No WKms from Static artifact for this range.");
                // FIXME: why does in error case we return Q's?
                return new WQKms();
            }
            return wkms; /* No need for additional km filtering */
        } else if (d4eArtifact instanceof StaticWQKmsArtifact) {
            final StaticWQKmsArtifact staticWKms = (StaticWQKmsArtifact) d4eArtifact;
            log.debug("WDifferencesState obtain data from StaticWQKms");
            WQKms wkms = staticWKms.getWQKms(from, to);
            if (wkms == null) {
                log.error("No WKms from Static artifact for this range.");
                return new WQKms();
            }
            return wkms; /* No need for additional km filtering */
        } else if (d4eArtifact instanceof WINFOArtifact) {
            log.debug("Get WKms from WINFOArtifact");
            WINFOArtifact flys = (WINFOArtifact) d4eArtifact;

            WKms[] wkms = (WKms[]) flys.getWaterlevelData(context).
                                              getData();
            if (wkms == null || wkms.length == 0) {
                log.warn("no waterlevels in artifact");
                // FIXME: fall through will lead to exception...
            }
            else if (wkms.length < idx+1) {
                log.warn("Not enough waterlevels in artifact.");
                retval = new WQKms();
            }
            retval = wkms[idx];
        } else if (d4eArtifact instanceof MINFOArtifact) {
            log.warn("Get WKms from MINFOArtifact not implemented!");
//            CalculationResult r = (CalculationResult)
//                d4eArtifact.compute(context, ComputeType.ADVANCE, false);
        } else if (d4eArtifact instanceof FixationArtifact) {
            log.debug ("Get WKms from FixationArtifact.");
            CalculationResult r = (CalculationResult)
                d4eArtifact.compute(context, ComputeType.ADVANCE, false);
            FixRealizingResult frR = (FixRealizingResult) r.getData();
            retval = frR.getWQKms()[idx];
        }


        if (retval == null) {
            log.error(
                "Do not know how to handle (getWKms) minuend/subtrahend");
        } else if (!Double.isNaN(from) && !Double.isNaN(to)) {
            /* Filter out only relevant data points for calulation results.*/
            log.debug("Before filter: " + retval.size());
            retval = retval.filteredKms(from, to);
            log.debug("After filter: " + retval.size());
        }

        return retval;
    }


    /**
     * Return CalculationResult with Array of WKms that are difference of
     * Waterlevels. Add respective facets (DifferencesCurveFacet, DataFacet).
     */
    @Override
    public Object computeAdvance(
        D4EArtifact artifact,
        String       hash,
        CallContext  context,
        List<Facet>  facets,
        Object       old
    ) {
        if (artifact instanceof ChartArtifact) {
            ChartArtifact chart = (ChartArtifact)artifact;
            facets.add(new EmptyFacet());
            return null;
        }
        WINFOArtifact winfo = (WINFOArtifact) artifact;
        String id = getID();
        RangeAccess rangeAccess = new RangeAccess(artifact);
        double from = rangeAccess.getFrom(true);
        double to = rangeAccess.getTo(true);
        // Load the Artifacts/facets that we want to subtract and display.
        // Expected format is:
        //[42537f1e-3522-42ef-8968-635b03d8e9c6;longitudinal_section.w;0]#[...]
        String diffids = winfo.getDataAsString("diffids");
        log.debug("WDifferencesState has: " + diffids);
        String datas[] = diffids.split("#");

        log.debug("Difference from: " + from + " to: " + to);
        /* Check if we need to obtain the data in a different order */
        // Validate the Data-Strings.
        for (String s: datas) {
            if (!WaterlevelSelectState.isValueValid(s)) {
                // TODO: escalate.
            }
        }

        if (datas.length < 2) {
            // TODO crash with style
        }

        List<WKms> wkmss = new ArrayList<WKms>();

        for(int i = 0; i < datas.length; i+=2) {
            // e.g.:
            // 42537f1e-3522-42ef-8968-635b03d8e9c6;longitudinal_section.w;1
            WKms minuendWKms = getWKms(StringUtil.unbracket(datas[i+0]),
                context, from, to);
            WKms subtrahendWKms = getWKms(StringUtil.unbracket(datas[i+1]),
                context, from, to);

            String facetName = "diff ()";
            String minName = "min";
            String subName = "sub";

            if (minuendWKms != null && subtrahendWKms != null) {
                minName = StringUtil.wWrap(minuendWKms.getName());
                subName = StringUtil.wWrap(subtrahendWKms.getName());
                facetName = minName + " - " + subName;
                WKms wkms = WKmsOperation.SUBTRACTION.operate(minuendWKms,
                     subtrahendWKms);
                wkms.setName(facetName);
                wkmss.add(wkms);
                log.debug("WKMSSubtraction happened");
            }
            if (facets != null) {
                facets.add(new DifferenceCurveFacet(
                    i/2,
                    W_DIFFERENCES,
                    Resources.getMsg(
                        context.getMeta(),
                        I18N_DIFFERENCES_FACET_NAME_RAW,
                        facetName,
                        new Object[] { minName, subName }),
                    ComputeType.ADVANCE,
                    id,
                    hash));
                facets.add(new DifferenceCurveFilterFacet(i/2,
                    W_DIFFERENCES_FILTERED,
                    Resources.getMsg(
                        context.getMeta(),
                        I18N_DIFFERENCES_FACET_NAME,
                        facetName,
                        new Object[] { minName, subName }),
                    ComputeType.ADVANCE,
                    id,
                    hash));
            }
        }

        if (facets != null) {
            facets.add(new DataFacet(CSV, "CSV data"));
            facets.add(new DataFacet(PDF, "PDF data"));
            log.debug("Adding facets in WDifferencesState.");
        }
        else {
            log.debug("Not adding facets in WDifferencesState.");
        }

        // TODO Evaluate whether null is okay as reports.
        WKms[] diffs = wkmss.toArray(new WKms[wkmss.size()]);
        CalculationResult result = new CalculationResult(diffs, null);
        return result;
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :

http://dive4elements.wald.intevation.org