view flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java @ 1625:c45e8d6b99a2

#315 Added new state for entering a kilometer range without step width. flys-artifacts/trunk@2800 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Ingo Weinzierl <ingo.weinzierl@intevation.de>
date Wed, 21 Sep 2011 13:03:54 +0000
parents 111794adf285
children a7def20539fb
line wrap: on
line source
package de.intevation.flys.artifacts.charts;

import java.math.MathContext;

import java.awt.Dimension;
import java.awt.BorderLayout;
import java.awt.FlowLayout;

import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JTextField;
import javax.swing.DefaultComboBoxModel;

import java.awt.event.ItemListener;
import java.awt.event.ItemEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

import java.awt.geom.Point2D;
import java.awt.geom.Line2D;

import java.util.ArrayList;
import java.util.List;
import java.util.Comparator;
import java.util.Collections;
import java.util.Iterator;

import java.io.File;
import java.io.IOException;
import java.io.FileWriter;
import java.io.PrintWriter;

import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RefineryUtilities;

import org.jfree.chart.ChartFactory;

import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;

import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.ChartPanel;

import org.jfree.chart.axis.NumberAxis;

import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.DefaultXYDataset;

import de.intevation.flys.model.CrossSection;
import de.intevation.flys.model.CrossSectionLine;
import de.intevation.flys.model.CrossSectionPoint;

import de.intevation.flys.geom.Lines;

import de.intevation.flys.backend.SessionFactoryProvider;

import org.hibernate.Session;
import org.hibernate.Query;

import gnu.trove.TDoubleArrayList;

