diff artifacts/src/main/java/org/dive4elements/river/artifacts/charts/CrossSectionApp.java @ 5838:5aa05a7a34b7

Rename modules to more fitting names.
author Sascha L. Teichmann <teichmann@intevation.de>
date Thu, 25 Apr 2013 15:23:37 +0200
parents flys-artifacts/src/main/java/org/dive4elements/river/artifacts/charts/CrossSectionApp.java@bd047b71ab37
children 4897a58c8746
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/charts/CrossSectionApp.java	Thu Apr 25 15:23:37 2013 +0200
@@ -0,0 +1,694 @@
+package org.dive4elements.river.artifacts.charts;
+
+import org.dive4elements.river.backend.SessionFactoryProvider;
+
+import org.dive4elements.river.artifacts.geom.Lines;
+
+import org.dive4elements.river.model.CrossSection;
+import org.dive4elements.river.model.CrossSectionLine;
+import org.dive4elements.river.model.CrossSectionPoint;
+
+import org.dive4elements.river.utils.Pair;
+
+import org.dive4elements.river.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 :

http://dive4elements.wald.intevation.org