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

http://dive4elements.wald.intevation.org