Mercurial > dive4elements > river
comparison artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadDataFacet.java @ 8090:01f778f84330
Move the old data facet out of the way.
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Fri, 15 Aug 2014 14:28:26 +0200 |
parents | artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadFacet.java@5e3f4b4fcb28 |
children | bb0d35d32b01 |
comparison
equal
deleted
inserted
replaced
8088:b834caf0a4f0 | 8090:01f778f84330 |
---|---|
1 /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde | |
2 * Software engineering by Intevation GmbH | |
3 * | |
4 * This file is Free Software under the GNU AGPL (>=v3) | |
5 * and comes with ABSOLUTELY NO WARRANTY! Check out the | |
6 * documentation coming with Dive4Elements River for details. | |
7 */ | |
8 | |
9 package org.dive4elements.river.artifacts.model.minfo; | |
10 | |
11 import gnu.trove.TDoubleArrayList; | |
12 | |
13 import org.dive4elements.artifactdatabase.state.Facet; | |
14 | |
15 import org.dive4elements.artifacts.Artifact; | |
16 import org.dive4elements.artifacts.CallContext; | |
17 | |
18 import org.dive4elements.river.artifacts.D4EArtifact; | |
19 | |
20 import org.dive4elements.river.artifacts.model.CalculationResult; | |
21 import org.dive4elements.river.artifacts.model.DataFacet; | |
22 import org.dive4elements.river.artifacts.model.FacetTypes; | |
23 | |
24 import org.dive4elements.river.artifacts.states.DefaultState.ComputeType; | |
25 | |
26 import org.dive4elements.river.model.MeasurementStation; | |
27 | |
28 import org.dive4elements.river.utils.RiverUtils; | |
29 | |
30 import java.util.ArrayList; | |
31 import java.util.Collections; | |
32 import java.util.List; | |
33 import java.util.Map; | |
34 import java.util.TreeSet; | |
35 import java.util.TreeMap; | |
36 | |
37 import org.apache.log4j.Logger; | |
38 | |
39 | |
40 /** Facet to access various sediment loads. */ | |
41 public class SedimentLoadFacet | |
42 extends DataFacet | |
43 { | |
44 /** Very own logger. */ | |
45 private static Logger logger = Logger.getLogger(SedimentLoadFacet.class); | |
46 | |
47 /** Used as tolerance value when fetching measurement stations. */ | |
48 private static double EPSILON = 1e-5; | |
49 | |
50 | |
51 public SedimentLoadFacet() { | |
52 } | |
53 | |
54 public SedimentLoadFacet(int idx, String name, String description, | |
55 ComputeType type, String stateId, String hash) { | |
56 super(idx, name, description, type, hash, stateId); | |
57 this.metaData.put("X", "chart.longitudinal.section.xaxis.label"); | |
58 this.metaData.put("Y", ""); | |
59 } | |
60 | |
61 @Override | |
62 public Object getData(Artifact artifact, CallContext context) { | |
63 logger.debug("Get data for sediment load at index: " + index); | |
64 | |
65 D4EArtifact flys = (D4EArtifact) artifact; | |
66 | |
67 CalculationResult res = (CalculationResult) flys.compute(context, hash, | |
68 stateId, type, false); | |
69 | |
70 Object[] data = | |
71 (SedimentLoadResult[]) res.getData(); // TODO CAST TO SPECIFIC CLASS | |
72 | |
73 List<MeasurementStation> allStations = RiverUtils.getRiver(flys).getMeasurementStations(); | |
74 SedimentLoadResult result = data != null && data.length > index ? (SedimentLoadResult)data[index] : null; | |
75 if (result == null) { | |
76 return null; | |
77 } | |
78 | |
79 // These complicated calculations were necessary because | |
80 // SedimentLoad/Fraction did not contain the ranges of the given | |
81 // values. Since this changed, the code is somewhat obsolete, but stable. | |
82 // For an example of easier calculation, see the "total" part below. | |
83 | |
84 List<Double> sortedStarts = new ArrayList<Double>(); | |
85 // Filter stations according to type. | |
86 List<MeasurementStation> stations = new ArrayList<MeasurementStation>(); | |
87 for (MeasurementStation station: allStations) { | |
88 if (station.getRange() == null || station.getMeasurementType() == null) { | |
89 continue; | |
90 } | |
91 if (this.getName().contains("susp_sediment") | |
92 && station.getMeasurementType().equals("Schwebstoff")) { | |
93 stations.add(station); | |
94 sortedStarts.add(station.getStation()); | |
95 } | |
96 else if (!this.getName().contains("susp_sediment") | |
97 && station.getMeasurementType().equals("Geschiebe")) { | |
98 stations.add(station); | |
99 sortedStarts.add(station.getStation()); | |
100 } | |
101 } | |
102 Collections.sort(sortedStarts); | |
103 | |
104 // Handle sediment load differently, as it respects already | |
105 // the ranges that were added to SedimentLoad/Fraction. | |
106 if (getName().equals(FacetTypes.SEDIMENT_LOAD_TA_TOTAL) | |
107 ||getName().equals(FacetTypes.SEDIMENT_LOAD_M3A_TOTAL)) { | |
108 SedimentLoadLSData load = result.getLoad(); | |
109 TDoubleArrayList xPos = new TDoubleArrayList(); | |
110 TDoubleArrayList yPos = new TDoubleArrayList(); | |
111 double lastX = -1d; | |
112 for (double km: new TreeSet<Double>(load.getKms())) { | |
113 SedimentLoadFraction fraction = load.getFraction(km); | |
114 if (fraction.getTotal() != 0) { | |
115 if (Math.abs(lastX-km) >= EPSILON) { | |
116 xPos.add(Double.NaN); | |
117 yPos.add(Double.NaN); | |
118 } | |
119 xPos.add(km); | |
120 yPos.add(fraction.getTotal()); | |
121 xPos.add(fraction.getTotalRange().getEnd()); | |
122 yPos.add(fraction.getTotal()); | |
123 lastX = fraction.getTotalRange().getEnd(); | |
124 } | |
125 } | |
126 return new double[][] {xPos.toNativeArray(), yPos.toNativeArray()}; | |
127 } | |
128 | |
129 // Access data according to type (except total - see above). | |
130 double[][] sd = getLoadData(result); | |
131 | |
132 // Sort by km. | |
133 TreeMap<Double, Double> sortedKmLoad = new TreeMap<Double,Double>(); | |
134 | |
135 double[] km = sd[0]; | |
136 double[] load = sd[1]; | |
137 | |
138 // Build map of km->load, but directly exclude the ones which do | |
139 // not match against a measurements station ranges start. | |
140 for (int i = 0 ; i < km.length; i++) { | |
141 for (MeasurementStation station: stations) { | |
142 if (Math.abs(station.getStation() - km[i]) <= EPSILON) { | |
143 sortedKmLoad.put(km[i], load[i]); | |
144 continue; | |
145 } | |
146 } | |
147 } | |
148 | |
149 // [0] -> x, [1] -> y | |
150 double[][] values = new double[2][]; | |
151 values[0] = new double[sortedKmLoad.size()*3]; | |
152 values[1] = new double[sortedKmLoad.size()*3]; | |
153 | |
154 // Find station via its station (km). | |
155 // TODO use a binarySearch instead of linear absdiff approach | |
156 int i = 0; | |
157 for (Map.Entry<Double, Double> kmLoad: sortedKmLoad.entrySet()) { | |
158 boolean matchFound = false; | |
159 for (int k = 0, S = stations.size(); k < S; k++) { | |
160 MeasurementStation station = stations.get(k); | |
161 if (Math.abs(station.getStation() - kmLoad.getKey()) < EPSILON) { | |
162 // Value has been taken at measurement station. | |
163 values[0][i*3] = station.getRange().getA().doubleValue() + EPSILON; | |
164 values[1][i*3] = kmLoad.getValue(); | |
165 double endValue = 0d; | |
166 // Valid until next measurements stations begin of range, | |
167 // or end of current range if last value. | |
168 if (k+2 <= S) { | |
169 endValue = stations.get(k+1).getRange().getA().doubleValue(); | |
170 } | |
171 else { | |
172 endValue = station.getRange().getB().doubleValue(); | |
173 } | |
174 values[0][i*3+1] = endValue; | |
175 values[1][i*3+1] = kmLoad.getValue(); | |
176 values[0][i*3+2] = endValue; | |
177 values[1][i*3+2] = kmLoad.getValue(); | |
178 matchFound = true; | |
179 break; | |
180 } | |
181 } | |
182 // Store points without match for later assessment. | |
183 if (!matchFound) { | |
184 logger.warn("measurement without station ("+kmLoad.getKey()+")!"); | |
185 } | |
186 i++; | |
187 } | |
188 | |
189 for (int x = 0; x < values[0].length-1; x++) { | |
190 // Introduce gaps where no data in measurement station. | |
191 if (Math.abs(values[0][x+1] - values[0][x]) > 3*EPSILON | |
192 && values[1][x+1] != values[1][x]) { | |
193 values[0][x] = Double.NaN; | |
194 values[1][x] = Double.NaN; | |
195 } | |
196 } | |
197 | |
198 return values; | |
199 } | |
200 | |
201 | |
202 /** Get data according to type of facet. */ | |
203 private double[][] getLoadData(SedimentLoadResult result) { | |
204 String name = getName(); | |
205 if (FacetTypes.IS.SEDIMENT_LOAD_SAND(name)) | |
206 return result.getSandData(); | |
207 else if (FacetTypes.IS.SEDIMENT_LOAD_COARSE(name)) | |
208 return result.getCoarseData(); | |
209 else if (FacetTypes.IS.SEDIMENT_LOAD_FINEMIDDLE(name)) | |
210 return result.getFineMiddleData(); | |
211 else if (FacetTypes.IS.SEDIMENT_LOAD_SUSP_SAND(name)) | |
212 return result.getSuspSandData(); | |
213 else if (FacetTypes.IS.SEDIMENT_LOAD_SUSP_SAND_BED(name)) | |
214 return result.getSuspSandBedData(); | |
215 else if (FacetTypes.IS.SEDIMENT_LOAD_SUSP_SEDIMENT(name)) | |
216 return result.getSuspSedimentData(); | |
217 else if (FacetTypes.IS.SEDIMENT_LOAD_TOTAL_LOAD(name)) | |
218 return result.getTotalLoadData(); | |
219 else if (FacetTypes.IS.SEDIMENT_LOAD_TOTAL(name)) | |
220 return result.getTotalData(); | |
221 else { | |
222 logger.error("SedimentLoadFacet " + name + " cannot determine data type."); | |
223 return null; | |
224 } | |
225 } | |
226 | |
227 /** Copy deeply. */ | |
228 @Override | |
229 public Facet deepCopy() { | |
230 SedimentLoadFacet copy = new SedimentLoadFacet(); | |
231 copy.set(this); | |
232 copy.type = type; | |
233 copy.hash = hash; | |
234 copy.stateId = stateId; | |
235 return copy; | |
236 } | |
237 } | |
238 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : |