comparison backend/src/main/java/org/dive4elements/river/importer/parsers/WstParser.java @ 8976:e541938dd3ab

Range data handled consistently as BigDecimal to minimize the fractional part of a and b
author mschaefer
date Tue, 03 Apr 2018 10:43:53 +0200
parents 5e38e2924c07
children 5ff8ce9a2e06
comparison
equal deleted inserted replaced
8975:a0a0a7f912ab 8976:e541938dd3ab
6 * documentation coming with Dive4Elements River for details. 6 * documentation coming with Dive4Elements River for details.
7 */ 7 */
8 8
9 package org.dive4elements.river.importer.parsers; 9 package org.dive4elements.river.importer.parsers;
10 10
11 import java.io.File;
12 import java.io.FileInputStream;
13 import java.io.IOException;
14 import java.io.InputStreamReader;
15 import java.io.LineNumberReader;
16 import java.math.BigDecimal;
17 import java.text.NumberFormat;
11 import java.util.ArrayList; 18 import java.util.ArrayList;
19 import java.util.Arrays;
12 import java.util.HashSet; 20 import java.util.HashSet;
13 import java.util.Arrays; 21 import java.util.regex.Matcher;
14 22 import java.util.regex.Pattern;
15 import java.io.File;
16 import java.io.IOException;
17 import java.io.LineNumberReader;
18 import java.io.InputStreamReader;
19 import java.io.FileInputStream;
20
21 import java.text.NumberFormat;
22 23
23 import org.apache.log4j.Logger; 24 import org.apache.log4j.Logger;
24 25 import org.dive4elements.river.backend.utils.DateGuesser;
25 import org.dive4elements.river.backend.utils.StringUtil; 26 import org.dive4elements.river.backend.utils.StringUtil;
26 import org.dive4elements.river.backend.utils.DateGuesser; 27 import org.dive4elements.river.importer.ImportRange;
27
28 import java.util.regex.Pattern;
29 import java.util.regex.Matcher;
30
31 import java.math.BigDecimal;
32
33 import org.dive4elements.river.importer.ImportWstQRange;
34 import org.dive4elements.river.importer.ImportWstColumn;
35 import org.dive4elements.river.importer.ImportTimeInterval; 28 import org.dive4elements.river.importer.ImportTimeInterval;
36 import org.dive4elements.river.importer.ImportRange;
37 import org.dive4elements.river.importer.ImportUnit; 29 import org.dive4elements.river.importer.ImportUnit;
38 import org.dive4elements.river.importer.ImportWst; 30 import org.dive4elements.river.importer.ImportWst;
31 import org.dive4elements.river.importer.ImportWstColumn;
32 import org.dive4elements.river.importer.ImportWstQRange;
39 33
40 public class WstParser 34 public class WstParser
41 { 35 {
42 private static Logger log = Logger.getLogger(WstParser.class); 36 private static Logger log = Logger.getLogger(WstParser.class);
43 37
45 public static final String COLUMN_BEZ_BREITE = "column-bez-breite"; 39 public static final String COLUMN_BEZ_BREITE = "column-bez-breite";
46 public static final String COLUMN_QUELLE = "column-quelle"; 40 public static final String COLUMN_QUELLE = "column-quelle";
47 public static final String COLUMN_DATUM = "column-datum"; 41 public static final String COLUMN_DATUM = "column-datum";
48 42
49 public static final BigDecimal UNDEFINED_ZERO = 43 public static final BigDecimal UNDEFINED_ZERO =
50 new BigDecimal(0.0); 44 new BigDecimal(0.0);
51 public static final BigDecimal MIN_RANGE = 45 public static final BigDecimal MIN_RANGE =
52 new BigDecimal(-Double.MAX_VALUE); 46 new BigDecimal(-Double.MAX_VALUE);
53 public static final BigDecimal MAX_RANGE = 47 public static final BigDecimal MAX_RANGE =
54 new BigDecimal(Double.MAX_VALUE); 48 new BigDecimal(Double.MAX_VALUE);
55 49
56 public static final String ENCODING = "ISO-8859-1"; 50 public static final String ENCODING = "ISO-8859-1";
57 51
58 public static final Pattern UNIT_COMMENT = 52 public static final Pattern UNIT_COMMENT =
59 Pattern.compile("\\*\\s*[kK][mM]\\s+(.+)"); 53 Pattern.compile("\\*\\s*[kK][mM]\\s+(.+)");
60 54
61 public static final Pattern UNIT = 55 public static final Pattern UNIT =
62 Pattern.compile("[^\\[]*\\[([^]]+)\\].*"); 56 Pattern.compile("[^\\[]*\\[([^]]+)\\].*");
63 57
64 public static final Pattern YEAR_INTERVAL = 58 public static final Pattern YEAR_INTERVAL =
65 Pattern.compile("(\\d{4})\\s*[-/]\\s*(\\d{4})"); 59 Pattern.compile("(\\d{4})\\s*[-/]\\s*(\\d{4})");
66 60
67 public static final double INTERVAL_GAP = 0.00001d; 61 public static final BigDecimal INTERVAL_GAP = new BigDecimal("0.00001");
68 62
69 protected ImportWst wst; 63 protected ImportWst wst;
70 64
71 protected ImportRange lastRange; 65 protected ImportRange lastRange;
72 protected Double lastA;
73 protected Double lastB;
74 66
75 public WstParser() { 67 public WstParser() {
76 } 68 }
77 69
78 public WstParser(ImportWst wst) { 70 public WstParser(final ImportWst wst) {
79 this.wst = wst; 71 this.wst = wst;
80 } 72 }
81 73
82 public ImportWst getWst() { 74 public ImportWst getWst() {
83 return wst; 75 return this.wst;
84 } 76 }
85 77
86 public void setWst(ImportWst wst) { 78 public void setWst(final ImportWst wst) {
87 this.wst = wst; 79 this.wst = wst;
88 } 80 }
89 81
90 public static final class ParseException extends Exception { 82 public static final class ParseException extends Exception {
91 public ParseException() { 83 public ParseException() {
92 } 84 }
93 85
94 public ParseException(String msg) { 86 public ParseException(final String msg) {
95 super(msg); 87 super(msg);
96 } 88 }
97 } // class ParseException 89 } // class ParseException
98 90
99 /** Returns a new ImportTimeInterval with a date guessed from string. */ 91 /** Returns a new ImportTimeInterval with a date guessed from string. */
100 public static ImportTimeInterval guessDate(String string) { 92 public static ImportTimeInterval guessDate(final String string) {
101 try { 93 try {
102 Matcher m = YEAR_INTERVAL.matcher(string); 94 final Matcher m = YEAR_INTERVAL.matcher(string);
103 if (m.matches()) { 95 if (m.matches()) {
104 return new ImportTimeInterval( 96 return new ImportTimeInterval(
105 DateGuesser.guessDate(m.group(1)), 97 DateGuesser.guessDate(m.group(1)),
106 DateGuesser.guessDate(m.group(2))); 98 DateGuesser.guessDate(m.group(2)));
107 } 99 }
108 100
109 return new ImportTimeInterval( 101 return new ImportTimeInterval(
110 DateGuesser.guessDate(string)); 102 DateGuesser.guessDate(string));
111 } 103 }
112 catch (IllegalArgumentException iae) { 104 catch (final IllegalArgumentException iae) {
113 log.warn("WST: String '" + string + 105 log.warn("WST: String '" + string +
114 "' could not be interpreted as valid timestamp"); 106 "' could not be interpreted as valid timestamp");
115 } 107 }
116 return null; 108 return null;
117 } 109 }
118 110
119 public void parse(File file) throws IOException, ParseException { 111 public void parse(final File file) throws IOException, ParseException {
120 112
121 log.info("Parsing WST file '" + file + "'"); 113 log.info("Parsing WST file '" + file + "'");
122 114
123 if (wst == null) { 115 if (this.wst == null) {
124 wst = new ImportWst(file.getName()); 116 this.wst = new ImportWst(file.getName());
125 } 117 }
126 else { 118 else {
127 wst.setDescription(file.getName()); 119 this.wst.setDescription(file.getName());
128 } 120 }
129 121
130 LineNumberReader in = 122 final LineNumberReader in =
131 new LineNumberReader( 123 new LineNumberReader(
132 new InputStreamReader( 124 new InputStreamReader(
133 new FileInputStream(file), ENCODING)); 125 new FileInputStream(file), ENCODING));
134 try { 126 try {
135 String input; 127 String input;
136 boolean first = true; 128 boolean first = true;
137 int columnCount = 0; 129 int columnCount = 0;
138 130
156 * if none is found in WST-file. 148 * if none is found in WST-file.
157 * Use in case no unit comment is found in file */ 149 * Use in case no unit comment is found in file */
158 String einheit = "m ü. unbekannte Referenz"; 150 String einheit = "m ü. unbekannte Referenz";
159 boolean unitFound = false; 151 boolean unitFound = false;
160 152
161 HashSet<BigDecimal> kms = new HashSet<BigDecimal>(); 153 final HashSet<BigDecimal> kms = new HashSet<>();
162 154
163 while ((input = in.readLine()) != null) { 155 while ((input = in.readLine()) != null) {
164 String line = input; 156 String line = input;
165 if (first) { // fetch number of columns 157 if (first) { // fetch number of columns
166 if ((line = line.trim()).length() == 0) { 158 if ((line = line.trim()).length() == 0) {
168 } 160 }
169 try { 161 try {
170 columnCount = Integer.parseInt(line); 162 columnCount = Integer.parseInt(line);
171 if (columnCount <= 0) { 163 if (columnCount <= 0) {
172 throw new NumberFormatException( 164 throw new NumberFormatException(
173 "number of columns <= 0"); 165 "number of columns <= 0");
174 } 166 }
175 log.debug("Number of columns: " + columnCount); 167 log.debug("Number of columns: " + columnCount);
176 wst.setNumberColumns(columnCount); 168 this.wst.setNumberColumns(columnCount);
177 lsBezeichner = new String[columnCount]; 169 lsBezeichner = new String[columnCount];
178 } 170 }
179 catch (NumberFormatException nfe) { 171 catch (final NumberFormatException nfe) {
180 log.warn("WST: invalid number.", nfe); 172 log.warn("WST: invalid number.", nfe);
181 continue; 173 continue;
182 } 174 }
183 first = false; 175 first = false;
184 continue; 176 continue;
186 178
187 line = line.replace(',', '.'); 179 line = line.replace(',', '.');
188 180
189 // handle Q-lines 181 // handle Q-lines
190 if (line.startsWith("*\u001f")) { 182 if (line.startsWith("*\u001f")) {
191 BigDecimal [] data = parseLineAsDouble( 183 final BigDecimal [] data = parseLineAsDouble(
192 line, columnCount, false, true); 184 line, columnCount, false, true);
193 185
194 if (aktAbfluesse != null) { 186 if (aktAbfluesse != null) {
195 // add Q-ranges obtained from previous lines 187 // add Q-ranges obtained from previous lines
196 if (kmHist1 != null && kmHist2 != null 188 if (kmHist1 != null && kmHist2 != null
197 && kmHist1.compareTo(kmHist2) < 0) { 189 && kmHist1.compareTo(kmHist2) < 0) {
198 // stations descending in file 190 // stations descending in file
199 BigDecimal t = minKm; minKm = maxKm; maxKm = t; 191 final BigDecimal t = minKm; minKm = maxKm; maxKm = t;
200 } 192 }
201 addInterval(minKm, maxKm, aktAbfluesse); 193 addInterval(minKm, maxKm, aktAbfluesse);
202 minKm = MAX_RANGE; 194 minKm = MAX_RANGE;
203 maxKm = MIN_RANGE; 195 maxKm = MIN_RANGE;
204 } 196 }
214 } 206 }
215 207
216 // remember Q-values from first Q-line 208 // remember Q-values from first Q-line
217 // for header generation 209 // for header generation
218 if (firstAbfluesse == null) { 210 if (firstAbfluesse == null) {
219 firstAbfluesse = (BigDecimal [])aktAbfluesse.clone(); 211 firstAbfluesse = aktAbfluesse.clone();
220 } 212 }
221 continue; 213 continue;
222 } 214 }
223 215
224 // handle special column identifiers 216 // handle special column identifiers
229 continue; 221 continue;
230 } 222 }
231 223
232 if (spezial.startsWith(COLUMN_BEZ_TEXT)) { 224 if (spezial.startsWith(COLUMN_BEZ_TEXT)) {
233 spezial = spezial.substring( 225 spezial = spezial.substring(
234 COLUMN_BEZ_TEXT.length()).trim(); 226 COLUMN_BEZ_TEXT.length()).trim();
235 if (spezial.length() == 0) { 227 if (spezial.length() == 0) {
236 continue; 228 continue;
237 } 229 }
238 langBezeichner = StringUtil.splitQuoted(spezial, '"'); 230 langBezeichner = StringUtil.splitQuoted(spezial, '"');
239 } 231 }
240 else if (spezial.startsWith(COLUMN_BEZ_BREITE)) { 232 else if (spezial.startsWith(COLUMN_BEZ_BREITE)) {
241 spezial = spezial.substring( 233 spezial = spezial.substring(
242 COLUMN_BEZ_BREITE.length()).trim(); 234 COLUMN_BEZ_BREITE.length()).trim();
243 235
244 if (spezial.length() == 0) { 236 if (spezial.length() == 0) {
245 continue; 237 continue;
246 } 238 }
247 239
248 String[] split = spezial.split("\\s+"); 240 final String[] split = spezial.split("\\s+");
249 241
250 colNaWidths = new int[split.length]; 242 colNaWidths = new int[split.length];
251 for (int i=0; i < split.length; i++) { 243 for (int i=0; i < split.length; i++) {
252 colNaWidths[i] = Integer.parseInt(split[i]); 244 colNaWidths[i] = Integer.parseInt(split[i]);
253 } 245 }
254 } 246 }
255 else if (spezial.startsWith(COLUMN_QUELLE)) { 247 else if (spezial.startsWith(COLUMN_QUELLE)) {
256 spezial = spezial.substring( 248 spezial = spezial.substring(
257 COLUMN_QUELLE.length()).trim(); 249 COLUMN_QUELLE.length()).trim();
258 if (spezial.length() == 0) { 250 if (spezial.length() == 0) {
259 continue; 251 continue;
260 } 252 }
261 quellen = StringUtil.splitQuoted(spezial, '"'); 253 quellen = StringUtil.splitQuoted(spezial, '"');
262 log.debug("sources: " + Arrays.toString(quellen)); 254 log.debug("sources: " + Arrays.toString(quellen));
263 } 255 }
264 else if (spezial.startsWith(COLUMN_DATUM)) { 256 else if (spezial.startsWith(COLUMN_DATUM)) {
265 spezial = spezial.substring( 257 spezial = spezial.substring(
266 COLUMN_DATUM.length()).trim(); 258 COLUMN_DATUM.length()).trim();
267 if (spezial.length() == 0) { 259 if (spezial.length() == 0) {
268 continue; 260 continue;
269 } 261 }
270 daten = StringUtil.splitQuoted(spezial, '"'); 262 daten = StringUtil.splitQuoted(spezial, '"');
271 } 263 }
281 Matcher m = UNIT_COMMENT.matcher(line); 273 Matcher m = UNIT_COMMENT.matcher(line);
282 if (m.matches()) { 274 if (m.matches()) {
283 log.debug("unit comment found"); 275 log.debug("unit comment found");
284 // XXX: This hack is needed because desktop 276 // XXX: This hack is needed because desktop
285 // FLYS is broken figuring out the unit 277 // FLYS is broken figuring out the unit
286 String [] units = m.group(1).split("\\s{2,}"); 278 final String [] units = m.group(1).split("\\s{2,}");
287 m = UNIT.matcher(units[0]); 279 m = UNIT.matcher(units[0]);
288 einheit = m.matches() ? m.group(1) : units[0]; 280 einheit = m.matches() ? m.group(1) : units[0];
289 log.debug("unit: " + einheit); 281 log.debug("unit: " + einheit);
290 unitFound = true; 282 unitFound = true;
291 } 283 }
294 } 286 }
295 287
296 if (firstAbfluesse != null) { 288 if (firstAbfluesse != null) {
297 if (!columnHeaderChecked) { 289 if (!columnHeaderChecked) {
298 int unknownCount = 0; 290 int unknownCount = 0;
299 HashSet<String> uniqueColumnNames = 291 final HashSet<String> uniqueColumnNames =
300 new HashSet<String>(); 292 new HashSet<>();
301 if (langBezeichner != null) { 293 if (langBezeichner != null) {
302 // use column name from '*!column-bez-text'-line 294 // use column name from '*!column-bez-text'-line
303 lsBezeichner = StringUtil.fitArray( 295 lsBezeichner = StringUtil.fitArray(
304 langBezeichner, lsBezeichner); 296 langBezeichner, lsBezeichner);
305 } 297 }
306 for (int i = 0; i < lsBezeichner.length; ++i) { 298 for (int i = 0; i < lsBezeichner.length; ++i) {
307 if (lsBezeichner[i] == null 299 if (lsBezeichner[i] == null
308 || lsBezeichner[i].length() == 0) { 300 || lsBezeichner[i].length() == 0) {
309 // generate alternative column names 301 // generate alternative column names
310 double q = firstAbfluesse.length > i ? 302 final double q = firstAbfluesse.length > i ?
311 firstAbfluesse[i].doubleValue() : 0d; 303 firstAbfluesse[i].doubleValue() : 0d;
312 if (q < 0.001) { 304 if (q < 0.001) {
313 lsBezeichner[i] = 305 lsBezeichner[i] =
314 "<unbekannt #" + unknownCount + ">"; 306 "<unbekannt #" + unknownCount + ">";
315 ++unknownCount; 307 ++unknownCount;
316 } 308 }
317 else { 309 else {
318 lsBezeichner[i] = "Q="+format(q); 310 lsBezeichner[i] = "Q="+format(q);
319 } 311 }
320 } 312 }
321 String candidate = lsBezeichner[i]; 313 String candidate = lsBezeichner[i];
322 int collision = 1; 314 int collision = 1;
323 while (!uniqueColumnNames.add(candidate)) { 315 while (!uniqueColumnNames.add(candidate)) {
324 candidate = lsBezeichner[i] + 316 candidate = lsBezeichner[i] +
325 " (" + collision + ")"; 317 " (" + collision + ")";
326 ++collision; 318 ++collision;
327 } 319 }
328 ImportWstColumn iwc = wst.getColumn(i); 320 final ImportWstColumn iwc = this.wst.getColumn(i);
329 iwc.setName(candidate); 321 iwc.setName(candidate);
330 if (quellen != null && i < quellen.length) { 322 if (quellen != null && i < quellen.length) {
331 iwc.setSource(quellen[i]); 323 iwc.setSource(quellen[i]);
332 } 324 }
333 String potentialDate = 325 final String potentialDate =
334 daten != null && i < daten.length 326 daten != null && i < daten.length
335 ? daten[i] 327 ? daten[i]
336 : candidate; 328 : candidate;
337 iwc.setTimeInterval(guessDate(potentialDate)); 329 iwc.setTimeInterval(guessDate(potentialDate));
338 } 330 }
339 columnHeaderChecked = true; 331 columnHeaderChecked = true;
340 } 332 }
341 333
342 BigDecimal [] data = parseLineAsDouble( 334 final BigDecimal [] data = parseLineAsDouble(
343 line, columnCount, true, false); 335 line, columnCount, true, false);
344 336
345 BigDecimal kaem = data[0]; 337 final BigDecimal kaem = data[0];
346 338
347 if (!kms.add(kaem)) { 339 if (!kms.add(kaem)) {
348 log.warn( 340 log.warn(
349 "WST: km " + kaem + 341 "WST: km " + kaem +
350 " (line " + in.getLineNumber() + 342 " (line " + in.getLineNumber() +
351 ") found more than once. -> ignored"); 343 ") found more than once. -> ignored");
352 continue; 344 continue;
353 } 345 }
354 346
355 // check consistence of station ordering in file 347 // check consistence of station ordering in file
356 if (kmHist2 != null && 348 if (kmHist2 != null &&
357 kmHist2.compareTo(kmHist1) != kmHist1.compareTo(kaem) 349 kmHist2.compareTo(kmHist1) != kmHist1.compareTo(kaem)
358 ) { 350 ) {
359 throw new ParseException("WST: Stations in " + file + 351 throw new ParseException("WST: Stations in " + file +
360 " near line " + in.getLineNumber() + 352 " near line " + in.getLineNumber() +
361 " not ordered. File rejected."); 353 " not ordered. File rejected.");
362 } 354 }
363 355
364 // remember stations in two previous lines 356 // remember stations in two previous lines
365 kmHist2 = kmHist1; 357 kmHist2 = kmHist1;
366 kmHist1 = kaem; 358 kmHist1 = kaem;
383 if (langBezeichner != null) { 375 if (langBezeichner != null) {
384 // nothing to do 376 // nothing to do
385 } 377 }
386 else if (colNaWidths != null) { 378 else if (colNaWidths != null) {
387 for (int j = 0, i = 0, N = input.length(); 379 for (int j = 0, i = 0, N = input.length();
388 j < colNaWidths.length && i < N; 380 j < colNaWidths.length && i < N;
389 i += colNaWidths[j++] 381 i += colNaWidths[j++]
390 ) { 382 ) {
391 lsBezeichner[j] = input.substring( 383 lsBezeichner[j] = input.substring(
392 i, i+colNaWidths[j]).trim(); 384 i, i+colNaWidths[j]).trim();
393 } 385 }
394 } 386 }
395 else { // fetch column names from non-comment header line 387 else { // fetch column names from non-comment header line
396 // (above first Qs) 388 // (above first Qs)
397 // first column begins at position 8 in line 389 // first column begins at position 8 in line
398 for (int i = 8, col = 0; i < input.length(); i += 9) { 390 for (int i = 8, col = 0; i < input.length(); i += 9) {
399 // one column header is 9 chars wide 391 // one column header is 9 chars wide
400 // but the last one may be shorter 392 // but the last one may be shorter
401 if (col < lsBezeichner.length) { 393 if (col < lsBezeichner.length) {
402 lsBezeichner[col++] = 394 lsBezeichner[col++] =
403 input.substring( 395 input.substring(
404 i, 396 i,
405 Math.min(i + 9, input.length()) 397 Math.min(i + 9, input.length())
406 ).trim(); 398 ).trim();
407 } 399 }
408 if (col == lsBezeichner.length) { 400 if (col == lsBezeichner.length) {
409 break; 401 break;
410 } 402 }
411 } 403 }
415 } // for all lines in WST file 407 } // for all lines in WST file
416 408
417 if (!unitFound) { 409 if (!unitFound) {
418 log.warn("no unit and height reference found. Using default."); 410 log.warn("no unit and height reference found. Using default.");
419 } 411 }
420 wst.setUnit(new ImportUnit(einheit)); 412 this.wst.setUnit(new ImportUnit(einheit));
421 413
422 // add Q-ranges obtained from previous lines 414 // add Q-ranges obtained from previous lines
423 // in case there was no further Q-line 415 // in case there was no further Q-line
424 // but only if there were values following the last Q-line 416 // but only if there were values following the last Q-line
425 if (minKm != MAX_RANGE && maxKm != MIN_RANGE) { 417 if (minKm != MAX_RANGE && maxKm != MIN_RANGE) {
426 if (kmHist1 != null && kmHist2 != null 418 if (kmHist1 != null && kmHist2 != null
427 && kmHist1.compareTo(kmHist2) < 0) { 419 && kmHist1.compareTo(kmHist2) < 0) {
428 // stations descending in file 420 // stations descending in file
429 BigDecimal t = minKm; minKm = maxKm; maxKm = t; 421 final BigDecimal t = minKm; minKm = maxKm; maxKm = t;
430 } 422 }
431 addInterval(minKm, maxKm, aktAbfluesse); 423 addInterval(minKm, maxKm, aktAbfluesse);
432 } 424 }
433 } 425 }
434 finally { 426 finally {
435 in.close(); 427 in.close();
436 } 428 }
437 } 429 }
438 430
439 protected void addValue(BigDecimal km, BigDecimal w, int index) { 431 protected void addValue(final BigDecimal km, final BigDecimal w, final int index) {
440 if (w != null) { 432 if (w != null) {
441 ImportWstColumn column = wst.getColumn(index); 433 final ImportWstColumn column = this.wst.getColumn(index);
442 column.addColumnValue(km, w); 434 column.addColumnValue(km, w);
443 } 435 }
444 } 436 }
445 437
446 private static final NumberFormat NF = getNumberFormat(); 438 private static final NumberFormat NF = getNumberFormat();
447 439
448 private static final NumberFormat getNumberFormat() { 440 private static final NumberFormat getNumberFormat() {
449 NumberFormat nf = NumberFormat.getInstance(); 441 final NumberFormat nf = NumberFormat.getInstance();
450 nf.setMinimumFractionDigits(2); 442 nf.setMinimumFractionDigits(2);
451 nf.setMaximumFractionDigits(2); 443 nf.setMaximumFractionDigits(2);
452 return nf; 444 return nf;
453 } 445 }
454 446
455 protected static String format(double value) { 447 protected static String format(final double value) {
456 return NF.format(value); 448 return NF.format(value);
457 } 449 }
458 450
459 protected void addInterval( 451 protected void addInterval(
460 BigDecimal from, 452 final BigDecimal from,
461 BigDecimal to, 453 final BigDecimal to,
462 BigDecimal [] values 454 final BigDecimal[] values
463 ) { 455 ) {
464 log.debug("addInterval: " + from + " " + to); 456 log.debug("addInterval: " + from + " " + to);
465 457
466 if (values == null || from == MAX_RANGE || from == MIN_RANGE) { 458 if (values == null || from == MAX_RANGE || from == MIN_RANGE) {
467 return; 459 return;
468 } 460 }
469 461
470 // expand single-line i.e. 0-lenght Q-range to minimal length 462 // expand single-line i.e. 0-lenght Q-range to minimal length
463 final ImportRange range = new ImportRange(from, to);
471 if (from == to) { 464 if (from == to) {
472 if (lastRange != null && lastA > lastB) { 465 if ((this.lastRange != null) && (this.lastRange.difference() < 0.0))
473 to = new BigDecimal(from.doubleValue() - INTERVAL_GAP); 466 range.setB(from.subtract(INTERVAL_GAP));
474 } 467 else
475 else { 468 range.setB(from.add(INTERVAL_GAP));
476 to = new BigDecimal(from.doubleValue() + INTERVAL_GAP); 469 }
477 }
478 }
479
480 ImportRange range = new ImportRange(from, to);
481 470
482 // little workaround to make the q ranges tightly fit. 471 // little workaround to make the q ranges tightly fit.
483 // Leave a very small gap to ensure that the range queries 472 // Leave a very small gap to ensure that the range queries
484 // still work. 473 // still work.
485 474
486 if (lastRange != null) { 475 if (this.lastRange != null) {
487 double a2 = range.getA().doubleValue(); 476 if (this.lastRange.difference() > 0.0)
488 double b2 = range.getB().doubleValue(); 477 this.lastRange.setB(range.getA().subtract(INTERVAL_GAP));
489 478 else // lastA >= lastB
490 if (lastA < lastB) { 479 this.lastRange.setA(range.getB().add(INTERVAL_GAP));
491 lastRange.setB(new BigDecimal(a2 - INTERVAL_GAP)); 480 }
492 } 481
493 else { // lastA >= lastB 482 for (int i = 0; i < values.length; ++i)
494 lastRange.setA(new BigDecimal(b2 + INTERVAL_GAP)); 483 this.wst.getColumn(i).addColumnQRange(new ImportWstQRange(range, values[i]));
495 } 484
496 } 485 this.lastRange = range;
497
498 for (int i = 0; i < values.length; ++i) {
499 ImportWstColumn column = wst.getColumn(i);
500 ImportWstQRange wstQRange = new ImportWstQRange(range, values[i]);
501 column.addColumnQRange(wstQRange);
502 }
503
504 lastA = from.doubleValue();
505 lastB = to.doubleValue();
506 lastRange = range;
507 } 486 }
508 487
509 private static final BigDecimal [] parseLineAsDouble( 488 private static final BigDecimal [] parseLineAsDouble(
510 String line, 489 final String line,
511 int count, 490 final int count,
512 boolean bStation, 491 final boolean bStation,
513 boolean bParseEmptyAsZero 492 final boolean bParseEmptyAsZero
514 ) throws ParseException { 493 ) throws ParseException {
515 String [] tokens = parseLine(line, count, bStation); 494 final String [] tokens = parseLine(line, count, bStation);
516 495
517 BigDecimal [] doubles = new BigDecimal[tokens.length]; 496 final BigDecimal [] doubles = new BigDecimal[tokens.length];
518 497
519 for (int i = 0; i < doubles.length; ++i) { 498 for (int i = 0; i < doubles.length; ++i) {
520 String token = tokens[i].trim(); 499 final String token = tokens[i].trim();
521 if (token.length() != 0) { 500 if (token.length() != 0) {
522 doubles[i] = new BigDecimal(token); 501 doubles[i] = new BigDecimal(token);
523 } 502 }
524 else if (bParseEmptyAsZero) { 503 else if (bParseEmptyAsZero) {
525 doubles[i] = UNDEFINED_ZERO; 504 doubles[i] = UNDEFINED_ZERO;
528 507
529 return doubles; 508 return doubles;
530 } 509 }
531 510
532 private static String [] parseLine( 511 private static String [] parseLine(
533 String line, 512 final String line,
534 int tokenCount, 513 final int tokenCount,
535 boolean bParseStation 514 final boolean bParseStation
536 ) throws ParseException { 515 ) throws ParseException {
537 ArrayList<String> strings = new ArrayList<String>(); 516 final ArrayList<String> strings = new ArrayList<>();
538 517
539 if (bParseStation) { 518 if (bParseStation) {
540 if (line.length() < 8) { 519 if (line.length() < 8) {
541 throw new IllegalArgumentException("station too short"); 520 throw new IllegalArgumentException("station too short");
542 } 521 }
548 pos += 9; 527 pos += 9;
549 if (pos >= line.length()) { 528 if (pos >= line.length()) {
550 break; 529 break;
551 } 530 }
552 strings.add(line.substring(pos, 531 strings.add(line.substring(pos,
553 Math.min(pos + 8, line.length()))); 532 Math.min(pos + 8, line.length())));
554 } 533 }
555 534
556 return strings.toArray(new String[strings.size()]); 535 return strings.toArray(new String[strings.size()]);
557 } 536 }
558 } 537 }

http://dive4elements.wald.intevation.org