Mercurial > dive4elements > river
comparison artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadDataFacet.java @ 8125:f01c65261963
(issue1448) Add WIP SedimentLoadDataFacet and use it in calculation.
The unit handling is currently just a stub
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Mon, 25 Aug 2014 15:56:09 +0200 |
parents | bb0d35d32b01 |
children | 58e3fed51e9e |
comparison
equal
deleted
inserted
replaced
8124:2c21fd1ade39 | 8125:f01c65261963 |
---|---|
21 | 21 |
22 import org.dive4elements.artifactdatabase.state.Facet; | 22 import org.dive4elements.artifactdatabase.state.Facet; |
23 | 23 |
24 import org.dive4elements.artifacts.Artifact; | 24 import org.dive4elements.artifacts.Artifact; |
25 import org.dive4elements.artifacts.CallContext; | 25 import org.dive4elements.artifacts.CallContext; |
26 import org.dive4elements.artifacts.CallMeta; | |
26 | 27 |
28 import org.dive4elements.river.artifacts.resources.Resources; | |
27 import org.dive4elements.river.artifacts.D4EArtifact; | 29 import org.dive4elements.river.artifacts.D4EArtifact; |
28 | 30 |
29 import org.dive4elements.river.artifacts.model.CalculationResult; | 31 import org.dive4elements.river.artifacts.model.CalculationResult; |
30 import org.dive4elements.river.artifacts.model.DataFacet; | 32 import org.dive4elements.river.artifacts.model.DataFacet; |
31 import org.dive4elements.river.artifacts.model.FacetTypes; | 33 import org.dive4elements.river.artifacts.model.FacetTypes; |
34 | |
35 import org.dive4elements.river.artifacts.model.minfo.SedimentLoadDataResult; | |
36 import org.dive4elements.river.artifacts.model.minfo.SedimentLoadDataResult.Fraction; | |
32 | 37 |
33 import org.dive4elements.river.artifacts.states.DefaultState.ComputeType; | 38 import org.dive4elements.river.artifacts.states.DefaultState.ComputeType; |
34 | 39 |
35 import org.dive4elements.river.model.MeasurementStation; | 40 import org.dive4elements.river.model.MeasurementStation; |
36 | 41 |
51 extends DataFacet | 56 extends DataFacet |
52 { | 57 { |
53 /** Very own logger. */ | 58 /** Very own logger. */ |
54 private static Logger logger = Logger.getLogger(SedimentLoadDataFacet.class); | 59 private static Logger logger = Logger.getLogger(SedimentLoadDataFacet.class); |
55 | 60 |
56 /** Used as tolerance value when fetching measurement stations. */ | 61 private static final String BASE_NAME = "sedimentload"; |
57 private static double EPSILON = 1e-5; | |
58 | 62 |
63 public String fraction; | |
59 | 64 |
60 public SedimentLoadDataFacet() { | 65 public SedimentLoadDataFacet() { |
61 } | 66 } |
62 | 67 |
63 public SedimentLoadDataFacet(int idx, String name, String description, | 68 public SedimentLoadDataFacet(int idx, String fraction_name, String unit, |
64 ComputeType type, String stateId, String hash) { | 69 ComputeType type, String stateId, String hash, CallContext context) { |
65 super(idx, name, description, type, hash, stateId); | 70 super(idx, /*name*/"", ""/*description*/, type, hash, stateId); |
66 this.metaData.put("X", "chart.longitudinal.section.xaxis.label"); | 71 |
67 this.metaData.put("Y", ""); | 72 this.fraction = fraction_name; |
73 | |
74 String typeUnit; /* Gnah someone should unify unit strings,... */ | |
75 String i18nUnit; | |
76 if (unit != null && unit.equals("m3/a")) { | |
77 typeUnit = "m3a"; | |
78 i18nUnit = "m\u00b3/a"; | |
79 } else { | |
80 typeUnit = "ta"; | |
81 i18nUnit = "t/a"; | |
82 } | |
83 | |
84 name = BASE_NAME + "." + typeUnit + "." + fraction_name; | |
85 | |
86 CallMeta meta = context.getMeta(); | |
87 description = Resources.getMsg( | |
88 meta, | |
89 "facet.sedimentload." + fraction_name, | |
90 "Error: please add l10n for facet.sedimentload." + fraction_name, | |
91 new Object[] { i18nUnit }); | |
92 | |
93 /* Is this necessary? */ | |
94 metaData.put("X", "chart.longitudinal.section.xaxis.label"); | |
95 metaData.put("Y", ""); | |
68 } | 96 } |
69 | 97 |
70 @Override | 98 @Override |
71 public Object getData(Artifact artifact, CallContext context) { | 99 public Object getData(Artifact artifact, CallContext context) { |
72 logger.debug("Get data for sediment load at index: " + index); | 100 logger.debug("Get data for sediment load with fraction: " + fraction); |
73 | 101 |
74 D4EArtifact flys = (D4EArtifact) artifact; | 102 D4EArtifact flys = (D4EArtifact) artifact; |
75 | 103 |
76 CalculationResult res = (CalculationResult) flys.compute(context, hash, | 104 CalculationResult res = (CalculationResult) flys.compute(context, hash, stateId, type, false); |
77 stateId, type, false); | 105 Object payload = res.getData(); |
78 | 106 |
79 Object[] data = | 107 if (!(payload instanceof SedimentLoadDataResult)) { |
80 (SedimentLoadResult[]) res.getData(); // TODO CAST TO SPECIFIC CLASS | 108 logger.error("Invalid result!"); |
81 | |
82 List<MeasurementStation> allStations = RiverUtils.getRiver(flys).getMeasurementStations(); | |
83 SedimentLoadResult result = data != null && data.length > index ? (SedimentLoadResult)data[index] : null; | |
84 if (result == null) { | |
85 return null; | |
86 } | 109 } |
87 | 110 |
88 // These complicated calculations were necessary because | 111 SedimentLoadDataResult sdResult = (SedimentLoadDataResult) payload; |
89 // SedimentLoad/Fraction did not contain the ranges of the given | |
90 // values. Since this changed, the code is somewhat obsolete, but stable. | |
91 // For an example of easier calculation, see the "total" part below. | |
92 | 112 |
93 List<Double> sortedStarts = new ArrayList<Double>(); | 113 Fraction frac = sdResult.getFractionByName(fraction); |
94 // Filter stations according to type. | 114 if (frac == null) { |
95 List<MeasurementStation> stations = new ArrayList<MeasurementStation>(); | 115 logger.warn("No result for: " + fraction); |
96 for (MeasurementStation station: allStations) { | |
97 if (station.getRange() == null || station.getMeasurementType() == null) { | |
98 continue; | |
99 } | |
100 if (this.getName().contains("susp_sediment") | |
101 && station.getMeasurementType().equals("Schwebstoff")) { | |
102 stations.add(station); | |
103 sortedStarts.add(station.getStation()); | |
104 } | |
105 else if (!this.getName().contains("susp_sediment") | |
106 && station.getMeasurementType().equals("Geschiebe")) { | |
107 stations.add(station); | |
108 sortedStarts.add(station.getStation()); | |
109 } | |
110 } | |
111 Collections.sort(sortedStarts); | |
112 | |
113 // Handle sediment load differently, as it respects already | |
114 // the ranges that were added to SedimentLoad/Fraction. | |
115 if (getName().equals(FacetTypes.SEDIMENT_LOAD_TA_TOTAL) | |
116 ||getName().equals(FacetTypes.SEDIMENT_LOAD_M3A_TOTAL)) { | |
117 SedimentLoadLSData load = result.getLoad(); | |
118 TDoubleArrayList xPos = new TDoubleArrayList(); | |
119 TDoubleArrayList yPos = new TDoubleArrayList(); | |
120 double lastX = -1d; | |
121 for (double km: new TreeSet<Double>(load.getKms())) { | |
122 SedimentLoadFraction fraction = load.getFraction(km); | |
123 if (fraction.getTotal() != 0) { | |
124 if (Math.abs(lastX-km) >= EPSILON) { | |
125 xPos.add(Double.NaN); | |
126 yPos.add(Double.NaN); | |
127 } | |
128 xPos.add(km); | |
129 yPos.add(fraction.getTotal()); | |
130 xPos.add(fraction.getTotalRange().getEnd()); | |
131 yPos.add(fraction.getTotal()); | |
132 lastX = fraction.getTotalRange().getEnd(); | |
133 } | |
134 } | |
135 return new double[][] {xPos.toNativeArray(), yPos.toNativeArray()}; | |
136 } | 116 } |
137 | 117 |
138 // Access data according to type (except total - see above). | 118 return frac.getData(); |
139 double[][] sd = getLoadData(result); | |
140 | 119 |
141 // Sort by km. | 120 /* Are gaps neccessary now? |
142 TreeMap<Double, Double> sortedKmLoad = new TreeMap<Double,Double>(); | |
143 | |
144 double[] km = sd[0]; | |
145 double[] load = sd[1]; | |
146 | |
147 // Build map of km->load, but directly exclude the ones which do | |
148 // not match against a measurements station ranges start. | |
149 for (int i = 0 ; i < km.length; i++) { | |
150 for (MeasurementStation station: stations) { | |
151 if (Math.abs(station.getStation() - km[i]) <= EPSILON) { | |
152 sortedKmLoad.put(km[i], load[i]); | |
153 continue; | |
154 } | |
155 } | |
156 } | |
157 | |
158 // [0] -> x, [1] -> y | |
159 double[][] values = new double[2][]; | |
160 values[0] = new double[sortedKmLoad.size()*3]; | |
161 values[1] = new double[sortedKmLoad.size()*3]; | |
162 | |
163 // Find station via its station (km). | |
164 // TODO use a binarySearch instead of linear absdiff approach | |
165 int i = 0; | |
166 for (Map.Entry<Double, Double> kmLoad: sortedKmLoad.entrySet()) { | |
167 boolean matchFound = false; | |
168 for (int k = 0, S = stations.size(); k < S; k++) { | |
169 MeasurementStation station = stations.get(k); | |
170 if (Math.abs(station.getStation() - kmLoad.getKey()) < EPSILON) { | |
171 // Value has been taken at measurement station. | |
172 values[0][i*3] = station.getRange().getA().doubleValue() + EPSILON; | |
173 values[1][i*3] = kmLoad.getValue(); | |
174 double endValue = 0d; | |
175 // Valid until next measurements stations begin of range, | |
176 // or end of current range if last value. | |
177 if (k+2 <= S) { | |
178 endValue = stations.get(k+1).getRange().getA().doubleValue(); | |
179 } | |
180 else { | |
181 endValue = station.getRange().getB().doubleValue(); | |
182 } | |
183 values[0][i*3+1] = endValue; | |
184 values[1][i*3+1] = kmLoad.getValue(); | |
185 values[0][i*3+2] = endValue; | |
186 values[1][i*3+2] = kmLoad.getValue(); | |
187 matchFound = true; | |
188 break; | |
189 } | |
190 } | |
191 // Store points without match for later assessment. | |
192 if (!matchFound) { | |
193 logger.warn("measurement without station ("+kmLoad.getKey()+")!"); | |
194 } | |
195 i++; | |
196 } | |
197 | |
198 for (int x = 0; x < values[0].length-1; x++) { | 121 for (int x = 0; x < values[0].length-1; x++) { |
199 // Introduce gaps where no data in measurement station. | 122 // Introduce gaps where no data in measurement station. |
200 if (Math.abs(values[0][x+1] - values[0][x]) > 3*EPSILON | 123 if (Math.abs(values[0][x+1] - values[0][x]) > 3*EPSILON |
201 && values[1][x+1] != values[1][x]) { | 124 && values[1][x+1] != values[1][x]) { |
202 values[0][x] = Double.NaN; | 125 values[0][x] = Double.NaN; |
203 values[1][x] = Double.NaN; | 126 values[1][x] = Double.NaN; |
204 } | 127 } |
205 } | 128 } |
206 | 129 |
207 return values; | 130 return values; */ |
208 } | |
209 | |
210 | |
211 /** Get data according to type of facet. */ | |
212 private double[][] getLoadData(SedimentLoadResult result) { | |
213 String name = getName(); | |
214 if (FacetTypes.IS.SEDIMENT_LOAD_SAND(name)) | |
215 return result.getSandData(); | |
216 else if (FacetTypes.IS.SEDIMENT_LOAD_COARSE(name)) | |
217 return result.getCoarseData(); | |
218 else if (FacetTypes.IS.SEDIMENT_LOAD_FINEMIDDLE(name)) | |
219 return result.getFineMiddleData(); | |
220 else if (FacetTypes.IS.SEDIMENT_LOAD_SUSP_SAND(name)) | |
221 return result.getSuspSandData(); | |
222 else if (FacetTypes.IS.SEDIMENT_LOAD_SUSP_SAND_BED(name)) | |
223 return result.getSuspSandBedData(); | |
224 else if (FacetTypes.IS.SEDIMENT_LOAD_SUSP_SEDIMENT(name)) | |
225 return result.getSuspSedimentData(); | |
226 else if (FacetTypes.IS.SEDIMENT_LOAD_TOTAL_LOAD(name)) | |
227 return result.getTotalLoadData(); | |
228 else if (FacetTypes.IS.SEDIMENT_LOAD_TOTAL(name)) | |
229 return result.getTotalData(); | |
230 else { | |
231 logger.error("SedimentLoadDataFacet " + name + " cannot determine data type."); | |
232 return null; | |
233 } | |
234 } | 131 } |
235 | 132 |
236 /** Copy deeply. */ | 133 /** Copy deeply. */ |
237 @Override | 134 @Override |
238 public Facet deepCopy() { | 135 public Facet deepCopy() { |
239 SedimentLoadDataFacet copy = new SedimentLoadDataFacet(); | 136 SedimentLoadDataFacet copy = new SedimentLoadDataFacet(); |
240 copy.set(this); | 137 copy.set(this); |
241 copy.type = type; | 138 copy.type = type; |
242 copy.hash = hash; | 139 copy.hash = hash; |
243 copy.stateId = stateId; | 140 copy.stateId = stateId; |
141 copy.fraction = fraction; | |
244 return copy; | 142 return copy; |
245 } | 143 } |
246 } | 144 } |
247 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : | 145 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : |