Mercurial > dive4elements > river
comparison flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FixingsOverview.java @ 3318:dbe2f85bf160
merged flys-artifacts/2.8
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:14:35 +0200 |
parents | 29022c93027d |
children | 086326be721c |
comparison
equal
deleted
inserted
replaced
2987:98c7a46ec5ae | 3318:dbe2f85bf160 |
---|---|
1 package de.intevation.flys.artifacts.model; | |
2 | |
3 import java.io.Serializable; | |
4 | |
5 import java.text.SimpleDateFormat; | |
6 | |
7 import java.util.ArrayList; | |
8 import java.util.Collections; | |
9 import java.util.Comparator; | |
10 import java.util.Date; | |
11 import java.util.List; | |
12 | |
13 import org.apache.log4j.Logger; | |
14 | |
15 import org.hibernate.SQLQuery; | |
16 import org.hibernate.Session; | |
17 | |
18 import org.hibernate.type.StandardBasicTypes; | |
19 | |
20 import org.w3c.dom.Document; | |
21 import org.w3c.dom.Element; | |
22 | |
23 public class FixingsOverview | |
24 implements Serializable | |
25 { | |
26 private static Logger log = Logger.getLogger(FixingsOverview.class); | |
27 | |
28 public static final double EPSILON = 1e-1; | |
29 | |
30 public static final String DATE_FORMAT = "dd.MM.yyyy"; | |
31 | |
32 public static final String SQL_RIVER_ID = | |
33 "SELECT" + | |
34 " id AS river_id," + | |
35 " km_up " + | |
36 "FROM rivers " + | |
37 "WHERE" + | |
38 " name = :name"; | |
39 | |
40 public static final String SQL_FIXINGS = | |
41 "SELECT" + | |
42 " id AS wst_id," + | |
43 " description " + | |
44 "FROM wsts " + | |
45 "WHERE" + | |
46 " river_id = :river_id AND kind = 2"; | |
47 | |
48 public static final String SQL_FIXING_COLUMNS = | |
49 "SELECT" + | |
50 " wc.id AS wst_column_id," + | |
51 " ti.start_time AS start_time," + | |
52 " wc.name AS name " + | |
53 "FROM wst_columns wc" + | |
54 " JOIN time_intervals ti ON wc.time_interval_id = ti.id " + | |
55 "WHERE" + | |
56 " wc.wst_id = :wst_id " + | |
57 "ORDER BY position"; | |
58 | |
59 public static final String SQL_FIXING_COLUMN_Q_RANGES = | |
60 "SELECT" + | |
61 " wqr.q AS q," + | |
62 " r.a AS start_km," + | |
63 " r.b AS stop_km " + | |
64 "FROM wst_column_q_ranges wcqr" + | |
65 " JOIN wst_q_ranges wqr ON wcqr.wst_q_range_id = wqr.id" + | |
66 " JOIN ranges r ON wqr.range_id = r.id " + | |
67 "WHERE" + | |
68 " wcqr.wst_column_id = :column_id " + | |
69 "ORDER BY r.a"; | |
70 | |
71 public static final String SQL_FIXING_COLUMN_KM_RANGE = | |
72 "SELECT" + | |
73 " MIN(position) AS start_km," + | |
74 " MAX(position) AS stop_km " + | |
75 "FROM" + | |
76 " wst_column_values " + | |
77 "WHERE" + | |
78 " wst_column_id = :column_id"; | |
79 | |
80 | |
81 | |
82 public static class QRange extends Range { | |
83 | |
84 protected double q; | |
85 | |
86 public QRange() { | |
87 } | |
88 | |
89 public QRange(double start, double end, double q) { | |
90 super(start, end); | |
91 this.q = q; | |
92 } | |
93 } // class QRange | |
94 | |
95 public static class SectorRange extends Range { | |
96 | |
97 protected int sector; | |
98 | |
99 public SectorRange() { | |
100 } | |
101 | |
102 public SectorRange(SectorRange other) { | |
103 start = other.start; | |
104 end = other.end; | |
105 sector = other.sector; | |
106 } | |
107 | |
108 public SectorRange(double start, double end, int sector) { | |
109 super(start, end); | |
110 this.sector = sector; | |
111 } | |
112 | |
113 public int getSector() { | |
114 return sector; | |
115 } | |
116 | |
117 public boolean enlarge(SectorRange other) { | |
118 if (sector == other.sector | |
119 && Math.abs(end-other.start) < EPSILON) { | |
120 end = other.end; | |
121 return true; | |
122 } | |
123 return false; | |
124 } | |
125 } // class SectorRange | |
126 | |
127 public static class Fixing implements Serializable { | |
128 | |
129 public static final Comparator<Column> DATE_CMP = | |
130 new Comparator<Column>() { | |
131 @Override | |
132 public int compare(Column a, Column b) { | |
133 return a.startTime.compareTo(b.startTime); | |
134 } | |
135 }; | |
136 | |
137 public interface Filter { | |
138 | |
139 boolean accept(Column column); | |
140 | |
141 } // interface Filter | |
142 | |
143 public class Column extends Range { | |
144 | |
145 protected int columnId; | |
146 protected Date startTime; | |
147 protected String name; | |
148 | |
149 protected List<SectorRange> sectors; | |
150 | |
151 public Column() { | |
152 } | |
153 | |
154 public Column(int columnId, Date startTime, String name) { | |
155 this.columnId = columnId; | |
156 this.startTime = startTime; | |
157 this.name = name; | |
158 | |
159 sectors = new ArrayList<SectorRange>(); | |
160 } | |
161 | |
162 public int getId() { | |
163 return columnId; | |
164 } | |
165 | |
166 public Fixing getFixing() { | |
167 return Fixing.this; | |
168 } | |
169 | |
170 public Date getStartTime() { | |
171 return startTime; | |
172 } | |
173 | |
174 public String getName() { | |
175 return name; | |
176 } | |
177 | |
178 public String getDescription() { | |
179 return Fixing.this.description + "/" + name; | |
180 } | |
181 | |
182 public List<SectorRange> getSectors() { | |
183 return sectors; | |
184 } | |
185 | |
186 public List<SectorRange> getSectors(Range range) { | |
187 | |
188 List<SectorRange> result = | |
189 new ArrayList<SectorRange>(sectors.size()); | |
190 | |
191 for (SectorRange src: sectors) { | |
192 SectorRange dst = new SectorRange(src); | |
193 if (range == null || dst.clip(range)) { | |
194 result.add(dst); | |
195 } | |
196 } | |
197 | |
198 return result; | |
199 } | |
200 | |
201 public int findQSector(double km) { | |
202 for (SectorRange sector: sectors) { | |
203 if (sector.inside(km)) { | |
204 return sector.getSector(); | |
205 } | |
206 } | |
207 return -1; | |
208 } | |
209 | |
210 public void buildSectors(GaugeRange gauge, List<QRange> qRanges) { | |
211 | |
212 for (QRange qRange: qRanges) { | |
213 SectorRange sector = new SectorRange( | |
214 qRange.start, qRange.end, | |
215 gauge.classify(qRange.q)); | |
216 if (sectors.isEmpty() | |
217 || !sectors.get(sectors.size()-1).enlarge(sector)) { | |
218 sectors.add(sector); | |
219 } | |
220 } | |
221 } | |
222 | |
223 public void loadKmRange(SQLQuery query) { | |
224 query.setInteger("column_id", columnId); | |
225 | |
226 List<Object []> kms = query.list(); | |
227 | |
228 if (kms.isEmpty()) { | |
229 log.warn("No km range for column " + columnId + "."); | |
230 } | |
231 else { | |
232 Object [] obj = kms.get(0); | |
233 start = (Double)obj[0]; | |
234 end = (Double)obj[1]; | |
235 } | |
236 } | |
237 | |
238 public void loadQRanges( | |
239 SQLQuery query, | |
240 GaugeFinder gaugeFinder | |
241 ) { | |
242 query.setInteger("column_id", columnId); | |
243 List<Object []> list = query.list(); | |
244 | |
245 List<QRange> qRanges = new ArrayList<QRange>(list.size()); | |
246 | |
247 for (Object [] row: list) { | |
248 double q = (Double)row[0]; | |
249 double start = (Double)row[1]; | |
250 double end = (Double)row[2]; | |
251 QRange qRange = new QRange(start, end, q); | |
252 if (qRange.clip(this)) { | |
253 qRanges.add(qRange); | |
254 } | |
255 } | |
256 | |
257 GaugeRange gauge = gaugeFinder.find(this); | |
258 | |
259 if (gauge != null) { | |
260 buildSectors(gauge, qRanges); | |
261 } | |
262 else { | |
263 log.warn("No gauge found for column " + columnId + "."); | |
264 } | |
265 } | |
266 } // class Column | |
267 | |
268 protected int wstId; | |
269 protected String description; | |
270 protected List<Column> columns; | |
271 | |
272 public Fixing() { | |
273 } | |
274 | |
275 public int getId() { | |
276 return wstId; | |
277 } | |
278 | |
279 public String getDescription() { | |
280 return description; | |
281 } | |
282 | |
283 public Fixing(int wstId, String description) { | |
284 this.wstId = wstId; | |
285 this.description = description; | |
286 columns = new ArrayList<Column>(); | |
287 } | |
288 | |
289 public void loadColumns(SQLQuery query) { | |
290 query.setInteger("wst_id", wstId); | |
291 List<Object []> list = query.list(); | |
292 for (Object [] row: list) { | |
293 int columnId = (Integer)row[0]; | |
294 Date startTime = (Date) row[1]; | |
295 String name = (String) row[2]; | |
296 columns.add(new Column(columnId, startTime, name)); | |
297 } | |
298 } | |
299 | |
300 public void loadColumnsKmRange(SQLQuery query) { | |
301 for (Column column: columns) { | |
302 column.loadKmRange(query); | |
303 } | |
304 } | |
305 | |
306 public void adjustExtent(Range extent) { | |
307 for (Column column: columns) { | |
308 extent.extend(column); | |
309 } | |
310 } | |
311 | |
312 public void loadColumnsQRanges( | |
313 SQLQuery query, | |
314 GaugeFinder gaugeFinder | |
315 ) { | |
316 for (Column column: columns) { | |
317 column.loadQRanges(query, gaugeFinder); | |
318 } | |
319 } | |
320 | |
321 public void addAllColumns( | |
322 List<Column> allColumns, | |
323 Range range, | |
324 Filter filter | |
325 ) { | |
326 for (Column column: columns) { | |
327 if ((range == null || column.intersects(range)) | |
328 && (filter == null || filter.accept(column))) { | |
329 allColumns.add(column); | |
330 } | |
331 } | |
332 } | |
333 } // class Fixing | |
334 | |
335 | |
336 protected String riverName; | |
337 protected int riverId; | |
338 protected boolean isKmUp; | |
339 protected List<Fixing> fixings; | |
340 protected Range extent; | |
341 | |
342 public FixingsOverview() { | |
343 fixings = new ArrayList<Fixing>(); | |
344 extent = new Range(Double.MAX_VALUE, -Double.MAX_VALUE); | |
345 } | |
346 | |
347 public FixingsOverview(String riverName) { | |
348 this(); | |
349 this.riverName = riverName; | |
350 } | |
351 | |
352 protected boolean loadRiver(Session session) { | |
353 SQLQuery query = session.createSQLQuery(SQL_RIVER_ID) | |
354 .addScalar("river_id", StandardBasicTypes.INTEGER) | |
355 .addScalar("km_up", StandardBasicTypes.BOOLEAN); | |
356 | |
357 query.setString("name", riverName); | |
358 | |
359 List<Object []> list = query.list(); | |
360 | |
361 if (list.isEmpty()) { | |
362 log.warn("No river '" + riverName + "' found."); | |
363 return false; | |
364 } | |
365 | |
366 Object [] row = list.get(0); | |
367 | |
368 riverId = (Integer)row[0]; | |
369 isKmUp = (Boolean)row[1]; | |
370 | |
371 return true; | |
372 } | |
373 | |
374 protected void loadFixings(Session session) { | |
375 SQLQuery query = session.createSQLQuery(SQL_FIXINGS) | |
376 .addScalar("wst_id", StandardBasicTypes.INTEGER) | |
377 .addScalar("description", StandardBasicTypes.STRING); | |
378 | |
379 query.setInteger("river_id", riverId); | |
380 | |
381 List<Object []> list = query.list(); | |
382 | |
383 if (list.isEmpty()) { | |
384 log.warn("River " + riverId + " has no fixings."); | |
385 // Its pretty fine to have no fixings. | |
386 } | |
387 | |
388 for (Object [] row: list) { | |
389 int wstId = (Integer)row[0]; | |
390 String description = (String) row[1]; | |
391 Fixing fixing = new Fixing(wstId, description); | |
392 fixings.add(fixing); | |
393 } | |
394 } | |
395 | |
396 protected void loadFixingsColumns(Session session) { | |
397 SQLQuery query = session.createSQLQuery(SQL_FIXING_COLUMNS) | |
398 .addScalar("wst_column_id", StandardBasicTypes.INTEGER) | |
399 .addScalar("start_time", StandardBasicTypes.DATE) | |
400 .addScalar("name", StandardBasicTypes.STRING); | |
401 | |
402 for (Fixing fixing: fixings) { | |
403 fixing.loadColumns(query); | |
404 } | |
405 } | |
406 | |
407 protected void loadFixingsColumnsKmRange(Session session) { | |
408 SQLQuery query = session.createSQLQuery(SQL_FIXING_COLUMN_KM_RANGE) | |
409 .addScalar("start_km", StandardBasicTypes.DOUBLE) | |
410 .addScalar("stop_km", StandardBasicTypes.DOUBLE); | |
411 | |
412 for (Fixing fixing: fixings) { | |
413 fixing.loadColumnsKmRange(query); | |
414 } | |
415 } | |
416 | |
417 protected void loadFixingsColumnsQRanges( | |
418 Session session, | |
419 GaugeFinder gaugeFinder | |
420 ) { | |
421 SQLQuery query = session.createSQLQuery(SQL_FIXING_COLUMN_Q_RANGES) | |
422 .addScalar("q", StandardBasicTypes.DOUBLE) | |
423 .addScalar("start_km", StandardBasicTypes.DOUBLE) | |
424 .addScalar("stop_km", StandardBasicTypes.DOUBLE); | |
425 | |
426 for (Fixing fixing: fixings) { | |
427 fixing.loadColumnsQRanges(query, gaugeFinder); | |
428 } | |
429 } | |
430 | |
431 protected void adjustExtent() { | |
432 for (Fixing fixing: fixings) { | |
433 fixing.adjustExtent(extent); | |
434 } | |
435 } | |
436 | |
437 public boolean load(Session session) { | |
438 | |
439 if (!loadRiver(session)) { | |
440 return false; | |
441 } | |
442 | |
443 GaugeFinderFactory gff = GaugeFinderFactory.getInstance(); | |
444 | |
445 GaugeFinder gaugeFinder = gff.getGaugeFinder(riverId, isKmUp); | |
446 | |
447 if (gaugeFinder == null) { | |
448 return false; | |
449 } | |
450 | |
451 loadFixings(session); | |
452 loadFixingsColumns(session); | |
453 loadFixingsColumnsKmRange(session); | |
454 | |
455 adjustExtent(); | |
456 | |
457 loadFixingsColumnsQRanges(session, gaugeFinder); | |
458 | |
459 return true; | |
460 } | |
461 | |
462 public static final Range FULL_EXTENT = | |
463 new Range(-Double.MAX_VALUE, Double.MAX_VALUE); | |
464 | |
465 public static final Fixing.Filter ACCEPT = new Fixing.Filter() { | |
466 @Override | |
467 public boolean accept(Fixing.Column column) { | |
468 return true; | |
469 } | |
470 }; | |
471 | |
472 public static class NotFilter implements Fixing.Filter { | |
473 protected Fixing.Filter child; | |
474 | |
475 public NotFilter(Fixing.Filter child) { | |
476 this.child = child; | |
477 } | |
478 | |
479 @Override | |
480 public boolean accept(Fixing.Column column) { | |
481 return !child.accept(column); | |
482 } | |
483 } // class NotFilter | |
484 | |
485 public static abstract class ComponentFilter implements Fixing.Filter { | |
486 protected List<Fixing.Filter> children; | |
487 | |
488 public ComponentFilter() { | |
489 children = new ArrayList<Fixing.Filter>(); | |
490 } | |
491 | |
492 public ComponentFilter(List<Fixing.Filter> children) { | |
493 this.children = children; | |
494 } | |
495 | |
496 public ComponentFilter add(Fixing.Filter filter) { | |
497 children.add(filter); | |
498 return this; | |
499 } | |
500 } // class ComponentFilter | |
501 | |
502 public static class OrFilter extends ComponentFilter { | |
503 | |
504 public OrFilter() { | |
505 } | |
506 | |
507 public OrFilter(List<Fixing.Filter> children) { | |
508 super(children); | |
509 } | |
510 | |
511 @Override | |
512 public boolean accept(Fixing.Column column) { | |
513 for (Fixing.Filter child: children) { | |
514 if (child.accept(column)) { | |
515 return true; | |
516 } | |
517 } | |
518 return false; | |
519 } | |
520 } // class OrFilter | |
521 | |
522 public static class AndFilter extends ComponentFilter { | |
523 | |
524 public AndFilter() { | |
525 } | |
526 | |
527 public AndFilter(List<Fixing.Filter> children) { | |
528 super(children); | |
529 } | |
530 | |
531 @Override | |
532 public boolean accept(Fixing.Column column) { | |
533 for (Fixing.Filter child: children) { | |
534 if (!child.accept(column)) { | |
535 return false; | |
536 } | |
537 } | |
538 return true; | |
539 } | |
540 } // class AndFilter | |
541 | |
542 public static class IdFilter implements Fixing.Filter { | |
543 | |
544 protected int columnId; | |
545 | |
546 public IdFilter(int columnId) { | |
547 this.columnId = columnId; | |
548 } | |
549 | |
550 @Override | |
551 public boolean accept(Fixing.Column column) { | |
552 return column.getId() == columnId; | |
553 } | |
554 } // class IdFilter | |
555 | |
556 public static class IdsFilter implements Fixing.Filter { | |
557 | |
558 protected int [] columnIds; | |
559 | |
560 public IdsFilter(int [] columnIds) { | |
561 this.columnIds = columnIds; | |
562 } | |
563 | |
564 @Override | |
565 public boolean accept(Fixing.Column column) { | |
566 int cid = column.getId(); | |
567 for (int i = columnIds.length-1; i >= 0; --i) { | |
568 if (columnIds[i] == cid) { | |
569 return true; | |
570 } | |
571 } | |
572 return false; | |
573 } | |
574 } // class IdFilter | |
575 | |
576 public static class DateFilter implements Fixing.Filter { | |
577 | |
578 protected Date date; | |
579 | |
580 public DateFilter(Date date) { | |
581 this.date = date; | |
582 } | |
583 | |
584 @Override | |
585 public boolean accept(Fixing.Column column) { | |
586 return date.equals(column.getStartTime()); | |
587 } | |
588 } // class DateFilter | |
589 | |
590 public static class DateRangeFilter implements Fixing.Filter { | |
591 | |
592 protected Date start; | |
593 protected Date end; | |
594 | |
595 public DateRangeFilter(Date start, Date end) { | |
596 this.start = start; | |
597 this.end = end; | |
598 } | |
599 | |
600 @Override | |
601 public boolean accept(Fixing.Column column) { | |
602 Date date = column.getStartTime(); | |
603 return start.compareTo(date) <= 0 && end.compareTo(date) >= 0; | |
604 } | |
605 } // class DateRangeFilter | |
606 | |
607 public static class SectorFilter implements Fixing.Filter { | |
608 | |
609 protected int sector; | |
610 | |
611 public SectorFilter(int sector) { | |
612 this.sector = sector; | |
613 } | |
614 | |
615 @Override | |
616 public boolean accept(Fixing.Column column) { | |
617 for (SectorRange s: column.getSectors()) { | |
618 if (s.getSector() == sector) { | |
619 return true; | |
620 } | |
621 } | |
622 return false; | |
623 } | |
624 } // class SectorFilter | |
625 | |
626 public static class SectorRangeFilter implements Fixing.Filter { | |
627 | |
628 protected int min; | |
629 protected int max; | |
630 | |
631 public SectorRangeFilter(int min, int max) { | |
632 this.min = Math.min(min, max); | |
633 this.max = Math.max(min, max); | |
634 } | |
635 | |
636 @Override | |
637 public boolean accept(Fixing.Column column) { | |
638 for (SectorRange s: column.getSectors()) { | |
639 int v = s.getSector(); | |
640 if (v >= min && v <= max) { | |
641 return true; | |
642 } | |
643 } | |
644 return false; | |
645 } | |
646 } // class SectorRangeFilter | |
647 | |
648 public static class KmFilter implements Fixing.Filter { | |
649 | |
650 protected double km; | |
651 | |
652 public KmFilter(double km) { | |
653 this.km = km; | |
654 } | |
655 | |
656 @Override | |
657 public boolean accept(Fixing.Column column) { | |
658 for (SectorRange s: column.getSectors()) { | |
659 if (s.inside(km)) { | |
660 return true; | |
661 } | |
662 } | |
663 return false; | |
664 } | |
665 } // class KmFilter | |
666 | |
667 public void generateOverview(Document document) { | |
668 generateOverview(document, FULL_EXTENT, ACCEPT); | |
669 } | |
670 | |
671 public List<Fixing.Column> filter(Range range, Fixing.Filter filter) { | |
672 List<Fixing.Column> allColumns = new ArrayList<Fixing.Column>(); | |
673 | |
674 for (Fixing fixing: fixings) { | |
675 fixing.addAllColumns(allColumns, range, filter); | |
676 } | |
677 | |
678 Collections.sort(allColumns, Fixing.DATE_CMP); | |
679 | |
680 return allColumns; | |
681 } | |
682 | |
683 public void generateOverview( | |
684 Document document, | |
685 Range range, | |
686 Fixing.Filter filter | |
687 ) { | |
688 List<Fixing.Column> allColumns = filter(range, filter); | |
689 | |
690 Element fixingsElement = document.createElement("fixings"); | |
691 | |
692 Element riverElement = document.createElement("river"); | |
693 | |
694 riverElement.setAttribute("from", String.valueOf(extent.start)); | |
695 riverElement.setAttribute("to", String.valueOf(extent.end)); | |
696 riverElement.setAttribute("rid", String.valueOf(riverId)); | |
697 riverElement.setAttribute("name", riverName); | |
698 | |
699 fixingsElement.appendChild(riverElement); | |
700 | |
701 SimpleDateFormat df = new SimpleDateFormat(DATE_FORMAT); | |
702 | |
703 Element esE = document.createElement("events"); | |
704 | |
705 for (Fixing.Column column: allColumns) { | |
706 | |
707 List<SectorRange> sectors = column.getSectors(range); | |
708 | |
709 if (!sectors.isEmpty()) { | |
710 Element eE = document.createElement("event"); | |
711 eE.setAttribute("description", | |
712 String.valueOf(column.getDescription())); | |
713 eE.setAttribute("cid", String.valueOf(column.columnId)); | |
714 eE.setAttribute("date", df.format(column.startTime)); | |
715 | |
716 for (SectorRange sector: sectors) { | |
717 Element sE = document.createElement("sector"); | |
718 | |
719 sE.setAttribute("from", String.valueOf(sector.start)); | |
720 sE.setAttribute("to", String.valueOf(sector.end)); | |
721 sE.setAttribute("class", String.valueOf(sector.sector)); | |
722 | |
723 eE.appendChild(sE); | |
724 } | |
725 | |
726 esE.appendChild(eE); | |
727 } | |
728 } | |
729 | |
730 fixingsElement.appendChild(esE); | |
731 | |
732 document.appendChild(fixingsElement); | |
733 } | |
734 } | |
735 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : |