Mercurial > dive4elements > river
view flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java @ 5415:5bac3e75f59c
WaterlevelSelectState: Avoid npe with extreme results.
author | Felix Wolfsteller <felix.wolfsteller@intevation.de> |
---|---|
date | Tue, 26 Mar 2013 09:40:05 +0100 |
parents | 522b424c9c2a |
children |
line wrap: on
line source
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); } /** Query and return CrossSections for named river. */ 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() { JFreeChart chart = createChart(); chartPanel.setChart(chart); } protected ChartPanel createChartPanel() { 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(); 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 :