Mercurial > dive4elements > river
comparison artifacts/src/main/java/org/dive4elements/river/exports/ATWriter.java @ 6304:152e1ba8e5bd
Rework ATWriter/Exporter
- Make the write function static as both data and gauge information
is necessary for writing.
- Use the rivers WST unit for the gauge datum
- Handle the case that we are at a gauge
- Differentiate between calculated values
- Always work with w in CM and thus avoid double number format problems
author | Andre Heinecke <aheinecke@intevation.de> |
---|---|
date | Wed, 12 Jun 2013 16:07:28 +0200 |
parents | 02ea1c3fba8c |
children | 3ac657d60e60 |
comparison
equal
deleted
inserted
replaced
6303:5fda7065b74a | 6304:152e1ba8e5bd |
---|---|
44 "export.discharge.curve.at.header"; | 44 "export.discharge.curve.at.header"; |
45 | 45 |
46 public static final String I18N_AT_GAUGE_HEADER = | 46 public static final String I18N_AT_GAUGE_HEADER = |
47 "export.discharge.curve.at.gauge.header"; | 47 "export.discharge.curve.at.gauge.header"; |
48 | 48 |
49 public static final String I18N_AT_CALC_GAUGE_HEADER = | |
50 "export.discharge.curve.at.gauge.calc.header"; | |
51 | |
49 public static final String EMPTY = " "; | 52 public static final String EMPTY = " "; |
50 | 53 |
51 protected double minW; | 54 public static double getQ(int w, UnivariateRealFunction qFunc) { |
52 protected double maxW; | |
53 protected double minQ; | |
54 protected double maxQ; | |
55 | |
56 protected UnivariateRealFunction qFunc; | |
57 | |
58 public ATWriter() { | |
59 } | |
60 | |
61 public ATWriter(WQ wq) throws IllegalArgumentException { | |
62 | |
63 int [] bounds = wq.longestIncreasingWRangeIndices(); | |
64 | |
65 if (logger.isDebugEnabled()) { | |
66 logger.debug("exporting w between indices " + | |
67 bounds[0] + " and " + bounds[1] + " (" + | |
68 wq.getW(bounds[0]) + ", " + wq.getW(bounds[1])); | |
69 } | |
70 | |
71 if (bounds[1]-bounds[0] < 1) { // Only first w can be written out. | |
72 minW = maxW = wq.getW(bounds[0]); | |
73 minQ = maxQ = wq.getQ(bounds[0]); | |
74 // constant function | |
75 qFunc = new PolynomialFunction(new double [] { minQ }); | |
76 return; | |
77 } | |
78 | |
79 double [] ws = new double[bounds[1]-bounds[0]]; | |
80 double [] qs = new double[ws.length]; | |
81 | |
82 for (int i = 0; i < ws.length; ++i) { | |
83 int idx = bounds[0]+i; | |
84 ws[i] = wq.getW(idx); | |
85 qs[i] = wq.getQ(idx); | |
86 } | |
87 | |
88 qFunc = ws.length < 3 | |
89 ? new LinearInterpolator().interpolate(ws, qs) | |
90 : new SplineInterpolator().interpolate(ws, qs); | |
91 | |
92 minW = wq.getW(bounds[0]); | |
93 maxW = wq.getW(bounds[1]); | |
94 minQ = wq.getQ(bounds[0]); | |
95 maxQ = wq.getQ(bounds[1]); | |
96 } | |
97 | |
98 public double getQ(double w) { | |
99 | |
100 try { | 55 try { |
101 return qFunc.value(w); | 56 double val = qFunc.value(w); |
57 return val; | |
102 } | 58 } |
103 catch (FunctionEvaluationException aode) { | 59 catch (FunctionEvaluationException aode) { |
104 // should not happen | 60 // should not happen |
105 logger.warn("spline interpolation failed", aode); | 61 logger.error("spline interpolation failed", aode); |
106 return w <= minW ? minQ : maxQ; | 62 return Double.NaN; |
107 } | 63 } |
108 } | 64 } |
109 | 65 |
110 public static void printQ(PrintWriter out, double q) { | 66 public static void printQ(PrintWriter out, double q) { |
111 String format; | 67 String format; |
117 if (q > 1000d) q = Math.rint(q/10d)*10d; | 73 if (q > 1000d) q = Math.rint(q/10d)*10d; |
118 } | 74 } |
119 out.printf(Locale.US, format, q); | 75 out.printf(Locale.US, format, q); |
120 } | 76 } |
121 | 77 |
78 protected static void printCalculatedGaugeHeader( | |
79 PrintWriter out, | |
80 CallMeta callMeta, | |
81 String river, | |
82 double km, | |
83 String gName, | |
84 BigDecimal datum, | |
85 Date date, | |
86 String unit | |
87 ) { | |
88 out.print("*" + Resources.getMsg( | |
89 callMeta, | |
90 I18N_AT_CALC_GAUGE_HEADER, | |
91 I18N_AT_CALC_GAUGE_HEADER, | |
92 new Object[] { river, gName, datum, unit } )); | |
93 out.print("\r\n"); | |
94 } | |
122 | 95 |
123 protected static void printGaugeHeader( | 96 protected static void printGaugeHeader( |
124 PrintWriter out, | 97 PrintWriter out, |
125 CallMeta callMeta, | 98 CallMeta callMeta, |
126 String river, | 99 String river, |
127 double km, | 100 double km, |
128 String gName, | 101 String gName, |
129 BigDecimal datum, | 102 BigDecimal datum, |
130 Date date | 103 Date date, |
104 String unit | |
131 ) { | 105 ) { |
132 DateFormat f = DateFormat.getDateInstance(); | 106 DateFormat f = DateFormat.getDateInstance(DateFormat.MEDIUM, |
107 Resources.getLocale(callMeta)); | |
133 out.print("*" + Resources.getMsg( | 108 out.print("*" + Resources.getMsg( |
134 callMeta, | 109 callMeta, |
135 I18N_AT_GAUGE_HEADER, | 110 I18N_AT_GAUGE_HEADER, |
136 I18N_AT_GAUGE_HEADER, | 111 I18N_AT_GAUGE_HEADER, |
137 new Object[] { river, gName, f.format(date), datum } )); | 112 new Object[] { river, gName, f.format(date), datum, unit} )); |
138 out.print("\r\n"); | 113 out.print("\r\n"); |
139 } | 114 } |
140 | 115 |
141 protected static void printHeader( | 116 protected static void printHeader( |
142 PrintWriter out, | 117 PrintWriter out, |
150 I18N_AT_HEADER, | 125 I18N_AT_HEADER, |
151 new Object[] { river, km } )); | 126 new Object[] { river, km } )); |
152 out.print("\r\n"); | 127 out.print("\r\n"); |
153 } | 128 } |
154 | 129 |
155 public void write( | 130 public static void write( |
131 WQ values, | |
156 Writer writer, | 132 Writer writer, |
157 CallMeta meta, | 133 CallMeta meta, |
158 String river, | 134 String river, |
159 double km, | 135 double km, |
160 String gName, | 136 String gName, |
161 BigDecimal datum, | 137 BigDecimal datum, |
162 Date date, | 138 Date date, |
163 boolean wOutUnitIsMeter) | 139 String unit) |
164 throws IOException | 140 throws IOException |
165 { | 141 { |
142 int minW; | |
143 int maxW; | |
144 double minQ; | |
145 double maxQ; | |
146 | |
147 UnivariateRealFunction qFunc; | |
148 | |
149 WQ wq = WQ.getFixedWQforExportAtGauge(values, datum); | |
150 | |
151 // If we converted to centimeter we know that the WQ table is | |
152 // calculated because of the assumption that all calculations | |
153 // are in Meter and only the discharge tables data is in meter. | |
154 boolean isCalculation = wq.getReferenceSystem() != values.getReferenceSystem(); | |
155 | |
156 int [] bounds = wq.longestIncreasingWRangeIndices(); | |
157 | |
158 if (logger.isDebugEnabled()) { | |
159 logger.debug("exporting " + (isCalculation ? "calculated " : "") + | |
160 "w between indices " + bounds[0] + " and " + bounds[1] + " (" + | |
161 (int)Math.ceil(wq.getW(bounds[0])) + ", " + | |
162 (int)Math.floor(wq.getW(bounds[1]))+ ")"); | |
163 } | |
164 | |
165 if (bounds[1]-bounds[0] < 1) { // Only first w can be written out. | |
166 minW = maxW = (int)Math.round(wq.getW(bounds[0])); | |
167 minQ = maxQ = wq.getQ(bounds[0]); | |
168 // constant function | |
169 qFunc = new PolynomialFunction(new double [] { minQ }); | |
170 return; | |
171 } | |
172 | |
173 double [] ws = new double[bounds[1]-bounds[0]]; | |
174 double [] qs = new double[ws.length]; | |
175 | |
176 for (int i = 0; i < ws.length; ++i) { | |
177 int idx = bounds[0]+i; | |
178 ws[i] = wq.getW(idx); | |
179 qs[i] = wq.getQ(idx); | |
180 } | |
181 | |
182 qFunc = ws.length < 3 | |
183 ? new LinearInterpolator().interpolate(ws, qs) | |
184 : new SplineInterpolator().interpolate(ws, qs); | |
185 | |
186 minW = (int)Math.ceil(wq.getW(bounds[0])); | |
187 maxW = (int)Math.floor(wq.getW(bounds[1])); | |
188 minQ = wq.getQ(bounds[0]); | |
189 maxQ = wq.getQ(bounds[1]); | |
166 PrintWriter out = new PrintWriter(writer); | 190 PrintWriter out = new PrintWriter(writer); |
167 | 191 |
168 // A header is required, because the desktop version of FLYS will skip | 192 // A header is required, because the desktop version of FLYS will skip |
169 // the first row. | 193 // the first row. |
170 if (gName != null) { | 194 if (gName != null) { |
171 printGaugeHeader(out, meta, river, km, gName, datum, date); | 195 printGaugeHeader(out, meta, river, km, gName, datum, date, unit); |
196 if (isCalculation) { | |
197 printCalculatedGaugeHeader(out, meta, river, km, gName, datum, date, unit); | |
198 } | |
172 } | 199 } |
173 else { | 200 else { |
174 printHeader(out, meta, river, km); | 201 printHeader(out, meta, river, km); |
175 } | 202 } |
176 | 203 |
177 double rest = (minW * 100.0) % 10.0; | 204 int rest = minW % 10; |
178 | 205 |
179 double startW = Math.rint((minW - rest*0.01)*10.0)*0.1; | 206 int startW = minW - rest; |
180 | 207 |
181 if (logger.isDebugEnabled()) { | 208 if (logger.isDebugEnabled()) { |
182 logger.debug("startW: " + startW); | 209 logger.debug("startW: " + startW); |
183 logger.debug("rest: " + rest); | 210 logger.debug("rest: " + rest); |
211 logger.debug("maxW: " + maxW); | |
184 } | 212 } |
185 | 213 |
186 int col = 0; | 214 int col = 0; |
187 for (double w = startW; w <= maxW; w += 0.01) { | 215 for (int w = startW; w < maxW; w++) { |
188 if (col == 0) { | 216 if (col == 0) { |
189 if (wOutUnitIsMeter) { | 217 out.printf(Locale.US, "%8d", w); |
190 // For some crazy reason W's at a gauge should be in the | |
191 // unit but W's exported elsewhere should be in Meter | |
192 out.printf(Locale.US, "%5.2f", w); | |
193 } else { | |
194 out.printf(Locale.US, "%8d", (int)Math.round(w * 100d)); | |
195 } | |
196 } | 218 } |
197 | 219 |
198 if (w < minW) { | 220 if (w < minW) { |
199 out.print(EMPTY); | 221 out.print(EMPTY); |
200 } | 222 } else { |
201 else { | 223 double actQ = getQ(w, qFunc); |
202 printQ(out, getQ(w)); | 224 if (Double.isNaN(actQ)) { |
225 // Can't happen™ | |
226 break; | |
227 } else { | |
228 printQ(out, actQ); | |
229 } | |
203 } | 230 } |
204 | 231 |
205 if (++col >= COLUMNS) { | 232 if (++col >= COLUMNS) { |
206 out.print("\r\n"); | 233 out.print("\r\n"); |
207 col = 0; | 234 col = 0; |