Mercurial > dive4elements > river
view flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java @ 2089:0da8874bd378
Added initial state to map artifact to be able to advance and step back.
The map artifact overrides describe() to have the complete UI information in the
describe response document.
flys-artifacts/trunk@3613 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author | Raimund Renkert <raimund.renkert@intevation.de> |
---|---|
date | Fri, 06 Jan 2012 12:02:10 +0000 |
parents | e384d78ff78b |
children | 9d2a06c3a134 |
line wrap: on
line source
package de.intevation.flys.artifacts.charts; import de.intevation.flys.backend.SessionFactoryProvider; import de.intevation.flys.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.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( point.getX().round(mc) + " " + 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); 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); 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 :