Mercurial > dive4elements > river
diff flys-artifacts/src/main/java/de/intevation/flys/artifacts/CrossSectionArtifact.java @ 3468:f37e7e8907cb
merged flys-artifacts/2.8.1
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:14:39 +0200 |
parents | b0ba96bbf01d |
children | afc7bfb4800b |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/CrossSectionArtifact.java Fri Sep 28 12:14:39 2012 +0200 @@ -0,0 +1,341 @@ +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.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.State; + +import de.intevation.flys.utils.FLYSUtils; + +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); + + + /** 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()) { + facets.put(getCurrentStateId(), fs); + } + } + + + /** Copy km where master-artifact "starts". */ + @Override + protected void initialize( + Artifact artifact, + Object context, + CallMeta callMeta) + { + FLYSArtifact winfo = (FLYSArtifact) 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)); + } + + + /** 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 = 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.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); + } + + + /** + * 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 :