org.dawnsci.plotting.tools.fitting.LineFittingTool.java Source code

Java tutorial

Introduction

Here is the source code for org.dawnsci.plotting.tools.fitting.LineFittingTool.java

Source

/*
 * Copyright (c) 2012 Diamond Light Source Ltd.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.dawnsci.plotting.tools.fitting;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;

import org.dawb.common.ui.image.IconUtils;
import org.dawb.common.ui.menu.CheckableActionGroup;
import org.dawb.common.ui.menu.MenuAction;
import org.dawb.common.ui.util.EclipseUtils;
import org.dawnsci.plotting.tools.Activator;
import org.eclipse.dawnsci.analysis.dataset.impl.Dataset;
import org.eclipse.dawnsci.analysis.dataset.roi.LinearROI;
import org.eclipse.dawnsci.analysis.dataset.roi.RectangularROI;
import org.eclipse.dawnsci.plotting.api.annotation.AnnotationUtils;
import org.eclipse.dawnsci.plotting.api.annotation.IAnnotation;
import org.eclipse.dawnsci.plotting.api.region.IRegion;
import org.eclipse.dawnsci.plotting.api.region.IRegion.RegionType;
import org.eclipse.dawnsci.plotting.api.region.RegionUtils;
import org.eclipse.dawnsci.plotting.api.trace.ILineTrace;
import org.eclipse.dawnsci.plotting.api.trace.TraceUtils;
import org.eclipse.dawnsci.plotting.api.views.ISettablePlotView;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.window.ToolTip;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.widgets.Display;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import uk.ac.diamond.scisoft.analysis.fitting.FittingConstants;
import uk.ac.diamond.scisoft.analysis.fitting.functions.Polynomial;

public class LineFittingTool extends AbstractFittingTool {

    private static final Logger logger = LoggerFactory.getLogger(LineFittingTool.class);

    /**
     * Columns in the UI Table.
     */
    @Override
    protected List<TableViewerColumn> createColumns(TableViewer viewer) {
        // Columns for coefficients of polynomials maybe?
        ColumnViewerToolTipSupport.enableFor(viewer, ToolTip.NO_RECREATE);

        List<TableViewerColumn> ret = new ArrayList<TableViewerColumn>(9);
        TableViewerColumn var = new TableViewerColumn(viewer, SWT.LEFT, 0);
        var.getColumn().setText("Trace");
        var.getColumn().setWidth(80);
        var.setLabelProvider(new LineLabelProvider(0));
        ret.add(var);

        var = new TableViewerColumn(viewer, SWT.LEFT, 1);
        var.getColumn().setText("Name");
        var.getColumn().setWidth(150);
        var.setLabelProvider(new LineLabelProvider(1));
        ret.add(var);

        var = new TableViewerColumn(viewer, SWT.LEFT, 2);
        var.getColumn().setText("Type");
        var.getColumn().setWidth(200);
        var.setLabelProvider(new LineLabelProvider(2));
        ret.add(var);

        var = new TableViewerColumn(viewer, SWT.CENTER, 3);
        var.getColumn().setText("Equation");
        var.getColumn().setWidth(300);
        var.setLabelProvider(new LineLabelProvider(3));
        ret.add(var);

        return ret;

    }

    /**
     * The actual algorithm run
     */
    @Override
    protected FittedFunctions getFittedFunctions(FittedPeaksInfo fittedPeaksInfo) throws Exception {
        // Drive the fitting, maybe exactly the same as the peak fitting or maybe different options.
        return FittingUtils.getFittedPolynomial(fittedPeaksInfo);
    }

    /**
     * What happens when the line is plotted
     */
    @Override
    protected void createFittedFunctionUI(final FittedFunctions newBean) {
        if (newBean == null) {
            fittedFunctions = null;
            logger.error("Cannot fit the the given selection.");
            return;
        }
        composite.getDisplay().syncExec(new Runnable() {

            public void run() {
                try {

                    boolean warnLarge = false;

                    boolean requireRange = Activator.getPlottingPreferenceStore()
                            .getBoolean(FittingConstants.SHOW_POLY_RANGE);
                    boolean requireTrace = Activator.getPlottingPreferenceStore()
                            .getBoolean(FittingConstants.SHOW_POLY_TRACE);

                    int ifit = 1;
                    // Draw the regions
                    for (FittedFunction fp : newBean.getFunctionList()) {

                        RectangularROI rb = fp.getRoi();
                        final IRegion area = RegionUtils.replaceCreateRegion(getPlottingSystem(),
                                "Fit Area " + ifit, RegionType.XAXIS);
                        area.setRegionColor(ColorConstants.orange);
                        area.setROI(rb);
                        area.setMobile(false);
                        getPlottingSystem().addRegion(area);
                        fp.setFwhm(area);
                        if (!requireRange)
                            area.setVisible(false);

                        final Dataset[] pair = fp.getPeakFunctions();
                        final ILineTrace trace = TraceUtils.replaceCreateLineTrace(getPlottingSystem(),
                                "Fit " + ifit);
                        //set user trace false before setting data otherwise the trace sent to events will be a true by default
                        trace.setUserTrace(false);
                        trace.setData(pair[0], pair[1]);
                        trace.setLineWidth(1);
                        trace.setTraceColor(ColorConstants.black);
                        getPlottingSystem().addTrace(trace);
                        fp.setTrace(trace);
                        if (!requireTrace)
                            trace.setVisible(false);

                        //No annotation used at the moment
                        //But leaving it in since some functions in the abstract class
                        //need one to be there
                        final IAnnotation ann = AnnotationUtils.replaceCreateAnnotation(getPlottingSystem(),
                                "Fit " + ifit);
                        ann.setLocation(0, 0);
                        getPlottingSystem().addAnnotation(ann);
                        fp.setAnnotation(ann);
                        ann.setVisible(false);

                        final IRegion line = RegionUtils.replaceCreateRegion(getPlottingSystem(),
                                "Peak Line " + ifit, RegionType.XAXIS_LINE);
                        line.setRegionColor(ColorConstants.black);
                        line.setAlpha(150);
                        line.setLineWidth(1);
                        getPlottingSystem().addRegion(line);
                        line.setROI(new LinearROI(rb.getMidPoint(), rb.getMidPoint()));
                        line.setMobile(false);
                        fp.setCenter(line);
                        line.setVisible(false);

                        //Fitting can go badly if x is too large and x step too small
                        //Warn if this is the case
                        double max = trace.getXData().max().doubleValue();
                        double min = trace.getXData().min().doubleValue();
                        double step = (max - min) / trace.getXData().getSize();
                        double midVal = ((max + min) / 2);

                        if (midVal / step > 1E6)
                            warnLarge = true;

                        ++ifit;
                    }

                    LineFittingTool.this.fittedFunctions = newBean;
                    viewer.setInput(newBean);
                    viewer.refresh();
                    if (warnLarge) {
                        algorithmMessage.setText(
                                "Warning: Large x values. Consider subtracting constant from x before fitting.");
                    } else
                        algorithmMessage.setText("Polynomial Line Fit");

                    algorithmMessage.getParent().layout();

                } catch (Exception ne) {
                    logger.error("Cannot create fitted peaks!", ne);
                }
            }
        });

    }

    /**
     * Actions appearing in the tool.
     */
    @Override
    protected void createActions() {
        final Action createNewSelection = new Action("New fit selection.", IAction.AS_PUSH_BUTTON) {
            public void run() {
                createNewFit();
            }
        };

        //Stealing all the icons from the peak fitting tool
        createNewSelection.setImageDescriptor(Activator.getImageDescriptor("icons/plot-tool-peak-fit.png"));
        getSite().getActionBars().getToolBarManager().add(createNewSelection);
        getSite().getActionBars().getToolBarManager().add(new Separator());

        //Action to show/remove fit trace from plot
        final Action showTrace = new Action("Show fitting traces.", IAction.AS_CHECK_BOX) {
            public void run() {
                final boolean isChecked = isChecked();
                Activator.getPlottingPreferenceStore().setValue(FittingConstants.SHOW_POLY_TRACE, isChecked);
                if (fittedFunctions != null)
                    fittedFunctions.setTracesVisible(isChecked);
            }
        };
        showTrace.setImageDescriptor(Activator.getImageDescriptor("icons/plot-tool-peak-fit-showFittingTrace.png"));
        getSite().getActionBars().getToolBarManager().add(showTrace);

        showTrace.setChecked(Activator.getPlottingPreferenceStore().getBoolean(FittingConstants.SHOW_POLY_TRACE));
        ;

        //Action to show/remove fit region from plot
        final Action showFitRegion = new Action("Show selection regions for fit", IAction.AS_CHECK_BOX) {
            public void run() {
                final boolean isChecked = isChecked();
                Activator.getPlottingPreferenceStore().setValue(FittingConstants.SHOW_POLY_RANGE, isChecked);
                if (fittedFunctions != null)
                    fittedFunctions.setAreasVisible(isChecked);
            }
        };
        showFitRegion.setImageDescriptor(Activator.getImageDescriptor("icons/plot-tool-peak-fit-showFWHM.png"));
        getSite().getActionBars().getToolBarManager().add(showFitRegion);

        showFitRegion
                .setChecked(Activator.getPlottingPreferenceStore().getBoolean(FittingConstants.SHOW_POLY_RANGE));

        final Separator sep = new Separator(getClass().getName() + ".separator1");
        getSite().getActionBars().getToolBarManager().add(sep);

        //Add select traces menu created in updateTracesChoice()
        this.tracesMenu = new MenuAction("Traces");
        tracesMenu.setToolTipText("Choose trace for fit.");
        tracesMenu.setImageDescriptor(Activator.getImageDescriptor("icons/plot-tool-trace-choice.png"));

        getSite().getActionBars().getToolBarManager().add(tracesMenu);
        getSite().getActionBars().getMenuManager().add(tracesMenu);

        //Add menu to choose polynomial order
        final MenuAction numberPeaks = new MenuAction("Polynomial order to fit");
        numberPeaks.setToolTipText("Polynomial order to fit");

        CheckableActionGroup group = new CheckableActionGroup();

        final int npeak = Activator.getPlottingPreferenceStore().getDefaultInt(FittingConstants.POLY_CHOICES);
        for (int m = 0; m <= npeak; m++) {

            final int peak = m;
            final Action action = new Action("Polynomial Order: " + String.valueOf(m), IAction.AS_CHECK_BOX) {
                public void run() {
                    Activator.getPlottingPreferenceStore().setValue(FittingConstants.POLY_ORDER, peak);
                    numberPeaks.setSelectedAction(this);
                    setChecked(true);
                    if (isActive())
                        fittingJob.fit(false);
                }
            };

            action.setImageDescriptor(IconUtils.createIconDescriptor(String.valueOf(m)));
            numberPeaks.add(action);
            group.add(action);
            action.setChecked(false);
            action.setToolTipText("Polynomial order " + m);

        }

        final int ipeak = Activator.getPlottingPreferenceStore().getInt(FittingConstants.POLY_ORDER);
        numberPeaks.setSelectedAction(ipeak);
        numberPeaks.setCheckedAction(ipeak, true);

        getSite().getActionBars().getToolBarManager().add(numberPeaks);

        //Clear All regions action
        final Action clear = new Action("Clear all",
                Activator.getImageDescriptor("icons/plot-tool-peak-fit-clear.png")) {
            public void run() {
                clearAll();
            }
        };
        clear.setToolTipText("Clear all regions found in the fitting");

        getSite().getActionBars().getToolBarManager().add(clear);
        getSite().getActionBars().getMenuManager().add(clear);

        //Clear All Fits action
        final Action delete = new Action("Delete fit selected", Activator.getImageDescriptor("icons/delete.gif")) {
            public void run() {
                if (!isActive())
                    return;
                if (fittedFunctions != null)
                    fittedFunctions.deleteSelectedFunction(getPlottingSystem());
                viewer.refresh();
            }
        };
        delete.setToolTipText("Delete line selected, if any");

        getSite().getActionBars().getToolBarManager().add(delete);

        //Added to right click menus
        //Export to .dat
        final Action export = new Action("Export...", IAction.AS_PUSH_BUTTON) {
            public void run() {
                try {
                    EclipseUtils.openWizard(FittedPeaksExportWizard.ID, true);
                } catch (Exception e) {
                    logger.error("Cannot open wizard " + FittedPeaksExportWizard.ID, e);
                }
            }
        };

        //Copy to clipboard
        final Action copy = new Action("Copy", IAction.AS_PUSH_BUTTON) {
            public void run() {
                try {
                    StructuredSelection ss = (StructuredSelection) viewer.getSelection();
                    Object element = ss.getFirstElement();

                    if (!(element instanceof FittedFunction))
                        return;
                    FittedFunction ff = (FittedFunction) element;

                    if (ff.getFunction().getFunction(0) == null
                            || !(ff.getFunction().getFunction(0) instanceof Polynomial))
                        return;

                    String equation = ((Polynomial) ff.getFunction().getFunction(0)).getStringEquation();

                    Clipboard cb = new Clipboard(Display.getDefault());
                    cb.clearContents();
                    TextTransfer textTransfer = TextTransfer.getInstance();
                    cb.setContents(new Object[] { equation }, new TextTransfer[] { textTransfer });
                    cb.dispose();
                } catch (Exception e) {
                    logger.error("Copy Unsuccessful", e);
                }
            }
        };

        final MenuManager menuManager = new MenuManager();
        menuManager.add(clear);
        menuManager.add(delete);
        menuManager.add(new Separator());
        menuManager.add(showTrace);
        menuManager.add(new Separator());
        menuManager.add(export);
        menuManager.add(copy);

        viewer.getControl().setMenu(menuManager.createContextMenu(viewer.getControl()));

    }

    String exportFittedData(final String path) throws Exception {
        //Export file to .dat
        File file = new File(path);
        if (!file.getName().toLowerCase().endsWith(".dat"))
            file = new File(path + ".dat");
        if (file.exists())
            file.delete();

        final BufferedWriter writer = new BufferedWriter(
                new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
        try {
            writer.write("#Polynomial fitting coefficients");
            writer.newLine();
            for (FittedFunction peak : this.fittedFunctions.getFunctionList()) {
                Polynomial poly = (Polynomial) peak.getFunction().getFunction(0);
                writer.write("#For Trace: " + peak.getDataTrace().getName());
                writer.newLine();
                writer.write("#Equation: " + poly.getStringEquation());
                writer.newLine();
                writer.write("#High precision coefficients");
                writer.newLine();
                writer.write("#");
                for (int i = 0; i < poly.getNoOfParameters(); i++) {
                    writer.write("X^" + String.valueOf((poly.getNoOfParameters() - i - 1)) + "\t");
                }
                writer.newLine();
                for (int i = 0; i < poly.getNoOfParameters(); i++) {
                    writer.write(String.valueOf(poly.getParameter(i).getValue()) + "\t");
                }
                writer.newLine();
                writer.newLine();
            }

        } finally {
            writer.close();
        }

        return file.getAbsolutePath();
    }

    @Override
    void pushFunctionsToPlotter() {
        if (getPart() instanceof ISettablePlotView) {
            //TODO
        }
    }

}