public class CrossSectionApp
extends      ApplicationFrame
{
    public static final String RIVER = System.getProperty("river", "Saar");

    public static final double EPSILON   = 1e-4;

    public static final double TOO_SMALL = 0.2;
    public static final double TOO_BIG   = 500;

    public static final Comparator<CrossSectionPoint> COL_POS_CMP =
        new Comparator<CrossSectionPoint>() {
            @Override
            public int compare(CrossSectionPoint a, CrossSectionPoint b) {
                // TODO evaluate: isnt it enough to
                // return (|d| > |EPS|) ? d : diff
                double xa = a.getX().doubleValue();
                double xb = b.getX().doubleValue();
                double d = xa - xb;
                if (d < -EPSILON) return -1;
                if (d > +EPSILON) return +1;
                int diff = a.getColPos() - b.getColPos();
                return diff < 0 ? -1 : diff > 0 ? +1 : 0;
            }
        };

    public static final boolean isValid(double x) {
        x = Math.abs(x);
        return x > TOO_SMALL && x < TOO_BIG;
    }

    protected Session session;

    protected JComboBox crossSectionsCB;
    protected JComboBox crossSectionLinesCB;
    protected JTextField waterlevelTF;

    protected ChartPanel chartPanel;

    protected Double lastWaterLevel;


    public static class CrossSectionItem {

        CrossSection crossSection;

        public CrossSectionItem(CrossSection crossSection) {
            this.crossSection = crossSection;
        }

        public String toString() {
            return crossSection.getDescription();
        }
    } // CrossSectionItem

    public static class CrossSectionLineItem {

        CrossSectionLine line;

        public CrossSectionLineItem(CrossSectionLine line) {
            this.line = line;
        }

        public String toString() {
            double v = line.getKm().doubleValue();
            return String.valueOf(Math.round(v * 1000.0)/1000d);
        }
    } // CrossSectionLineItem

    public CrossSectionApp(String title) {
        super(title);

        session = SessionFactoryProvider
            .createSessionFactory()
            .openSession();

        JPanel content = createContent();
        content.setPreferredSize(new Dimension(640, 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();
    }

    public JPanel createContent() {
        JPanel panel = new JPanel(new BorderLayout());


        JPanel nav = new JPanel(new FlowLayout());

        Object [] csis = createCrossSectionItems();
        crossSectionsCB = new JComboBox(csis);

        DefaultComboBoxModel dcbm;

        if (csis.length > 0) {
            Object [] cslis =
                createCrossSectionLineItems(
                    ((CrossSectionItem)csis[0]).crossSection);
            dcbm = new DefaultComboBoxModel(cslis);
            if (cslis.length > 0) {
                dcbm.setSelectedItem(cslis[0]);
            }
        }
        else {
            dcbm = new DefaultComboBoxModel(new Object[0]);
        }

        crossSectionLinesCB = new JComboBox(dcbm);

        nav.add(crossSectionsCB);
        nav.add(crossSectionLinesCB);

        crossSectionsCB.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent ie) {
                if (ie.getStateChange() == ItemEvent.SELECTED) {
                    updateCrossSection(
                        ((CrossSectionItem)ie.getItem()).crossSection);
                }
            }
        });

        crossSectionLinesCB.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent ie) {
                if (ie.getStateChange() == ItemEvent.SELECTED) {
                    updateChart();
                }
            }
        });


        waterlevelTF = new JTextField(5);

        waterlevelTF.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                waterLevelChanged();
            }
        });

        nav.add(waterlevelTF);

        JButton dump = new JButton("dump");

        dump.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                dumpData();
            }
        });

        nav.add(dump);

        panel.add(nav, BorderLayout.SOUTH);

        chartPanel = createChartPanel();

        panel.add(chartPanel, BorderLayout.CENTER);

        return panel;
    }

    protected void waterLevelChanged() {
        String value = waterlevelTF.getText();
        try {
            lastWaterLevel = Double.parseDouble(value);
        }
        catch (NumberFormatException nfe) {
            waterlevelTF.setText(
                lastWaterLevel != null ? lastWaterLevel.toString() : "");
            return;
        }
        updateChart();
    }

    protected void updateChart() {

        CrossSectionLineItem csli =
            (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem();

        JFreeChart chart = createChart(csli == null
            ? new DefaultXYDataset()
            : generateDataset(csli.line, lastWaterLevel));

        chartPanel.setChart(chart);
    }

    protected ChartPanel createChartPanel() {
        CrossSectionLineItem csli =
            (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem();

        JFreeChart chart = createChart(csli == null
            ? new DefaultXYDataset()
            : generateDataset(csli.line, lastWaterLevel));

        return new ChartPanel(chart);
    }

    protected void dumpData() {

        CrossSectionLineItem csli =
            (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem();

        if (csli == null) {
            return;
        }

        CrossSectionLine line = csli.line;

        double km = Math.round(line.getKm().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 + "'");

        List<CrossSectionPoint> points = line.getPoints();

        PrintWriter out = null;

        MathContext mc = new MathContext(3);

        try {
            out =
                new PrintWriter(
                new FileWriter(file));

            for (CrossSectionPoint point: points) {
                out.println(
                    point.getX().round(mc) + " " +
                    point.getY().round(mc));
            }

            out.flush();
        }
        catch (IOException ioe) {
            ioe.printStackTrace();
        }
        finally {
            if (out != null) {
                out.close();
            }
        }
    }

    public static XYDataset generateDataset(
        CrossSectionLine line,
        Double           waterlevel
    ) {
        DefaultXYDataset dataset = new DefaultXYDataset();

        List<CrossSectionPoint> ps = line.getPoints();

        if (ps.isEmpty()) {
            return dataset;
        }

        Collections.sort(ps, COL_POS_CMP);

        List<Point2D> points = new ArrayList<Point2D>(ps.size());

        for (CrossSectionPoint p: ps) {
            double x = p.getX().doubleValue();
            double y = p.getY().doubleValue();
            if (isValid(x) && isValid(y)) {
                points.add(new Point2D.Double(x, y));
            }
        }

        if (points.isEmpty()) {
            return dataset;
        }

        double [] xs = new double[points.size()];
        double [] ys = new double[xs.length];

        xs[0] = points.get(0).getX();
        ys[0] = points.get(0).getY();

        for (int i = 1; i < xs.length; ++i) {
            Point2D p = points.get(i);
            double x = p.getX();
            double y = p.getY();

            if (x <= xs[i-1]) {
                x = xs[i-1] + EPSILON;
            }
            xs[i] = x;
            ys[i] = y;
        }

        if (waterlevel != null) {
            double [][] data = createWaterLines(points, waterlevel);
            dataset.addSeries(String.valueOf(waterlevel), data);
        }

        CrossSection cs = line.getCrossSection();

        String legend = (cs != null ? cs.getDescription() : "???")
            + " " + Math.round(line.getKm().doubleValue() * 1000d)/1000d;

        dataset.addSeries(legend, new double [][] { xs, ys });

        return dataset;
    }

    public static double [][] createWaterLines(
        List<Point2D> points,
        double        waterlevel
    ) {
        List<Line2D> lines = Lines.fillWater(points, waterlevel);

        TDoubleArrayList lxs = new TDoubleArrayList();
        TDoubleArrayList lys = new TDoubleArrayList();

        for (Iterator<Line2D> iter = lines.iterator(); iter.hasNext();) {
            Line2D  l  = iter.next();
            Point2D p1 = l.getP1();
            Point2D p2 = l.getP2();
            lxs.add(p1.getX());
            lys.add(p1.getY());
            lxs.add(p2.getX());
            lys.add(p2.getY());
            if (iter.hasNext()) {
                lxs.add(Double.NaN);
                lys.add(Double.NaN);
            }
        }

        return new double [][] { lxs.toNativeArray(), lys.toNativeArray() };
    }

    protected void updateCrossSection(CrossSection crossSection) {
        Object [] cslis = createCrossSectionLineItems(crossSection);
        DefaultComboBoxModel dcbm = new DefaultComboBoxModel(cslis);
        if (cslis.length > 0) {
            dcbm.setSelectedItem(cslis[0]);
        }
        crossSectionLinesCB.setModel(dcbm);
        if (cslis.length > 0) {
            CrossSectionLine line =
                ((CrossSectionLineItem)cslis[0]).line;
        }
        updateChart();
    }

    protected Object [] createCrossSectionLineItems(CrossSection cs) {
        List<CrossSectionLine> lines = cs.getLines();
        Object [] result = new Object[lines.size()];
        for (int i = 0; i < result.length; ++i) {
            result[i] = new CrossSectionLineItem(lines.get(i));
        }
        return result;
    }


    protected Object [] createCrossSectionItems() {
        List<CrossSection> crossSections = crossSections(RIVER);
        Object [] result = new Object[crossSections.size()];
        for (int i = 0; i < result.length; ++i) {
            result[i] = new CrossSectionItem(crossSections.get(i));
        }
        return result;
    }

    public static JFreeChart createChart(XYDataset dataset) {
        JFreeChart chart = ChartFactory.createXYLineChart(
            null,
            "Abstand [m]",
            "H\u00f6he [m]",
            dataset,
            PlotOrientation.VERTICAL,
            true,
            true,
            false);

        XYPlot plot = chart.getXYPlot();
        NumberAxis yAxis = (NumberAxis)plot.getRangeAxis();
        yAxis.setAutoRangeIncludesZero(false);

        ChartUtilities.applyCurrentTheme(chart);
        return chart;
    }

    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