view flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java @ 4255:670e98f5a441

Fixed leak while merging facets. The ThemeList that is used by OutputHelper to sort the Facets for an Output now uses a list to store the ManagedFacets. The correct order is made up by sorting the List using Collections.sort() function of the Java JDK. Therfore, the ManagedFacet class implements the Comparable interface. The return value of its compareTo(other) method depends on the value of the 'position' field.
author Ingo Weinzierl <weinzierl.ingo@googlemail.com>
date Thu, 25 Oct 2012 14:01:46 +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 :

http://dive4elements.wald.intevation.org