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: 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; felix@1723: 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; felix@1723: teichmann@5831: import org.dive4elements.artifacts.common.utils.XMLUtils; felix@1723: teichmann@5831: import org.dive4elements.river.artifacts.geom.Lines; sascha@2120: teichmann@5831: import org.dive4elements.river.artifacts.math.Distance; teichmann@5831: import org.dive4elements.river.artifacts.math.Linear; teichmann@5831: 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: teichmann@5831: import org.dive4elements.river.artifacts.resources.Resources; teichmann@5831: teichmann@5831: import org.dive4elements.river.artifacts.states.StaticState; teichmann@5831: teichmann@5831: import org.dive4elements.river.model.FastCrossSectionLine; felix@1981: sascha@3556: import java.awt.geom.Point2D; sascha@3556: felix@1723: import java.util.ArrayList; felix@1723: import java.util.List; felix@1723: felix@1723: import org.apache.log4j.Logger; felix@1723: 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: */ felix@1723: public class StaticWKmsArtifact teichmann@5867: extends StaticD4EArtifact felix@1981: implements FacetTypes, WaterLineArtifact felix@1723: { felix@1723: /** The logger for this class. */ felix@1723: private static Logger logger = felix@1723: 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. sascha@3556: FacetActivity.Registry.getInstance() sascha@3556: .register(NAME, FacetActivity.INACTIVE); sascha@3556: } felix@1723: felix@1771: public static final String STATIC_STATE_NAME = felix@1771: "state.additional_wkms.static"; felix@1771: felix@3272: /** Data Item name to know whether we are Heighmarks and reveive felix@3272: * some data slightly different. */ felix@3272: public static final String DATA_HEIGHT_TYPE = felix@3272: "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: /** felix@1723: * Trivial Constructor. felix@1723: */ felix@1723: public StaticWKmsArtifact() { felix@1723: logger.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 felix@1723: public void setup( felix@1723: String identifier, felix@1723: ArtifactFactory factory, felix@1723: Object context, felix@1723: CallMeta callMeta, felix@1723: Document data) felix@1723: { felix@1723: logger.debug("StaticWKmsArtifact.setup"); felix@1723: felix@1787: state = new StaticState(STATIC_STATE_NAME); felix@1725: sascha@3556: if (logger.isDebugEnabled()) { sascha@3556: logger.debug(XMLUtils.toString(data)); sascha@3556: } sascha@3556: felix@1771: List fs = new ArrayList(); felix@2743: String code = getDatacageIDValue(data); felix@1723: felix@1725: // TODO Go for JSON, one day. felix@1723: //ex.: flood_protection-wstv-114-12 felix@1723: if (code != null) { felix@1723: String [] parts = code.split("-"); felix@1725: felix@1725: if (parts.length >= 4) { felix@1981: int col = -1; sascha@3405: 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]); ingo@3804: addStringData("wst_id", parts[3]); felix@1815: felix@1981: String wkmsName; felix@2088: if (col >= 0) { felix@1981: wkmsName = WKmsFactory.getWKmsName(col, wst); felix@1981: } felix@1981: else { felix@1981: wkmsName = WKmsFactory.getWKmsName(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"); felix@1725: } felix@4143: else if (parts[0].equals("additionalsmarks")) { felix@4143: name = STATIC_WKMS_MARKS; felix@4143: } felix@7524: else if (parts[0].equals("delta_w")) { felix@7524: name = STATIC_DELTA_W; felix@7524: } felix@7524: else if (parts[0].equals("delta_w_cma")) { felix@7524: name = STATIC_DELTA_W_CMA; felix@7524: } ingo@3804: else { ingo@3804: name = STATIC_WKMS; ingo@3804: } ingo@3804: felix@1981: String facetDescription = Resources.getMsg( felix@1981: callMeta, wkmsName, wkmsName); felix@1981: Facet wKmsFacet = new WKmsFacet( ingo@3804: name, felix@1981: facetDescription); felix@1981: Facet csFacet = new CrossSectionWaterLineFacet(0, felix@1981: facetDescription); felix@2743: Facet rpFacet = new RelativePointFacet(facetDescription); felix@2743: felix@1981: fs.add(wKmsFacet); felix@1981: fs.add(csFacet); felix@2743: fs.add(rpFacet); bjoern@4497: addFacets(state.getID(), fs); felix@1725: } felix@1723: } felix@1723: felix@1723: spawnState(); felix@1723: super.setup(identifier, factory, context, callMeta, data); felix@1723: } felix@1723: felix@1723: felix@1723: /** felix@1723: * Initialize the static state with output. felix@1725: * @return static state felix@1723: */ felix@1723: protected State spawnState() { felix@1787: state = new StaticState(STATIC_STATE_NAME); bjoern@4497: List fs = getFacets(STATIC_STATE_NAME); ingo@3804: DefaultOutput output = new DefaultOutput( ingo@3804: "general", ingo@3804: "general", "image/png", felix@1771: fs, felix@1765: "chart"); felix@1723: ingo@3804: state.getOutputs().add(output); felix@1723: return state; felix@1723: } felix@1723: felix@1723: felix@1723: /** felix@1723: * Called via setup. felix@1809: * felix@1809: * @param artifact The master-artifact. felix@1723: */ felix@1723: @Override felix@1809: protected void initialize( felix@1809: Artifact artifact, felix@1809: Object context, felix@1809: CallMeta meta) felix@1809: { felix@1723: logger.debug("StaticWKmsArtifact.initialize"); teichmann@5867: 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: /** felix@1723: * Get a list containing the one and only State. felix@1723: * @param context ignored. felix@1723: * @return list with one and only state. felix@1723: */ felix@1723: @Override felix@1723: protected List getStates(Object context) { felix@1723: ArrayList states = new ArrayList(); felix@1723: states.add(getState()); felix@1723: return states; felix@1723: } felix@1723: felix@1723: felix@1723: /** felix@1723: * Get the "current" state (there is but one). felix@1723: * @param cc ignored. felix@1723: * @return the "current" (only possible) state. felix@1723: */ felix@1723: @Override felix@1723: public State getCurrentState(Object cc) { felix@1723: return getState(); felix@1723: } felix@1723: felix@1723: felix@1723: /** felix@1723: * Get the only possible state. 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: /** felix@1723: * Get the state. felix@1723: * @param context ignored. felix@1723: * @param stateID ignored. felix@1723: * @return the state. felix@1723: */ felix@1723: @Override felix@1723: protected State getState(Object context, String stateID) { felix@1809: return (state != null) felix@1809: ? state felix@1809: : spawnState(); felix@1723: } felix@1723: felix@1723: felix@1723: /** felix@1723: * Get WKms from factory. felix@3284: * @param idx param is not needed (TODO?) felix@1723: * @return WKms according to parameterization (can be null); felix@1723: */ felix@1723: public WKms getWKms(int idx) { felix@1723: logger.debug("StaticWKmsArtifact.getWKms"); felix@1723: felix@1723: return WKmsFactory.getWKms( sascha@3405: Integer.parseInt(getDataAsString("col_pos")), sascha@3405: Integer.parseInt(getDataAsString("wst_id"))); felix@1723: } felix@1729: felix@1729: felix@1729: /** felix@2745: * Returns W at Km of WKms, linearly interpolated. felix@2745: * Returns -1 if not found. felix@2745: */ felix@2745: public static double getWAtKmLin(WKms wkms, double km) { felix@2745: // Uninformed search. felix@2745: 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; felix@4031: } felix@4031: else { felix@4031: kmIncreasing = (wkms.getKm(0) < wkms.getKm(wkms.size()-1)) felix@4031: ? true : false; felix@4031: } felix@2745: if (kmIncreasing) { felix@2745: while (idx < size && wkms.getKm(idx) < km) { felix@2745: idx++; felix@2745: } felix@2745: } felix@2745: else { felix@2745: idx = wkms.size() -1; felix@2745: while (idx > 0 && wkms.getKm(idx) > km) { felix@2745: idx--; felix@2745: } felix@2745: } felix@2745: felix@4031: if (wkms.getKm(idx) == km) { felix@4031: return wkms.getW(idx); felix@4031: } felix@4031: felix@2745: if (idx == size -1 || idx == 0) { felix@2745: return -1; felix@2745: } felix@2745: felix@6573: // Do linear interpolation. felix@2745: int mod = kmIncreasing ? -1 : +1; felix@2745: 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@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: * felix@3272: * @param wkms WKms in which to search for a spatially close W value. felix@3272: * @param km the input km, which is compared to values from wkms. felix@3273: * @param next the next available input km (-1 if unavailable). felix@3273: * @param prev the previous available input km (-1 if unavailable). felix@1729: * felix@3272: * @return W in wkms that is closer to km than to next and prev, or Double.NaN. felix@3272: */ felix@3272: public double getWAtCloseKm(WKms wkms, double km, double next, double prev) { felix@3903: // TODO symbolic "-1" pr next/prev is a bad idea (tm), as we compare felix@3903: // distances to these values later. felix@3903: // TODO issue888 felix@3903: felix@3272: int size = wkms.size(); felix@3272: for (int i = 0; i < size; i++) { felix@3272: double wkmsKm = wkms.getKm(i); felix@3272: 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-----|------- felix@3903: // (1) (2) (3) (4) sascha@3909: // felix@3903: // With prev==-1 felix@3903: // -1 ------X-------|------ felix@3903: // (5) (6) (7) felix@3903: // felix@3903: // With next==-1 felix@3903: // felix@3903: // ---|-----X----- -1 felix@3903: // (8) (9) (10) felix@3903: felix@3903: if (dist <= Distance.distance(wkmsKm, prev) felix@3903: && 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@3272: /** felix@1981: * Returns W at Km of WKms, searching linearly. felix@1981: * Returns -1 if not found. felix@3044: * @param wkms the WKms object to search for given km. felix@3044: * @param km The searched km. felix@3044: * @return W at given km if in WKms, -1 if not found. felix@1981: */ felix@2745: public static double getWAtKm(WKms wkms, double km) { felix@3272: // Uninformed search, intolerant. felix@3272: double TOLERANCE = 0.0d; felix@1981: int size = wkms.size(); 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: felix@1981: return -1; felix@1981: } felix@1981: felix@1981: felix@1981: /** felix@1981: * Get points of line describing the surface of water at cross section. felix@1981: * felix@3272: * @param idx Index of facet and in wkms array. felix@3272: * @param csl FastCrossSectionLine to compute water surface agains. felix@3272: * @param next The km of the next crosssectionline. felix@3272: * @param prev The km of the previous crosssectionline. felix@4478: * @param context 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 felix@3272: public Lines.LineData getWaterLines(int idx, FastCrossSectionLine csl, felix@4478: double next, double prev, CallContext context felix@3272: ) { felix@3272: logger.debug("getWaterLines(" + idx + ")/" + identifier()); felix@1981: sascha@2120: List points = csl.getPoints(); felix@1981: felix@1981: WKms wkms = getWKms(0); felix@1981: sascha@2120: double km = csl.getKm(); felix@1981: felix@1981: // Find W at km. felix@3272: double wAtKm; felix@3272: felix@3272: // If heightmarks, only deliver if data snaps. felix@3272: if (getDataAsString(DATA_HEIGHT_TYPE) != null && felix@3272: getDataAsString(DATA_HEIGHT_TYPE).equals("true")) { felix@3272: wAtKm = getWAtCloseKm(wkms, km, next, prev); felix@3272: } felix@3272: else { felix@3272: wAtKm = getWAtKm(wkms, km); felix@3272: } felix@3272: felix@3272: if (wAtKm == -1 || Double.isNaN(wAtKm)) { felix@1981: logger.warn("Waterlevel at km " + km + " unknown."); felix@2673: return new Lines.LineData(new double[][] {{}}, 0d, 0d); felix@1981: } felix@1981: felix@1981: return Lines.createWaterLines(points, wAtKm); felix@1729: } felix@1723: } felix@1723: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :