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;

http://dive4elements.wald.intevation.org