Mercurial > dive4elements > river
view flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java @ 5818:a4ff4167be1e
Request feature info on all layers and show it as html if
the server does not return valid gml.
Non queryable layers produce an error message when the request
fails. This is good enough
author | Andre Heinecke <aheinecke@intevation.de> |
---|---|
date | Wed, 24 Apr 2013 17:33:27 +0200 |
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 :