All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jdplus.tramoseats.desktop.plugin.anomalydetection.ui.OutliersTopComponent Maven / Gradle / Ivy

/*
 * Copyright 2013 National Bank of Belgium
 *
 * Licensed under the EUPL, Version 1.1 or – as soon they will be approved 
 * by the European Commission - subsequent versions of the EUPL (the "Licence");
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at:
 * 
 * http://ec.europa.eu/idabc/eupl
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the Licence is distributed on an "AS IS" basis,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Licence for the specific language governing permissions and 
 * limitations under the Licence.
 */
package jdplus.tramoseats.desktop.plugin.anomalydetection.ui;

import jdplus.toolkit.desktop.plugin.util.Stopwatch;
import jdplus.toolkit.desktop.plugin.notification.MessageType;
import jdplus.toolkit.desktop.plugin.notification.NotifyUtil;
import jdplus.toolkit.desktop.plugin.DemetraIcons;
import jdplus.tramoseats.desktop.plugin.anomalydetection.ControlNode;
import jdplus.toolkit.desktop.plugin.util.NbComponents;
import jdplus.toolkit.desktop.plugin.properties.PropertySheetDialogBuilder;
import jdplus.toolkit.desktop.plugin.components.JTsGrid;
import ec.util.chart.ObsIndex;
import jdplus.toolkit.desktop.plugin.components.TsSelectionBridge;
import jdplus.toolkit.desktop.plugin.ui.ActiveView;
import jdplus.toolkit.desktop.plugin.ui.ActiveViewManager;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JPopupMenu;
import javax.swing.JSplitPane;
import javax.swing.JToolBar;
import javax.swing.SwingWorker;
import jdplus.toolkit.base.core.regsarima.regular.RegSarimaModel;
import nbbrd.design.ClassNameConstant;
import org.netbeans.core.spi.multiview.CloseOperationState;
import org.netbeans.core.spi.multiview.MultiViewElement;
import org.netbeans.core.spi.multiview.MultiViewElementCallback;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.DropDownButtonFactory;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.ExplorerUtils;
import org.openide.nodes.Node;
import org.openide.util.Exceptions;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle;
import org.openide.windows.TopComponent;

/**
 * Top component for the outlier detection of Ts
 *
 * @author Mats Maggi
 */
@TopComponent.Description(preferredID = "OutliersTopComponent",
        persistenceType = TopComponent.PERSISTENCE_ONLY_OPENED)
@TopComponent.Registration(mode = "editor", openAtStartup = false)
@ActionID(category = "Window", id = OutliersTopComponent.ID)
@ActionReference(path = "Menu/Statistical methods/Anomaly Detection")
@TopComponent.OpenActionRegistration(displayName = "#CTL_OutliersTopComponentAction")
@NbBundle.Messages({
    "CTL_OutliersTopComponentAction=Outliers Detection",
    "CTL_OutliersTopComponent=Outliers Detection Window",
    "HINT_OutliersTopComponent=This is an Outlier Detection Window"
})
public final class OutliersTopComponent extends TopComponent implements ExplorerManager.Provider, MultiViewElement, ActiveView {

    @ClassNameConstant
    public static final String ID = "jdplus.tramoseats.desktop.plugin.anomalydetection.ui.OutliersTopComponent";

    public static final String STATE_CHANGED = "Processing state changed";
    private final JTsAnomalyGrid grid;
    private final JAnomalyDetectionSummary summary;
    private final ExplorerManager mgr = new ExplorerManager();
    private final Node node;
    private final JSplitPane visualRepresentation;
    private final JSplitPane tsInformation;
    private final JAnomalyDetectionChart chart;
    private final JToolBar toolBar;
    private JButton runButton;
    private JLabel itemsLabel;
    private JButton prefButton;
    private Stopwatch stopwatch;
    private int nbElements = 0;

    private void calculateNbItems() {
        if (grid.getTsCollection() != null) {
            nbElements = grid.getTsCollection().size();
            itemsLabel.setText(nbElements == 0 ? "No items" : nbElements + (nbElements < 2 ? " item" : " items"));
        } else {
            nbElements = 0;
            itemsLabel.setText(String.valueOf(nbElements));
        }
    }

    // 
    public OutliersTopComponent() {
        setName("Outliers Detection");
        grid = new JTsAnomalyGrid();
        summary = new JAnomalyDetectionSummary();

        grid.addPropertyChangeListener(new PropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                switch (evt.getPropertyName()) {
                    case JTsAnomalyGrid.COLLECTION_PROPERTY:
                    case JTsAnomalyGrid.TYPES_PROPERTY:
                        refreshSummary();
                        calculateNbItems();
                        runButton.setEnabled(nbElements != 0);
                        break;
                    case JTsAnomalyGrid.DEFAULT_SPEC_PROPERTY:
                    case JTsAnomalyGrid.CRITICAL_VALUE_PROPERTY:
                    case TsSelectionBridge.TS_SELECTION_PROPERTY:
                    case JTsAnomalyGrid.TRANSFORMATION_PROPERTY:
                        refreshSummary();
                        break;
                    case STATE_CHANGED:
                        onStateChanged((SwingWorker.StateValue) evt.getNewValue());
                        break;
                    case JTsAnomalyGrid.HOVERED_OBS_PROPERTY:
                        ObsIndex obs = (ObsIndex) evt.getNewValue();
                        JAnomalyDetectionChart.Model model = chart.getModel();
                        if (obs.getObs() == -1 || model == null || !grid.getTsCollection().get(obs.getSeries()).equals(model.getTs())) {
                            chart.setHoveredObs(-1);
                        } else {
                            chart.setHoveredObs(obs.getObs());
                        }
                        break;
                }
            }
        });

        chart = new JAnomalyDetectionChart();
        chart.addPropertyChangeListener(evt -> {
            switch (evt.getPropertyName()) {
                case JAnomalyDetectionChart.HOVERED_OBS_PROPERTY:
                    int obs = (Integer) evt.getNewValue();
                    JAnomalyDetectionChart.Model model = chart.getModel();
                    if (obs == -1 || model == null) {
                        grid.setHoveredObs(ObsIndex.NULL);
                    } else {
                        grid.setHoveredObs(ObsIndex.valueOf(grid.getTsCollection().indexOf(s -> s == model.getTs()), obs));
                    }
                    break;
            }
        });

        tsInformation = NbComponents.newJSplitPane(JSplitPane.VERTICAL_SPLIT, summary, chart);
        tsInformation.setResizeWeight(0.7);

        visualRepresentation = NbComponents.newJSplitPane(JSplitPane.HORIZONTAL_SPLIT, grid, tsInformation);
        visualRepresentation.setResizeWeight(0.6);

        toolBar = createToolBar();

        setLayout(new BorderLayout());
        add(toolBar, BorderLayout.NORTH);
        add(visualRepresentation, BorderLayout.CENTER);

        node = ControlNode.onComponentOpened(mgr, grid);

        associateLookup(ExplorerUtils.createLookup(mgr, getActionMap()));
        try {
            mgr.setSelectedNodes(new Node[]{node});
        } catch (PropertyVetoException ex) {
            Exceptions.printStackTrace(ex);
        }
    }
    // 

    // 
    private void refreshSummary() {
        RegSarimaModel ppm = grid.getModelOfSelection();
        if (ppm != null) {
            summary.set(ppm);
            chart.setModel(new JAnomalyDetectionChart.Model(grid.getSelectedItem(), ppm));
        } else {
            summary.set(null);
            chart.setModel(null);
        }
        summary.repaint();
    }

    private void onStateChanged(SwingWorker.StateValue state) {
        switch (state) {
            case STARTED:
                if (stopwatch == null) {
                    stopwatch = Stopwatch.createStarted();
                } else if (!stopwatch.isRunning()) {
                    stopwatch.reset();
                    stopwatch.start();
                }
                makeBusy(true);
                runButton.setEnabled(false);
                break;
            case DONE:
                runButton.setEnabled(!grid.getTsCollection().isEmpty());
                makeBusy(false);
                try {
                    NotifyUtil.show("Done !", "Processed " + grid.getTsCollection().size() + " items in " + stopwatch.stop().toString(), MessageType.SUCCESS, null, null, null);
                } catch (IllegalStateException e) {
                }

                if (ActiveViewManager.getInstance().isActive(this)) {
                    requestAttention(false);
                }
                break;
            case PENDING:
                runButton.setEnabled(!grid.getTsCollection().isEmpty());
        }
    }
    // 

    // 
    private JToolBar createToolBar() {
        JToolBar result = NbComponents.newInnerToolbar();
        result.setFloatable(false);
        result.addSeparator();
        result.add(Box.createRigidArea(new Dimension(5, 0)));
        runButton = result.add(new AbstractAction("", DemetraIcons.COMPILE_16) {
            @Override
            public void actionPerformed(ActionEvent e) {
                grid.calculateOutliers();
            }
        });
        runButton.setDisabledIcon(ImageUtilities.createDisabledIcon(runButton.getIcon()));
        runButton.setToolTipText("Run Outliers Detection");
        runButton.setEnabled(false);
        result.addSeparator();

        itemsLabel = (JLabel) result.add(new JLabel("No items"));
        result.addSeparator();

        prefButton = result.add(new AbstractAction("", DemetraIcons.PREFERENCES) {
            @Override
            public void actionPerformed(ActionEvent e) {
                new PropertySheetDialogBuilder().title("Properties").editNode(node);
            }
        });
        prefButton.setToolTipText("Open the properties dialog");
        result.addSeparator();

        result.add(createZoomButton());
        result.add(Box.createHorizontalGlue());
        result.addSeparator();

        return result;
    }
    // 

    // 
    private JButton createZoomButton() {
        final JPopupMenu addPopup = new JPopupMenu();
        int[] zoomValues = {200, 100, 75, 50, 25};
        for (int i = 0; i < zoomValues.length; ++i) {
            JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem(zoomValues[i] + " %");
            menuItem.setName(Integer.toString(zoomValues[i]));
            menuItem.addActionListener(new AbstractAction(Integer.toString(zoomValues[i])) {
                @Override
                public void actionPerformed(ActionEvent e) {
                    grid.setZoomPercentage(Integer.parseInt(getValue(NAME).toString()));
                }
            });
            menuItem.setState(i == 1);
            menuItem.setEnabled(i != 1);
            addPopup.add(menuItem);
        }
        grid.addPropertyChangeListener(JTsGrid.ZOOM_RATIO_PROPERTY, evt -> {
            for (Component o : addPopup.getComponents()) {
                JCheckBoxMenuItem item = (JCheckBoxMenuItem) o;
                item.setState(grid.getZoomPercentage() == Integer.parseInt(o.getName()));
                item.setEnabled(!item.isSelected());
            }
        });

        JButton result = DropDownButtonFactory.createDropDownButton(DemetraIcons.MAGNIFYING_TOOL, addPopup);
        result.setToolTipText("Zoom ratio of the current view");
        return result;
    }
    // 

    // 
    @Override
    public int getPersistenceType() {
        return TopComponent.PERSISTENCE_NEVER;
    }

    @Override
    public ExplorerManager getExplorerManager() {
        return mgr;
    }

    @Override
    public JComponent getVisualRepresentation() {
        return visualRepresentation;
    }

    @Override
    public JComponent getToolbarRepresentation() {
        return toolBar;
    }

    @Override
    public void setMultiViewCallback(MultiViewElementCallback mvec) {
        // Do nothing
    }

    @Override
    public CloseOperationState canCloseElement() {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public boolean fill(JMenu menu) {
        return false;
    }

    @Override
    public boolean hasContextMenu() {
        return false;
    }

    // 

    // 
    @Override
    public void componentOpened() {
        tsInformation.setDividerLocation((int) (((double) visualRepresentation.getPreferredSize().height) * 0.8));
    }

    @Override
    public void componentClosed() {
        mgr.setRootContext(Node.EMPTY);
    }

    @Override
    public void componentDeactivated() {
        super.componentDeactivated();
        ActiveViewManager.getInstance().set(null);
     }

    @Override
    public void componentActivated() {
        super.componentActivated();
        ActiveViewManager.getInstance().set(this);
    }

    @Override
    public void componentHidden() {
        super.componentHidden();
    }

    @Override
    public void componentShowing() {
        super.componentShowing();
    }
    // 
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy