comparison flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java @ 3812:f788d2d901d6

merged flys-artifacts/pre2.6-2011-12-05
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:14:53 +0200
parents 4ae9c92feb8c
children e384d78ff78b
comparison
equal deleted inserted replaced
3808:5fab0fe3c445 3812:f788d2d901d6
1 package de.intevation.flys.artifacts.charts;
2
3 import de.intevation.flys.backend.SessionFactoryProvider;
4
5 import de.intevation.flys.geom.Lines;
6
7 import de.intevation.flys.model.CrossSection;
8 import de.intevation.flys.model.CrossSectionLine;
9 import de.intevation.flys.model.CrossSectionPoint;
10
11 import de.intevation.flys.utils.Pair;
12
13 import de.intevation.flys.jfree.StableXYDifferenceRenderer;
14
15 import java.awt.BorderLayout;
16 import java.awt.Dimension;
17 import java.awt.FlowLayout;
18
19 import java.awt.event.ActionEvent;
20 import java.awt.event.ActionListener;
21 import java.awt.event.ItemEvent;
22 import java.awt.event.ItemListener;
23
24 import java.awt.geom.Point2D;
25 import java.awt.geom.Rectangle2D;
26
27 import java.io.File;
28 import java.io.FileWriter;
29 import java.io.IOException;
30 import java.io.PrintWriter;
31
32 import java.math.MathContext;
33
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.TreeMap;
39
40 import java.awt.Color;
41 import java.awt.Paint;
42 import java.awt.TexturePaint;
43
44 import java.awt.image.BufferedImage;
45
46 import javax.swing.DefaultComboBoxModel;
47 import javax.swing.JButton;
48 import javax.swing.JComboBox;
49 import javax.swing.JPanel;
50 import javax.swing.JScrollPane;
51 import javax.swing.JTable;
52 import javax.swing.JTextField;
53
54 import javax.swing.event.TableModelEvent;
55 import javax.swing.event.TableModelListener;
56
57 import javax.swing.table.AbstractTableModel;
58
59 import org.hibernate.Query;
60 import org.hibernate.Session;
61
62 import org.jfree.chart.ChartFactory;
63 import org.jfree.chart.ChartPanel;
64 import org.jfree.chart.ChartUtilities;
65 import org.jfree.chart.JFreeChart;
66
67 import org.jfree.chart.axis.NumberAxis;
68
69 import org.jfree.chart.plot.PlotOrientation;
70 import org.jfree.chart.plot.XYPlot;
71
72 import org.jfree.data.xy.DefaultXYDataset;
73 import org.jfree.data.xy.XYSeries;
74 import org.jfree.data.xy.XYDataset;
75 import org.jfree.data.xy.XYSeriesCollection;
76 import org.jfree.chart.renderer.xy.XYItemRenderer;
77
78 import org.jfree.ui.ApplicationFrame;
79 import org.jfree.ui.RefineryUtilities;
80
81 public class CrossSectionApp
82 extends ApplicationFrame
83 {
84 public static final String RIVER = System.getProperty("river", "Saar");
85
86 public static final String WATER_LEVEL = System.getProperty("waterlevel");
87
88 public static final String KM = System.getProperty("km");
89
90 public static final double EPSILON = 1e-4;
91
92 protected Session session;
93
94 protected JComboBox crossSectionLinesCB;
95 protected JTextField waterlevelTF;
96
97 protected ChartPanel chartPanel;
98
99 protected Double lastWaterLevel;
100
101 protected List<CrossSection> crossSections;
102 protected boolean [] drawCrossSection;
103 protected boolean [] drawWaterLevel;
104 protected boolean [] drawGround;
105 protected boolean [] drawFill;
106
107 protected Map<Double, List<Pair<CrossSection, CrossSectionLine>>> km2lines;
108
109 protected static final Paint TRANSPARENT = createTransparentPaint();
110
111 public class CrossSectionTableModel extends AbstractTableModel {
112
113 @Override
114 public String getColumnName(int col) {
115 switch (col) {
116 case 0: return "Peilungsname";
117 case 1: return "Peilung";
118 case 2: return "Wasserstand";
119 case 3: return "Boden";
120 case 4: return "Wasser";
121 }
122 return "";
123 }
124
125 @Override
126 public int getColumnCount() {
127 return 5;
128 }
129
130 @Override
131 public int getRowCount() {
132 return crossSections != null ? crossSections.size() : 0;
133 }
134
135 @Override
136 public Object getValueAt(int row, int col) {
137 if (crossSections == null) return null;
138 switch (col) {
139 case 0: return crossSections.get(row).getDescription();
140 case 1: return drawCrossSection[row];
141 case 2: return drawWaterLevel[row];
142 case 3: return drawGround[row];
143 case 4: return drawFill[row];
144 }
145 return null;
146 }
147
148 @Override
149 public void setValueAt(Object value, int row, int col) {
150 switch (col) {
151 case 1:
152 if (change(drawCrossSection, row, (Boolean)value)) {
153 fireTableCellUpdated(row, col);
154 }
155 break;
156 case 2:
157 if (change(drawWaterLevel, row, (Boolean)value)) {
158 fireTableCellUpdated(row, col);
159 }
160 break;
161 case 3:
162 if (change(drawGround, row, (Boolean)value)) {
163 fireTableCellUpdated(row, col);
164 }
165 break;
166 case 4:
167 if (change(drawFill, row, (Boolean)value)) {
168 fireTableCellUpdated(row, col);
169 }
170 break;
171 }
172 }
173
174 @Override
175 public Class<?> getColumnClass(int columnIndex) {
176 switch (columnIndex) {
177 case 0: return String.class;
178 case 1:
179 case 2:
180 case 3:
181 case 4: return Boolean.class;
182 }
183 return null;
184 }
185
186 @Override
187 public boolean isCellEditable(
188 int rowIndex,
189 int columnIndex
190 ) {
191 return columnIndex >= 1 && columnIndex <= 4;
192 }
193 } // class CrossSectionTableModel
194
195 private static boolean change(
196 boolean [] values,
197 int index,
198 boolean value
199 ) {
200 if (values[index] != value) {
201 values[index] = value;
202 return true;
203 }
204 return false;
205 }
206
207 public static class CrossSectionLineItem {
208
209 Double km;
210 List<Pair<CrossSection, CrossSectionLine>> lines;
211
212 public CrossSectionLineItem(
213 Double km,
214 List<Pair<CrossSection, CrossSectionLine>> lines
215 ) {
216 this.km = km;
217 this.lines = lines;
218 }
219
220 public String toString() {
221 return String.valueOf(km);
222 }
223 } // CrossSectionLineItem
224
225 public CrossSectionApp(String title) {
226 super(title);
227
228 session = SessionFactoryProvider
229 .createSessionFactory()
230 .openSession();
231
232 JPanel content = createContent();
233 content.setPreferredSize(new Dimension(800, 480));
234 setContentPane(content);
235
236 }
237
238 public List<CrossSection> crossSections(String river) {
239 Query query = session.createQuery(
240 "from CrossSection where river.name = :river");
241 query.setParameter("river", river);
242 return query.list();
243 }
244
245 protected Map<Double, List<Pair<CrossSection, CrossSectionLine>>>
246 loadAllLines(List<CrossSection> crossSections) {
247 Map<Double, List<Pair<CrossSection, CrossSectionLine>>> km2lines =
248 new TreeMap<Double, List<Pair<CrossSection, CrossSectionLine>>>();
249 for (CrossSection cs: crossSections) {
250 List<CrossSectionLine> lines = cs.getLines();
251 for (CrossSectionLine csl: lines) {
252 Double km = Math.round(csl.getKm().doubleValue() * 1000d)/1000d;
253 List<Pair<CrossSection, CrossSectionLine>> ls
254 = km2lines.get(km);
255 if (ls == null) {
256 ls = new ArrayList<Pair<CrossSection, CrossSectionLine>>(2);
257 km2lines.put(km, ls);
258 }
259 ls.add(new Pair<CrossSection, CrossSectionLine>(cs, csl));
260 }
261 }
262 return km2lines;
263 }
264
265 public JPanel createContent() {
266 JPanel panel = new JPanel(new BorderLayout());
267
268
269 JPanel nav = new JPanel(new FlowLayout());
270
271 crossSections = crossSections(RIVER);
272 km2lines = loadAllLines(crossSections);
273
274 int CS = crossSections.size();
275 Arrays.fill(drawCrossSection = new boolean[CS], true);
276 drawWaterLevel = new boolean[CS];
277 drawGround = new boolean[CS];
278 drawFill = new boolean[CS];
279
280 Object [] clis = createCrossSectionLineItems(km2lines);
281
282 DefaultComboBoxModel dcbm = new DefaultComboBoxModel(clis);
283
284 crossSectionLinesCB = new JComboBox(dcbm);
285
286 if (KM != null) {
287 try {
288 double km = Double.parseDouble(KM);
289
290 CrossSectionLineItem found = null;
291
292 for (Object o: clis) {
293 CrossSectionLineItem csli = (CrossSectionLineItem)o;
294 if (Math.abs(csli.km - km) < EPSILON) {
295 found = csli;
296 break;
297 }
298 }
299
300 if (found != null) {
301 crossSectionLinesCB.setSelectedItem(found);
302 }
303 }
304 catch (NumberFormatException nfe) {
305 System.err.println("km is not a number: "
306 + nfe.getMessage());
307 }
308 }
309
310 nav.add(crossSectionLinesCB);
311
312 crossSectionLinesCB.addItemListener(new ItemListener() {
313 @Override
314 public void itemStateChanged(ItemEvent ie) {
315 if (ie.getStateChange() == ItemEvent.SELECTED) {
316 updateChart();
317 }
318 }
319 });
320
321 waterlevelTF = new JTextField(5);
322
323 if (WATER_LEVEL != null) {
324 try {
325 waterlevelTF.setText(
326 (lastWaterLevel = Double.valueOf(WATER_LEVEL)).toString());
327 }
328 catch (NumberFormatException nfe) {
329 System.err.println("Water level not a number: " +
330 nfe.getMessage());
331 }
332 }
333
334 waterlevelTF.addActionListener(new ActionListener() {
335 @Override
336 public void actionPerformed(ActionEvent ae) {
337 waterLevelChanged();
338 }
339 });
340
341 nav.add(waterlevelTF);
342
343 JButton dump = new JButton("dump");
344
345 dump.addActionListener(new ActionListener() {
346 @Override
347 public void actionPerformed(ActionEvent ae) {
348 dumpData();
349 }
350 });
351
352 nav.add(dump);
353
354
355 chartPanel = createChartPanel();
356
357 panel.add(chartPanel, BorderLayout.CENTER);
358
359
360 CrossSectionTableModel cstm = new CrossSectionTableModel();
361
362 cstm.addTableModelListener(new TableModelListener() {
363 @Override
364 public void tableChanged(TableModelEvent e) {
365 updateChart();
366 }
367 });
368
369 JTable crossTable = new JTable(cstm);
370
371 JPanel west = new JPanel(new BorderLayout());
372 JScrollPane scrollPane = new JScrollPane(crossTable);
373 west.add(scrollPane);
374
375 west.add(nav, BorderLayout.SOUTH);
376
377 panel.add(west, BorderLayout.WEST);
378
379 return panel;
380 }
381
382 protected void waterLevelChanged() {
383 String value = waterlevelTF.getText();
384 try {
385 lastWaterLevel = Double.valueOf(value);
386 }
387 catch (NumberFormatException nfe) {
388 waterlevelTF.setText(
389 lastWaterLevel != null ? lastWaterLevel.toString() : "");
390 return;
391 }
392 updateChart();
393 }
394
395 protected void updateChart() {
396
397 CrossSectionLineItem csli =
398 (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem();
399
400 JFreeChart chart = createChart();
401
402 chartPanel.setChart(chart);
403 }
404
405 protected ChartPanel createChartPanel() {
406 CrossSectionLineItem csli =
407 (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem();
408
409 JFreeChart chart = createChart();
410
411 return new ChartPanel(chart);
412 }
413
414 protected void dumpData() {
415
416 CrossSectionLineItem csli =
417 (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem();
418
419 if (csli == null) {
420 return;
421 }
422
423
424 double km = Math.round(csli.km.doubleValue() * 1000d)/1000d;
425
426 String kmS = String.valueOf(km).replace(".", "-");
427
428 int i = 1;
429 File file = new File("cross-section-" + kmS + ".txt");
430 while (file.exists()) {
431 file = new File("cross-section-" + kmS + "[" + (i++) + "].txt");
432 }
433
434 System.err.println("dump points to file '" + file + "'");
435
436 PrintWriter out = null;
437
438 MathContext mc = new MathContext(3);
439
440 try {
441 out =
442 new PrintWriter(
443 new FileWriter(file));
444
445 for (Pair<CrossSection, CrossSectionLine> pair: csli.lines) {
446 out.println("# " + pair.getA().getDescription());
447 for (CrossSectionPoint point: pair.getB().getPoints()) {
448 out.println(
449 point.getX().round(mc) + " " +
450 point.getY().round(mc));
451 }
452 }
453
454 out.flush();
455 }
456 catch (IOException ioe) {
457 ioe.printStackTrace();
458 }
459 finally {
460 if (out != null) {
461 out.close();
462 }
463 }
464 }
465
466 public void generateWaterLevels(
467 List<Point2D> points,
468 List<Pair<XYDataset, XYItemRenderer>> datasets
469 ) {
470 if (points == null || points.isEmpty() || lastWaterLevel == null) {
471 return;
472 }
473
474 double [][] data = Lines.createWaterLines(points, lastWaterLevel);
475 XYSeries series =
476 new XYSeries(String.valueOf(lastWaterLevel), false);
477
478 double [] x = data[0];
479 double [] y = data[1];
480 for (int i = 0; i < x.length; ++i) {
481 series.add(x[i], y[i], false);
482 }
483
484 datasets.add(new Pair<XYDataset, XYItemRenderer>(
485 new XYSeriesCollection(series), null));
486 }
487
488 public void generateFill(
489 List<Point2D> points,
490 String legend,
491 List<Pair<XYDataset, XYItemRenderer>> datasets
492 ) {
493 if (points == null || points.isEmpty() || lastWaterLevel == null) {
494 return;
495 }
496
497 double [][] data = Lines.createWaterLines(points, lastWaterLevel);
498 double [][] values = CrossSectionLine.fetchCrossSectionProfile(points);
499
500 DefaultXYDataset dataset = new DefaultXYDataset();
501
502 dataset.addSeries(legend + "-Linie", values);
503 dataset.addSeries(legend + "-Fl\u00e4che", data);
504
505 datasets.add(new Pair<XYDataset, XYItemRenderer>(
506 dataset,
507 new StableXYDifferenceRenderer(
508 TRANSPARENT, Color.blue, false)));
509 }
510
511 public void generateProfile(
512 List<Point2D> points,
513 String legend,
514 List<Pair<XYDataset, XYItemRenderer>> datasets
515 ) {
516 if (points == null || points.isEmpty()) {
517 return;
518 }
519
520 double [][] values = CrossSectionLine.fetchCrossSectionProfile(points);
521
522 XYSeries series = new XYSeries(legend, false);
523
524 double [] x = values[0];
525 double [] y = values[1];
526 for (int i = 0; i < x.length; ++i) {
527 series.add(x[i], y[i], false);
528 }
529
530 datasets.add(new Pair<XYDataset, XYItemRenderer>(
531 new XYSeriesCollection(series), null));;
532 }
533
534 public void generateGround(
535 List<Point2D> points,
536 String legend,
537 List<Pair<XYDataset, XYItemRenderer>> datasets
538 ) {
539 if (points == null || points.isEmpty()) {
540 return;
541 }
542
543 double [][] values = CrossSectionLine.fetchCrossSectionProfile(points);
544
545 DefaultXYDataset dataset = new DefaultXYDataset();
546
547 XYSeries series = new XYSeries(legend, false);
548
549 dataset.addSeries(legend, values);
550
551 StableXYDifferenceRenderer renderer =
552 new StableXYDifferenceRenderer();
553
554 datasets.add(new Pair<XYDataset, XYItemRenderer>(
555 dataset, renderer));
556 }
557
558 public List<Pair<XYDataset, XYItemRenderer>> generateDatasets() {
559
560 List<Pair<XYDataset, XYItemRenderer>> datasets =
561 new ArrayList<Pair<XYDataset, XYItemRenderer>>();
562
563 CrossSectionLineItem csli =
564 (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem();
565
566 for (int i = 0; i < drawCrossSection.length; ++i) {
567 List<Point2D> points = null;
568 CrossSection cs = crossSections.get(i);
569
570 if (drawGround[i]) {
571 for (Pair<CrossSection, CrossSectionLine> csl: csli.lines) {
572 if (csl.getA() == cs) {
573 if (points == null) {
574 points = csl.getB().fetchCrossSectionLinesPoints();
575 }
576 generateGround(
577 points,
578 cs.getDescription() + "/Boden",
579 datasets);
580 break;
581 }
582 }
583 }
584
585 if (drawFill[i]) {
586 for (Pair<CrossSection, CrossSectionLine> csl: csli.lines) {
587 if (csl.getA() == cs) {
588 if (points == null) {
589 points = csl.getB().fetchCrossSectionLinesPoints();
590 }
591
592 generateFill(
593 points, cs.getDescription(), datasets);
594 break;
595 }
596 }
597 }
598
599 if (drawCrossSection[i]) {
600 for (Pair<CrossSection, CrossSectionLine> csl: csli.lines) {
601 if (csl.getA() == cs) {
602 if (points == null) {
603 points = csl.getB().fetchCrossSectionLinesPoints();
604 }
605
606 generateProfile(
607 points, cs.getDescription(), datasets);
608 break;
609 }
610 }
611 }
612
613 if (drawWaterLevel[i]) {
614 for (Pair<CrossSection, CrossSectionLine> csl: csli.lines) {
615 if (csl.getA() == cs) {
616 if (points == null) {
617 points = csl.getB().fetchCrossSectionLinesPoints();
618 }
619 generateWaterLevels(points, datasets);
620 break;
621 }
622 }
623 }
624
625 }
626
627 return datasets;
628 }
629
630 protected Object [] createCrossSectionLineItems(
631 Map<Double, List<Pair<CrossSection, CrossSectionLine>>> km2lines
632 ) {
633 Object [] result = new Object[km2lines.size()];
634 int i = 0;
635 for (Map.Entry<Double, List<Pair<CrossSection, CrossSectionLine>>> entry:
636 km2lines.entrySet()) {
637 result[i++] = new CrossSectionLineItem(
638 entry.getKey(),
639 entry.getValue());
640 }
641 return result;
642 }
643
644
645 public JFreeChart createChart() {
646 JFreeChart chart = ChartFactory.createXYLineChart(
647 null,
648 "Abstand [m]",
649 "H\u00f6he [m]",
650 null,
651 PlotOrientation.VERTICAL,
652 true,
653 true,
654 false);
655
656 List<Pair<XYDataset, XYItemRenderer>> datasets =
657 generateDatasets();
658
659 XYPlot plot = chart.getXYPlot();
660
661 for (int i = 0, N = datasets.size(); i < N; ++i) {
662 Pair<XYDataset, XYItemRenderer> p = datasets.get(i);
663 plot.setDataset(i, p.getA());
664 plot.mapDatasetToRangeAxis(i, 0);
665 XYItemRenderer renderer = p.getB();
666 if (renderer != null) {
667 plot.setRenderer(i, renderer);
668 }
669 }
670
671 NumberAxis yAxis = (NumberAxis)plot.getRangeAxis();
672 yAxis.setAutoRangeIncludesZero(false);
673
674 ChartUtilities.applyCurrentTheme(chart);
675 return chart;
676 }
677
678 protected static Paint createTransparentPaint() {
679 BufferedImage texture = new BufferedImage(
680 1, 1, BufferedImage.TYPE_4BYTE_ABGR);
681
682 return new TexturePaint(
683 texture, new Rectangle2D.Double(0d, 0d, 0d, 0d));
684 }
685
686 public static void main(String [] args) {
687 CrossSectionApp csa = new CrossSectionApp("Querprofile");
688 csa.pack();
689 RefineryUtilities.centerFrameOnScreen(csa);
690 csa.setVisible(true);
691 }
692 }
693 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org