Mercurial > dive4elements > river
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java Fri Sep 28 12:14:35 2012 +0200 @@ -0,0 +1,701 @@ +package de.intevation.flys.artifacts.charts; + +import de.intevation.flys.backend.SessionFactoryProvider; + +import de.intevation.flys.artifacts.geom.Lines; + +import de.intevation.flys.model.CrossSection; +import de.intevation.flys.model.CrossSectionLine; +import de.intevation.flys.model.CrossSectionPoint; + +import de.intevation.flys.utils.Pair; + +import de.intevation.flys.jfree.StableXYDifferenceRenderer; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; + +import java.math.BigDecimal; +import java.math.MathContext; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import java.awt.Color; +import java.awt.Paint; +import java.awt.TexturePaint; + +import java.awt.image.BufferedImage; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JTextField; + +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; + +import javax.swing.table.AbstractTableModel; + +import org.hibernate.Query; +import org.hibernate.Session; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.ChartUtilities; +import org.jfree.chart.JFreeChart; + +import org.jfree.chart.axis.NumberAxis; + +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; + +import org.jfree.data.xy.DefaultXYDataset; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYDataset; +import org.jfree.data.xy.XYSeriesCollection; +import org.jfree.chart.renderer.xy.XYItemRenderer; + +import org.jfree.ui.ApplicationFrame; +import org.jfree.ui.RefineryUtilities; + +/** + * Standalone tech-demo. + */ +public class CrossSectionApp +extends ApplicationFrame +{ + public static final String RIVER = System.getProperty("river", "Saar"); + + public static final String WATER_LEVEL = System.getProperty("waterlevel"); + + public static final String KM = System.getProperty("km"); + + public static final double EPSILON = 1e-4; + + protected Session session; + + protected JComboBox crossSectionLinesCB; + protected JTextField waterlevelTF; + + protected ChartPanel chartPanel; + + protected Double lastWaterLevel; + + protected List<CrossSection> crossSections; + protected boolean [] drawCrossSection; + protected boolean [] drawWaterLevel; + protected boolean [] drawGround; + protected boolean [] drawFill; + + protected Map<Double, List<Pair<CrossSection, CrossSectionLine>>> km2lines; + + protected static final Paint TRANSPARENT = createTransparentPaint(); + + public class CrossSectionTableModel extends AbstractTableModel { + + @Override + public String getColumnName(int col) { + switch (col) { + case 0: return "Peilungsname"; + case 1: return "Peilung"; + case 2: return "Wasserstand"; + case 3: return "Boden"; + case 4: return "Wasser"; + } + return ""; + } + + @Override + public int getColumnCount() { + return 5; + } + + @Override + public int getRowCount() { + return crossSections != null ? crossSections.size() : 0; + } + + @Override + public Object getValueAt(int row, int col) { + if (crossSections == null) return null; + switch (col) { + case 0: return crossSections.get(row).getDescription(); + case 1: return drawCrossSection[row]; + case 2: return drawWaterLevel[row]; + case 3: return drawGround[row]; + case 4: return drawFill[row]; + } + return null; + } + + @Override + public void setValueAt(Object value, int row, int col) { + switch (col) { + case 1: + if (change(drawCrossSection, row, (Boolean)value)) { + fireTableCellUpdated(row, col); + } + break; + case 2: + if (change(drawWaterLevel, row, (Boolean)value)) { + fireTableCellUpdated(row, col); + } + break; + case 3: + if (change(drawGround, row, (Boolean)value)) { + fireTableCellUpdated(row, col); + } + break; + case 4: + if (change(drawFill, row, (Boolean)value)) { + fireTableCellUpdated(row, col); + } + break; + } + } + + @Override + public Class<?> getColumnClass(int columnIndex) { + switch (columnIndex) { + case 0: return String.class; + case 1: + case 2: + case 3: + case 4: return Boolean.class; + } + return null; + } + + @Override + public boolean isCellEditable( + int rowIndex, + int columnIndex + ) { + return columnIndex >= 1 && columnIndex <= 4; + } + } // class CrossSectionTableModel + + private static boolean change( + boolean [] values, + int index, + boolean value + ) { + if (values[index] != value) { + values[index] = value; + return true; + } + return false; + } + + public static class CrossSectionLineItem { + + Double km; + List<Pair<CrossSection, CrossSectionLine>> lines; + + public CrossSectionLineItem( + Double km, + List<Pair<CrossSection, CrossSectionLine>> lines + ) { + this.km = km; + this.lines = lines; + } + + public String toString() { + return String.valueOf(km); + } + } // CrossSectionLineItem + + public CrossSectionApp(String title) { + super(title); + + session = SessionFactoryProvider + .createSessionFactory() + .openSession(); + + JPanel content = createContent(); + content.setPreferredSize(new Dimension(800, 480)); + setContentPane(content); + + } + + public List<CrossSection> crossSections(String river) { + Query query = session.createQuery( + "from CrossSection where river.name = :river"); + query.setParameter("river", river); + return query.list(); + } + + protected Map<Double, List<Pair<CrossSection, CrossSectionLine>>> + loadAllLines(List<CrossSection> crossSections) { + Map<Double, List<Pair<CrossSection, CrossSectionLine>>> km2lines = + new TreeMap<Double, List<Pair<CrossSection, CrossSectionLine>>>(); + for (CrossSection cs: crossSections) { + List<CrossSectionLine> lines = cs.getLines(); + for (CrossSectionLine csl: lines) { + Double km = Math.round(csl.getKm().doubleValue() * 1000d)/1000d; + List<Pair<CrossSection, CrossSectionLine>> ls + = km2lines.get(km); + if (ls == null) { + ls = new ArrayList<Pair<CrossSection, CrossSectionLine>>(2); + km2lines.put(km, ls); + } + ls.add(new Pair<CrossSection, CrossSectionLine>(cs, csl)); + } + } + return km2lines; + } + + public JPanel createContent() { + JPanel panel = new JPanel(new BorderLayout()); + + + JPanel nav = new JPanel(new FlowLayout()); + + crossSections = crossSections(RIVER); + km2lines = loadAllLines(crossSections); + + int CS = crossSections.size(); + Arrays.fill(drawCrossSection = new boolean[CS], true); + drawWaterLevel = new boolean[CS]; + drawGround = new boolean[CS]; + drawFill = new boolean[CS]; + + Object [] clis = createCrossSectionLineItems(km2lines); + + DefaultComboBoxModel dcbm = new DefaultComboBoxModel(clis); + + crossSectionLinesCB = new JComboBox(dcbm); + + if (KM != null) { + try { + double km = Double.parseDouble(KM); + + CrossSectionLineItem found = null; + + for (Object o: clis) { + CrossSectionLineItem csli = (CrossSectionLineItem)o; + if (Math.abs(csli.km - km) < EPSILON) { + found = csli; + break; + } + } + + if (found != null) { + crossSectionLinesCB.setSelectedItem(found); + } + } + catch (NumberFormatException nfe) { + System.err.println("km is not a number: " + + nfe.getMessage()); + } + } + + nav.add(crossSectionLinesCB); + + crossSectionLinesCB.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent ie) { + if (ie.getStateChange() == ItemEvent.SELECTED) { + updateChart(); + } + } + }); + + waterlevelTF = new JTextField(5); + + if (WATER_LEVEL != null) { + try { + waterlevelTF.setText( + (lastWaterLevel = Double.valueOf(WATER_LEVEL)).toString()); + } + catch (NumberFormatException nfe) { + System.err.println("Water level not a number: " + + nfe.getMessage()); + } + } + + waterlevelTF.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent ae) { + waterLevelChanged(); + } + }); + + nav.add(waterlevelTF); + + JButton dump = new JButton("dump"); + + dump.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent ae) { + dumpData(); + } + }); + + nav.add(dump); + + + chartPanel = createChartPanel(); + + panel.add(chartPanel, BorderLayout.CENTER); + + + CrossSectionTableModel cstm = new CrossSectionTableModel(); + + cstm.addTableModelListener(new TableModelListener() { + @Override + public void tableChanged(TableModelEvent e) { + updateChart(); + } + }); + + JTable crossTable = new JTable(cstm); + + JPanel west = new JPanel(new BorderLayout()); + JScrollPane scrollPane = new JScrollPane(crossTable); + west.add(scrollPane); + + west.add(nav, BorderLayout.SOUTH); + + panel.add(west, BorderLayout.WEST); + + return panel; + } + + protected void waterLevelChanged() { + String value = waterlevelTF.getText(); + try { + lastWaterLevel = Double.valueOf(value); + } + catch (NumberFormatException nfe) { + waterlevelTF.setText( + lastWaterLevel != null ? lastWaterLevel.toString() : ""); + return; + } + updateChart(); + } + + protected void updateChart() { + + CrossSectionLineItem csli = + (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem(); + + JFreeChart chart = createChart(); + + chartPanel.setChart(chart); + } + + protected ChartPanel createChartPanel() { + CrossSectionLineItem csli = + (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem(); + + JFreeChart chart = createChart(); + + return new ChartPanel(chart); + } + + protected void dumpData() { + + CrossSectionLineItem csli = + (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem(); + + if (csli == null) { + return; + } + + + double km = Math.round(csli.km.doubleValue() * 1000d)/1000d; + + String kmS = String.valueOf(km).replace(".", "-"); + + int i = 1; + File file = new File("cross-section-" + kmS + ".txt"); + while (file.exists()) { + file = new File("cross-section-" + kmS + "[" + (i++) + "].txt"); + } + + System.err.println("dump points to file '" + file + "'"); + + PrintWriter out = null; + + MathContext mc = new MathContext(3); + + try { + out = + new PrintWriter( + new FileWriter(file)); + + for (Pair<CrossSection, CrossSectionLine> pair: csli.lines) { + out.println("# " + pair.getA().getDescription()); + for (CrossSectionPoint point: pair.getB().getPoints()) { + out.println( + new BigDecimal(point.getX()).round(mc) + " " + + new BigDecimal(point.getY()).round(mc)); + } + } + + out.flush(); + } + catch (IOException ioe) { + ioe.printStackTrace(); + } + finally { + if (out != null) { + out.close(); + } + } + } + + public void generateWaterLevels( + List<Point2D> points, + List<Pair<XYDataset, XYItemRenderer>> datasets + ) { + if (points == null || points.isEmpty() || lastWaterLevel == null) { + return; + } + + double [][] data = Lines.createWaterLines(points, lastWaterLevel).points; + XYSeries series = + new XYSeries(String.valueOf(lastWaterLevel), false); + + double [] x = data[0]; + double [] y = data[1]; + for (int i = 0; i < x.length; ++i) { + series.add(x[i], y[i], false); + } + + datasets.add(new Pair<XYDataset, XYItemRenderer>( + new XYSeriesCollection(series), null)); + } + + public void generateFill( + List<Point2D> points, + String legend, + List<Pair<XYDataset, XYItemRenderer>> datasets + ) { + if (points == null || points.isEmpty() || lastWaterLevel == null) { + return; + } + + double [][] data = Lines.createWaterLines(points, lastWaterLevel).points; + double [][] values = CrossSectionLine.fetchCrossSectionProfile(points); + + DefaultXYDataset dataset = new DefaultXYDataset(); + + dataset.addSeries(legend + "-Linie", values); + dataset.addSeries(legend + "-Fl\u00e4che", data); + + datasets.add(new Pair<XYDataset, XYItemRenderer>( + dataset, + new StableXYDifferenceRenderer( + TRANSPARENT, Color.blue, false))); + } + + public void generateProfile( + List<Point2D> points, + String legend, + List<Pair<XYDataset, XYItemRenderer>> datasets + ) { + if (points == null || points.isEmpty()) { + return; + } + + double [][] values = CrossSectionLine.fetchCrossSectionProfile(points); + + XYSeries series = new XYSeries(legend, false); + + double [] x = values[0]; + double [] y = values[1]; + for (int i = 0; i < x.length; ++i) { + series.add(x[i], y[i], false); + } + + datasets.add(new Pair<XYDataset, XYItemRenderer>( + new XYSeriesCollection(series), null));; + } + + + /** + * @param legend the legend entry. + */ + public void generateGround( + List<Point2D> points, + String legend, + List<Pair<XYDataset, XYItemRenderer>> datasets + ) { + if (points == null || points.isEmpty()) { + return; + } + + double [][] values = CrossSectionLine.fetchCrossSectionProfile(points); + + DefaultXYDataset dataset = new DefaultXYDataset(); + + XYSeries series = new XYSeries(legend, false); + + dataset.addSeries(legend, values); + + StableXYDifferenceRenderer renderer = + new StableXYDifferenceRenderer(); + + datasets.add(new Pair<XYDataset, XYItemRenderer>( + dataset, renderer)); + } + + public List<Pair<XYDataset, XYItemRenderer>> generateDatasets() { + + List<Pair<XYDataset, XYItemRenderer>> datasets = + new ArrayList<Pair<XYDataset, XYItemRenderer>>(); + + CrossSectionLineItem csli = + (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem(); + + for (int i = 0; i < drawCrossSection.length; ++i) { + List<Point2D> points = null; + CrossSection cs = crossSections.get(i); + + if (drawGround[i]) { + for (Pair<CrossSection, CrossSectionLine> csl: csli.lines) { + if (csl.getA() == cs) { + if (points == null) { + points = csl.getB().fetchCrossSectionLinesPoints(); + } + generateGround( + points, + cs.getDescription() + "/Boden", + datasets); + break; + } + } + } + + if (drawFill[i]) { + for (Pair<CrossSection, CrossSectionLine> csl: csli.lines) { + if (csl.getA() == cs) { + if (points == null) { + points = csl.getB().fetchCrossSectionLinesPoints(); + } + + generateFill( + points, cs.getDescription(), datasets); + break; + } + } + } + + if (drawCrossSection[i]) { + for (Pair<CrossSection, CrossSectionLine> csl: csli.lines) { + if (csl.getA() == cs) { + if (points == null) { + points = csl.getB().fetchCrossSectionLinesPoints(); + } + + generateProfile( + points, cs.getDescription(), datasets); + break; + } + } + } + + if (drawWaterLevel[i]) { + for (Pair<CrossSection, CrossSectionLine> csl: csli.lines) { + if (csl.getA() == cs) { + if (points == null) { + points = csl.getB().fetchCrossSectionLinesPoints(); + } + generateWaterLevels(points, datasets); + break; + } + } + } + + } + + return datasets; + } + + protected Object [] createCrossSectionLineItems( + Map<Double, List<Pair<CrossSection, CrossSectionLine>>> km2lines + ) { + Object [] result = new Object[km2lines.size()]; + int i = 0; + for (Map.Entry<Double, List<Pair<CrossSection, CrossSectionLine>>> entry: + km2lines.entrySet()) { + result[i++] = new CrossSectionLineItem( + entry.getKey(), + entry.getValue()); + } + return result; + } + + + public JFreeChart createChart() { + JFreeChart chart = ChartFactory.createXYLineChart( + null, + "Abstand [m]", + "H\u00f6he [m]", + null, + PlotOrientation.VERTICAL, + true, + true, + false); + + List<Pair<XYDataset, XYItemRenderer>> datasets = + generateDatasets(); + + XYPlot plot = chart.getXYPlot(); + + for (int i = 0, N = datasets.size(); i < N; ++i) { + Pair<XYDataset, XYItemRenderer> p = datasets.get(i); + plot.setDataset(i, p.getA()); + plot.mapDatasetToRangeAxis(i, 0); + XYItemRenderer renderer = p.getB(); + if (renderer != null) { + plot.setRenderer(i, renderer); + } + } + + NumberAxis yAxis = (NumberAxis)plot.getRangeAxis(); + yAxis.setAutoRangeIncludesZero(false); + + ChartUtilities.applyCurrentTheme(chart); + return chart; + } + + protected static Paint createTransparentPaint() { + BufferedImage texture = new BufferedImage( + 1, 1, BufferedImage.TYPE_4BYTE_ABGR); + + return new TexturePaint( + texture, new Rectangle2D.Double(0d, 0d, 0d, 0d)); + } + + public static void main(String [] args) { + CrossSectionApp csa = new CrossSectionApp("Querprofile"); + csa.pack(); + RefineryUtilities.centerFrameOnScreen(csa); + csa.setVisible(true); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :