view flys-artifacts/src/main/java/de/intevation/flys/artifacts/CrossSectionArtifact.java @ 5818:a4ff4167be1e

Request feature info on all layers and show it as html if the server does not return valid gml. Non queryable layers produce an error message when the request fails. This is good enough
author Andre Heinecke <aheinecke@intevation.de>
date Wed, 24 Apr 2013 17:33:27 +0200
parents f2e7f07f608d
children
line wrap: on
line source
package de.intevation.flys.artifacts;

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

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.access.RangeAccess;
import de.intevation.flys.artifacts.model.CrossSectionFacet;
import de.intevation.flys.artifacts.model.FastCrossSectionLineFactory;

import de.intevation.flys.model.FastCrossSectionLine;

import de.intevation.flys.model.CrossSection;
import de.intevation.flys.model.CrossSectionLine;
import de.intevation.flys.artifacts.model.CrossSectionFactory;

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

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

import de.intevation.flys.artifacts.services.CrossSectionKMService;


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

    /** 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?";

    /** Name of data item storing the previous possible km. */
    public static final String DATA_PREV_KM = "cross_section.km.previous";

    /** Name of data item storing the next possible km. */
    public static final String DATA_NEXT_KM = "cross_section.km.next";

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

    static {
        // TODO: Move to configuration.
        FacetActivity.Registry.getInstance().register(
            CS_ARTIFACT_NAME,
            new FacetActivity() {
                @Override
                public Boolean isInitialActive(
                    Artifact artifact,
                    Facet    facet,
                    String   outputName
                ) {
                    if (artifact instanceof FLYSArtifact) {
                        FLYSArtifact flys = (FLYSArtifact)artifact;
                        String data = flys.getDataAsString(DATA_IS_NEWEST);
                        return data != null && data.equals("1");
                    }
                    return null;
                }
            });
    }

    /** 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 = getDatacageIDValue(data);

        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.parseInt(ids));

        List<CrossSectionLine> csls = cs.getLines();
        if (!csls.isEmpty()) {
            CrossSectionLine csl = csls.get(0);
            // Find min-km of cross sections,
            // then set DATA_KM to min(DATA_KM, minCross).
            double dataKm = Double.valueOf(getDataAsString(DATA_KM));
            if (dataKm < csl.getKm().doubleValue()) {
                addStringData(DATA_KM, csl.getKm().toString());
            }
        }
        fs.add(new CrossSectionFacet(0, cs.getDescription()));

        // Find out if we are newest and become master if so.
        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()) {
            addFacets(getCurrentStateId(), fs);
        }
    }


    /** Copy km where master-artifact "starts". */
    @Override
    protected void initialize(
        Artifact artifact,
        Object   context,
        CallMeta callMeta)
    {
        FLYSArtifact flys = (FLYSArtifact) artifact;

        RangeAccess rangeAccess = new RangeAccess(flys, null);
        double[] range = rangeAccess.getKmRange();
        double min = 0.0f;
        if (range != null && range.length > 0) {
            min = range[0];
        }
        this.addStringData(DATA_KM, Double.toString(min));
    }


    /** Returns next possible km for a cross-section. */
    public Double getNextKm() {
        return getDataAsDouble(DATA_NEXT_KM);
    }


    /** Returns previous possible km for a cross-section. */
    public Double getPrevKm() {
        return getDataAsDouble(DATA_PREV_KM);
    }


    /**
     * Create and return a new StaticState with charting output.
     */
    @Override
    public State getCurrentState(Object cc) {
        final List<Facet> fs = getFacets(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.parseInt(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));
        FastCrossSectionLine line = searchCrossSectionLine();

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


    /**
     * Get CrossSectionLine spatially closest to what is specified in the data
     * "cross_section.km", null if considered too far.
     *
     * It also adds DataItems to store the next and previous (numerically)
     * values at which cross-section data was recorded.
     *
     * @return CrossSectionLine closest to "cross_section.km", might be null
     *         if considered too far.
     */
    public FastCrossSectionLine searchCrossSectionLine() {
        double TOO_FAR = 1d;
        CrossSection crossSection = CrossSectionFactory
            .getCrossSection(getDBID());

        if (logger.isDebugEnabled()) {
            logger.debug("dbid " + getDBID() + " : " + crossSection);
        }

        NavigableMap<Double, Integer> kms = CrossSectionKMService
            .getKms(crossSection.getId());

        Double wishKM = getKm();

        Double floor = kms.floorKey(wishKM);
        Double ceil  = kms.ceilingKey(wishKM);

        Double nextKm;
        Double prevKm;

        double floorD = floor != null
            ? Math.abs(floor - wishKM)
            : Double.MAX_VALUE;

        double ceilD = ceil != null
            ? Math.abs(ceil - wishKM)
            : Double.MAX_VALUE;

        double km;
        if (floorD < ceilD) {
            km = floor;
        }
        else {
            km = ceil;
        }

        // If we are too far from the wished km, return null.
        if (Math.abs(km - wishKM) > TOO_FAR) {
            return null;
        }

        // Store next and previous km.
        nextKm = kms.higherKey(km);
        prevKm = kms.lowerKey(km);

        if (prevKm == null) {
            prevKm = -1d;
        }
        if (nextKm == null) {
            nextKm = -1d;
        }

        addStringData(DATA_PREV_KM, prevKm.toString());
        addStringData(DATA_NEXT_KM, nextKm.toString());

        return FastCrossSectionLineFactory
            .getCrossSectionLine(crossSection, km);
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org