felix@1964: package de.intevation.flys.artifacts; felix@1964: felix@1964: import java.util.ArrayList; felix@1964: import java.util.List; sascha@2120: import java.util.NavigableMap; felix@1964: felix@1964: import org.apache.log4j.Logger; felix@1964: felix@1964: import org.w3c.dom.Document; felix@1964: felix@1964: import de.intevation.artifacts.Artifact; felix@1964: import de.intevation.artifacts.ArtifactFactory; felix@1964: import de.intevation.artifacts.CallMeta; felix@1964: felix@1964: import de.intevation.flys.artifacts.model.CrossSectionFacet; sascha@2120: import de.intevation.flys.artifacts.model.FastCrossSectionLineFactory; felix@1964: sascha@2126: import de.intevation.flys.model.FastCrossSectionLine; sascha@2126: felix@1967: import de.intevation.flys.model.CrossSection; felix@1967: import de.intevation.flys.model.CrossSectionLine; felix@1967: import de.intevation.flys.artifacts.model.CrossSectionFactory; felix@1964: felix@1964: import de.intevation.flys.artifacts.states.StaticState; felix@1964: felix@1964: import de.intevation.artifactdatabase.state.Facet; sascha@3556: import de.intevation.artifactdatabase.state.FacetActivity; felix@1964: import de.intevation.artifactdatabase.state.State; felix@1964: felix@2037: import de.intevation.flys.utils.FLYSUtils; felix@2037: sascha@2120: import de.intevation.flys.artifacts.services.CrossSectionKMService; sascha@2120: felix@1964: felix@1985: /** felix@1985: * Artifact describing a cross-section. felix@1985: */ felix@1964: public class CrossSectionArtifact extends StaticFLYSArtifact { felix@1964: felix@1964: /** Name of Artifact. */ felix@1964: public static final String CS_ARTIFACT_NAME = "cross_section"; felix@1964: felix@1964: /** Name of state. */ felix@1964: public static final String STATIC_STATE_NAME = "state.cross_section"; felix@1964: felix@1967: /** Name of data item keeping the position. */ felix@1967: public static final String DATA_KM = "cross_section.km"; felix@1967: felix@1967: /** Name of data item keeping the database id of this c.s.. */ felix@1967: public static final String DATA_DBID = "cross_section.dbid"; felix@1967: felix@2040: /** Name of data item flagging whether we think that we are master. */ felix@1967: public static final String DATA_IS_MASTER = "cross_section.master?"; felix@1967: felix@2040: /** Name of data item flagging whether we are the newest. */ felix@2040: public static final String DATA_IS_NEWEST = "cross_section.newest?"; felix@2040: felix@3272: /** Name of data item storing the previous possible km. */ felix@3272: public static final String DATA_PREV_KM = "cross_section.km.previous"; felix@3272: felix@3272: /** Name of data item storing the next possible km. */ felix@3272: public static final String DATA_NEXT_KM = "cross_section.km.next"; felix@3272: felix@1964: /** Own logger. */ felix@1964: private static final Logger logger = felix@1964: Logger.getLogger(CrossSectionArtifact.class); felix@1964: sascha@3556: static { sascha@3556: // TODO: Move to configuration. sascha@3556: FacetActivity.Registry.getInstance().register( sascha@3556: CS_ARTIFACT_NAME, sascha@3556: new FacetActivity() { sascha@3556: @Override sascha@3556: public boolean isInitialActive( sascha@3556: Artifact artifact, sascha@3556: Facet facet, sascha@3556: String outputName sascha@3556: ) { sascha@3556: if (artifact instanceof FLYSArtifact) { sascha@3556: FLYSArtifact flys = (FLYSArtifact)artifact; sascha@3556: String data = flys.getDataAsString(DATA_IS_NEWEST); sascha@3556: return data != null && data.equals("1"); sascha@3556: } sascha@3556: return false; sascha@3556: } sascha@3556: }); sascha@3556: } felix@1964: felix@1964: /** Return given name. */ felix@1964: @Override felix@1964: public String getName() { felix@1964: return CS_ARTIFACT_NAME; felix@1964: } felix@1964: felix@1964: felix@1985: /** Store ids, create a CrossSectionFacet. */ felix@1964: @Override felix@1964: public void setup( felix@1964: String identifier, felix@1964: ArtifactFactory factory, felix@1964: Object context, felix@1964: CallMeta callMeta, felix@1964: Document data) felix@1964: { felix@1964: logger.info("CrossSectionArtifact.setup"); felix@1964: felix@1964: super.setup(identifier, factory, context, callMeta, data); felix@1964: felix@2741: String ids = getDatacageIDValue(data); felix@1964: felix@1964: if (ids != null && ids.length() > 0) { felix@1967: addStringData(DATA_DBID, ids); felix@1967: logger.debug("CrossSectionArtifacts db-id: " + ids); felix@1964: } felix@1964: else { felix@1964: throw new IllegalArgumentException("No attribute 'ids' found!"); felix@1964: } felix@1964: felix@1964: List fs = new ArrayList(); sascha@2120: CrossSection cs = CrossSectionFactory.getCrossSection( sascha@3405: Integer.parseInt(ids)); sascha@2120: sascha@2120: List csls = cs.getLines(); sascha@2120: if (!csls.isEmpty()) { sascha@2120: CrossSectionLine csl = csls.get(0); sascha@2120: // Find min-km of cross sections, sascha@2120: // then set DATA_KM to min(DATA_KM, minCross). felix@3272: double dataKm = Double.valueOf(getDataAsString(DATA_KM)); felix@3272: if (dataKm < csl.getKm().doubleValue()) { felix@2036: addStringData(DATA_KM, csl.getKm().toString()); felix@2036: } felix@2036: } felix@1971: fs.add(new CrossSectionFacet(0, cs.getDescription())); felix@1964: felix@3272: // Find out if we are newest and become master if so. felix@2040: boolean isNewest = CrossSectionFactory.isNewest(cs); felix@2040: String newString = (isNewest) ? "1" : "0"; felix@2040: addStringData(DATA_IS_NEWEST, newString); felix@2040: addStringData(DATA_IS_MASTER, newString); felix@2040: felix@1964: StaticState state = (StaticState) getCurrentState(context); felix@1964: felix@1964: if (!fs.isEmpty()) { felix@1964: facets.put(getCurrentStateId(), fs); felix@1964: } felix@1964: } felix@1964: felix@1964: felix@2036: /** Copy km where master-artifact "starts". */ felix@1964: @Override felix@1964: protected void initialize( felix@1964: Artifact artifact, felix@1964: Object context, felix@1964: CallMeta callMeta) felix@1964: { raimund@2153: FLYSArtifact winfo = (FLYSArtifact) artifact; felix@2037: double[] range = FLYSUtils.getKmRange(winfo); felix@2037: double min = 0.0f; felix@2037: if (range != null && range.length > 0) { felix@2037: min = range[0]; felix@2037: } felix@2037: this.addStringData(DATA_KM, Double.toString(min)); felix@1964: } felix@1964: felix@1964: felix@3284: /** Returns next possible km for a cross-section. */ felix@3272: public Double getNextKm() { felix@3272: return getDataAsDouble(DATA_NEXT_KM); felix@3272: } felix@3272: felix@3284: felix@3284: /** Returns previous possible km for a cross-section. */ felix@3272: public Double getPrevKm() { felix@3272: return getDataAsDouble(DATA_PREV_KM); felix@3272: } felix@3272: felix@3272: felix@1964: /** felix@1964: * Create and return a new StaticState with charting output. felix@1964: */ felix@1964: @Override felix@1964: public State getCurrentState(Object cc) { felix@1971: final List fs = facets.get(getCurrentStateId()); felix@1964: felix@1971: StaticState state = new StaticState(STATIC_STATE_NAME) { felix@1982: @Override felix@1971: public Object staticCompute(List facets) { felix@1971: if (facets != null) { felix@1971: facets.addAll(fs); felix@1971: } felix@1971: return null; felix@1971: } felix@1971: }; felix@1964: felix@1967: state.addDefaultChartOutput("cross_section", fs); felix@1964: felix@1964: return state; felix@1964: } felix@1964: felix@1964: felix@1964: /** felix@1964: * Get a list containing the one and only State. felix@1964: * @param context ignored. felix@1964: * @return list with one and only state. felix@1964: */ felix@1964: @Override felix@1964: protected List getStates(Object context) { felix@1964: ArrayList states = new ArrayList(); felix@1964: states.add(getCurrentState(context)); felix@1964: felix@1964: return states; felix@1964: } felix@1967: felix@1967: // TODO all data access needs proper caching. felix@1967: felix@1967: /** felix@1967: * Get a DataItem casted to int (0 if fails). felix@1967: */ felix@1967: public int getDataAsIntNull(String dataName) { felix@1967: String val = getDataAsString(dataName); felix@1967: try { sascha@3405: return Integer.parseInt(val); felix@1967: } felix@1967: catch (NumberFormatException e) { felix@1967: logger.warn("Could not get data " + dataName + " as int", e); felix@1967: return 0; felix@1967: } felix@1967: } felix@1967: felix@1967: felix@1967: /** Returns database-id of cross-section (from data). */ felix@1967: protected int getDBID() { felix@1967: return getDataAsIntNull(DATA_DBID); felix@1967: } felix@1967: felix@1967: felix@1967: /** felix@1967: * Return position (km) from data, 0 if not found. felix@1967: */ felix@1967: protected double getKm() { felix@1967: String val = getDataAsString(DATA_KM); felix@1967: try { felix@1967: return Double.valueOf(val); felix@1967: } felix@1967: catch (NumberFormatException e) { felix@1967: logger.warn("Could not get data " + DATA_KM + " as double", e); felix@1967: return 0; felix@1967: } felix@1967: } felix@1967: felix@1967: felix@1967: /** Returns true if artifact is set to be a "master" (other facets will felix@1967: * refer to this). */ felix@1967: public boolean isMaster() { felix@1967: return !getDataAsString(DATA_IS_MASTER).equals("0"); felix@1967: } felix@1967: felix@1967: felix@1967: /** felix@1967: * Get points of Profile of cross section at given kilometer. felix@1967: * felix@1967: * @return an array holding coordinates of points of profile ( felix@1967: * in the form {{x1, x2} {y1, y2}} ). felix@1967: */ felix@1967: public double [][] getCrossSectionData() { felix@1967: logger.info("getCrossSectionData() for cross_section.km " felix@1967: + getDataAsString(DATA_KM)); sascha@2120: FastCrossSectionLine line = searchCrossSectionLine(); felix@1967: felix@1967: return line != null felix@1967: ? line.fetchCrossSectionProfile() felix@1967: : null; felix@1967: } felix@1967: felix@1967: felix@1967: /** felix@1967: * Get CrossSectionLine spatially closest to what is specified in the data felix@2678: * "cross_section.km", null if considered too far. felix@1967: * felix@3272: * It also adds DataItems to store the next and previous (numerically) felix@3272: * values at which cross-section data was recorded. felix@3272: * felix@2678: * @return CrossSectionLine closest to "cross_section.km", might be null felix@2678: * if considered too far. felix@1967: */ sascha@2120: public FastCrossSectionLine searchCrossSectionLine() { felix@2678: double TOO_FAR = 1d; sascha@2120: CrossSection crossSection = CrossSectionFactory sascha@2120: .getCrossSection(getDBID()); sascha@2120: sascha@2120: if (logger.isDebugEnabled()) { sascha@2120: logger.debug("dbid " + getDBID() + " : " + crossSection); felix@1967: } sascha@2120: sascha@2120: NavigableMap kms = CrossSectionKMService sascha@2120: .getKms(crossSection.getId()); sascha@2120: sascha@2120: Double wishKM = getKm(); sascha@2120: sascha@2120: Double floor = kms.floorKey(wishKM); sascha@2120: Double ceil = kms.ceilingKey(wishKM); sascha@2120: felix@3272: Double nextKm; felix@3272: Double prevKm; felix@3272: sascha@2120: double floorD = floor != null sascha@2120: ? Math.abs(floor - wishKM) sascha@2120: : Double.MAX_VALUE; sascha@2120: sascha@2120: double ceilD = ceil != null sascha@2120: ? Math.abs(ceil - wishKM) sascha@2120: : Double.MAX_VALUE; sascha@2120: felix@3272: double km; felix@3272: if (floorD < ceilD) { felix@3272: km = floor; felix@3272: } felix@3272: else { felix@3272: km = ceil; felix@3272: } sascha@2120: felix@2678: // If we are too far from the wished km, return null. felix@2678: if (Math.abs(km - wishKM) > TOO_FAR) { felix@2678: return null; felix@2678: } felix@2678: felix@3272: // Store next and previous km. felix@3272: nextKm = kms.higherKey(km); felix@3272: prevKm = kms.lowerKey(km); felix@3272: felix@3272: if (prevKm == null) { felix@3272: prevKm = -1d; felix@3272: } felix@3272: if (nextKm == null) { felix@3272: nextKm = -1d; felix@3272: } felix@3272: felix@3272: addStringData(DATA_PREV_KM, prevKm.toString()); felix@3272: addStringData(DATA_NEXT_KM, nextKm.toString()); felix@3272: sascha@2120: return FastCrossSectionLineFactory sascha@2120: .getCrossSectionLine(crossSection, km); felix@1967: } felix@1964: } felix@1964: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :