comparison flys-artifacts/src/main/java/de/intevation/flys/artifacts/CrossSectionArtifact.java @ 3651:06a65baae494

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

http://dive4elements.wald.intevation.org