view flys-artifacts/src/main/java/de/intevation/flys/artifacts/CrossSectionArtifact.java @ 2089:0da8874bd378

Added initial state to map artifact to be able to advance and step back. The map artifact overrides describe() to have the complete UI information in the describe response document. flys-artifacts/trunk@3613 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Raimund Renkert <raimund.renkert@intevation.de>
date Fri, 06 Jan 2012 12:02:10 +0000
parents cd9bcdcf6597
children f021080cb409
line wrap: on
line source
package de.intevation.flys.artifacts;

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

import org.apache.log4j.Logger;

import org.w3c.dom.Document;

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

import de.intevation.flys.artifacts.model.CrossSectionFacet;

import de.intevation.flys.model.CrossSection;
import de.intevation.flys.model.CrossSectionLine;
import de.intevation.flys.artifacts.model.CrossSectionFactory;
import de.intevation.artifacts.common.ArtifactNamespaceContext;
import de.intevation.artifacts.common.utils.XMLUtils;

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

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

import de.intevation.flys.utils.FLYSUtils;


/**
 * Artifact describing a cross-section.
 */
public class CrossSectionArtifact extends StaticFLYSArtifact {

    /** Access ids of doc. */
    public static final String XPATH_IDS = "/art:action/art:ids/@value";

    /** Name of Artifact. */
    public static final String CS_ARTIFACT_NAME = "cross_section";

    /** Name of state. */
    public static final String STATIC_STATE_NAME = "state.cross_section";

    /** Name of data item keeping the position. */
    public static final String DATA_KM = "cross_section.km";

    /** Name of data item keeping the database id of this c.s.. */
    public static final String DATA_DBID = "cross_section.dbid";

    /** Name of data item flagging whether we think that we are master. */
    public static final String DATA_IS_MASTER = "cross_section.master?";

    /** Name of data item flagging whether we are the newest. */
    public static final String DATA_IS_NEWEST = "cross_section.newest?";

    /** Own logger. */
    private static final Logger logger =
        Logger.getLogger(CrossSectionArtifact.class);


    /** Return given name. */
    @Override
    public String getName() {
        return CS_ARTIFACT_NAME;
    }


    /** Store ids, create a CrossSectionFacet. */
    @Override
    public void setup(
        String          identifier,
        ArtifactFactory factory,
        Object          context,
        CallMeta        callMeta,
        Document        data)
    {
        logger.info("CrossSectionArtifact.setup");

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

        String ids = XMLUtils.xpathString(
            data, XPATH_IDS, ArtifactNamespaceContext.INSTANCE);

        if (ids != null && ids.length() > 0) {
            addStringData(DATA_DBID, ids);
            logger.debug("CrossSectionArtifacts db-id: " + ids);
        }
        else {
            throw new IllegalArgumentException("No attribute 'ids' found!");
        }

        List<Facet> fs = new ArrayList<Facet>();
        CrossSection cs = CrossSectionFactory.getCrossSection(Integer.valueOf(ids));
        CrossSectionLine csl = cs.getLines().get(0);
        // Find min-km of cross sections, than set DATA_KM to min(DATA_KM, minCross).
        if (csl != null) {
            double masterKm = Double.valueOf(getDataAsString(DATA_KM));
            if (masterKm < csl.getKm().doubleValue()) {
                addStringData(DATA_KM, csl.getKm().toString());
            }
        }
        fs.add(new CrossSectionFacet(0, cs.getDescription()));

        // Find out if we are newest.
        boolean isNewest = CrossSectionFactory.isNewest(cs);
        String newString = (isNewest) ? "1" : "0";
        addStringData(DATA_IS_NEWEST, newString);
        addStringData(DATA_IS_MASTER, newString);

        StaticState state = (StaticState) getCurrentState(context);

        if (!fs.isEmpty()) {
            facets.put(getCurrentStateId(), fs);
        }
    }


