comparison artifacts/src/main/java/org/dive4elements/river/artifacts/StaticWKmsArtifact.java @ 5838:5aa05a7a34b7

Rename modules to more fitting names.
author Sascha L. Teichmann <teichmann@intevation.de>
date Thu, 25 Apr 2013 15:23:37 +0200
parents flys-artifacts/src/main/java/org/dive4elements/river/artifacts/StaticWKmsArtifact.java@bd047b71ab37
children 4897a58c8746
comparison
equal deleted inserted replaced
5837:d9901a08d0a6 5838:5aa05a7a34b7
1 package org.dive4elements.river.artifacts;
2
3 import org.dive4elements.artifactdatabase.state.DefaultOutput;
4 import org.dive4elements.artifactdatabase.state.Facet;
5 import org.dive4elements.artifactdatabase.state.FacetActivity;
6 import org.dive4elements.artifactdatabase.state.State;
7
8 import org.dive4elements.artifacts.Artifact;
9 import org.dive4elements.artifacts.ArtifactFactory;
10 import org.dive4elements.artifacts.CallContext;
11 import org.dive4elements.artifacts.CallMeta;
12
13 import org.dive4elements.artifacts.common.utils.XMLUtils;
14
15 import org.dive4elements.river.artifacts.geom.Lines;
16
17 import org.dive4elements.river.artifacts.math.Distance;
18 import org.dive4elements.river.artifacts.math.Linear;
19
20 import org.dive4elements.river.artifacts.model.CrossSectionWaterLineFacet;
21 import org.dive4elements.river.artifacts.model.FacetTypes;
22 import org.dive4elements.river.artifacts.model.RelativePointFacet;
23 import org.dive4elements.river.artifacts.model.WKms;
24 import org.dive4elements.river.artifacts.model.WKmsFacet;
25 import org.dive4elements.river.artifacts.model.WKmsFactory;
26
27 import org.dive4elements.river.artifacts.resources.Resources;
28
29 import org.dive4elements.river.artifacts.states.StaticState;
30
31 import org.dive4elements.river.model.FastCrossSectionLine;
32
33 import java.awt.geom.Point2D;
34
35 import java.util.ArrayList;
36 import java.util.List;
37
38 import org.apache.log4j.Logger;
39
40 import org.w3c.dom.Document;
41
42 /**
43 * Artifact to access additional "waterlevel"-type of data, like the height
44 * of protective measures (dikes).
45 *
46 * This artifact neglects (Static)FLYSArtifacts capabilities of interaction
47 * with the StateEngine by overriding the getState*-methods.
48 */
49 public class StaticWKmsArtifact
50 extends StaticFLYSArtifact
51 implements FacetTypes, WaterLineArtifact
52 {
53 /** The logger for this class. */
54 private static Logger logger =
55 Logger.getLogger(StaticWKmsArtifact.class);
56
57 private static final String NAME = "staticwkms";
58
59 static {
60 // TODO: Move to configuration.
61 FacetActivity.Registry.getInstance()
62 .register(NAME, FacetActivity.INACTIVE);
63 }
64
65 public static final String STATIC_STATE_NAME =
66 "state.additional_wkms.static";
67
68 /** Data Item name to know whether we are Heighmarks and reveive
69 * some data slightly different. */
70 public static final String DATA_HEIGHT_TYPE =
71 "height_marks";
72
73 /** One and only state to be in. */
74 protected transient State state = null;
75
76
77 /**
78 * Trivial Constructor.
79 */
80 public StaticWKmsArtifact() {
81 logger.debug("StaticWKmsArtifact.StaticWKmsArtifact");
82 }
83
84 @Override
85 public String getName() {
86 return NAME;
87 }
88
89 /**
90 * Gets called from factory, to set things up.
91 */
92 @Override
93 public void setup(
94 String identifier,
95 ArtifactFactory factory,
96 Object context,
97 CallMeta callMeta,
98 Document data)
99 {
100 logger.debug("StaticWKmsArtifact.setup");
101
102 state = new StaticState(STATIC_STATE_NAME);
103
104 if (logger.isDebugEnabled()) {
105 logger.debug(XMLUtils.toString(data));
106 }
107
108 List<Facet> fs = new ArrayList<Facet>();
109 String code = getDatacageIDValue(data);
110
111 // TODO Go for JSON, one day.
112 //ex.: flood_protection-wstv-114-12
113 if (code != null) {
114 String [] parts = code.split("-");
115
116 if (parts.length >= 4) {
117 int col = -1;
118 int wst = Integer.parseInt(parts[3]);
119
120 if (!parts[2].equals("A")) {
121 col = Integer.parseInt(parts[2]);
122 }
123
124 addStringData("col_pos", parts[2]);
125 addStringData("wst_id", parts[3]);
126
127 String wkmsName;
128 if (col >= 0) {
129 wkmsName = WKmsFactory.getWKmsName(col, wst);
130 }
131 else {
132 wkmsName = WKmsFactory.getWKmsName(wst);
133 }
134
135 String name;
136 if (parts[0].equals(HEIGHTMARKS_POINTS)) {
137 name = HEIGHTMARKS_POINTS;
138 addStringData(DATA_HEIGHT_TYPE, "true");
139 }
140 else if (parts[0].equals("additionalsmarks")) {
141 name = STATIC_WKMS_MARKS;
142 }
143 else {
144 name = STATIC_WKMS;
145 }
146
147 String facetDescription = Resources.getMsg(
148 callMeta, wkmsName, wkmsName);
149 Facet wKmsFacet = new WKmsFacet(
150 name,
151 facetDescription);
152 Facet csFacet = new CrossSectionWaterLineFacet(0,
153 facetDescription);
154 Facet rpFacet = new RelativePointFacet(facetDescription);
155
156 fs.add(wKmsFacet);
157 fs.add(csFacet);
158 fs.add(rpFacet);
159 addFacets(state.getID(), fs);
160 }
161 }
162
163 spawnState();
164 super.setup(identifier, factory, context, callMeta, data);
165 }
166
167
168 /**
169 * Initialize the static state with output.
170 * @return static state
171 */
172 protected State spawnState() {
173 state = new StaticState(STATIC_STATE_NAME);
174 List<Facet> fs = getFacets(STATIC_STATE_NAME);
175 DefaultOutput output = new DefaultOutput(
176 "general",
177 "general", "image/png",
178 fs,
179 "chart");
180
181 state.getOutputs().add(output);
182 return state;
183 }
184
185
186 /**
187 * Called via setup.
188 *
189 * @param artifact The master-artifact.
190 */
191 @Override
192 protected void initialize(
193 Artifact artifact,
194 Object context,
195 CallMeta meta)
196 {
197 logger.debug("StaticWKmsArtifact.initialize");
198 FLYSArtifact winfo = (FLYSArtifact) artifact;
199 // TODO: The river is of no interest, so far.
200 addData("river", winfo.getData("river"));
201 }
202
203
204 /**
205 * Get a list containing the one and only State.
206 * @param context ignored.
207 * @return list with one and only state.
208 */
209 @Override
210 protected List<State> getStates(Object context) {
211 ArrayList<State> states = new ArrayList<State>();
212 states.add(getState());
213 return states;
214 }
215
216
217 /**
218 * Get the "current" state (there is but one).
219 * @param cc ignored.
220 * @return the "current" (only possible) state.
221 */
222 @Override
223 public State getCurrentState(Object cc) {
224 return getState();
225 }
226
227
228 /**
229 * Get the only possible state.
230 * @return the state.
231 */
232 protected State getState() {
233 return getState(null, null);
234 }
235
236
237 /**
238 * Get the state.
239 * @param context ignored.
240 * @param stateID ignored.
241 * @return the state.
242 */
243 @Override
244 protected State getState(Object context, String stateID) {
245 return (state != null)
246 ? state
247 : spawnState();
248 }
249
250
251 /**
252 * Get WKms from factory.
253 * @param idx param is not needed (TODO?)
254 * @return WKms according to parameterization (can be null);
255 */
256 public WKms getWKms(int idx) {
257 logger.debug("StaticWKmsArtifact.getWKms");
258
259 return WKmsFactory.getWKms(
260 Integer.parseInt(getDataAsString("col_pos")),
261 Integer.parseInt(getDataAsString("wst_id")));
262 }
263
264
265 /**
266 * Returns W at Km of WKms, linearly interpolated.
267 * Returns -1 if not found.
268 */
269 public static double getWAtKmLin(WKms wkms, double km) {
270 // Uninformed search.
271 int size = wkms.size();
272 if (size == 0) {
273 return -1;
274 }
275 int idx = 0;
276
277 boolean kmIncreasing;
278 if (size == 1) {
279 kmIncreasing = true;
280 }
281 else {
282 kmIncreasing = (wkms.getKm(0) < wkms.getKm(wkms.size()-1))
283 ? true : false;
284 }
285 if (kmIncreasing) {
286 while (idx < size && wkms.getKm(idx) < km) {
287 idx++;
288 }
289 }
290 else {
291 idx = wkms.size() -1;
292 while (idx > 0 && wkms.getKm(idx) > km) {
293 idx--;
294 }
295 }
296
297 if (wkms.getKm(idx) == km) {
298 return wkms.getW(idx);
299 }
300
301 if (idx == size -1 || idx == 0) {
302 return -1;
303 }
304
305 // Do linear interpolation
306 int mod = kmIncreasing ? -1 : +1;
307 return Linear.linear(km, wkms.getKm(idx+mod), wkms.getKm(idx), wkms.getW(idx+mod), wkms.getW(idx));
308 }
309
310
311 /**
312 * Get the W at a specific km, only if it is closer to km than to any of
313 * the other given km.
314 * Return Double.NaN otherwise
315 *
316 * @param wkms WKms in which to search for a spatially close W value.
317 * @param km the input km, which is compared to values from wkms.
318 * @param next the next available input km (-1 if unavailable).
319 * @param prev the previous available input km (-1 if unavailable).
320 *
321 * @return W in wkms that is closer to km than to next and prev, or Double.NaN.
322 */
323 public double getWAtCloseKm(WKms wkms, double km, double next, double prev) {
324 // TODO symbolic "-1" pr next/prev is a bad idea (tm), as we compare
325 // distances to these values later.
326 // TODO issue888
327
328 int size = wkms.size();
329 for (int i = 0; i < size; i++) {
330 double wkmsKm = wkms.getKm(i);
331 double dist = Distance.distance(wkmsKm, km);
332 if (dist == 0d) {
333 return wkms.getW(i);
334 }
335
336 // Problematic Cases:
337 // X == km , | and | == prev and next, (?) == wkmsKm
338 //
339 // Standard case:
340 // ----------|----X-----|-------
341 // (1) (2) (3) (4)
342 //
343 // With prev==-1
344 // -1 ------X-------|------
345 // (5) (6) (7)
346 //
347 // With next==-1
348 //
349 // ---|-----X----- -1
350 // (8) (9) (10)
351
352 if (dist <= Distance.distance(wkmsKm, prev)
353 && dist <= Distance.distance(wkmsKm, next)) {
354 return wkms.getW(i);
355 }
356 }
357
358 return Double.NaN;
359 }
360
361
362 /**
363 * Returns W at Km of WKms, searching linearly.
364 * Returns -1 if not found.
365 * @param wkms the WKms object to search for given km.
366 * @param km The searched km.
367 * @return W at given km if in WKms, -1 if not found.
368 */
369 public static double getWAtKm(WKms wkms, double km) {
370 // Uninformed search, intolerant.
371 double TOLERANCE = 0.0d;
372 int size = wkms.size();
373 for (int i = 0; i < size; i++) {
374 if (Distance.within(wkms.getKm(i), km, TOLERANCE)) {
375 return wkms.getW(i);
376 }
377 }
378
379 return -1;
380 }
381
382
383 /**
384 * Get points of line describing the surface of water at cross section.
385 *
386 * @param idx Index of facet and in wkms array.
387 * @param csl FastCrossSectionLine to compute water surface agains.
388 * @param next The km of the next crosssectionline.
389 * @param prev The km of the previous crosssectionline.
390 * @param context Ignored in this implementation.
391 *
392 * @return an array holding coordinates of points of surface of water (
393 * in the form {{x1, x2}, {y1, y2}} ).
394 */
395 @Override
396 public Lines.LineData getWaterLines(int idx, FastCrossSectionLine csl,
397 double next, double prev, CallContext context
398 ) {
399 logger.debug("getWaterLines(" + idx + ")/" + identifier());
400
401 List<Point2D> points = csl.getPoints();
402
403 WKms wkms = getWKms(0);
404
405 double km = csl.getKm();
406
407 // Find W at km.
408 double wAtKm;
409
410 // If heightmarks, only deliver if data snaps.
411 if (getDataAsString(DATA_HEIGHT_TYPE) != null &&
412 getDataAsString(DATA_HEIGHT_TYPE).equals("true")) {
413 wAtKm = getWAtCloseKm(wkms, km, next, prev);
414 }
415 else {
416 wAtKm = getWAtKm(wkms, km);
417 }
418
419 if (wAtKm == -1 || Double.isNaN(wAtKm)) {
420 logger.warn("Waterlevel at km " + km + " unknown.");
421 return new Lines.LineData(new double[][] {{}}, 0d, 0d);
422 }
423
424 return Lines.createWaterLines(points, wAtKm);
425 }
426 }
427 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :

http://dive4elements.wald.intevation.org