org.locationtech.udig.processingtoolbox.tools.BoxPlotDialog.java Source code

Java tutorial

Introduction

Here is the source code for org.locationtech.udig.processingtoolbox.tools.BoxPlotDialog.java

Source

/*
 * uDig - User Friendly Desktop Internet GIS client
 * (C) MangoSystem - www.mangosystem.com 
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * (http://www.eclipse.org/legal/epl-v10.html), and the Refractions BSD
 * License v1.0 (http://udig.refractions.net/files/bsd3-v10.html).
 */
package org.locationtech.udig.processingtoolbox.tools;

import java.awt.Font;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.PlatformUI;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.process.spatialstatistics.StatisticsFeaturesProcess;
import org.geotools.process.spatialstatistics.operations.DataStatisticsOperation.DataStatisticsResult;
import org.geotools.util.logging.Logging;
import org.jfree.chart.ChartMouseEvent;
import org.jfree.chart.ChartMouseListener;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.BoxAndWhiskerToolTipGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BoxAndWhiskerRenderer;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.statistics.BoxAndWhiskerCategoryDataset;
import org.jfree.data.statistics.DefaultBoxAndWhiskerCategoryDataset;
import org.jfree.experimental.chart.swt.ChartComposite;
import org.jfree.ui.RectangleInsets;
import org.locationtech.udig.catalog.util.GeoToolsAdapters;
import org.locationtech.udig.processingtoolbox.ToolboxPlugin;
import org.locationtech.udig.processingtoolbox.internal.Messages;
import org.locationtech.udig.processingtoolbox.styler.MapUtils;
import org.locationtech.udig.processingtoolbox.styler.MapUtils.VectorLayerType;
import org.locationtech.udig.project.ILayer;
import org.locationtech.udig.project.IMap;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.expression.Expression;
import org.opengis.util.ProgressListener;

/**
 * Box Plot(Box and Whisker) Dialog
 * 
 * @author Minpa Lee, MangoSystem
 * 
 * @source $URL$
 */
public class BoxPlotDialog extends AbstractGeoProcessingDialog implements IRunnableWithProgress {
    protected static final Logger LOGGER = Logging.getLogger(BoxPlotDialog.class);

    protected final FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(null);

    private ChartComposite chartComposite;

    private ILayer inputLayer;

    private Combo cboLayer;

    private Table schemaTable;

    private Button chkStatistics;

    private Browser browser;

    private String selectedFields;

    private CTabItem inputTab, plotTab, outputTab;

    private XYMinMaxVisitor minMaxVisitor = new XYMinMaxVisitor();

    public BoxPlotDialog(Shell parentShell, IMap map) {
        super(parentShell, map);

        setShellStyle(SWT.CLOSE | SWT.MIN | SWT.TITLE | SWT.BORDER | SWT.MODELESS | SWT.RESIZE);

        this.windowTitle = Messages.BoxPlotDialog_title;
        this.windowDesc = Messages.BoxPlotDialog_description;
        this.windowSize = new Point(650, 450);
    }

    @Override
    protected Control createDialogArea(final Composite parent) {
        Composite area = (Composite) super.createDialogArea(parent);

        // 0. Tab Folder
        final CTabFolder parentTabFolder = new CTabFolder(area, SWT.BOTTOM);
        parentTabFolder.setUnselectedCloseVisible(false);
        parentTabFolder.setLayout(new FillLayout());
        parentTabFolder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

        // 1. Input Tab
        createInputTab(parentTabFolder);

        parentTabFolder.setSelection(inputTab);
        parentTabFolder.pack();
        area.pack(true);
        return area;
    }

    private void createInputTab(final CTabFolder parentTabFolder) {
        inputTab = new CTabItem(parentTabFolder, SWT.NONE);
        inputTab.setText(Messages.ProcessExecutionDialog_tabparameters);

        ScrolledComposite scroller = new ScrolledComposite(parentTabFolder, SWT.NONE | SWT.V_SCROLL | SWT.H_SCROLL);
        scroller.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

        Composite container = new Composite(scroller, SWT.NONE);
        container.setLayout(new GridLayout(1, false));
        container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

        // local moran's i
        Image image = ToolboxPlugin.getImageDescriptor("icons/public_co.gif").createImage(); //$NON-NLS-1$
        uiBuilder.createLabel(container, Messages.ScatterPlotDialog_InputLayer, EMPTY, image, 1);
        cboLayer = uiBuilder.createCombo(container, 1, true);
        fillLayers(map, cboLayer, VectorLayerType.ALL);

        uiBuilder.createLabel(container, Messages.BoxPlotDialog_Fields, EMPTY, image, 1);
        schemaTable = uiBuilder.createTable(container, new String[] { Messages.General_Name }, 1);
        schemaTable.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent event) {
                StringBuffer buffer = new StringBuffer();
                for (TableItem item : schemaTable.getItems()) {
                    if (item.getChecked()) {
                        if (buffer.length() > 0) {
                            buffer.append(",").append(item.getText()); //$NON-NLS-1$
                        } else {
                            buffer.append(item.getText());
                        }
                    }
                }
                selectedFields = buffer.toString();
            }
        });

        uiBuilder.createLabel(container, null, null, 1);
        chkStatistics = uiBuilder.createCheckbox(container, Messages.ScatterPlotDialog_BasicStatistics, null, 1);

        // register events
        cboLayer.addModifyListener(new ModifyListener() {
            @Override
            public void modifyText(ModifyEvent e) {
                schemaTable.removeAll();
                inputLayer = MapUtils.getLayer(map, cboLayer.getText());
                if (inputLayer != null) {
                    for (AttributeDescriptor dsc : inputLayer.getSchema().getAttributeDescriptors()) {
                        Class<?> binding = dsc.getType().getBinding();
                        if (Number.class.isAssignableFrom(binding)) {
                            TableItem item = new TableItem(schemaTable, SWT.NULL);
                            item.setText(dsc.getLocalName());
                        }
                    }
                }
            }
        });

        // finally
        scroller.setContent(container);
        inputTab.setControl(scroller);

        scroller.setMinSize(450, container.getSize().y - 2);
        scroller.setExpandVertical(true);
        scroller.setExpandHorizontal(true);

        scroller.pack();
        container.pack();
    }

    private void createOutputTab(final CTabFolder parentTabFolder) {
        outputTab = new CTabItem(parentTabFolder, SWT.NONE);
        outputTab.setText(Messages.ScatterPlotDialog_Summary);

        try {
            browser = new Browser(parentTabFolder, SWT.NONE);
            GridData layoutData = new GridData(GridData.FILL_BOTH);
            browser.setLayoutData(layoutData);
            outputTab.setControl(browser);
        } catch (Exception e) {
            LOGGER.log(Level.WARNING, e.getMessage(), e);
        }
    }

    private void createGraphTab(final CTabFolder parentTabFolder) {
        plotTab = new CTabItem(parentTabFolder, SWT.NONE);
        plotTab.setText(Messages.ScatterPlotDialog_Graph);

        CategoryPlot plot = new CategoryPlot();
        plot.setOrientation(PlotOrientation.VERTICAL);
        plot.setBackgroundPaint(java.awt.Color.WHITE);
        plot.setRangePannable(false);

        JFreeChart chart = new JFreeChart(EMPTY, JFreeChart.DEFAULT_TITLE_FONT, plot, false);
        chart.setBackgroundPaint(java.awt.Color.WHITE);
        chart.setBorderVisible(false);

        chartComposite = new ChartComposite(parentTabFolder, SWT.NONE | SWT.EMBEDDED, chart, true);
        chartComposite.setLayout(new FillLayout());
        chartComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        chartComposite.setDomainZoomable(false);
        chartComposite.setRangeZoomable(false);
        // chartComposite.setMap(map);
        chartComposite.addChartMouseListener(new PlotMouseListener());

        plotTab.setControl(chartComposite);

        chartComposite.pack();
    }

    class PlotMouseListener implements ChartMouseListener {
        @Override
        public void chartMouseMoved(ChartMouseEvent event) {
        }

        @Override
        public void chartMouseClicked(ChartMouseEvent event) {
            // ChartEntity entity = event.getEntity();
        }
    }

    private void updateChart(SimpleFeatureCollection features, String[] fields) {
        // Setup Box plot
        int fontStyle = java.awt.Font.BOLD;
        FontData fontData = getShell().getDisplay().getSystemFont().getFontData()[0];

        CategoryAxis xPlotAxis = new CategoryAxis(EMPTY); // Type
        xPlotAxis.setLabelFont(new Font(fontData.getName(), fontStyle, 12));
        xPlotAxis.setTickLabelFont(new Font(fontData.getName(), fontStyle, 12));

        NumberAxis yPlotAxis = new NumberAxis("Value"); // Value //$NON-NLS-1$
        yPlotAxis.setLabelFont(new Font(fontData.getName(), fontStyle, 12));
        yPlotAxis.setTickLabelFont(new Font(fontData.getName(), fontStyle, 10));
        yPlotAxis.setAutoRangeIncludesZero(false);

        BoxAndWhiskerRenderer renderer = new BoxAndWhiskerRenderer();
        renderer.setMedianVisible(true);
        renderer.setMeanVisible(false);
        renderer.setFillBox(true);
        renderer.setSeriesFillPaint(0, java.awt.Color.CYAN);
        renderer.setBaseFillPaint(java.awt.Color.CYAN);
        renderer.setBaseToolTipGenerator(new BoxAndWhiskerToolTipGenerator());

        // Set the scatter data, renderer, and axis into plot
        CategoryDataset dataset = getDataset(features, fields);
        CategoryPlot plot = new CategoryPlot(dataset, xPlotAxis, yPlotAxis, renderer);
        plot.setOrientation(PlotOrientation.VERTICAL);
        plot.setBackgroundPaint(java.awt.Color.WHITE);
        plot.setRangePannable(false);

        plot.setDomainCrosshairVisible(false);
        plot.setRangeCrosshairVisible(false);
        plot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0));
        plot.setForegroundAlpha(0.85f);

        // Map the scatter to the first Domain and first Range
        plot.mapDatasetToDomainAxis(0, 0);
        plot.mapDatasetToRangeAxis(0, 0);

        // 3. Setup Selection
        /*****************************************************************************************
         * CategoryAxis xSelectionAxis = new CategoryAxis(EMPTY);
         * xSelectionAxis.setTickMarksVisible(false); xSelectionAxis.setTickLabelsVisible(false);
         * 
         * NumberAxis ySelectionAxis = new NumberAxis(EMPTY);
         * ySelectionAxis.setTickMarksVisible(false); ySelectionAxis.setTickLabelsVisible(false);
         * 
         * BoxAndWhiskerRenderer selectionRenderer = new BoxAndWhiskerRenderer();
         * selectionRenderer.setSeriesShape(0, new Ellipse2D.Double(0, 0, 6, 6));
         * selectionRenderer.setSeriesPaint(0, java.awt.Color.RED); // dot
         * 
         * plot.setDataset(2, new DefaultBoxAndWhiskerCategoryDataset()); plot.setRenderer(2,
         * selectionRenderer); plot.setDomainAxis(2, xSelectionAxis); plot.setRangeAxis(2,
         * ySelectionAxis);
         * 
         * // Map the scatter to the second Domain and second Range plot.mapDatasetToDomainAxis(2,
         * 0); plot.mapDatasetToRangeAxis(2, 0);
         *****************************************************************************************/

        // 5. Finally, Create the chart with the plot and a legend
        java.awt.Font titleFont = new Font(fontData.getName(), fontStyle, 20);
        JFreeChart chart = new JFreeChart(EMPTY, titleFont, plot, false);
        chart.setBackgroundPaint(java.awt.Color.WHITE);
        chart.setBorderVisible(false);

        chartComposite.setChart(chart);
        chartComposite.forceRedraw();
    }

    private BoxAndWhiskerCategoryDataset getDataset(SimpleFeatureCollection features, String[] fields) {
        minMaxVisitor.reset();

        Expression[] expression = new Expression[fields.length];
        Map<String, List<Double>> listMap = new TreeMap<String, List<Double>>();
        for (int index = 0; index < expression.length; index++) {
            expression[index] = ff.property(fields[index]);
            listMap.put(fields[index], new ArrayList<Double>());
        }

        SimpleFeatureIterator featureIter = features.features();
        try {
            while (featureIter.hasNext()) {
                SimpleFeature feature = featureIter.next();
                for (int index = 0; index < expression.length; index++) {
                    Double val = expression[index].evaluate(feature, Double.class);
                    if (val == null || val.isNaN() || val.isInfinite()) {
                        continue;
                    }
                    minMaxVisitor.visit(val, val);
                    listMap.get(fields[index]).add(val);
                }
            }
        } finally {
            featureIter.close();
        }

        DefaultBoxAndWhiskerCategoryDataset dataset = new DefaultBoxAndWhiskerCategoryDataset();
        for (int index = 0; index < fields.length; index++) {
            dataset.add(listMap.get(fields[index]), "Series1", fields[index]); //$NON-NLS-1$
        }
        return dataset;
    }

    @Override
    protected void okPressed() {
        if (invalidWidgetValue(cboLayer, schemaTable) || selectedFields == null || selectedFields.length() == 0) {
            openInformation(getShell(), Messages.Task_ParameterRequired);
            return;
        }

        if (inputLayer.getFilter() != Filter.EXCLUDE) {
            map.select(Filter.EXCLUDE, inputLayer);
        }

        try {
            PlatformUI.getWorkbench().getProgressService().run(false, true, this);
        } catch (InvocationTargetException e) {
            MessageDialog.openError(getShell(), Messages.General_Error, e.getMessage());
        } catch (InterruptedException e) {
            MessageDialog.openInformation(getShell(), Messages.General_Cancelled, e.getMessage());
        }
    }

    @SuppressWarnings("nls")
    @Override
    public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
        monitor.beginTask(String.format(Messages.Task_Executing, windowTitle), 100);
        try {
            if (plotTab == null) {
                monitor.subTask("Preparing box plot...");
                createGraphTab(inputTab.getParent());
            }

            monitor.worked(increment);

            SimpleFeatureCollection features = MapUtils.getFeatures(inputLayer);
            if (chkStatistics.getSelection()) {
                if (outputTab == null) {
                    createOutputTab(inputTab.getParent());
                }

                ProgressListener subMonitor = GeoToolsAdapters
                        .progress(SubMonitor.convert(monitor, Messages.Task_Internal, 20));
                HtmlWriter writer = new HtmlWriter(inputLayer.getName());
                DataStatisticsResult statistics = null;
                statistics = StatisticsFeaturesProcess.process(features, selectedFields, subMonitor);
                writer.writeDataStatistics(statistics);
                browser.setText(writer.getHTML());
            }

            monitor.subTask("Updating box plot...");
            // chartComposite.setLayer(inputLayer);
            updateChart(features, selectedFields.split(","));
            plotTab.getParent().setSelection(plotTab);
            monitor.worked(increment);
        } catch (Exception e) {
            e.printStackTrace();
            ToolboxPlugin.log(e.getMessage());
            throw new InvocationTargetException(e.getCause(), e.getMessage());
        } finally {
            ToolboxPlugin.log(String.format(Messages.Task_Completed, windowTitle));
            monitor.done();
        }
    }
}