    /** Copy km where master-artifact "starts". */
    @Override
    protected void initialize(
        Artifact artifact,
        Object   context,
        CallMeta callMeta)
    {
        WINFOArtifact winfo = (WINFOArtifact) artifact;
        double[] range = FLYSUtils.getKmRange(winfo);
        double min = 0.0f;
        if (range != null && range.length > 0) {
            min = range[0];
        }
        this.addStringData(DATA_KM, Double.toString(min));
    }


    /**
     * Create and return a new StaticState with charting output.
     */
    @Override
    public State getCurrentState(Object cc) {
        final List<Facet> fs = facets.get(getCurrentStateId());

        StaticState state = new StaticState(STATIC_STATE_NAME) {
            @Override
            public Object staticCompute(List<Facet> facets) {
                if (facets != null) {
                    facets.addAll(fs);
                }
                return null;
            }
        };

        state.addDefaultChartOutput("cross_section", fs);

        return state;
    }


    /**
     * 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(getCurrentState(context));

        return states;
    }

    // TODO all data access needs proper caching.

    /**
     * Get a DataItem casted to int (0 if fails).
     */
    public int getDataAsIntNull(String dataName) {
        String val = getDataAsString(dataName);
        try {
            return Integer.valueOf(val);
        }
        catch (NumberFormatException e) {
            logger.warn("Could not get data " + dataName + " as int", e);
            return 0;
        }
    }


    /** Returns database-id of cross-section (from data). */
    protected int getDBID() {
        return getDataAsIntNull(DATA_DBID);
    }


    /**
     * Return position (km) from data, 0 if not found.
     */
    protected double getKm() {
        String val = getDataAsString(DATA_KM);
        try {
            return Double.valueOf(val);
        }
        catch (NumberFormatException e) {
            logger.warn("Could not get data " + DATA_KM + " as double", e);
            return 0;
        }
    }


    /** Returns true if artifact is set to be a "master" (other facets will
     * refer to this). */
    public boolean isMaster() {
        return !getDataAsString(DATA_IS_MASTER).equals("0");
    }


    /**
     * Get points of Profile of cross section at given kilometer.
     *
     * @return an array holding coordinates of points of profile (
     *         in the form {{x1, x2} {y1, y2}} ).
     */
    public double [][] getCrossSectionData() {
        logger.info("getCrossSectionData() for cross_section.km "
            + getDataAsString(DATA_KM));
        CrossSectionLine line = searchCrossSectionLine();

        return line != null
               ? line.fetchCrossSectionProfile()
               : null;
    }


    /**
     * Get CrossSectionLine spatially closest to what is specified in the data
     * "cross_section.km".
     *
     * @return CrossSectionLine closest to "cross_section.km".
     */
    public CrossSectionLine searchCrossSectionLine() {
        double wishKM = getKm();

        CrossSection crossSection = CrossSectionFactory.getCrossSection(getDBID());
        logger.debug("dbid " + getDBID() + " : " + crossSection);
        List<CrossSectionLine> crossSectionLines =
            crossSection.getLines();
            
        // Get the cross section closest to requested km.
        // Naive, linear approach.
        CrossSectionLine oldLine = crossSectionLines.get(0);
        double oldDiff = Math.abs(wishKM - oldLine.getKm().doubleValue());
        for (CrossSectionLine line: crossSectionLines) {
            double diff = Math.abs(wishKM - line.getKm().doubleValue());
            if (diff > oldDiff) {
                break;
            }
            oldDiff = diff;
            oldLine = line;
        }
        return oldLine;
    }


    /**
     * Determines Facets initial disposition regarding activity (think of
     * selection in Client ThemeList GUI). This will be checked one time
     * when the facet enters a collections describe document.
     *
     * @param outputName Ignored.
     * @param facetName Ignored.
     * @param index     Ignored.
     * @return 0 if not active
     */
    @Override
    public int getInitialFacetActivity(String outputName, String facetName, int index) {
        return (getDataAsString(DATA_IS_NEWEST) != null
            && getDataAsString(DATA_IS_NEWEST).equals("1")) ? 1 : 0;
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org