Mercurial > dive4elements > river
view flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java @ 4221:480de0dbca8e
Extended location input helper.
The locationpicker has now an attribute whether the input is distance or
location to display one or two clickable columns.
Replaced the record click handler with cell click handler.
author | Raimund Renkert <rrenkert@intevation.de> |
---|---|
date | Tue, 23 Oct 2012 13:17:20 +0200 |
parents | 975f608dd254 |
children | 522b424c9c2a |
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); } 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 :