comparison flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/FixingsOverview.java @ 3786:4adc35aa655c

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

http://dive4elements.wald.intevation.org