comparison flys-artifacts/src/main/java/de/intevation/flys/artifacts/CrossSectionArtifact.java @ 3318:dbe2f85bf160

merged flys-artifacts/2.8
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:14:35 +0200
parents d9af29a4bb85
children b0ba96bbf01d
comparison
equal deleted inserted replaced
2987:98c7a46ec5ae 3318:dbe2f85bf160
1 package de.intevation.flys.artifacts;
2
3 import java.util.ArrayList;
4 import java.util.List;
5 import java.util.NavigableMap;
6
7 import org.apache.log4j.Logger;
8
9 import org.w3c.dom.Document;
10
11 import de.intevation.artifacts.Artifact;
12 import de.intevation.artifacts.ArtifactFactory;
13 import de.intevation.artifacts.CallMeta;
14
15 import de.intevation.flys.artifacts.model.CrossSectionFacet;
16 import de.intevation.flys.artifacts.model.FastCrossSectionLineFactory;
17
18 import de.intevation.flys.model.FastCrossSectionLine;
19
20 import de.intevation.flys.model.CrossSection;
21 import de.intevation.flys.model.CrossSectionLine;
22 import de.intevation.flys.artifacts.model.CrossSectionFactory;
23
24 import de.intevation.flys.artifacts.states.StaticState;
25
26 import de.intevation.artifactdatabase.state.Facet;
27 import de.intevation.artifactdatabase.state.State;
28
29 import de.intevation.flys.utils.FLYSUtils;
30
31 import de.intevation.flys.artifacts.services.CrossSectionKMService;
32
33
34 /**
35 * Artifact describing a cross-section.
36 */
37 public class CrossSectionArtifact extends StaticFLYSArtifact {
38
39 /** Name of Artifact. */
40 public static final String CS_ARTIFACT_NAME = "cross_section";
41
42 /** Name of state. */
43 public static final String STATIC_STATE_NAME = "state.cross_section";
44
45 /** Name of data item keeping the position. */
46 public static final String DATA_KM = "cross_section.km";
47
48 /** Name of data item keeping the database id of this c.s.. */
49 public static final String DATA_DBID = "cross_section.dbid";
50
51 /** Name of data item flagging whether we think that we are master. */
52 public static final String DATA_IS_MASTER = "cross_section.master?";
53
54 /** Name of data item flagging whether we are the newest. */
55 public static final String DATA_IS_NEWEST = "cross_section.newest?";
56
57 /** Name of data item storing the previous possible km. */
58 public static final String DATA_PREV_KM = "cross_section.km.previous";
59
60 /** Name of data item storing the next possible km. */
61 public static final String DATA_NEXT_KM = "cross_section.km.next";
62
63 /** Own logger. */
64 private static final Logger logger =
65 Logger.getLogger(CrossSectionArtifact.class);
66
67
68 /** Return given name. */
69 @Override
70 public String getName() {
71 return CS_ARTIFACT_NAME;
72 }
73
74
75 /** Store ids, create a CrossSectionFacet. */
76 @Override
77 public void setup(
78 String identifier,
79 ArtifactFactory factory,
80 Object context,
81 CallMeta callMeta,
82 Document data)
83 {
84 logger.info("CrossSectionArtifact.setup");
85
86 super.setup(identifier, factory, context, callMeta, data);
87
88 String ids = getDatacageIDValue(data);
89
90 if (ids != null && ids.length() > 0) {
91 addStringData(DATA_DBID, ids);
92 logger.debug("CrossSectionArtifacts db-id: " + ids);
93 }
94 else {
95 throw new IllegalArgumentException("No attribute 'ids' found!");
96 }
97
98 List<Facet> fs = new ArrayList<Facet>();
99 CrossSection cs = CrossSectionFactory.getCrossSection(
100 Integer.valueOf(ids));
101
102 List<CrossSectionLine> csls = cs.getLines();
103 if (!csls.isEmpty()) {
104 CrossSectionLine csl = csls.get(0);
105 // Find min-km of cross sections,
106 // then set DATA_KM to min(DATA_KM, minCross).
107 double dataKm = Double.valueOf(getDataAsString(DATA_KM));
108 if (dataKm < csl.getKm().doubleValue()) {
109 addStringData(DATA_KM, csl.getKm().toString());
110 }
111 }
112 fs.add(new CrossSectionFacet(0, cs.getDescription()));
113
114 // Find out if we are newest and become master if so.
115 boolean isNewest = CrossSectionFactory.isNewest(cs);
116 String newString = (isNewest) ? "1" : "0";
117 addStringData(DATA_IS_NEWEST, newString);
118 addStringData(DATA_IS_MASTER, newString);
119
120 StaticState state = (StaticState) getCurrentState(context);
121
122 if (!fs.isEmpty()) {
123 facets.put(getCurrentStateId(), fs);
124 }
125 }
126
127
128 /** Copy km where master-artifact "starts". */
129 @Override
130 protected void initialize(
131 Artifact artifact,
132 Object context,
133 CallMeta callMeta)
134 {
135 FLYSArtifact winfo = (FLYSArtifact) artifact;
136 double[] range = FLYSUtils.getKmRange(winfo);
137 double min = 0.0f;
138 if (range != null && range.length > 0) {
139 min = range[0];
140 }
141 this.addStringData(DATA_KM, Double.toString(min));
142 }
143
144
145 /** Returns next possible km for a cross-section. */
146 public Double getNextKm() {
147 return getDataAsDouble(DATA_NEXT_KM);
148 }
149
150
151 /** Returns previous possible km for a cross-section. */
152 public Double getPrevKm() {
153 return getDataAsDouble(DATA_PREV_KM);
154 }
155
156
157 /**
158 * Create and return a new StaticState with charting output.
159 */
160 @Override
161 public State getCurrentState(Object cc) {
162 final List<Facet> fs = facets.get(getCurrentStateId());
163
164 StaticState state = new StaticState(STATIC_STATE_NAME) {
165 @Override
166 public Object staticCompute(List<Facet> facets) {
167 if (facets != null) {
168 facets.addAll(fs);
169 }
170 return null;
171 }
172 };
173
174 state.addDefaultChartOutput("cross_section", fs);
175
176 return state;
177 }
178
179
180 /**
181 * Get a list containing the one and only State.
182 * @param context ignored.
183 * @return list with one and only state.
184 */
185 @Override
186 protected List<State> getStates(Object context) {
187 ArrayList<State> states = new ArrayList<State>();
188 states.add(getCurrentState(context));
189
190 return states;
191 }
192
193 // TODO all data access needs proper caching.
194
195 /**
196 * Get a DataItem casted to int (0 if fails).
197 */
198 public int getDataAsIntNull(String dataName) {
199 String val = getDataAsString(dataName);
200 try {
201 return Integer.valueOf(val);
202 }
203 catch (NumberFormatException e) {
204 logger.warn("Could not get data " + dataName + " as int", e);
205 return 0;
206 }
207 }
208
209
210 /** Returns database-id of cross-section (from data). */
211 protected int getDBID() {
212 return getDataAsIntNull(DATA_DBID);
213 }
214
215
216 /**
217 * Return position (km) from data, 0 if not found.
218 */
219 protected double getKm() {
220 String val = getDataAsString(DATA_KM);
221 try {
222 return Double.valueOf(val);
223 }
224 catch (NumberFormatException e) {
225 logger.warn("Could not get data " + DATA_KM + " as double", e);
226 return 0;
227 }
228 }
229
230
231 /** Returns true if artifact is set to be a "master" (other facets will
232 * refer to this). */
233 public boolean isMaster() {
234 return !getDataAsString(DATA_IS_MASTER).equals("0");
235 }
236
237
238 /**
239 * Get points of Profile of cross section at given kilometer.
240 *
241 * @return an array holding coordinates of points of profile (
242 * in the form {{x1, x2} {y1, y2}} ).
243 */
244 public double [][] getCrossSectionData() {
245 logger.info("getCrossSectionData() for cross_section.km "
246 + getDataAsString(DATA_KM));
247 FastCrossSectionLine line = searchCrossSectionLine();
248
249 return line != null
250 ? line.fetchCrossSectionProfile()
251 : null;
252 }
253
254
255 /**
256 * Get CrossSectionLine spatially closest to what is specified in the data
257 * "cross_section.km", null if considered too far.
258 *
259 * It also adds DataItems to store the next and previous (numerically)
260 * values at which cross-section data was recorded.
261 *
262 * @return CrossSectionLine closest to "cross_section.km", might be null
263 * if considered too far.
264 */
265 public FastCrossSectionLine searchCrossSectionLine() {
266 double TOO_FAR = 1d;
267 CrossSection crossSection = CrossSectionFactory
268 .getCrossSection(getDBID());
269
270 if (logger.isDebugEnabled()) {
271 logger.debug("dbid " + getDBID() + " : " + crossSection);
272 }
273
274 NavigableMap<Double, Integer> kms = CrossSectionKMService
275 .getKms(crossSection.getId());
276
277 Double wishKM = getKm();
278
279 Double floor = kms.floorKey(wishKM);
280 Double ceil = kms.ceilingKey(wishKM);
281
282 Double nextKm;
283 Double prevKm;
284
285 double floorD = floor != null
286 ? Math.abs(floor - wishKM)
287 : Double.MAX_VALUE;
288
289 double ceilD = ceil != null
290 ? Math.abs(ceil - wishKM)
291 : Double.MAX_VALUE;
292
293 double km;
294 if (floorD < ceilD) {
295 km = floor;
296 }
297 else {
298 km = ceil;
299 }
300
301 // If we are too far from the wished km, return null.
302 if (Math.abs(km - wishKM) > TOO_FAR) {
303 return null;
304 }
305
306 // Store next and previous km.
307 nextKm = kms.higherKey(km);
308 prevKm = kms.lowerKey(km);
309
310 if (prevKm == null) {
311 prevKm = -1d;
312 }
313 if (nextKm == null) {
314 nextKm = -1d;
315 }
316
317 addStringData(DATA_PREV_KM, prevKm.toString());
318 addStringData(DATA_NEXT_KM, nextKm.toString());
319
320 return FastCrossSectionLineFactory
321 .getCrossSectionLine(crossSection, km);
322 }
323
324
325 /**
326 * Determines Facets initial disposition regarding activity (think of
327 * selection in Client ThemeList GUI). This will be checked one time
328 * when the facet enters a collections describe document.
329 *
330 * @param outputName Ignored.
331 * @param facetName Ignored.
332 * @param index Ignored.
333 * @return 0 if not active
334 */
335 @Override
336 public int getInitialFacetActivity(String outputName, String facetName, int index) {
337 return (getDataAsString(DATA_IS_NEWEST) != null
338 && getDataAsString(DATA_IS_NEWEST).equals("1")) ? 1 : 0;
339 }
340 }
341 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org