teichmann@5863: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde teichmann@5863: * Software engineering by Intevation GmbH teichmann@5863: * teichmann@5994: * This file is Free Software under the GNU AGPL (>=v3) teichmann@5863: * and comes with ABSOLUTELY NO WARRANTY! Check out the teichmann@5994: * documentation coming with Dive4Elements River for details. teichmann@5863: */ teichmann@5863: teichmann@5831: package org.dive4elements.river.artifacts; sascha@3556: gernotbelger@9425: import java.io.Serializable; gernotbelger@9425: import java.util.ArrayList; gernotbelger@9425: import java.util.List; gernotbelger@9425: gernotbelger@9425: import org.apache.log4j.Logger; teichmann@5831: import org.dive4elements.artifactdatabase.state.DefaultOutput; teichmann@5831: import org.dive4elements.artifactdatabase.state.Facet; teichmann@5831: import org.dive4elements.artifactdatabase.state.FacetActivity; teichmann@5831: import org.dive4elements.artifactdatabase.state.State; teichmann@5831: import org.dive4elements.artifacts.Artifact; teichmann@5831: import org.dive4elements.artifacts.ArtifactFactory; teichmann@5831: import org.dive4elements.artifacts.CallContext; teichmann@5831: import org.dive4elements.artifacts.CallMeta; teichmann@5831: import org.dive4elements.artifacts.common.utils.XMLUtils; teichmann@5831: import org.dive4elements.river.artifacts.math.Distance; teichmann@5831: import org.dive4elements.river.artifacts.math.Linear; teichmann@5831: import org.dive4elements.river.artifacts.model.CrossSectionWaterLineFacet; teichmann@5831: import org.dive4elements.river.artifacts.model.FacetTypes; teichmann@5831: import org.dive4elements.river.artifacts.model.RelativePointFacet; teichmann@5831: import org.dive4elements.river.artifacts.model.WKms; teichmann@5831: import org.dive4elements.river.artifacts.model.WKmsFacet; teichmann@5831: import org.dive4elements.river.artifacts.model.WKmsFactory; teichmann@5831: import org.dive4elements.river.artifacts.resources.Resources; gernotbelger@9425: import org.dive4elements.river.artifacts.states.DefaultState.ComputeType; teichmann@5831: import org.dive4elements.river.artifacts.states.StaticState; felix@1723: import org.w3c.dom.Document; felix@1723: felix@1723: /** felix@1723: * Artifact to access additional "waterlevel"-type of data, like the height felix@1723: * of protective measures (dikes). felix@1723: * teichmann@5867: * This artifact neglects (Static)D4EArtifacts capabilities of interaction felix@1723: * with the StateEngine by overriding the getState*-methods. felix@1723: */ gernotbelger@9425: public class StaticWKmsArtifact extends StaticD4EArtifact implements FacetTypes, WaterLineArtifact { teichmann@8202: /** The log for this class. */ gernotbelger@9425: private static Logger log = Logger.getLogger(StaticWKmsArtifact.class); felix@1723: sascha@3556: private static final String NAME = "staticwkms"; sascha@3556: sascha@3556: static { sascha@3556: // TODO: Move to configuration. gernotbelger@9425: FacetActivity.Registry.getInstance().register(NAME, FacetActivity.INACTIVE); sascha@3556: } felix@1723: gernotbelger@9425: public static final String STATIC_STATE_NAME = "state.additional_wkms.static"; felix@1771: gernotbelger@9425: /** gernotbelger@9425: * Data Item name to know whether we are Heighmarks and reveive gernotbelger@9425: * some data slightly different. gernotbelger@9425: */ gernotbelger@9425: public static final String DATA_HEIGHT_TYPE = "height_marks"; felix@3272: felix@1723: /** One and only state to be in. */ felix@1723: protected transient State state = null; felix@1723: felix@1723: /** felix@1723: * Trivial Constructor. felix@1723: */ felix@1723: public StaticWKmsArtifact() { teichmann@8202: log.debug("StaticWKmsArtifact.StaticWKmsArtifact"); felix@1723: } felix@1723: sascha@3556: @Override sascha@3556: public String getName() { sascha@3556: return NAME; sascha@3556: } felix@1723: felix@1723: /** felix@1723: * Gets called from factory, to set things up. felix@1723: */ felix@1723: @Override gernotbelger@9425: public void setup(final String identifier, final ArtifactFactory factory, final Object context, final CallMeta callMeta, final Document data, gernotbelger@9425: final List loadFacets) { teichmann@8202: log.debug("StaticWKmsArtifact.setup"); felix@1723: gernotbelger@9425: this.state = new StaticState(STATIC_STATE_NAME); felix@1725: teichmann@8202: if (log.isDebugEnabled()) { teichmann@8202: log.debug(XMLUtils.toString(data)); sascha@3556: } sascha@3556: gernotbelger@9425: final List fs = new ArrayList<>(); gernotbelger@9425: final String code = getDatacageIDValue(data); felix@1723: felix@1725: // TODO Go for JSON, one day. gernotbelger@9425: // ex.: flood_protection-wstv-114-12 felix@1723: if (code != null) { gernotbelger@9425: final String[] parts = code.split("-"); felix@1725: felix@1725: if (parts.length >= 4) { felix@1981: int col = -1; gernotbelger@9425: final int wst = Integer.parseInt(parts[3]); felix@1729: felix@1981: if (!parts[2].equals("A")) { sascha@3405: col = Integer.parseInt(parts[2]); felix@1981: } felix@1729: ingo@3804: addStringData("col_pos", parts[2]); gernotbelger@9425: addStringData("wst_id", parts[3]); felix@1815: felix@1981: String wkmsName; felix@2088: if (col >= 0) { felix@7686: // The W-Wrapping could be done in here (like in felix@7686: // StaticWQKmsArtifact), with benefit of i18nation, felix@7686: // but slower execution (it wrappes based on kind felix@7686: // which can be fetched in same sql query). felix@7686: wkmsName = WKmsFactory.getWKmsNameWWrapped(col, wst); gernotbelger@9425: } else { felix@7686: wkmsName = WKmsFactory.getWKmsNameWWrapped(wst); felix@1981: } ingo@3804: ingo@3804: String name; ingo@3804: if (parts[0].equals(HEIGHTMARKS_POINTS)) { ingo@3804: name = HEIGHTMARKS_POINTS; felix@3272: addStringData(DATA_HEIGHT_TYPE, "true"); gernotbelger@9425: } else if (parts[0].equals("additionalsmarks")) { felix@4143: name = STATIC_WKMS_MARKS; gernotbelger@9425: } else if (parts[0].equals("delta_w")) { felix@7524: name = STATIC_DELTA_W; gernotbelger@9425: } else if (parts[0].equals("delta_w_cma")) { felix@7524: name = STATIC_DELTA_W_CMA; gernotbelger@9425: } else { ingo@3804: name = STATIC_WKMS; ingo@3804: } ingo@3804: gernotbelger@9425: final String facetDescription = Resources.getMsg(callMeta, wkmsName, wkmsName); gernotbelger@9425: final Facet wKmsFacet = new WKmsFacet(name, facetDescription); gernotbelger@9425: final Facet csFacet = new CrossSectionWaterLineFacet(0, facetDescription, null, null, null, null); gernotbelger@9425: final Facet rpFacet = new RelativePointFacet(facetDescription); felix@2743: felix@1981: fs.add(wKmsFacet); felix@1981: fs.add(csFacet); felix@2743: fs.add(rpFacet); gernotbelger@9425: addFacets(this.state.getID(), fs); felix@1725: } felix@1723: } felix@1723: felix@1723: spawnState(); rrenkert@7842: super.setup(identifier, factory, context, callMeta, data, loadFacets); felix@1723: } felix@1723: felix@1723: /** felix@1723: * Initialize the static state with output. gernotbelger@9425: * felix@1725: * @return static state felix@1723: */ felix@1723: protected State spawnState() { gernotbelger@9425: this.state = new StaticState(STATIC_STATE_NAME); gernotbelger@9425: final List fs = getFacets(STATIC_STATE_NAME); gernotbelger@9425: final DefaultOutput output = new DefaultOutput("general", "general", "image/png", fs, "chart"); felix@1723: gernotbelger@9425: this.state.getOutputs().add(output); gernotbelger@9425: return this.state; felix@1723: } felix@1723: felix@1723: /** felix@1723: * Called via setup. felix@1809: * gernotbelger@9425: * @param artifact gernotbelger@9425: * The master-artifact. felix@1723: */ felix@1723: @Override gernotbelger@9425: protected void initialize(final Artifact artifact, final Object context, final CallMeta meta) { teichmann@8202: log.debug("StaticWKmsArtifact.initialize"); gernotbelger@9425: final D4EArtifact winfo = (D4EArtifact) artifact; felix@1723: // TODO: The river is of no interest, so far. felix@1723: addData("river", winfo.getData("river")); felix@1723: } felix@1723: felix@1723: /** felix@1723: * Get a list containing the one and only State. gernotbelger@9425: * gernotbelger@9425: * @param context gernotbelger@9425: * ignored. felix@1723: * @return list with one and only state. felix@1723: */ felix@1723: @Override gernotbelger@9425: protected List getStates(final Object context) { gernotbelger@9425: final ArrayList states = new ArrayList<>(); felix@1723: states.add(getState()); felix@1723: return states; felix@1723: } felix@1723: felix@1723: /** felix@1723: * Get the "current" state (there is but one). gernotbelger@9425: * gernotbelger@9425: * @param cc gernotbelger@9425: * ignored. felix@1723: * @return the "current" (only possible) state. felix@1723: */ felix@1723: @Override gernotbelger@9425: public State getCurrentState(final Object cc) { felix@1723: return getState(); felix@1723: } felix@1723: felix@1723: /** felix@1723: * Get the only possible state. gernotbelger@9425: * felix@1723: * @return the state. felix@1723: */ felix@1723: protected State getState() { felix@1723: return getState(null, null); felix@1723: } felix@1723: felix@1723: /** felix@1723: * Get the state. gernotbelger@9425: * gernotbelger@9425: * @param context gernotbelger@9425: * ignored. gernotbelger@9425: * @param stateID gernotbelger@9425: * ignored. felix@1723: * @return the state. felix@1723: */ felix@1723: @Override gernotbelger@9425: protected State getState(final Object context, final String stateID) { gernotbelger@9425: return (this.state != null) ? this.state : spawnState(); felix@1723: } felix@1723: felix@1723: /** felix@1723: * Get WKms from factory. gernotbelger@9425: * gernotbelger@9425: * @param idx gernotbelger@9425: * param is not needed (TODO?) felix@1723: * @return WKms according to parameterization (can be null); felix@1723: */ gernotbelger@9425: public WKms getWKms() { teichmann@8202: log.debug("StaticWKmsArtifact.getWKms"); felix@1723: gernotbelger@9425: return WKmsFactory.getWKms(Integer.parseInt(getDataAsString("col_pos")), Integer.parseInt(getDataAsString("wst_id"))); felix@1723: } felix@1729: gernotbelger@9425: public WKms getWKms(final double from, final double to) { andre@8547: log.debug("StaticWKmsArtifact.getWKms"); andre@8547: gernotbelger@9425: return WKmsFactory.getWKms(Integer.parseInt(getDataAsString("col_pos")), Integer.parseInt(getDataAsString("wst_id")), from, to); andre@8547: } felix@1729: felix@1729: /** felix@2745: * Returns W at Km of WKms, linearly interpolated. felix@2745: * Returns -1 if not found. felix@2745: */ gernotbelger@9425: public static double getWAtKmLin(final WKms wkms, final double km) { felix@2745: // Uninformed search. gernotbelger@9425: final int size = wkms.size(); felix@4031: if (size == 0) { felix@4031: return -1; felix@4031: } felix@2745: int idx = 0; felix@4031: felix@4031: boolean kmIncreasing; felix@4031: if (size == 1) { felix@4031: kmIncreasing = true; gernotbelger@9425: } else { gernotbelger@9425: kmIncreasing = (wkms.getKm(0) < wkms.getKm(wkms.size() - 1)) ? true : false; felix@4031: } felix@2745: if (kmIncreasing) { felix@2745: while (idx < size && wkms.getKm(idx) < km) { felix@2745: idx++; felix@2745: } gernotbelger@9425: } else { gernotbelger@9425: idx = wkms.size() - 1; felix@2745: while (idx > 0 && wkms.getKm(idx) > km) { felix@2745: idx--; felix@2745: } felix@2745: } felix@2745: gernotbelger@9425: if (wkms.getKm(idx) == km) { gernotbelger@9425: return wkms.getW(idx); gernotbelger@9425: } felix@4031: gernotbelger@9425: if (idx == size - 1 || idx == 0) { felix@2745: return -1; felix@2745: } felix@2745: felix@6573: // Do linear interpolation. gernotbelger@9425: final int mod = kmIncreasing ? -1 : +1; gernotbelger@9425: return Linear.linear(km, wkms.getKm(idx + mod), wkms.getKm(idx), wkms.getW(idx + mod), wkms.getW(idx)); felix@2745: } felix@2745: felix@2745: /** felix@3272: * Get the W at a specific km, only if it is closer to km than to any of felix@3272: * the other given km. felix@3272: * Return Double.NaN otherwise felix@1729: * gernotbelger@9425: * @param wkms gernotbelger@9425: * WKms in which to search for a spatially close W value. gernotbelger@9425: * @param km gernotbelger@9425: * the input km, which is compared to values from wkms. gernotbelger@9425: * @param next gernotbelger@9425: * the next available input km (-1 if unavailable). gernotbelger@9425: * @param prev gernotbelger@9425: * the previous available input km (-1 if unavailable). felix@1729: * tom@8856: * @return W in wkms that is closer to km than to next and prev, tom@8856: * or Double.NaN. felix@3272: */ gernotbelger@9425: public double getWAtCloseKm(final WKms wkms, final double km, final double next, final double prev) { felix@3903: // TODO symbolic "-1" pr next/prev is a bad idea (tm), as we compare gernotbelger@9425: // distances to these values later. felix@3903: // TODO issue888 felix@3903: gernotbelger@9425: final int size = wkms.size(); felix@3272: for (int i = 0; i < size; i++) { gernotbelger@9425: final double wkmsKm = wkms.getKm(i); gernotbelger@9425: final double dist = Distance.distance(wkmsKm, km); felix@3903: if (dist == 0d) { felix@3903: return wkms.getW(i); felix@3903: } felix@3903: felix@3903: // Problematic Cases: felix@3903: // X == km , | and | == prev and next, (?) == wkmsKm felix@3903: // felix@3903: // Standard case: felix@3903: // ----------|----X-----|------- gernotbelger@9425: // (1) (2) (3) (4) sascha@3909: // felix@3903: // With prev==-1 felix@3903: // -1 ------X-------|------ gernotbelger@9425: // (5) (6) (7) felix@3903: // felix@3903: // With next==-1 felix@3903: // felix@3903: // ---|-----X----- -1 gernotbelger@9425: // (8) (9) (10) felix@3903: gernotbelger@9425: if (dist <= Distance.distance(wkmsKm, prev) && dist <= Distance.distance(wkmsKm, next)) { felix@3273: return wkms.getW(i); felix@3272: } felix@3272: } felix@3272: felix@3272: return Double.NaN; felix@3272: } felix@3272: felix@3272: /** felix@1981: * Returns W at Km of WKms, searching linearly. felix@1981: * Returns -1 if not found. gernotbelger@9425: * gernotbelger@9425: * @param wkms gernotbelger@9425: * the WKms object to search for given km. gernotbelger@9425: * @param km gernotbelger@9425: * The searched km. felix@3044: * @return W at given km if in WKms, -1 if not found. felix@1981: */ gernotbelger@9425: public static double getWAtKm(final WKms wkms, final double km) { gernotbelger@9425: felix@3272: // Uninformed search, intolerant. gernotbelger@9425: final double TOLERANCE = 0.0d; gernotbelger@9425: final int size = wkms.size(); gernotbelger@9425: if (size == 0) gernotbelger@9425: return Double.NaN; gernotbelger@9425: felix@1981: for (int i = 0; i < size; i++) { felix@3272: if (Distance.within(wkms.getKm(i), km, TOLERANCE)) { felix@1981: return wkms.getW(i); felix@1981: } felix@1981: } felix@1981: gernotbelger@9425: return Double.NaN; felix@1981: } felix@1981: felix@1981: /** felix@1981: * Get points of line describing the surface of water at cross section. felix@1981: * gernotbelger@9425: * @param idx gernotbelger@9425: * Index of facet and in wkms array. gernotbelger@9425: * @param csl gernotbelger@9425: * FastCrossSectionLine to compute water surface agains. gernotbelger@9425: * @param next gernotbelger@9425: * The km of the next crosssectionline. gernotbelger@9425: * @param prev gernotbelger@9425: * The km of the previous crosssectionline. gernotbelger@9425: * @param context gernotbelger@9425: * Ignored in this implementation. felix@3272: * felix@1981: * @return an array holding coordinates of points of surface of water ( felix@3044: * in the form {{x1, x2}, {y1, y2}} ). felix@1729: */ felix@1809: @Override gernotbelger@9425: public double getWaterLevel(final ComputeType type, final String hash, final String stateId, final double currentKm, final Serializable waterLineIndex, gernotbelger@9425: final double nextKm, final double prevKm, final CallContext context) { felix@1981: gernotbelger@9425: final WKms wkms = getWKms(); felix@1981: felix@1981: // Find W at km. felix@3272: felix@3272: // If heightmarks, only deliver if data snaps. gernotbelger@9425: if (getDataAsString(DATA_HEIGHT_TYPE) != null && getDataAsString(DATA_HEIGHT_TYPE).equals("true")) gernotbelger@9425: return getWAtCloseKm(wkms, currentKm, nextKm, prevKm); felix@3272: gernotbelger@9425: return getWAtKm(wkms, currentKm); felix@1729: } gernotbelger@9425: }