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

gov.nasa.worldwind.formats.vpf.VPFCoveragePanel Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2012 United States Government as represented by the Administrator of the
 * National Aeronautics and Space Administration.
 * All Rights Reserved.
 */

package gov.nasa.worldwind.formats.vpf;

import gov.nasa.worldwind.WorldWindow;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.avlist.AVList;
import gov.nasa.worldwind.geom.Angle;
import gov.nasa.worldwind.geom.Extent;
import gov.nasa.worldwind.geom.Position;
import gov.nasa.worldwind.geom.Sector;

import javax.swing.*;
import javax.swing.Timer;
import javax.swing.border.CompoundBorder;
import javax.swing.border.TitledBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.*;

/**
 * VPF Layer selection panel
 *
 * @version $Id: VPFCoveragePanel.java 1171 2013-02-11 21:45:02Z dcollins $
 */
public class VPFCoveragePanel extends JPanel
{
    private WorldWindow wwd;
    private Dimension preferredSize;
    private JTabbedPane libraryTabbedPane;
    private VPFLayer layer;

    private JPanel legendPanel;

    public VPFCoveragePanel(WorldWindow wwd, VPFDatabase db)
    {
        // Make a panel at a default size.
        super(new BorderLayout());
        this.wwd = wwd;
        this.makePanel(db, new Dimension(200, 400));
    }

    public VPFCoveragePanel(VPFDatabase db, Dimension size)
    {
        // Make a panel at a specified size.
        super(new BorderLayout());
        this.makePanel(db, size);
    }

    public VPFLayer getLayer()
    {
        return this.layer;
    }

    public void setLayer(VPFLayer layer)
    {
        this.layer = layer;
        this.fillLegendPanel();
    }

    private void makePanel(VPFDatabase db, Dimension size)
    {
        this.preferredSize = size;
        this.libraryTabbedPane = new JTabbedPane();
        this.add(this.libraryTabbedPane, BorderLayout.CENTER);

        this.fill(db);
        this.startLegendUpdateTimer();
    }

    private void fill(VPFDatabase db)
    {
        this.addDatabase(db);
        this.addLegend();
    }

    public void addDatabase(VPFDatabase db)
    {
        // Sort the library list alphabetically.
        ArrayList sortedList = new ArrayList();
        sortedList.addAll(db.getLibraries());
        this.sortPropertyLists(sortedList, AVKey.DISPLAY_NAME);

        for (VPFLibrary lib : sortedList)
        {
            this.addLibrary(db, lib);
        }
    }

    public void addLibrary(VPFDatabase db, VPFLibrary lib)
    {
        // Make and fill the panel holding the library coverages.
        JPanel selectionPanel = new JPanel(new GridLayout(0, 1, 0, 4));
        selectionPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));

        // Must put the layer grid in a container to prevent scroll panel from stretching their vertical spacing.
        JPanel dummyPanel = new JPanel(new BorderLayout());
        dummyPanel.add(selectionPanel, BorderLayout.NORTH);

        // Put the name panel in a scroll bar.
        JScrollPane scrollPane = new JScrollPane(dummyPanel);
        scrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
        if (this.preferredSize != null)
            scrollPane.setPreferredSize(this.preferredSize);

        // Add the scroll bar and name panel to a titled panel that will resize with the main window.
        JPanel westPanel = new JPanel(new GridLayout(0, 1, 0, 0));
        westPanel.setBorder(
            new CompoundBorder(BorderFactory.createEmptyBorder(9, 9, 9, 9), new TitledBorder("Coverage")));
        westPanel.setToolTipText("Coverage to Show");
        westPanel.add(scrollPane);

        // Add the library enable/disable check box
        Box box = Box.createVerticalBox();
        box.setBorder(BorderFactory.createEmptyBorder(18, 9, 0, 9)); // top, left, bottom, right

        ZoomAction zoomAction = new ZoomAction(lib);
        JButton button = new JButton(zoomAction);
        button.setAlignmentX(Component.LEFT_ALIGNMENT);
        box.add(button);
        box.add(Box.createVerticalStrut(18));

        LibraryAction libraryAction = new LibraryAction(db, lib, false); // default to non selected
        JCheckBox jcb = new JCheckBox(libraryAction);
        jcb.setSelected(libraryAction.selected);
        jcb.setAlignmentX(Component.LEFT_ALIGNMENT);
        box.add(jcb);

        // Must put the box in a container to prevent layout from stretching their vertical spacing.
        dummyPanel = new JPanel(new BorderLayout(0, 0)); // hgap, vgap
        dummyPanel.add(box, BorderLayout.NORTH);
        dummyPanel.add(westPanel, BorderLayout.CENTER);

        // Put the coverage titled panel in the library tabbed pane.
        this.libraryTabbedPane.add(lib.getName(), dummyPanel);
        int index = this.libraryTabbedPane.indexOfComponent(dummyPanel);
        this.libraryTabbedPane.setToolTipTextAt(index, lib.getDescription());

        // Sort the coverage list alphabetically.
        ArrayList sortedList = new ArrayList();
        sortedList.addAll(lib.getCoverages());
        this.sortPropertyLists(sortedList, AVKey.DESCRIPTION);

        for (VPFCoverage cov : sortedList)
        {
            if (cov.isReferenceCoverage())
                continue;

            this.addCoverage(db, cov, selectionPanel);
        }
    }

    protected void sortPropertyLists(java.util.List propertyList, final String propertyName)
    {
        Collections.sort(propertyList, new Comparator()
        {
            public int compare(AVList a, AVList b)
            {
                String aValue = (a.getValue(propertyName) != null) ? a.getValue(propertyName).toString() : "";
                String bValue = (b.getValue(propertyName) != null) ? b.getValue(propertyName).toString() : "";
                return String.CASE_INSENSITIVE_ORDER.compare(aValue, bValue);
            }
        });
    }

    public void addLegend()
    {
        // Make and fill the panel holding the legend items.
        this.legendPanel = new JPanel();
        this.legendPanel.setLayout(new BoxLayout(this.legendPanel, BoxLayout.PAGE_AXIS));
        this.legendPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));

        // Must put the grid in a container to prevent scroll panel from stretching their vertical spacing.
        JPanel dummyPanel = new JPanel(new BorderLayout());
        dummyPanel.add(this.legendPanel, BorderLayout.NORTH);

        // Put the panel in a scroll bar.
        JScrollPane scrollPane = new JScrollPane(dummyPanel);
        scrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
        if (this.preferredSize != null)
            scrollPane.setPreferredSize(this.preferredSize);

        // Add the scroll bar and panel to a titled panel that will resize with the main window.
        JPanel westPanel = new JPanel(new GridLayout(0, 1, 0, 0));
        westPanel.setBorder(
            new CompoundBorder(BorderFactory.createEmptyBorder(9, 9, 9, 9), new TitledBorder("Legend")));
        westPanel.setToolTipText("VPF Layer Legend");
        westPanel.add(scrollPane);
        westPanel.setAlignmentX(Component.LEFT_ALIGNMENT);

        Box box = Box.createVerticalBox();
        box.add(westPanel);

        // Must put the box in a container to prevent layout from stretching their vertical spacing.
        dummyPanel = new JPanel(new BorderLayout());
        dummyPanel.add(box, BorderLayout.CENTER);

        // Put the titled panel in the library tabbed pane.
        this.libraryTabbedPane.add("Legend", dummyPanel);
    }

    protected void fillLegendPanel()
    {
        this.legendPanel.removeAll();

        if (this.layer == null)
            return;

        Iterable symbols = this.layer.getActiveSymbols();
        if (symbols == null)
            return;

        // Sort rendering attributes, and eliminate duplicate entries.
        Iterable symbolInfo = this.getUniqueSymbols(symbols);

        // Compose legend panel
        VPFLegendSupport legendSupport = new VPFLegendSupport();
        String coverageName = null;
        for (SymbolInfo info : symbolInfo)
        {
            // Insert coverage title
            if (coverageName == null || !info.getFeatureClass().getCoverage().getName().equals(coverageName))
            {
                if (coverageName != null)
                    this.legendPanel.add(Box.createVerticalStrut(5));
                JLabel label = new JLabel(info.getFeatureClass().getCoverage().getDescription());
                this.legendPanel.add(label);
                coverageName = info.getFeatureClass().getCoverage().getName();
                this.legendPanel.add(new JSeparator(JSeparator.HORIZONTAL));
                this.legendPanel.add(Box.createVerticalStrut(5));
            }
            // Add legend item
            String description = info.getDescription() != null ? info.getDescription() : "";
            if (description.length() > 0)
                description = description.substring(0, 1).toUpperCase() + description.substring(1);
            BufferedImage legendImage = legendSupport.createLegendImage(info.getAttributes(), 60, 22, 0);
            if (legendImage != null && description.length() > 0)
            {
                Icon icon = new ImageIcon(legendImage);
                JLabel label = new JLabel(description, icon, SwingConstants.LEFT);
                label.setIconTextGap(8);
                this.legendPanel.add(label);
                this.legendPanel.add(Box.createVerticalStrut(2));
            }
        }

        this.legendPanel.revalidate();
        this.legendPanel.repaint();
    }

    protected Iterable getUniqueSymbols(Iterable iterable)
    {
        // Use a TreeSet to consolidate duplicate symbol attributes and simultaneously sort the attributes.

        Set set = new TreeSet(new Comparator()
        {
            public int compare(SymbolInfo a, SymbolInfo b)
            {
                String aCoverageName = (a.getFeatureClass().getCoverage().getName() != null)
                    ? a.getFeatureClass().getCoverage().getName() : "";
                String bCoverageName = (b.getFeatureClass().getCoverage().getName() != null)
                    ? b.getFeatureClass().getCoverage().getName() : "";

                int i = String.CASE_INSENSITIVE_ORDER.compare(aCoverageName, bCoverageName);
                if (i != 0)
                    return i;

                String aKey = (a.getAttributes().getSymbolKey() != null) ? a.getAttributes().getSymbolKey().toString()
                    : "";
                String bKey = (b.getAttributes().getSymbolKey() != null) ? b.getAttributes().getSymbolKey().toString()
                    : "";

                i = String.CASE_INSENSITIVE_ORDER.compare(aKey, bKey);
                if (i != 0)
                    return i;

                int aType = (a.getFeatureClass().getType() != null) ? a.getFeatureClass().getType().ordinal() : -1;
                int bType = (b.getFeatureClass().getType() != null) ? b.getFeatureClass().getType().ordinal() : -1;

                return (aType < bType) ? -1 : ((aType > bType) ? 1 : 0);
            }
        });

        for (VPFSymbol symbol : iterable)
        {
            if (symbol != null && symbol.getFeature() != null && symbol.getAttributes() != null)
            {
                set.add(new SymbolInfo(symbol.getFeature().getFeatureClass(), symbol.getAttributes()));
            }
        }

        return set;
    }

    protected static class SymbolInfo
    {
        protected VPFFeatureClass featureClass;
        protected VPFSymbolAttributes attributes;

        public SymbolInfo(VPFFeatureClass featureClass, VPFSymbolAttributes attributes)
        {
            this.featureClass = featureClass;
            this.attributes = attributes;
        }

        public VPFFeatureClass getFeatureClass()
        {
            return this.featureClass;
        }

        public VPFSymbolAttributes getAttributes()
        {
            return this.attributes;
        }

        public String getDescription()
        {
            return this.attributes.getDescription();
        }
    }

    protected void addCoverage(VPFDatabase db, VPFCoverage cov, Container parent)
    {
        CoverageAction action = new CoverageAction(db, cov, false); // default to non selected
        JCheckBox jcb = new JCheckBox(action);
        jcb.setSelected(action.selected);
        parent.add(jcb);
    }

    public void update(VPFDatabase db)
    {
        // Refresh the coverage list from the given db
        this.libraryTabbedPane.removeAll();
        this.fill(db);
        this.libraryTabbedPane.revalidate();
        this.libraryTabbedPane.repaint();
    }

    public void clear()
    {
        this.libraryTabbedPane.removeAll();
        this.libraryTabbedPane.revalidate();
        this.libraryTabbedPane.repaint();
    }

    private void startLegendUpdateTimer()
    {
        Timer timer = new Timer(3000, new ActionListener()
        {
            public void actionPerformed(ActionEvent event)
            {
                fillLegendPanel();
            }
        });
        timer.start();
    }

    private class LibraryAction extends AbstractAction
    {
        VPFDatabase db;
        VPFLibrary library;
        private boolean selected;

        public LibraryAction(VPFDatabase db, VPFLibrary library, boolean selected)
        {
            super("Show Library");
            this.putValue(Action.SHORT_DESCRIPTION, "Show " + library.getName());
            this.db = db;
            this.library = library;
            this.selected = selected;
        }

        public void actionPerformed(ActionEvent actionEvent)
        {
            // Fire property change event on the database
            boolean newState = ((JCheckBox) actionEvent.getSource()).isSelected();
            this.db.firePropertyChange(
                new PropertyChangeEvent(this.library, VPFLayer.LIBRARY_CHANGED, this.selected, newState));
            this.selected = newState;
            wwd.redraw();
        }
    }

    private class CoverageAction extends AbstractAction implements PropertyChangeListener
    {
        VPFDatabase db;
        VPFCoverage coverage;
        private boolean selected;

        public CoverageAction(VPFDatabase db, VPFCoverage coverage, boolean selected)
        {
            super(coverage.getDescription());
            this.putValue(Action.SHORT_DESCRIPTION, "Show " + coverage.getDescription());
            this.db = db;
            this.coverage = coverage;
            this.selected = selected;
            this.db.addPropertyChangeListener(this);
            this.setEnabled(false);
        }

        public void actionPerformed(ActionEvent actionEvent)
        {
            // Fire property change event on the database
            boolean newState = ((JCheckBox) actionEvent.getSource()).isSelected();
            this.db.firePropertyChange(
                new PropertyChangeEvent(this.coverage, VPFLayer.COVERAGE_CHANGED, this.selected, newState));
            this.selected = newState;
            wwd.redraw();
        }

        public void propertyChange(PropertyChangeEvent event)
        {
            if (event.getPropertyName().equals(VPFLayer.LIBRARY_CHANGED))
            {
                VPFLibrary library = (VPFLibrary) event.getSource();
                boolean enabled = (Boolean) event.getNewValue();

                if (library.getFilePath().equals(this.coverage.getLibrary().getFilePath()))
                {
                    this.setEnabled(enabled);
                }
            }
        }
    }

    private class ZoomAction extends AbstractAction
    {
        private VPFLibrary library;

        private ZoomAction(VPFLibrary library)
        {
            super("Zoom To Library");
            this.library = library;
            this.setEnabled(this.library.getBounds() != null);
        }

        public void actionPerformed(ActionEvent actionEvent)
        {
            Sector sector = this.library.getBounds().toSector();
            Extent extent = Sector.computeBoundingCylinder(wwd.getModel().getGlobe(),
                wwd.getSceneController().getVerticalExaggeration(), sector);

            Angle fov = wwd.getView().getFieldOfView();
            Position centerPos = new Position(sector.getCentroid(), 0d);
            double zoom = extent.getRadius() / fov.cosHalfAngle() / fov.tanHalfAngle();

            wwd.getView().goTo(centerPos, zoom);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy