view flys-artifacts/src/main/java/de/intevation/flys/artifacts/StaticWKmsArtifact.java @ 4478:6153c50f78cf

WaterLineArtifact: Added callcontext-parameter to interfaces getWaterLine. Update all implementations. The change was done to be able to compute the extreme values during getWaterLine to access data needed in CrossSectionProfile Diagrams.
author Felix Wolfsteller <felix.wolfsteller@intevation.de>
date Tue, 13 Nov 2012 14:46:44 +0100
parents 58864f4f6e3b
children a2735a4bf75e
line wrap: on
line source
package de.intevation.flys.artifacts;

import de.intevation.artifactdatabase.state.DefaultOutput;
import de.intevation.artifactdatabase.state.Facet;
import de.intevation.artifactdatabase.state.FacetActivity;
import de.intevation.artifactdatabase.state.State;

import de.intevation.artifacts.Artifact;
import de.intevation.artifacts.ArtifactFactory;
import de.intevation.artifacts.CallContext;
import de.intevation.artifacts.CallMeta;

import de.intevation.artifacts.common.utils.XMLUtils;

import de.intevation.flys.artifacts.geom.Lines;

import de.intevation.flys.artifacts.math.Distance;
import de.intevation.flys.artifacts.math.Linear;

import de.intevation.flys.artifacts.model.CrossSectionWaterLineFacet;
import de.intevation.flys.artifacts.model.FacetTypes;
import de.intevation.flys.artifacts.model.RelativePointFacet;
import de.intevation.flys.artifacts.model.WKms;
import de.intevation.flys.artifacts.model.WKmsFacet;
import de.intevation.flys.artifacts.model.WKmsFactory;

import de.intevation.flys.artifacts.resources.Resources;

import de.intevation.flys.artifacts.states.StaticState;

import de.intevation.flys.model.FastCrossSectionLine;

import java.awt.geom.Point2D;

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

import org.apache.log4j.Logger;

import org.w3c.dom.Document;

/**
 * Artifact to access additional "waterlevel"-type of data, like the height
 * of protective measures (dikes).
 *
 * This artifact neglects (Static)FLYSArtifacts capabilities of interaction
 * with the StateEngine by overriding the getState*-methods.
 */
public class StaticWKmsArtifact
extends      StaticFLYSArtifact
implements   FacetTypes, WaterLineArtifact
{
    /** The logger for this class. */
    private static Logger logger =
        Logger.getLogger(StaticWKmsArtifact.class);

    private static final String NAME = "staticwkms";

    static {
        // TODO: Move to configuration.
        FacetActivity.Registry.getInstance()
            .register(NAME, FacetActivity.INACTIVE);
    }

    public static final String STATIC_STATE_NAME =
        "state.additional_wkms.static";

    /** Data Item name to know whether we are Heighmarks and reveive
     * some data slightly different. */
    public static final String DATA_HEIGHT_TYPE =
        "height_marks";

    /** One and only state to be in. */
    protected transient State state = null;


    /**
     * Trivial Constructor.
     */
    public StaticWKmsArtifact() {
        logger.debug("StaticWKmsArtifact.StaticWKmsArtifact");
    }

    @Override
    public String getName() {
        return NAME;
    }

    /**
     * Gets called from factory, to set things up.
     */
    @Override
    public void setup(
        String          identifier,
        ArtifactFactory factory,
        Object          context,
        CallMeta        callMeta,
        Document        data)
    {
        logger.debug("StaticWKmsArtifact.setup");

        state = new StaticState(STATIC_STATE_NAME);

        if (logger.isDebugEnabled()) {
            logger.debug(XMLUtils.toString(data));
        }

        List<Facet> fs = new ArrayList<Facet>();
        String code = getDatacageIDValue(data);

        // TODO Go for JSON, one day.
        //ex.: flood_protection-wstv-114-12
        if (code != null) {
            String [] parts = code.split("-");

            if (parts.length >= 4) {
                int col = -1;
                int wst = Integer.parseInt(parts[3]);

                if (!parts[2].equals("A")) {
                    col = Integer.parseInt(parts[2]);
                }

                addStringData("col_pos", parts[2]);
                addStringData("wst_id",  parts[3]);

                String wkmsName;
                if (col >= 0) {
                    wkmsName = WKmsFactory.getWKmsName(col, wst);
                }
                else {
                    wkmsName = WKmsFactory.getWKmsName(wst);
                }

                String name;
                if (parts[0].equals(HEIGHTMARKS_POINTS)) {
                    name = HEIGHTMARKS_POINTS;
                    addStringData(DATA_HEIGHT_TYPE, "true");
                }
                else if (parts[0].equals("additionalsmarks")) {
                    name = STATIC_WKMS_MARKS;
                }
                else {
                    name = STATIC_WKMS;
                }

                String facetDescription = Resources.getMsg(
                    callMeta, wkmsName, wkmsName);
                Facet wKmsFacet = new WKmsFacet(
                    name,
                    facetDescription);
                Facet csFacet = new CrossSectionWaterLineFacet(0,
                    facetDescription);
                Facet rpFacet = new RelativePointFacet(facetDescription);

                fs.add(wKmsFacet);
                fs.add(csFacet);
                fs.add(rpFacet);
                facets.put(state.getID(), fs);
            }
        }

        spawnState();
        super.setup(identifier, factory, context, callMeta, data);
    }


    /**
     * Initialize the static state with output.
     * @return static state
     */
    protected State spawnState() {
        state = new StaticState(STATIC_STATE_NAME);
        List<Facet> fs = facets.get(STATIC_STATE_NAME);
        DefaultOutput output = new DefaultOutput(
            "general",
            "general", "image/png",
            fs,
            "chart");

        state.getOutputs().add(output);
        return state;
    }


    /**
     * Called via setup.
     *
     * @param artifact The master-artifact.
     */
    @Override
    protected void initialize(
        Artifact artifact,
        Object context,
        CallMeta meta)
    {
        logger.debug("StaticWKmsArtifact.initialize");
        FLYSArtifact winfo = (FLYSArtifact) artifact;
        // TODO: The river is of no interest, so far.
        addData("river", winfo.getData("river"));
    }


    /**
     * Get a list containing the one and only State.
     * @param  context ignored.
     * @return list with one and only state.
     */
    @Override
    protected List<State> getStates(Object context) {
        ArrayList<State> states = new ArrayList<State>();
        states.add(getState());
        return states;
    }


    /**
     * Get the "current" state (there is but one).
     * @param cc ignored.
     * @return the "current" (only possible) state.
     */
    @Override
    public State getCurrentState(Object cc) {
        return getState();
    }


    /**
     * Get the only possible state.
     * @return the state.
     */
    protected State getState() {
        return getState(null, null);
    }


    /**
     * Get the state.
     * @param context ignored.
     * @param stateID ignored.
     * @return the state.
     */
    @Override
    protected State getState(Object context, String stateID) {
        return (state != null)
            ? state
            : spawnState();
    }


    /**
     * Get WKms from factory.
     * @param idx param is not needed (TODO?)
     * @return WKms according to parameterization (can be null);
     */
    public WKms getWKms(int idx) {
        logger.debug("StaticWKmsArtifact.getWKms");

        return WKmsFactory.getWKms(
            Integer.parseInt(getDataAsString("col_pos")),
            Integer.parseInt(getDataAsString("wst_id")));
    }


    /**
     * Returns W at Km of WKms, linearly interpolated.
     * Returns -1 if not found.
     */
    public static double getWAtKmLin(WKms wkms, double km) {
        // Uninformed search.
        int size = wkms.size();
        if (size == 0) {
            return -1;
        }
        int idx = 0;

        boolean kmIncreasing;
        if (size == 1) {
            kmIncreasing = true;
        }
        else {
            kmIncreasing = (wkms.getKm(0) < wkms.getKm(wkms.size()-1))
                ? true : false;
        }
        if (kmIncreasing) {
            while (idx < size && wkms.getKm(idx) < km) {
                idx++;
            }
        }
        else {
            idx = wkms.size() -1;
            while (idx > 0 && wkms.getKm(idx) > km) {
                idx--;
            }
        }

       if (wkms.getKm(idx) == km) {
           return wkms.getW(idx);
       }

        if (idx == size -1 || idx == 0) {
            return -1;
        }

        // Do linear interpolation
        int mod = kmIncreasing ? -1 : +1;
        return Linear.linear(km, wkms.getKm(idx+mod), wkms.getKm(idx), wkms.getW(idx+mod), wkms.getW(idx));
    }


    /**
     * Get the W at a specific km, only if it is closer to km than to any of
     * the other given km.
     * Return Double.NaN otherwise
     *
     * @param wkms WKms in which to search for a spatially close W value.
     * @param km the input km, which is compared to values from wkms.
     * @param next the next available input km (-1 if unavailable).
     * @param prev the previous available input km (-1 if unavailable).
     *
     * @return W in wkms that is closer to km than to next and prev, or Double.NaN.
     */
    public double getWAtCloseKm(WKms wkms, double km, double next, double prev) {
        // TODO symbolic "-1" pr next/prev is a bad idea (tm), as we compare
        //      distances to these values later.
        // TODO issue888

        int size = wkms.size();
        for (int i = 0; i < size; i++) {
            double wkmsKm = wkms.getKm(i);
            double dist = Distance.distance(wkmsKm, km);
            if (dist == 0d) {
                return wkms.getW(i);
            }

            // Problematic Cases:
            // X == km , | and | == prev and next, (?) == wkmsKm
            //
            // Standard case:
            // ----------|----X-----|-------
            //     (1)    (2)    (3)   (4)
            //
            // With prev==-1
            // -1 ------X-------|------
            //    (5)      (6)     (7)
            //
            // With next==-1
            //
            // ---|-----X----- -1
            // (8)  (9)   (10)

            if (dist <= Distance.distance(wkmsKm, prev)
                && dist <= Distance.distance(wkmsKm, next)) {
                return wkms.getW(i);
            }
        }

        return Double.NaN;
    }


    /**
     * Returns W at Km of WKms, searching linearly.
     * Returns -1 if not found.
     * @param wkms the WKms object to search for given km.
     * @param km The searched km.
     * @return W at given km if in WKms, -1 if not found.
     */
    public static double getWAtKm(WKms wkms, double km) {
        // Uninformed search, intolerant.
        double TOLERANCE = 0.0d;
        int size = wkms.size();
        for (int i = 0; i < size; i++) {
            if (Distance.within(wkms.getKm(i), km, TOLERANCE)) {
                return wkms.getW(i);
            }
        }

        return -1;
    }


    /**
     * Get points of line describing the surface of water at cross section.
     *
     * @param idx Index of facet and in wkms array.
     * @param csl FastCrossSectionLine to compute water surface agains.
     * @param next The km of the next crosssectionline.
     * @param prev The km of the previous crosssectionline.
     * @param context Ignored in this implementation.
     *
     * @return an array holding coordinates of points of surface of water (
     *         in the form {{x1, x2}, {y1, y2}} ).
     */
    @Override
    public Lines.LineData getWaterLines(int idx, FastCrossSectionLine csl,
        double next, double prev, CallContext context
    ) {
        logger.debug("getWaterLines(" + idx + ")/" + identifier());

        List<Point2D> points = csl.getPoints();

        WKms wkms = getWKms(0);

        double km = csl.getKm();

        // Find W at km.
        double wAtKm;

        // If heightmarks, only deliver if data snaps.
        if (getDataAsString(DATA_HEIGHT_TYPE) != null &&
            getDataAsString(DATA_HEIGHT_TYPE).equals("true")) {
            wAtKm = getWAtCloseKm(wkms, km, next, prev);
        }
        else {
            wAtKm = getWAtKm(wkms, km);
        }

        if (wAtKm == -1 || Double.isNaN(wAtKm)) {
            logger.warn("Waterlevel at km " + km + " unknown.");
            return new Lines.LineData(new double[][] {{}}, 0d, 0d);
        }

        return Lines.createWaterLines(points, wAtKm);
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :

http://dive4elements.wald.intevation.org