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 :

http://dive4elements.wald.intevation.org