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

http://dive4elements.wald.intevation.org