comparison flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java @ 3814:8083f6384023

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

http://dive4elements.wald.intevation.org