/* Copyright 2012, UCAR/Unidata.
   See the LICENSE file for more information.
*/

package dap4.dap4lib.serial;

import dap4.core.dmr.*;
import dap4.core.util.*;
import dap4.dap4lib.AbstractCursor;
import dap4.dap4lib.LibTypeFcns;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

public class D4Cursor extends AbstractCursor
{
    //////////////////////////////////////////////////
    // Mnemonics
    static final long NULLOFFSET = -1;

    static final int D4LENSIZE = 8;

    //////////////////////////////////////////////////
    // Instance Variables

    protected long offset = NULLOFFSET;

    protected long[] bytestrings = null;

    // For debugging purposes, we keep these separate,
    // but some merging could be done .

    // Track the array elements for a structure array
    protected D4Cursor[] elements = null; // scheme == STRUCTARRAY|SEQARRAY

    // Track the fields of a structure instance
    protected D4Cursor[] fieldcursors = null; // scheme == STRUCTURE|SEQUENCE

    // Track the records of a sequence instance
    protected List<D4Cursor> records = null; // scheme == SEQUENCE

    //////////////////////////////////////////////////
    // Constructor(s)

    public D4Cursor(Scheme scheme, D4DSP dsp, DapNode template, D4Cursor container)
    {
        super(scheme, dsp, template, container);
    }

    /**
     * Effectively a clone of c
     *
     * @param c cursor to clone
     */
    public D4Cursor(D4Cursor c)
    {
        super(c);
        assert false;
        this.offset = c.offset;
        this.bytestrings = c.bytestrings;
        this.fieldcursors = new D4Cursor[c.fieldcursors.length];
        for(int i = 0; i < c.fieldcursors.length; i++) {
            D4Cursor dc = c.fieldcursors[i];
            this.fieldcursors[i] = new D4Cursor(dc);
            this.fieldcursors[i].setContainer(this); // Change the parent in the clone
        }
        this.elements = new D4Cursor[c.elements.length];
        for(int i = 0; i < c.elements.length; i++) {
            D4Cursor dc = c.elements[i];
            this.elements[i] = new D4Cursor(dc);
            this.elements[i].setContainer(this); // Change the parent in the clone
        }
        this.records = new ArrayList<>();
        for(int i = 0; i < c.records.size(); i++) {
            D4Cursor dc = c.records.get(i);
            this.records.add(new D4Cursor(dc));
            this.records.get(i).setContainer(this); // Change the parent in the clone
        }
    }

    //////////////////////////////////////////////////
    // DataCursor API (Except as Implemented in AbstractCursor)

    @Override
    public Object
    read(Index index)
            throws DapException
    {
        return read(DapUtil.indexToSlices(index));
    }

    @Override
    public Object
    read(List<Slice> slices)
            throws DapException
    {
        switch (this.scheme) {
        case ATOMIC:
            return readAtomic(slices);
        case STRUCTURE:
        case SEQUENCE:
            if(((DapVariable) this.getTemplate()).getRank() == 0
                    || DapUtil.isScalarSlices(slices))
                throw new DapException("Cannot slice a scalar variable");
            return new D4Cursor(this);
        case STRUCTARRAY:
            // Read the structures specified by slices
            Odometer odom = Odometer.factory(slices);
            D4Cursor[] instances = new D4Cursor[(int) odom.totalSize()];
            for(int i = 0; odom.hasNext(); i++) {
                instances[i] = readStructure(odom.next());
            }
            return instances;
        case SEQARRAY:
            odom = Odometer.factory(slices);
            instances = new D4Cursor[(int) odom.totalSize()];
            for(int i = 0; odom.hasNext(); i++) {
                instances[i] = readSequence(odom.next());
            }
            return instances;
        default:
            throw new DapException("Attempt to slice a scalar object");
        }
    }

    @Override
    public D4Cursor
    readField(int findex)
            throws DapException
    {
        assert (this.scheme == scheme.RECORD || this.scheme == scheme.STRUCTURE);
        DapStructure basetype = (DapStructure) ((DapVariable) getTemplate()).getBaseType();
        if(findex < 0 || findex >= basetype.getFields().size())
            throw new DapException("Field index out of range: " + findex);
        D4Cursor field = this.fieldcursors[findex];
        return field;
    }

    @Override
    public D4Cursor
    readRecord(long i)
    {
        assert (this.scheme == Scheme.SEQUENCE);
        if(this.records == null || i < 0 || i > this.records.size())
            throw new IndexOutOfBoundsException("No such record: " + i);
        return this.records.get((int) i);
    }

    @Override
    public long
    getRecordCount()
    {
        assert (this.scheme == Scheme.SEQUENCE);
        return this.records == null ? 0 : this.records.size();
    }

    //////////////////////////////////////////////////
    // Support methods

    protected Object
    readAtomic(List<Slice> slices)
            throws DapException
    {
        if(slices == null)
            throw new DapException("DataCursor.read: null set of slices");
        assert this.scheme == Scheme.ATOMIC;
        DapVariable atomvar = (DapVariable) getTemplate();
        int rank = atomvar.getRank();
        assert slices != null && ((rank == 0 && slices.size() == 1) || (slices.size() == rank));
        DapType basetype = atomvar.getBaseType();
        return readAs(atomvar, basetype, slices);
    }

    /**
     * Allow specification of basetype to use; used for enumerations
     *
     * @param atomvar
     * @param basetype
     * @param slices
     * @return
     * @throws DapException
     */
    protected Object
    readAs(DapVariable atomvar, DapType basetype, List<Slice> slices)
            throws DapException
    {
        if(basetype.getTypeSort() == TypeSort.Enum) {// short circuit this case
            basetype = ((DapEnumeration) basetype).getBaseType();
            return readAs(atomvar, basetype, slices);
        }
        long count = DapUtil.sliceProduct(slices);
        Object result = LibTypeFcns.newVector(basetype, count);
        Odometer odom = Odometer.factory(slices);
        if(DapUtil.isContiguous(slices) && basetype.isFixedSize())
            readContig(slices, basetype, count, odom, result);
        else
            readOdom(slices, basetype, odom, result);
        return result;
    }

    protected void
    readContig(List<Slice> slices, DapType basetype, long count, Odometer odom, Object result)
            throws DapException
    {
        ByteBuffer alldata = ((D4DSP) this.dsp).getBuffer();
        long off = this.offset;
        long ix = odom.indices().index();
        int elemsize = basetype.getSize();
        alldata.position((int) (off + (ix * elemsize)));
        int icount = (int) count;
        long totalsize = count * basetype.getSize();
        switch (basetype.getTypeSort()) {
        case Int8:
        case UInt8:
            alldata.get((byte[]) result);
            break;
        case Char: // remember, we are reading 7-bit ascii, not utf-8 or utf-16
            byte[] ascii = new byte[icount];
            alldata.get(ascii);
            for(int i = 0; i < icount; i++) {
                ((char[]) result)[i] = (char) (ascii[i] & 0x7f);
            }
            break;
        case Int16:
        case UInt16:
            alldata.asShortBuffer().get((short[]) result);
            skip(totalsize, alldata);
            break;
        case Int32:
        case UInt32:
            alldata.asIntBuffer().get((int[]) result);
            skip(totalsize, alldata);
            break;
        case Int64:
        case UInt64:
            alldata.asLongBuffer().get((long[]) result);
            skip(totalsize, alldata);
            break;
        case Float32:
            alldata.asFloatBuffer().get((float[]) result);
            skip(totalsize, alldata);
            break;
        case Float64:
            alldata.asDoubleBuffer().get((double[]) result);
            skip(totalsize, alldata);
            break;
        default:
            throw new DapException("Contiguous read not supported for type: " + basetype.getTypeSort());
        }
    }

    protected Object
    readOdom(List<Slice> slices, DapType basetype, Odometer odom, Object result)
            throws DapException
    {
        ByteBuffer alldata = ((D4DSP) this.dsp).getBuffer();
        alldata.position((int) this.offset);
        ByteBuffer slice = alldata.slice();
        slice.order(alldata.order());
        for(int i = 0; odom.hasNext(); i++) {
            Index index = odom.next();
            int ipos = (int) index.index();
            switch (basetype.getTypeSort()) {
            case Int8:
            case UInt8:
                ((byte[]) result)[i] = slice.get(ipos);
                break;
            case Char: // remember, we are reading 7-bit ascii, not utf-8 or utf-16
                byte ascii = slice.get(ipos);
                ((char[]) result)[i] = (char) ascii;
                break;
            case Int16:
            case UInt16:
                ((short[]) result)[i] = slice.getShort(ipos);
                break;
            case Int32:
            case UInt32:
                ((int[]) result)[i] = slice.getInt(ipos);
                break;
            case Int64:
            case UInt64:
                ((long[]) result)[i] = slice.getLong(ipos);
                break;
            case Float32:
                ((float[]) result)[i] = slice.getFloat(ipos);
                break;
            case Float64:
                ((double[]) result)[i] = slice.getDouble(ipos);
                break;
            case String:
            case URL:
                int savepos = alldata.position();
                long pos = bytestrings[i];
                alldata.position((int) pos); // bytestring offsets are absolute
                long n = getLength(alldata);
                byte[] data = new byte[(int) n];
                alldata.get(data);
                ((String[]) result)[i] = new String(data, DapUtil.UTF8);
                alldata.position(savepos);
                break;
            case Opaque:
                savepos = alldata.position();
                pos = bytestrings[i];
                alldata.position((int) pos); // bytestring offsets are absolute
                n = getLength(alldata);
                data = new byte[(int) n];
                alldata.get(data);
                ByteBuffer buf = ByteBuffer.wrap(data);
                ((ByteBuffer[]) result)[i] = buf;
                alldata.position(savepos);
                break;
            default:
                throw new DapException("Attempt to read non-atomic value of type: " + basetype.getTypeSort());
            }
        }
        return result;
    }

    protected D4Cursor
    readStructure(Index index)
            throws DapException
    {
        assert (this.scheme == Scheme.STRUCTARRAY);
        long pos = index.index();
        long avail = (this.elements == null ? 0 : this.elements.length);
        if(pos < 0 || pos > avail)
            throw new IndexOutOfBoundsException("read: " + index);
        return this.elements[(int) pos];
    }

    public D4Cursor
    readSequence(Index index)
            throws DapException
    {
        assert (this.scheme == Scheme.SEQARRAY);
        long pos = index.index();
        long avail = (this.elements == null ? 0 : this.elements.length);
        if(pos < 0 || pos > avail)
            throw new IndexOutOfBoundsException("read: " + index);
        return this.elements[(int) pos];
    }

    //////////////////////////////////////////////////
    // D4Cursor Extensions

    public D4Cursor
    setElements(D4Cursor[] instances)
    {
        if(!(getScheme() == Scheme.SEQARRAY
                || getScheme() == Scheme.STRUCTARRAY))
            throw new IllegalStateException("Adding element to !(structure|sequence array) object");
        DapVariable var = (DapVariable) getTemplate();
        this.elements = instances;
        return this;
    }

    public D4Cursor
    setOffset(long pos)
    {
        this.offset = pos;
        return this;
    }

    public D4Cursor
    setByteStringOffsets(long total, long[] positions)
    {
        this.bytestrings = positions;
        return this;
    }

    public D4Cursor
    addField(int m, D4Cursor field)
    {
        if(getScheme() != Scheme.RECORD && getScheme() != Scheme.STRUCTURE)
            throw new IllegalStateException("Adding field to non-(structure|record) object");
        if(this.fieldcursors == null) {
            DapStructure ds = (DapStructure) ((DapVariable) getTemplate()).getBaseType();
            List<DapVariable> fields = ds.getFields();
            this.fieldcursors = new D4Cursor[fields.size()];
        }
        if(this.fieldcursors[m] != null)
            throw new IndexOutOfBoundsException("Adding duplicate fields at position:" + m);
        this.fieldcursors[m] = field;
        return this;
    }

    public D4Cursor
    addRecord(D4Cursor rec)
    {
        if(getScheme() != Scheme.SEQUENCE)
            throw new IllegalStateException("Adding record to non-sequence object");
        if(this.records == null)
            this.records = new ArrayList<>();
        this.records.add(rec);
        return this;
    }

    public long
    getElementSize(DapVariable v)
    {
        return v.getBaseType().isFixedSize() ? v.getBaseType().getSize() : 0;
    }

    static ByteBuffer
    skip(long n, ByteBuffer b)
    {
        if(b.position() + ((int) n) > b.limit())
            throw new IllegalArgumentException();
        b.position(b.position() + ((int) n));
        return b;
    }

    static public long
    getLength(ByteBuffer b)
    {
        if(b.position() + D4LENSIZE > b.limit())
            throw new IllegalArgumentException();
        long n = b.getLong();
        return n;
    }

}


/*
 * Copyright (c) 1998-2019 University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */

package ucar.nc2.ui.coverage2;

import ucar.nc2.ft2.coverage.*;
import ucar.nc2.ui.event.ActionCoordinator;
import ucar.nc2.ui.event.ActionSourceListener;
import ucar.nc2.ui.event.ActionValueEvent;
import ucar.nc2.ui.geoloc.*;
import ucar.nc2.ui.gis.MapBean;
import ucar.nc2.ui.grid.*;
import ucar.nc2.ui.widget.*;
import ucar.nc2.ui.widget.PopupMenu;
import ucar.nc2.util.NamedObject;
import ucar.unidata.geoloc.ProjectionImpl;
import ucar.unidata.geoloc.ProjectionPointImpl;
import ucar.unidata.geoloc.ProjectionRect;
import ucar.unidata.geoloc.projection.LatLonProjection;
import ucar.util.prefs.PreferencesExt;
import ucar.util.prefs.ui.Debug;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JToolBar;
import javax.swing.RootPaneContainer;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.EtchedBorder;

/**
 * ft2.coverage widget for displaying
 * more or less the controller in MVC
 *
 * @author John
 * @since 12/27/12
 */
public class CoverageViewer extends JPanel {
  static private final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CoverageViewer.class);

  // constants
  private static final int DELAY_DRAW_AFTER_DATA_EVENT = 250;   // quarter sec
  private static final String LastMapAreaName = "LastMapArea";
  private static final String LastProjectionName = "LastProjection";
  private static final String LastDatasetName = "LastDataset";
  private static final String ColorScaleName = "ColorScale";
  static private final String DATASET_URL = "DatasetURL";
  static private final String GEOTIFF_FILECHOOSER_DEFAULTDIR = "geotiffDefDir";

  private PreferencesExt store;
  private JFrame parent;
  private FileManager fileChooser;

  // UI components
  private ColorScale colorScale;
  private ColorScale.Panel colorScalePanel;
  //private VertPanel vertPanel;
  private List<Chooser> choosers;
  private SuperComboBox fieldChooser, levelChooser, timeChooser, ensembleChooser, runtimeChooser;
  private JLabel dataValueLabel, positionLabel;

  private NavigatedPanel navPanel;

  // the main components
  // private GridController controller;
  //private GeoGridTable dsTable;

  // UI components that need global scope
  private TextHistoryPane datasetInfoTA, ncmlTA;
  private JPanel drawingPanel;
  //private JSplitPane splitDraw;
  private JComboBox csDataMinMax;
  private PopupMenu mapBeanMenu;
  private JSpinner strideSpinner;

  private JLabel datasetNameLabel = new JLabel();
  // private Field.TextCombo gridUrlIF;
  // private PrefPanel gridPP;

  // the various managers and dialog boxes
  private ProjectionManager projManager;
  //private ColorScaleManager csManager;
  private IndependentWindow infoWindow = null;
  private IndependentWindow ncmlWindow = null;
  private IndependentWindow gtWindow = null;
  private JDialog dsDialog = null;
  private FileManager geotiffFileChooser;

  // toolbars
  private JPanel fieldPanel, toolPanel;
  private JToolBar navToolbar, moveToolbar;
  private AbstractAction navToolbarAction, moveToolbarAction;

  // actions
  private AbstractAction redrawAction;
  private AbstractAction showDatasetInfoAction;
  //private AbstractAction showNcMLAction;
  //private AbstractAction showGridTableAction;
  //private AbstractAction showGridDatasetInfoAction;
  //private AbstractAction showNetcdfDatasetAction;
  private AbstractAction minmaxHorizAction, minmaxLogAction, minmaxHoldAction;
  private AbstractAction fieldLoopAction, levelLoopAction, timeLoopAction, runtimeLoopAction;
  private AbstractAction chooseProjectionAction, saveCurrentProjectionAction;

  private AbstractAction dataProjectionAction, drawBBAction, helpAction, showGridAction, showContoursAction, showContourLabelsAction, showWindsAction;
  private AbstractAction drawHorizAction, drawVertAction;

  // data components
  private DataState dataState;
  private CoverageCollection coverageDataset;
  private Coverage currentField;
  private ProjectionImpl project;

  // state
  private List<NamedObject> levelNames, timeNames, ensembleNames, runtimeNames;
  private int currentLevel;
  private int currentSlice;
  private int currentTime;
  private int currentEnsemble;
  private int currentRunTime;
  private boolean drawHorizOn = true, drawVertOn = false;
  private boolean eventsOK = true;
  private Color mapColor = Color.black;
  private boolean drawWinds = false;
  private boolean hasDependentTimeAxis = false;
  private boolean selected = false;
  private int mapBeanCount = 0;

  // rendering
  private AffineTransform atI = new AffineTransform();  // identity transform
  private ucar.nc2.ui.util.Renderer mapRenderer = null;
  private CoverageRenderer coverageRenderer;
  //private WindRenderer renderWind;
  private javax.swing.Timer redrawTimer;

  // debugging
  private boolean debugBeans = false, debugChooser = false, debugPrint = false, debugHelp = false;
  private boolean debugTask = false, debugThread = false;

  public CoverageViewer(PreferencesExt pstore, RootPaneContainer root, FileManager fileChooser, int defaultHeight) {
    this.store = pstore;
    this.fileChooser = fileChooser;

    try {
      // choosers
      choosers = new ArrayList<>();
      fieldChooser = new SuperComboBox(root, "field", true, null);
      choosers.add(new Chooser("field", fieldChooser, true));
      levelChooser = new SuperComboBox(root, "level", false, null);
      choosers.add(new Chooser("level", levelChooser, false));
      timeChooser = new SuperComboBox(root, "time", false, null);
      choosers.add(new Chooser("time", timeChooser, false));
      ensembleChooser = new SuperComboBox(root, "ensemble", false, null);
      choosers.add(new Chooser("ensemble", ensembleChooser, false));
      runtimeChooser = new SuperComboBox(root, "runtime", false, null);
      choosers.add(new Chooser("runtime", runtimeChooser, false));

      // gridTable
      //gridTable = new GridTable("field");
      //gtWindow = new IndependentWindow("Grid Table Information", BAMutil.getImage( "GDVs"), gridTable.getPanel());

      //PreferencesExt dsNode = (PreferencesExt) pstore.node("DatasetTable");
      // dsTable = new GeoGridTable(dsNode, true);
      //dsDialog = dsTable.makeDialog(root, "NetcdfDataset Info", false);
      //dsDialog.setIconImage( BAMutil.getImage( "GDVs"));
      //Rectangle bounds = (Rectangle) dsNode.getBean("DialogBounds", new Rectangle(50, 50, 800, 450));
      //dsDialog.setBounds( bounds);

      // colorscale
      Object bean = store.getBean(ColorScaleName, null);
      if ((null == bean) || !(bean instanceof ColorScale))
        colorScale = new ColorScale("default");
      else
        colorScale = (ColorScale) store.getBean(ColorScaleName, null);

      colorScalePanel = new ColorScale.Panel(this, colorScale);
      csDataMinMax = new JComboBox(ColorScale.MinMaxType.values());
      csDataMinMax.setToolTipText("ColorScale Min/Max setting");
      csDataMinMax.addActionListener(e -> {
          coverageRenderer.setDataMinMaxType((ColorScale.MinMaxType) csDataMinMax.getSelectedItem());
          redrawLater();
      });

      // renderer
      // set up the renderers; Maps are added by addMapBean()
      coverageRenderer = new CoverageRenderer(store);
      coverageRenderer.setColorScale(colorScale);

      strideSpinner = new JSpinner(new SpinnerNumberModel(1, 1, 100, 1));
      strideSpinner.addChangeListener(e -> {
          Integer val = (Integer) strideSpinner.getValue();
          coverageRenderer.setHorizStride(val.intValue());
      });

      makeActionsDataset();
      makeActionsToolbars();
      makeActions();
      makeEventManagement();

      //// toolPanel
      toolPanel = new JPanel();
      toolPanel.setBorder(new EtchedBorder());
      toolPanel.setLayout(new MFlowLayout(FlowLayout.LEFT, 0, 0));

      // menus
      JMenu dataMenu = new JMenu("Dataset");
      dataMenu.setMnemonic('D');
      JMenu configMenu = new JMenu("Configure");
      configMenu.setMnemonic('C');
      JMenu toolMenu = new JMenu("Controls");
      toolMenu.setMnemonic('T');
      JMenuBar menuBar = new JMenuBar();
      menuBar.add(dataMenu);
      menuBar.add(configMenu);
      menuBar.add(toolMenu);
      toolPanel.add(menuBar);

      // field chooser panel - delay adding the choosers
      fieldPanel = new JPanel();
      fieldPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
      toolPanel.add(fieldPanel);

      // stride
      toolPanel.add(strideSpinner);

      // buttcons
      BAMutil.addActionToContainer(toolPanel, drawHorizAction);
      BAMutil.addActionToContainer(toolPanel, drawVertAction);
      mapBeanMenu = MapBean.makeMapSelectButton();
      toolPanel.add(mapBeanMenu.getParentComponent());

      // the Navigated panel and its toolbars
      navPanel = new NavigatedPanel();
      navPanel.setLayout(new FlowLayout());
      ProjectionRect ma = (ProjectionRect) store.getBean(LastMapAreaName, null);
      if (ma != null)
        navPanel.setMapArea( ma);

      navToolbar = navPanel.getNavToolBar();
      moveToolbar = navPanel.getMoveToolBar();
      if ((Boolean) navToolbarAction.getValue(BAMutil.STATE))
        toolPanel.add(navToolbar);
      if ((Boolean) moveToolbarAction.getValue(BAMutil.STATE))
        toolPanel.add(moveToolbar);
      makeNavPanelWiring();
      addActionsToMenus(dataMenu, configMenu, toolMenu);

      BAMutil.addActionToContainer(toolPanel, navPanel.setReferenceAction);
      BAMutil.addActionToContainer(toolPanel, dataProjectionAction);
      BAMutil.addActionToContainer(toolPanel, showGridAction);
      BAMutil.addActionToContainer(toolPanel, drawBBAction);
      // BAMutil.addActionToContainer(toolPanel, showContourLabelsAction);
      BAMutil.addActionToContainer(toolPanel, redrawAction);

      //  vertical split
      //vertPanel = new VertPanel();
      //splitDraw = new JSplitPane(JSplitPane.VERTICAL_SPLIT, panz, vertPanel);
      //int divLoc = store.getInt( "vertSplit", 2*defaultHeight/3);
      //splitDraw.setDividerLocation(divLoc);
      drawingPanel = new JPanel(new BorderLayout()); // filled later

      // status panel
      JPanel statusPanel = new JPanel(new BorderLayout());
      statusPanel.setBorder(new EtchedBorder());
      positionLabel = new JLabel("position");
      positionLabel.setToolTipText("position at cursor");
      dataValueLabel = new JLabel("data value", SwingConstants.CENTER);
      dataValueLabel.setToolTipText("data value (double click on grid)");
      statusPanel.add(positionLabel, BorderLayout.WEST);
      statusPanel.add(dataValueLabel, BorderLayout.CENTER);
      navPanel.setPositionLabel(positionLabel);

      // assemble
      JPanel westPanel = new JPanel(new BorderLayout());
      westPanel.add(colorScalePanel, BorderLayout.CENTER);
      westPanel.add(csDataMinMax, BorderLayout.NORTH);

      JPanel northPanel = new JPanel();
      //northPanel.setLayout( new BoxLayout(northPanel, BoxLayout.Y_AXIS));
      northPanel.setLayout(new BorderLayout());
      northPanel.add(datasetNameLabel, BorderLayout.NORTH);
      northPanel.add(toolPanel, BorderLayout.SOUTH);

      setLayout(new BorderLayout());
      add(northPanel, BorderLayout.NORTH);
      add(statusPanel, BorderLayout.SOUTH);
      add(westPanel, BorderLayout.WEST);
      add(drawingPanel, BorderLayout.CENTER);

      setDrawHorizAndVert(drawHorizOn, drawVertOn);

      // get last saved Projection
      project = (ProjectionImpl) store.getBean(LastProjectionName, null);
      if (project != null)
        setProjection(project);

            // other components
      //geotiffFileChooser = new FileManager( parent);
      //geotiffFileChooser.setCurrentDirectory( store.get(GEOTIFF_FILECHOOSER_DEFAULTDIR, "."));

     /* gridPP = new PrefPanel("GridView", (PreferencesExt) store.node("GridViewPrefs"));
     gridUrlIF = gridPP.addTextComboField("url", "Gridded Data URL", null, 10, false);
     gridPP.addButton( BAMutil.makeButtconFromAction( chooseLocalDatasetAction ));
     gridPP.finish(true, BorderLayout.EAST);
     gridPP.addActionListener(new ActionListener() {
       public void actionPerformed(ActionEvent e) {
         InvDatasetImpl ds = new InvDatasetImpl( gridUrlIF.getText(), thredds.catalog.DataType.GRID, ServiceType.NETCDF);
         setDataset( ds);
       }
     }); */

      // redraw timer
      redrawTimer = new javax.swing.Timer(0, new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          SwingUtilities.invokeLater(new Runnable() {  // invoke in event thread
            public void run() {
              draw(false);
            }
          });
          redrawTimer.stop(); // one-shot timer
        }
      });
      redrawTimer.setInitialDelay(DELAY_DRAW_AFTER_DATA_EVENT);
      redrawTimer.setRepeats(false);

    } catch (Exception e) {
      System.out.println("UI creation failed");
      e.printStackTrace();
    }
  }

  // actions that control the dataset
  private void makeActionsDataset() {

    /*  choose local dataset
    AbstractAction chooseLocalDatasetAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        String filename = fileChooser.chooseFilename();
        if (filename == null) return;

        Dataset invDs;
        try {     // DatasetNode parent, String name, Map<String, Object> flds, List< AccessBuilder > accessBuilders, List< DatasetBuilder > datasetBuilders
          Map<String, Object> flds = new HashMap<>();
          flds.put(Dataset.FeatureType, FeatureType.GRID.toString());
          flds.put(Dataset.ServiceName, ServiceType.File.toString());  // bogus
          invDs = new Dataset(null, filename, flds, null, null);
          setDataset(invDs);

        } catch (Exception ue) {
          JOptionPane.showMessageDialog(CoverageDisplay.this, "Invalid filename = <" + filename + ">\n" + ue.getMessage());
          ue.printStackTrace();
        }
      }
    };
    BAMutil.setActionProperties(chooseLocalDatasetAction, "FileChooser", "open Local dataset...", false, 'L', -1);

    /* saveDatasetAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        String fname = controller.getDatasetName();
        if (fname != null) {
          savedDatasetList.add( fname);
          BAMutil.addActionToMenu( savedDatasetMenu, new DatasetAction( fname), 0);
        }
      }
    };
    BAMutil.setActionProperties( saveDatasetAction, null, "save dataset", false, 'S', 0);
    */

    // Configure
    chooseProjectionAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        getProjectionManager().setVisible();
      }
    };
    BAMutil.setActionProperties(chooseProjectionAction, null, "Projection Manager...", false, 'P', 0);

    saveCurrentProjectionAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        getProjectionManager();
        // set the bounding box
        ProjectionImpl proj = navPanel.getProjectionImpl().constructCopy();
        proj.setDefaultMapArea(navPanel.getMapArea());
        //if (debug) System.out.println(" GV save projection "+ proj);

        // projManage.setMap(renderAll.get("Map"));   LOOK!
        //projManager.saveProjection( proj);
      }
    };
    BAMutil.setActionProperties(saveCurrentProjectionAction, null, "save Current Projection", false, 'S', 0);

    /* chooseColorScaleAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        if (null == csManager) // lazy instantiation
          makeColorScaleManager();
        csManager.show();
      }
    };
    BAMutil.setActionProperties( chooseColorScaleAction, null, "ColorScale Manager...", false, 'C', 0);

    */
    // redraw
    redrawAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        repaint();
        start(true);
        draw(true);
      }
    };
    BAMutil.setActionProperties(redrawAction, "alien", "RedRaw", false, 'W', 0);

    showDatasetInfoAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        if (infoWindow == null) {
          datasetInfoTA = new TextHistoryPane();
          infoWindow = new IndependentWindow("Dataset Information", BAMutil.getImage("GDVs"), datasetInfoTA);
          infoWindow.setSize(700, 700);
          infoWindow.setLocation(100, 100);
        }

        datasetInfoTA.clear();
        if (coverageDataset != null) {
          Formatter f = new Formatter();
          coverageDataset.toString(f);
          datasetInfoTA.appendLine(f.toString());
        } else {
          datasetInfoTA.appendLine("No coverageDataset loaded");
        }
        datasetInfoTA.gotoTop();
        infoWindow.show();
      }
    };
    BAMutil.setActionProperties(showDatasetInfoAction, "Information", "Show info...", false, 'S', -1);

    /*showNcMLAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        if (ncmlWindow == null) {
          ncmlTA = new TextHistoryPane();
          ncmlWindow = new IndependentWindow("Dataset NcML", BAMutil.getImage( "GDVs"), ncmlTA);
          ncmlWindow.setSize(700,700);
          ncmlWindow.setLocation(200, 70);
        }

        ncmlTA.clear();
        //datasetInfoTA.appendLine( "GeoGrid XML for "+ controller.getDatasetName()+"\n");
        ncmlTA.appendLine( controller.getNcML());
        ncmlTA.gotoTop();
        ncmlWindow.show();
      }
    };
    BAMutil.setActionProperties( showNcMLAction, null, "Show NcML...", false, 'X', -1);  */

    /* showGridDatasetInfoAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        if (ncmlWindow == null) {
          ncmlTA = new TextHistoryPane();
          ncmlWindow = new IndependentWindow("Dataset NcML", BAMutil.getImage( "GDVs"), ncmlTA);
          ncmlWindow.setSize(700,700);
          ncmlWindow.setLocation(200, 70);
        }

        ncmlTA.clear();
        //datasetInfoTA.appendLine( "GeoGrid XML for "+ controller.getDatasetName()+"\n");
        ncmlTA.appendLine( controller.getDatasetXML());
        ncmlTA.gotoTop();
        ncmlWindow.show();
      }
    };
    BAMutil.setActionProperties( showGridDatasetInfoAction, null, "Show GridDataset Info XML...", false, 'X', -1);

      // show netcdf dataset Table
    /* showNetcdfDatasetAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        NetcdfDataset netcdfDataset = controller.getNetcdfDataset();
        if (null != netcdfDataset) {
          try {
            dsTable.setDataset(netcdfDataset, null);
          } catch (IOException e1) {
            e1.printStackTrace();
            return;
          }
          dsDialog.show();
        }
      }
    };
    BAMutil.setActionProperties( showNetcdfDatasetAction, "netcdf", "NetcdfDataset Table Info...", false, 'D', -1);  */

    minmaxHorizAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        csDataMinMax.setSelectedItem(ColorScale.MinMaxType.horiz);
        setDataMinMaxType(ColorScale.MinMaxType.horiz);
      }
    };
    BAMutil.setActionProperties(minmaxHorizAction, null, "Horizontal plane", false, 'H', 0);

    minmaxLogAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        csDataMinMax.setSelectedItem(ColorScale.MinMaxType.log);
        setDataMinMaxType(ColorScale.MinMaxType.log);
      }
    };
    BAMutil.setActionProperties(minmaxLogAction, null, "log horiz plane", false, 'V', 0);

    /* minmaxVolAction =  new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        csDataMinMax.setSelectedIndex(GridRenderer.VOL_MinMaxType);
        controller.setDataMinMaxType(GridRenderer.MinMaxType.vert;
      }
    };
    BAMutil.setActionProperties( minmaxVolAction, null, "Grid volume", false, 'G', 0); */

    minmaxHoldAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        csDataMinMax.setSelectedItem(ColorScale.MinMaxType.hold);
        setDataMinMaxType(ColorScale.MinMaxType.hold);
      }
    };
    BAMutil.setActionProperties(minmaxHoldAction, null, "Hold scale constant", false, 'C', 0);

    fieldLoopAction = new LoopControlAction(fieldChooser);
    levelLoopAction = new LoopControlAction(levelChooser);
    timeLoopAction = new LoopControlAction(timeChooser);
    runtimeLoopAction = new LoopControlAction(runtimeChooser);
  }

  private void makeActionsToolbars() {

    navToolbarAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        Boolean state = (Boolean) getValue(BAMutil.STATE);
        if (state.booleanValue())
          toolPanel.add(navToolbar);
        else
          toolPanel.remove(navToolbar);
      }
    };
    BAMutil.setActionProperties(navToolbarAction, "MagnifyPlus", "show Navigate toolbar", true, 'M', 0);
    navToolbarAction.putValue(BAMutil.STATE, new Boolean(store.getBoolean("navToolbarAction", true)));

    moveToolbarAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        Boolean state = (Boolean) getValue(BAMutil.STATE);
        if (state.booleanValue())
          toolPanel.add(moveToolbar);
        else
          toolPanel.remove(moveToolbar);
      }
    };
    BAMutil.setActionProperties(moveToolbarAction, "Up", "show Move toolbar", true, 'M', 0);
    moveToolbarAction.putValue(BAMutil.STATE, new Boolean(store.getBoolean("moveToolbarAction", true)));
  }

  // create all actions here
  // the actions can then be attached to buttcons, menus, etc
  private void makeActions() {
    boolean state;

    dataProjectionAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        Boolean state = (Boolean) getValue(BAMutil.STATE);
        if (state) {
          ProjectionImpl dataProjection = coverageRenderer.getDataProjection();
          if (null != dataProjection)
            setProjection(dataProjection);
        } else {
          setProjection(new LatLonProjection());
        }
      }
    };
    BAMutil.setActionProperties(dataProjectionAction, "DataProjection", "use Data Projection", true, 'D', 0);
    dataProjectionAction.putValue(BAMutil.STATE, true);

    // contouring
    drawBBAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        Boolean state = (Boolean) getValue(BAMutil.STATE);
        coverageRenderer.setDrawBB(state.booleanValue());
        draw(false);
      }
    };
    BAMutil.setActionProperties(drawBBAction, "Contours", "draw bounding box", true, 'B', 0);
    drawBBAction.putValue(BAMutil.STATE, false);

    // draw horiz
    drawHorizAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        Boolean state = (Boolean) getValue(BAMutil.STATE);
        drawHorizOn = state.booleanValue();
        setDrawHorizAndVert(drawHorizOn, drawVertOn);
        draw(false);
      }
    };
    BAMutil.setActionProperties(drawHorizAction, "DrawHoriz", "draw horizontal", true, 'H', 0);
    state = store.getBoolean("drawHorizAction", true);
    drawHorizAction.putValue(BAMutil.STATE, new Boolean(state));
    drawHorizOn = state;

    // draw Vert
    drawVertAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        Boolean state = (Boolean) getValue(BAMutil.STATE);
        drawVertOn = state.booleanValue();
        setDrawHorizAndVert(drawHorizOn, drawVertOn);
        draw(false);
      }
    };
    BAMutil.setActionProperties(drawVertAction, "DrawVert", "draw vertical", true, 'V', 0);
    state = store.getBoolean("drawVertAction", false);
    drawVertAction.putValue(BAMutil.STATE, new Boolean(state));
    drawVertOn = state;

    // show grid
    showGridAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        Boolean state = (Boolean) getValue(BAMutil.STATE);
        coverageRenderer.setDrawGridLines(state.booleanValue());
        draw(false);
      }
    };
    BAMutil.setActionProperties(showGridAction, "Grid", "show grid lines", true, 'G', 0);
    state = store.getBoolean("showGridAction", false);
    showGridAction.putValue(BAMutil.STATE, new Boolean(state));
    coverageRenderer.setDrawGridLines(state);

    // contouring
    showContoursAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        Boolean state = (Boolean) getValue(BAMutil.STATE);
        coverageRenderer.setDrawContours(state.booleanValue());
        draw(false);
      }
    };
    BAMutil.setActionProperties(showContoursAction, "Contours", "show contours", true, 'C', 0);
    state = store.getBoolean("showContoursAction", false);
    showContoursAction.putValue(BAMutil.STATE, new Boolean(state));
    coverageRenderer.setDrawContours(state);

    // contouring labels
    showContourLabelsAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        Boolean state = (Boolean) getValue(BAMutil.STATE);
        coverageRenderer.setDrawContourLabels(state.booleanValue());
        draw(false);
      }
    };
    BAMutil.setActionProperties(showContourLabelsAction, "ContourLabels", "show contour labels", true, 'L', 0);
    state = store.getBoolean("showContourLabelsAction", false);
    showContourLabelsAction.putValue(BAMutil.STATE, new Boolean(state));
    coverageRenderer.setDrawContourLabels(state);
  }

  private void makeEventManagement() {
   // LOOK what prevents these ActionCoordinator from getting GC ?
    //// manage field selection events
    String actionName = "field";
    ActionCoordinator fieldCoordinator = new ActionCoordinator(actionName);
    // connect to the fieldChooser
    fieldCoordinator.addActionSourceListener(fieldChooser.getActionSourceListener());
    // connect to the gridTable
    //fieldCoordinator.addActionSourceListener(gridTable.getActionSourceListener());
    // heres what to do when the currentField changes
    ActionSourceListener fieldSource = new ActionSourceListener(actionName) {
      public void actionPerformed(ActionValueEvent e) {
        if (setField(e.getValue())) {
          if (e.getActionCommand().equals("redrawImmediate")) {
            draw(true);
            //colorScalePanel.paintImmediately(colorScalePanel.getBounds());   // kludgerino
          } else
            redrawLater();
        }
      }
    };
    fieldCoordinator.addActionSourceListener(fieldSource);

    //// manage level selection events
    actionName = "level";
    ActionCoordinator levelCoordinator = new ActionCoordinator(actionName);
    levelCoordinator.addActionSourceListener(levelChooser.getActionSourceListener());
    // connect to the vertPanel
    /* levelCoordinator.addActionSourceListener(vertPanel.getActionSourceListener());
      // also manage Pick events from the vertPanel
    vertPanel.getDrawArea().addPickEventListener( new PickEventListener() {
      public void actionPerformed(PickEvent e) {
        int level = renderGrid.findLevelCoordElement(e.getLocation().getY());
         if ((level != -1) && (level != currentLevel)) {
          currentLevel = level;
          redrawLater();
          String selectedName = levelNames.get(currentLevel).getName();
          if (Debug.isSet("pick/event"))
            System.out.println("pick.event Vert: "+selectedName);
          levelSource.fireActionValueEvent(ActionSourceListener.SELECTED, selectedName);
        }
      }
    }); */
    // heres what to do when a level changes
    ActionSourceListener levelSource = new ActionSourceListener(actionName) {
      public void actionPerformed(ActionValueEvent e) {
        int level = findIndexFromName(levelNames, e.getValue().toString());
        if ((level != -1) && (level != currentLevel)) {
          currentLevel = level;
          if (e.getActionCommand().equals("redrawImmediate")) {
            draw(true);
          } else
            redrawLater();
        }
      }
    };
    levelCoordinator.addActionSourceListener(levelSource);

    //// manage time selection events
    actionName = "time";
    ActionCoordinator timeCoordinator = new ActionCoordinator(actionName);
    timeCoordinator.addActionSourceListener(timeChooser.getActionSourceListener());
    // heres what to do when the time changes
    ActionSourceListener timeSource = new ActionSourceListener(actionName) {
      public void actionPerformed(ActionValueEvent e) {
        int time = findIndexFromName(timeNames, e.getValue().toString());
        if ((time != -1) && (time != currentTime)) {
          currentTime = time;
          if (e.getActionCommand().equals("redrawImmediate")) {
            draw(true);
            //colorScalePanel.paintImmediately(colorScalePanel.getBounds());   // kludgerino
          } else
            redrawLater();
        }
      }
    };
    timeCoordinator.addActionSourceListener(timeSource);

    // manage runtime selection events
    actionName = "runtime";
    ActionCoordinator runtimeCoordinator = new ActionCoordinator(actionName);
    runtimeCoordinator.addActionSourceListener(runtimeChooser.getActionSourceListener());
      // heres what to do when the runtime changes
    ActionSourceListener runtimeSource = new ActionSourceListener(actionName) {
      public void actionPerformed(ActionValueEvent e) {
        // Object dataValue = e.getValue();
        int runtime = findIndexFromName( runtimeNames, e.getValue().toString());
        if ((runtime != -1) && (runtime != currentRunTime)) {
          currentRunTime = runtime;

          if (dataState.taxis2D != null) {
            // CoverageCoordAxis1D taxis = dataState.taxis2D.getTimeAxisForRun((CalendarDate) dataValue);
            dataState.taxis = dataState.taxis2D.getTimeAxisForRun(currentRunTime);
            timeNames = dataState.taxis.getCoordValueNames();
            timeChooser.setCollection(timeNames.iterator());
            if (currentTime >= timeNames.size())
              currentTime = 0;
            timeChooser.setSelectedByIndex( currentTime);
          }

          if (e.getActionCommand().equals("redrawImmediate")) {
            draw(true);
          } else
            redrawLater();
        }
      }
    };
    runtimeCoordinator.addActionSourceListener(runtimeSource);

    //// manage runtime selection events
    actionName = "ensemble";
    ActionCoordinator ensembleCoordinator = new ActionCoordinator(actionName);
    ensembleCoordinator.addActionSourceListener(ensembleChooser.getActionSourceListener());
    // heres what to do when the time changes
    ActionSourceListener ensembleSource = new ActionSourceListener(actionName) {
      public void actionPerformed(ActionValueEvent e) {
        int ensIndex = findIndexFromName(ensembleNames, e.getValue().toString());
        if ((ensIndex != -1) && (ensIndex != currentEnsemble)) {
          currentEnsemble = ensIndex;
          if (e.getActionCommand().equals("redrawImmediate")) {
            draw(true);
          } else
            redrawLater();
        }
      }
    };
    ensembleCoordinator.addActionSourceListener(ensembleSource);
  }

  private void makeNavPanelWiring() {

    // get Projection Events from the navigated panel
    navPanel.addNewProjectionListener(new NewProjectionListener() {
      public void actionPerformed(NewProjectionEvent e) {
        if (Debug.isSet("event/NewProjection"))
          System.out.println("Controller got NewProjectionEvent " + navPanel.getMapArea());
        if (eventsOK && mapRenderer != null) {
          mapRenderer.setProjection(e.getProjection());
          coverageRenderer.setViewProjection(e.getProjection());
          drawH(false);
        }
      }
    });

    // get NewMapAreaEvents from the navigated panel
    navPanel.addNewMapAreaListener(new NewMapAreaListener() {
      public void actionPerformed(NewMapAreaEvent e) {
        if (Debug.isSet("event/NewMapArea"))
          System.out.println("Controller got NewMapAreaEvent " + navPanel.getMapArea());
        drawH(false);
      }
    });

    /* get Pick events from the navigated panel
    navPanel.addPickEventListener(new PickEventListener() {
      public void actionPerformed(PickEvent e) {
        projPoint.setLocation(e.getLocation());
        int slice = renderGrid.findSliceFromPoint(projPoint);
        if (Debug.isSet("pick/event"))
          System.out.println("pick.event: " + projPoint + " " + slice);
        if ((slice >= 0) && (slice != currentSlice)) {
          currentSlice = slice;
          vertPanel.setSlice(currentSlice);
          redrawLater();
        }
      }
    }); */

    // get Move events from the navigated panel
    navPanel.addCursorMoveEventListener(new CursorMoveEventListener() {
      public void actionPerformed(CursorMoveEvent e) {
        ProjectionPointImpl projPoint = new ProjectionPointImpl(e.getLocation());
        String valueS = coverageRenderer.getXYvalueStr(projPoint);
        dataValueLabel.setText(valueS);
      }
    });

  }

  private int findIndexFromName(List<NamedObject> list, String name) {
    for (int idx = 0; idx < list.size(); idx++) {
      NamedObject no = list.get(idx);
      if (name.equals(no.getName()))
        return idx;
    }
    log.error("findIndexFromName cant find " + name);
    return -1;
  }

  ////////////////////////////////////////////////////////////////////////////////////////////////

  /**
   * save all data in the PersistentStore
   */
  public void save() {
    //store.putInt( "vertSplit", splitDraw.getDividerLocation());

    store.putBoolean("navToolbarAction", ((Boolean) navToolbarAction.getValue(BAMutil.STATE)).booleanValue());
    store.putBoolean("moveToolbarAction", ((Boolean) moveToolbarAction.getValue(BAMutil.STATE)).booleanValue());

    if (projManager != null)
      projManager.storePersistentData();
    /* if (csManager != null)
      csManager.storePersistentData();
    if (sysConfigDialog != null)
      sysConfigDialog.storePersistentData(); */

    //dsTable.save();
    //dsTable.getPrefs().putBeanObject("DialogBounds", dsDialog.getBounds());

    store.put(GEOTIFF_FILECHOOSER_DEFAULTDIR, geotiffFileChooser.getCurrentDirectory());

    store.putBeanObject(LastMapAreaName, navPanel.getMapArea());
    store.putBeanObject(LastProjectionName, navPanel.getProjectionImpl());
    //if (gridDataset != null)
    //  store.put(LastDatasetName, gridDataset.getTitle());
    store.putBeanObject(ColorScaleName, colorScale);

    store.putBoolean("showGridAction", ((Boolean) showGridAction.getValue(BAMutil.STATE)).booleanValue());
    store.putBoolean("showContoursAction", ((Boolean) showContoursAction.getValue(BAMutil.STATE)).booleanValue());
    store.putBoolean("showContourLabelsAction", ((Boolean) showContourLabelsAction.getValue(BAMutil.STATE)).booleanValue());

  }

  /* private boolean chooseDataset(String url) {
   InvDataset invDs = new InvDatasetImpl( fname, ServerType.NETCDF);
   return chooseDataset( invDs);
 } */

  private void setSelected(boolean b) {
    selected = b;

    //showGridTableAction.setEnabled(b);
    //showNcMLAction.setEnabled(b);
    //showNcMLAction.setEnabled(b);
    //showNetcdfDatasetAction.setEnabled(b);
    //showGridDatasetInfoAction.setEnabled(b);
    //showNetcdfXMLAction.setEnabled( b);

    navToolbarAction.setEnabled(b);
    moveToolbarAction.setEnabled(b);

    //controller.showGridAction.setEnabled( b);
    //controller.showContoursAction.setEnabled( b);
    //controller.showContourLabelsAction.setEnabled( b);
    redrawAction.setEnabled(b);

    minmaxHorizAction.setEnabled(b);
    minmaxLogAction.setEnabled(b);
    minmaxHoldAction.setEnabled(b);

    fieldLoopAction.setEnabled(b);
    levelLoopAction.setEnabled(b);
    timeLoopAction.setEnabled(b);
    runtimeLoopAction.setEnabled(b);

    navPanel.setEnabledActions(b);
  }

  // add a MapBean to the User Interface
  public void addMapBean(MapBean mb) {
    mapBeanMenu.addAction(mb.getActionDesc(), mb.getIcon(), mb.getAction());

    // first one is the "default"
    if (mapBeanCount == 0) {
      setMapRenderer(mb.getRenderer());
    }
    mapBeanCount++;

    mb.addPropertyChangeListener(new PropertyChangeListener() {
      public void propertyChange(java.beans.PropertyChangeEvent e) {
        if (e.getPropertyName().equals("Renderer")) {
          setMapRenderer((ucar.nc2.ui.util.Renderer) e.getNewValue());
        }
      }
    });
  }

  void setMapRenderer(ucar.nc2.ui.util.Renderer mapRenderer) {
    this.mapRenderer = mapRenderer;
    mapRenderer.setProjection(navPanel.getProjectionImpl());
    mapRenderer.setColor(mapColor);
    redrawLater();
  }

  /* public void setDataset(Dataset ds) throws IOException {
    if (ds == null) return;

    OpenDatasetTask openTask = new OpenDatasetTask(ds);
    ucar.nc2.ui.widget.ProgressMonitor pm = new ucar.nc2.ui.widget.ProgressMonitor(openTask);
    pm.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        if (e.getActionCommand().equals("success")) {
          showDataset();
          //gridTable.setDataset(controller.getFields());
          datasetNameLabel.setText("Dataset:  " + coverageDataset.getName());
          setSelected(true);
          gtWindow.setVisible(false);
        }
      }
    });
    pm.start(this, "Open Dataset " + ds.getName(), 100);
  } */

  // assume that its done in the event thread
  boolean showDataset() {

    // temp kludge for initialization
    Iterable<Coverage> grids = coverageDataset.getCoverages();

    currentField = grids.iterator().next();   // first
    currentSlice = 0;
    currentLevel = 0;
    currentTime = 0;
    currentEnsemble = 0;
    currentRunTime = 0;

    eventsOK = false; // dont let this trigger redraw
    this.dataState = coverageRenderer.setCoverage(coverageDataset, currentField);
    coverageRenderer.setDataProjection(currentField.getCoordSys().getProjection());
    // setFields(grids);
    setField(currentField);

    // LOOK if possible, change the projection and the map area to one that fits this dataset
    ProjectionImpl dataProjection = currentField.getCoordSys().getProjection();
    if (dataProjection != null)
      setProjection(dataProjection);

    // ready to draw
    //draw(true);

    // events now ok
    eventsOK = true;
    return true;
  }

  public void setDataMinMaxType(ColorScale.MinMaxType type) {
    coverageRenderer.setDataMinMaxType(type);
    redrawLater();
  }

  private boolean startOK = true;

  public void setDataset(ucar.nc2.ui.coverage2.CoverageTable dsTable) {
    this.coverageDataset = dsTable.getCoverageDataset();
    setFieldsFromBeans(dsTable.getCoverageBeans());

    startOK = false; // wait till redraw is hit before drawing
    showDataset();
    datasetNameLabel.setText("Dataset:  " + coverageDataset.getName());
    //gridTable.setDataset(controller.getFields());
  }

  public void setDataset(CoverageCollection gcd) {
    this.coverageDataset = gcd;
    setFields(gcd.getCoverages());

    startOK = false; // wait till redraw is hit before drawing
    showDataset();
    datasetNameLabel.setText("Dataset:  " + coverageDataset.getName());
    //gridTable.setDataset(controller.getFields());
  }

  void setFieldsFromBeans(List<CoverageTable.CoverageBean> fields) {
    fieldChooser.setCollection(fields.iterator());
  }

  void setFields(Iterable<Coverage> fields) {
    fieldChooser.setCollection(fields.iterator());
  }

  private boolean setField(Object fld) {
    Coverage gg = null;
    if (fld instanceof Coverage)
      gg = (Coverage) fld;
    else if (fld instanceof String)
      gg = coverageDataset.findCoverage((String) fld);
    else if (fld instanceof NamedObject)
      gg = coverageDataset.findCoverage(((NamedObject) fld).getName());
    if (null == gg)
      return false;

    this.dataState = coverageRenderer.setCoverage(coverageDataset, gg);
    coverageRenderer.setDataProjection(this.dataState.geocs.getProjection());
    currentField = gg;

    // set runtimes
    if (this.dataState.rtaxis != null) {
      runtimeNames = this.dataState.rtaxis.getCoordValueNames();
      currentRunTime = runtimeNames.size() > 0 ? 0 : -1;
      if ((currentRunTime < 0) || (currentRunTime >= runtimeNames.size()))
        currentRunTime = 0;

      setChooserWanted("runtime", true);
      runtimeChooser.setCollection(runtimeNames.iterator(), true);
      NamedObject no = runtimeNames.get(currentRunTime);
      runtimeChooser.setSelectedByName(no.getName());

      if (this.dataState.taxis2D != null) {
        // CalendarDate runtime = (CalendarDate) runtimeNames.get(currentRunTime).getValue();
        this.dataState.taxis =  this.dataState.taxis2D.getTimeAxisForRun(currentRunTime);
      }

    } else {
      runtimeNames = new ArrayList<>();
      setChooserWanted("runtime", false);
      coverageRenderer.setRunTime(-1);
    }

    // set times
    if (this.dataState.taxis != null) {
      timeNames = this.dataState.taxis.getCoordValueNames();
      if ((currentTime < 0) || (currentTime >= timeNames.size()))
        currentTime = 0;
      hasDependentTimeAxis = true;

      setChooserWanted("time", true);
      timeChooser.setCollection(timeNames.iterator(), true);
      NamedObject no = timeNames.get(currentTime);
      timeChooser.setSelectedByName(no.getName());

    } else {
      timeNames = new ArrayList<>();
      hasDependentTimeAxis = false;
      setChooserWanted("time", false);
    }

    // set ensembles
    if (this.dataState.ensaxis != null) {
      ensembleNames = this.dataState.ensaxis.getCoordValueNames();
      currentEnsemble = ensembleNames.size() > 0 ? 0 : -1;
      if ((currentEnsemble < 0) || (currentEnsemble >= ensembleNames.size()))
        currentEnsemble = 0;

      setChooserWanted("ensemble", true);
      ensembleChooser.setCollection(ensembleNames.iterator(), true);
      NamedObject no = ensembleNames.get(currentEnsemble);
      ensembleChooser.setSelectedByName(no.getName());

    } else {
      ensembleNames = new ArrayList<>();
      setChooserWanted("ensemble", false);
      coverageRenderer.setEnsemble(-1);
    }

        // set levels
    if (this.dataState.zaxis != null) {
      levelNames = this.dataState.zaxis.getCoordValueNames();
      if ((currentLevel < 0) || (currentLevel >= levelNames.size()))
        currentLevel = 0;
      //vertPanel.setCoordSys(currentField.getCoordinateSystem(), currentLevel);

      setChooserWanted("level", true);
      levelChooser.setCollection(levelNames.iterator(), true);
      NamedObject no = levelNames.get(currentLevel);
      levelChooser.setSelectedByName(no.getName());

    } else {
      levelNames = new ArrayList<>();
      setChooserWanted("level", false);
      coverageRenderer.setLevel(-1);
    }

    addChoosers();

    fieldChooser.setToolTipText(gg.getName());
    colorScalePanel.setUnitString(gg.getUnitsString());
    return true;
  }

  void setDrawHorizAndVert(boolean drawHoriz, boolean drawVert) {
    drawingPanel.removeAll();
    if (drawHoriz && drawVert) {
      // splitDraw.setTopComponent(panz);
      //splitDraw.setBottomComponent(vertPanel);
      drawingPanel.add(navPanel, BorderLayout.CENTER);
    } else if (drawHoriz) {
      drawingPanel.add(navPanel, BorderLayout.CENTER);
    } else if (drawVert) {
      drawingPanel.add(navPanel, BorderLayout.CENTER); // LOOK drawVert not supported
    }
  }

  public void setProjection(ProjectionImpl p) {
    project = p;
    if (mapRenderer != null)
      mapRenderer.setProjection(p);
    coverageRenderer.setViewProjection(p);
    // renderWind.setProjection( p);
    navPanel.setProjectionImpl(p);
    redrawLater();
  }

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////

  void start(boolean ok) {
    startOK = ok;
  }

  synchronized void draw(boolean immediate) {
    if (!startOK) return;

    coverageRenderer.setLevel(currentLevel);
    coverageRenderer.setTime(currentTime);
    //renderGrid.setSlice(currentSlice);
    coverageRenderer.setEnsemble(currentEnsemble);
    coverageRenderer.setRunTime(currentRunTime);

    if (drawHorizOn)
      drawH(immediate);
    //if (drawVertOn)
    //  drawV(immediate);
  }

  private void drawH(boolean immediate) {
    if (!startOK) return;

    // cancel any redrawLater
    boolean already = redrawTimer.isRunning();
    if (debugThread && already) System.out.println("redrawLater canceled ");
    if (already)
      redrawTimer.stop();

    long tstart = System.currentTimeMillis();
    long startTime, tookTime;

    //// horizontal slice
    // the Navigated Panel's BufferedImage graphics
    Graphics2D gNP = navPanel.getBufferedImageGraphics();
    if (gNP == null) // panel not drawn on screen yet
      return;

    // clear
    gNP.setBackground(navPanel.getBackgroundColor());
    gNP.fill(gNP.getClipBounds());

    // draw grid
    startTime = System.currentTimeMillis();
    coverageRenderer.renderPlanView(gNP, atI);
    if (Debug.isSet("timing/GridDraw")) {
      tookTime = System.currentTimeMillis() - startTime;
      System.out.println("timing.GridDraw: " + tookTime * .001 + " seconds");
    }

    //draw Map
    if (mapRenderer != null) {
      startTime = System.currentTimeMillis();
      mapRenderer.draw(gNP, atI);
      if (Debug.isSet("timing/MapDraw")) {
        tookTime = System.currentTimeMillis() - startTime;
        System.out.println("timing/MapDraw: " + tookTime * .001 + " seconds");
      }
    }

    /* draw Winds
    if (drawWinds) {
      startTime = System.currentTimeMillis();
      renderWind.draw(gNP, currentLevel, currentTime);
      if (Debug.isSet("timing/WindsDraw")) {
        tookTime = System.currentTimeMillis() - startTime;
        System.out.println("timing.WindsDraw: " + tookTime*.001 + " seconds");
      }
    } */

    // copy buffer to the screen
    if (immediate)
      navPanel.drawG();
    else
      navPanel.repaint();

    // cleanup
    gNP.dispose();

    if (Debug.isSet("timing/total")) {
      tookTime = System.currentTimeMillis() - tstart;
      System.out.println("timing.total: " + tookTime * .001 + " seconds");
    }
  }

  /* private void drawV(boolean immediate) {
    if (!startOK) return;
    ScaledPanel drawArea = vertPanel.getDrawArea();
    Graphics2D gV = drawArea.getBufferedImageGraphics();
    if (gV == null)
      return;

    long startTime = System.currentTimeMillis();

    gV.setBackground(Color.white);
    gV.fill(gV.getClipBounds());
    renderGrid.renderVertView(gV, atI);

    if (Debug.isSet("timing/GridDrawVert")) {
      long tookTime = System.currentTimeMillis() - startTime;
      System.out.println("timing.GridDrawVert: " + tookTime*.001 + " seconds");
    }
    gV.dispose();

    // copy buffer to the screen
     if (immediate)
      drawArea.drawNow();
    else
      drawArea.repaint();
  } */

  private synchronized void redrawLater() {
    //redrawComplete |= complete;
    boolean already = redrawTimer.isRunning();
    if (debugThread) System.out.println("redrawLater isRunning= " + already);
    if (already)
      redrawTimer.restart();
    else
      redrawTimer.start();
  }

  public ProjectionManager getProjectionManager() {
    if (null != projManager)
      return projManager;

    projManager = new ProjectionManager(parent, store);
    projManager.addPropertyChangeListener(new PropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent e) {
        if (e.getPropertyName().equals("ProjectionImpl")) {
          ProjectionImpl p = (ProjectionImpl) e.getNewValue();
          p = p.constructCopy();
          //System.out.println("UI: new Projection "+p);
          setProjection(p);
        }
      }
    });

    return projManager;
  }

  private void addChoosers() {
    fieldPanel.removeAll();
    for (Chooser c : choosers) {
      if (c.isWanted)
        fieldPanel.add(c.field);
    }
  }

  private static class Chooser {
    Chooser(String name, SuperComboBox field, boolean want) {
      this.name = name;
      this.field = field;
      this.isWanted = want;
    }

    boolean isWanted;
    String name;
    SuperComboBox field;
  }

  private void setChooserWanted(String name, boolean want) {
    for (Chooser chooser : choosers) {
      if (chooser.name.equals(name)) chooser.isWanted = want;
    }
  }

  private void addToolbarOption(String toolbarName, JToolBar toolbar, AbstractAction act) {
    boolean wantsToolbar = store.getBoolean(toolbarName, true);
    if (wantsToolbar)
      toolPanel.add(toolbar);
  }

  private void addActionsToMenus(JMenu datasetMenu, JMenu configMenu, JMenu toolMenu) {
    // Info
    //BAMutil.addActionToMenu(datasetMenu, showGridTableAction);
    BAMutil.addActionToMenu(datasetMenu, showDatasetInfoAction);
    //BAMutil.addActionToMenu(datasetMenu, showNcMLAction);
    //BAMutil.addActionToMenu(datasetMenu, showGridDatasetInfoAction);
    //BAMutil.addActionToMenu(datasetMenu, showNetcdfDatasetAction);
    // BAMutil.addActionToMenu( datasetMenu, geotiffAction);
    //BAMutil.addActionToMenu( infoMenu, showNetcdfXMLAction);

    /// Configure
    JMenu toolbarMenu = new JMenu("Toolbars");
    toolbarMenu.setMnemonic('T');
    configMenu.add(toolbarMenu);
    BAMutil.addActionToMenu(toolbarMenu, navToolbarAction);
    BAMutil.addActionToMenu(toolbarMenu, moveToolbarAction);

    BAMutil.addActionToMenu(configMenu, chooseProjectionAction);
    BAMutil.addActionToMenu(configMenu, saveCurrentProjectionAction);

    /* BAMutil.addActionToMenu( configMenu, chooseColorScaleAction);
    BAMutil.addActionToMenu( configMenu, controller.dataProjectionAction);
    */

    //// tools menu
    JMenu displayMenu = new JMenu("Display control");
    displayMenu.setMnemonic('D');

    BAMutil.addActionToMenu(displayMenu, showGridAction);
    BAMutil.addActionToMenu(displayMenu, showContoursAction);
    BAMutil.addActionToMenu(displayMenu, showContourLabelsAction);
    BAMutil.addActionToMenu(displayMenu, redrawAction);
    toolMenu.add(displayMenu);

    // Loop Control
    JMenu loopMenu = new JMenu("Loop control");
    loopMenu.setMnemonic('L');

    BAMutil.addActionToMenu(loopMenu, fieldLoopAction);
    BAMutil.addActionToMenu(loopMenu, levelLoopAction);
    BAMutil.addActionToMenu(loopMenu, timeLoopAction);
    BAMutil.addActionToMenu(loopMenu, runtimeLoopAction);
    toolMenu.add(loopMenu);

    // MinMax Control
    JMenu mmMenu = new JMenu("ColorScale min/max");
    mmMenu.setMnemonic('C');
    BAMutil.addActionToMenu(mmMenu, minmaxHorizAction);
    BAMutil.addActionToMenu(mmMenu, minmaxLogAction);
    BAMutil.addActionToMenu(mmMenu, minmaxHoldAction);
    toolMenu.add(mmMenu);

    // Zoom/Pan
    JMenu zoomMenu = new JMenu("Zoom/Pan");
    zoomMenu.setMnemonic('Z');
    navPanel.addActionsToMenu(zoomMenu); // items are added by NavigatedPanelToolbar
    toolMenu.add(zoomMenu);
  }

  // loop control for SuperComboBox
  private static class LoopControlAction extends AbstractAction {
    SuperComboBox scbox;

    LoopControlAction(SuperComboBox cbox) {
      this.scbox = cbox;
      BAMutil.setActionProperties(this, null, cbox.getName(), false, 0, 0);
    }

    public void actionPerformed(ActionEvent e) {
      scbox.getLoopControl().show();
    }
  }

  /* open remote dataset in cancellable task
  private class OpenDatasetTask extends ProgressMonitorTask implements ucar.nc2.util.CancelTask {
    URI endpoint = null;

    OpenDatasetTask(Dataset ds) throws FileNotFoundException {
      for (Access a : ds.getAccess()) {
        if (a.getService().getType() == ServiceType.CdmrFeature)
          endpoint = a.getStandardUri();
      }
      if (endpoint == null)
        throw new FileNotFoundException("No CdmrFeature access");
    }

    public void run() {
      CoverageDataset gcd = null;
      try {
        gcd = CoverageDatasetFactory.openCoverage(endpoint.toString());
      } catch (IOException e) {
        setError("Failed to open datset: "+e.getMessage());
      }

      success = !cancel && (gcd != null);
      if (success) setDataset(gcd);
      done = true;
    }
  } */

}


/*
 * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */

package ucar.nc2.iosp.gempak;

import ucar.nc2.iosp.grid.*;

/**
 * GempakLookup
 * get all the information about a GEMPAK file.
 */
public final class GempakLookup implements GridTableLookup {

  /**
   * sample grid
   */
  private GempakGridRecord sample;

  /**
   * Gets a representative grid for this lookup
   */
  public GempakLookup(GempakGridRecord sample) {
    this.sample = sample;
  }

  public String getShapeName(GridDefRecord gds) {
    return "Spherical";
  }

  public final String getGridName(GridDefRecord gds) {
    return getProjectionName(gds);
  }

  /**
   * gets parameter table, then grib1 parameter based on number.
   *
   * @param gr GridRecord
   * @return Parameter
   */
  public final GridParameter getParameter(GridRecord gr) {
    String name = gr.getParameterName();
    GridParameter gp = GempakGridParameterTable.getParameter(name);
    if (gp != null) {
      return gp;
    }
    return new GridParameter(0, name, name, "");
  }

  public final String getDisciplineName(GridRecord gr) {
    // all disciplines are the same in GEMPAK
    return "Meteorological Products";
  }

  public final String getCategoryName(GridRecord gr) {
    // no categories in GEMPAK
    return "Meteorological Parameters";
  }

  public final String getLevelName(GridRecord gr) {
    // TODO:  flesh this out
    return GempakUtil.LV_CCRD(gr.getLevelType1());
  }

  public final String getLevelDescription(GridRecord gr) {
    // TODO:  flesh this out
    String levelName = getLevelName(gr);
    switch (levelName) {
      case "PRES":
        return "pressure";
      case "NONE":
        return "surface";
      case "HGHT":
        return "height_above_ground";
      case "THTA":
        return "isentropic";
      case "SGMA":
        return "sigma";
      case "DPTH":
        return "depth";
      case "PDLY":
        return "layer_between_two_pressure_difference_from_ground";
      case "FRZL":
        return "zeroDegC_isotherm";
      case "TROP":
        return "tropopause";
      case "CLDL":
        return "cloud_base";
      case "CLDT":
        return "cloud_tops";
      case "MWSL":
        return "maximum_wind_level";
    }
    return levelName;
  }

  public final String getLevelUnit(GridRecord gr) {
    // TODO:  flesh this out
    String levelName = getLevelName(gr);
    switch (levelName) {
      case "PRES":
        return "hPa";
      case "HGHT":
        return "m";
      case "THTA":
        return "K";
      case "SGMA":
        return "";
      case "DPTH":
        return "m";
      case "PDLY":
        return "hPa";
    }
    return "";
  }

  public final String getTimeRangeUnitName(int tunit) {
    return "minute";
  }

  /**
   * gets the BaseTime Forecastime.
   *
   * @return BaseTime
   */
  public final java.util.Date getFirstBaseTime() {
    return sample.getReferenceTime();
  }

  /**
   * is this a LatLon grid.
   */
  public final boolean isLatLon(GridDefRecord gds) {
    return getProjectionName(gds).equals("CED");
  }

  public final int getProjectionType(GridDefRecord gds) {
    String name = getProjectionName(gds).trim();
    switch (name) {
      case "CED":
        return -1;
      case "MER":
        return Mercator;
      case "MCD":
        return Mercator;
      case "LCC":
        return LambertConformal;
      case "SCC":
        return LambertConformal;
      case "PS":
        return PolarStereographic;
      case "STR":
        return PolarStereographic;
      default:
        return -1;
    }
  }

  public final boolean isVerticalCoordinate(GridRecord gr) {
    int type = gr.getLevelType1();
    if ((type > GempakUtil.vertCoords.length)
            || !GempakUtil.vertCoords[type].equals("NONE")) {
      return true;
    }
    return false;
  }

  /**
   * is this a PositiveUp VerticalCoordinate.
   */
  public final boolean isPositiveUp(GridRecord gr) {
    int type = gr.getLevelType1();
    if ((type == 1) || (type == 5)) {
      return false;
    }
    return true;
  }

  /**
   * gets the MissingValue.
   *
   * @return MissingValue
   */
  public final float getFirstMissingValue() {
    return GempakConstants.RMISSD;
  }

  /**
   * Is this a layer?
   *
   * @param gr record to check
   * @return true if a layer
   */
  public boolean isLayer(GridRecord gr) {
    if (gr.getLevel2() == -1) {
      return false;
    }
    return true;
  }

  /**
   * Get the projection name
   *
   * @param gds the projection name
   * @return the name or null if not set
   */
  private String getProjectionName(GridDefRecord gds) {
    return gds.getParam(GridDefRecord.PROJ);
  }

  // CF Conventions Global Attributes

  /**
   * gets the CF title.
   *
   * @return title
   */
  public final String getTitle() {
    return "GRID data";
  }

  /**
   * Institution for CF conventions
   *
   * @return Institution
   */
  public String getInstitution() {
    return null;

  }

  /**
   * gets the Source, Generating Process or Model.
   *
   * @return source
   */
  public final String getSource() {
    return null;

  }

  /**
   * comment for CF conventions.
   *
   * @return comment
   */
  public final String getComment() {
    return null;
  }

  /**
   * Get the grid type for labelling
   *
   * @return the grid type
   */
  public String getGridType() {
    return "GEMPAK";
  }

}


package ucar.nc2.ogc.gml;

import net.opengis.gml.x32.ReferenceType;
import ucar.nc2.VariableSimpleIF;

/**
 * Created by cwardgar on 3/7/14.
 */
public abstract class NcReferenceType {
    // wml2:Collection/wml2:observationMember/om:OM_Observation/om:observedProperty
    public static ReferenceType initObservedProperty(ReferenceType observedProperty, VariableSimpleIF dataVar) {
        // @xlink:title
        observedProperty.setTitle(dataVar.getShortName());

        return observedProperty;
    }

    // wml2:Collection/wml2:observationMember/om:OM_Observation/om:result/wml2:MeasurementTimeseries/
    //         wml2:defaultPointMetadata/wml2:DefaultTVPMeasurementMetadata/wml2:interpolationType
    public static ReferenceType initInterpolationType(ReferenceType interpolationType) {
        // FIXME: I may be able to do better if the NetCDF file has an interpolation_type variable.
        // See OGC 12-031r2, p. 35.

        // @xlink:title
        interpolationType.setTitle("Unknown");

        // @xlink:href
        interpolationType.setHref("http://www.opengis.net/def/nil/OGC/0/unknown");

        return interpolationType;
    }

    private NcReferenceType() { }
}


/*
 * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */
package ucar.nc2.ui.gis;

import ucar.nc2.ui.gis.shapefile.ShapeFileBean;
import ucar.nc2.ui.gis.worldmap.WorldMapBean;
import ucar.nc2.ui.widget.BAMutil;
import ucar.nc2.ui.widget.PopupMenu;

import javax.swing.*;
import javax.swing.event.EventListenerList;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;

/** Wrap map Renderers as beans.
  *
  * @author John Caron
  **/

public abstract class MapBean {
  private EventListenerList listenerList = new EventListenerList();

  public abstract javax.swing.ImageIcon getIcon();
  public abstract String getActionName();
  public abstract String getActionDesc();

    /** Each bean has one Renderer, made current when Action is called */
  public abstract ucar.nc2.ui.util.Renderer getRenderer();

 /** Construct the Action that is called when this bean's menu item/buttcon is selected.
   * Typically this routine is only called once when the bean is added.
   * The Action itself is called whenever the menu/buttcon is selected.
   *
   * The action should have NAME, SMALL_ICON and SHORT_DESC properties set.
   * The applications uses these to put up a buttcon and menu item.
   * The actionPerformed() method may do various things, but it must
   *   send a PropertyChangeEvent with newValue = Renderer.
   * @return the Action to be called.
   */
  public javax.swing.Action getAction() {
    AbstractAction useMap = new AbstractAction(getActionName(), getIcon()) {
      public void actionPerformed(java.awt.event.ActionEvent e) {
        firePropertyChangeEvent(this, "Renderer", null, getRenderer());
      }
    };
    useMap.putValue(Action.SHORT_DESCRIPTION, getActionDesc());

    return useMap;
  }

  /**
   * Add a PropertyChangeEvent Listener.
   */
  public void addPropertyChangeListener( PropertyChangeListener l) {
    listenerList.add(PropertyChangeListener.class, l);
  }

  /**
   * Remove a PropertyChangeEvent Listener.
   */
  public void removePropertyChangeListener( PropertyChangeListener l) {
    listenerList.remove(PropertyChangeListener.class, l);
  }

  protected void firePropertyChangeEvent(Object source, String propertyName, Object oldValue, Object newValue) {
    PropertyChangeEvent event = new PropertyChangeEvent(source, propertyName, oldValue, newValue);
    firePropertyChangeEvent( event);
  }

  protected void firePropertyChangeEvent(PropertyChangeEvent event) {
    // Process the listeners last to first
    Object[] listeners = listenerList.getListenerList();
    for (int i = listeners.length-2; i>=0; i-=2) {
      if (listeners[i] == PropertyChangeListener.class) {
        ((PropertyChangeListener)listeners[i+1]).propertyChange(event);
      }
    }
  }

  /**
   * Convenience routine to make a button with a popup menu attached. to use:
   * <pre>
      thredds.ui.PopupMenu mapBeanMenu = MapBean.makeMapSelectButton();
      AbstractButton butt = (AbstractButton) mapBeanMenu.getParentComponent();
      addToMenu (butt);

      // add map beans here
      mapBeanMenu.addAction( mb.getActionDesc(), mb.getIcon(), mb.getAction());

      mb.addPropertyChangeListener( new PropertyChangeListener() {
       public void propertyChange( java.beans.PropertyChangeEvent e) {
         if (e.getPropertyName().equals("Renderer")) {
           mapRender = (thredds.viewer.ui.Renderer) e.getNewValue();
           mapRender.setProjection( np.getProjectionImpl());
           redraw();
         }
       }
     });
   */
  static public PopupMenu makeMapSelectButton() {

    AbstractAction mapSelectAction = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        //System.out.println("mapSelectAction");
        //mapPopup.show();
      }
    };
    BAMutil.setActionProperties( mapSelectAction, "WorldMap", "select map", false, 'M', -1);
    AbstractButton mapSelectButton = BAMutil.makeButtconFromAction( mapSelectAction);

    PopupMenu mapPopup = new PopupMenu(mapSelectButton, "Select Map", true);
    return mapPopup;
  }

  static public PopupMenu getStandardMapSelectButton(PropertyChangeListener pcl) {
    PopupMenu mapBeanMenu = makeMapSelectButton();

        // standard maps
    ArrayList standardMaps = new ArrayList();
    standardMaps.add( new WorldMapBean());
    standardMaps.add( new ShapeFileBean("WorldDetailMap", "Global Detailed Map", "WorldDetailMap",
            "/resources/nj22/ui/maps/Countries.shp"));
    standardMaps.add( new ShapeFileBean("USDetailMap", "US Detailed Map", "USMap", "/resources/nj22/ui/maps/us_state.shp"));

    for (int i = 0; i < standardMaps.size(); i++) {
      MapBean mb = (MapBean) standardMaps.get(i);
      mapBeanMenu.addAction( mb.getActionDesc(), mb.getIcon(), mb.getAction());
      mb.addPropertyChangeListener( pcl);
    }

    return mapBeanMenu;
  }

}


/*
 * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */
package ucar.units;

import javax.annotation.concurrent.Immutable;
import java.io.Serializable;
import java.util.Date;

/**
 * Provides support for classes that implement units.
 *
 * @author Steven R. Emmerson
 */
@Immutable
public abstract class UnitImpl implements Unit, Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * The unit identifier.
     *
     * @serial
     */
    private final UnitName    id;

    /**
     * Constructs with no ID.
     */
    protected UnitImpl() {
        this(null);
    }

    /**
     * Constructs with the given ID.
     *
     * @param id
     *            The id of the unit (e.g. "foot"). May be null.
     */
    protected UnitImpl(final UnitName id) {
        this.id = id;
    }

    /**
     * Gets the identifier of this unit.
     *
     * @return The ID of this unit. May be null.
     */
    public final UnitName getUnitName() {
        return id;
    }

    /**
     * Gets the name of the unit.
     *
     * @return The name of the unit. May be null.
     */
    public final String getName() {
        return id == null
                ? null
                : id.getName();
    }

    /**
     * Gets the plural form of the name of the unit.
     *
     * @return The plural of the name of the unit. May be null.
     */
    public final String getPlural() {
        return id == null
                ? null
                : id.getPlural();
    }

    /**
     * Gets the symbol for the unit.
     *
     * @return The symbol of the unit. May be null.
     */
    public final String getSymbol() {
        return id == null
                ? null
                : id.getSymbol();
    }

    public Unit shiftTo(final double origin) throws ShiftException {
        return OffsetUnit.getInstance(this, origin);
    }

    public Unit shiftTo(final Date origin) throws ShiftException {
        return TimeScaleUnit.getInstance(this, origin);
    }

    /**
     * Multiplies this unit by another.
     *
     * @param that
     *            The other unit.
     * @return The product of this unit multiplied by the other unit.
     * @throws MultiplyException
     *             Can't multiply these units.
     */
    public final Unit multiplyBy(final Unit that) throws MultiplyException {
        return myMultiplyBy(that);
    }

    public Unit multiplyBy(final double scale) throws MultiplyException {
        return ScaledUnit.getInstance(scale, this);
    }

    /**
     * Multiplies this unit by another.
     *
     * @param that
     *            The other unit.
     * @return The product of this unit multiplied by the other unit.
     * @throws MultiplyException
     *             Can't multiply these units.
     */
    protected abstract Unit myMultiplyBy(Unit that) throws MultiplyException;

    /**
     * Divides this unit by another.
     *
     * @param that
     *            The other unit.
     * @return The quotient of this unit divided by the other unit.
     * @throws OperationException
     *             Can't divide these units.
     */
    public final Unit divideBy(final Unit that) throws OperationException {
        return myDivideBy(that);
    }

    /**
     * Divides this unit by another.
     *
     * @param unit
     *            The other unit.
     * @return The quotient of this unit divided by the other unit.
     * @throws OperationException
     *             Can't divide these units.
     */
    protected abstract Unit myDivideBy(Unit unit) throws OperationException;

    /**
     * Divides this unit into another.
     *
     * @param that
     *            The other unit.
     * @return The quotient of this unit divided into the other unit.
     * @throws OperationException
     *             Can't divide these units.
     */
    public final Unit divideInto(final Unit that) throws OperationException {
        return myDivideInto(that);
    }

    /**
     * Divides this unit into another.
     *
     * @param unit
     *            The other unit.
     * @return The quotient of this unit divided into the other unit.
     * @throws OperationException
     *             Can't divide these units.
     */
    protected abstract Unit myDivideInto(Unit unit) throws OperationException;

    /**
     * Raises this unit to a power.
     *
     * @param power
     *            The power.
     * @return The result of raising this unit to the power.
     * @throws RaiseException
     *             Can't raise this unit to a power.
     */
    public final Unit raiseTo(final int power) throws RaiseException {
        return myRaiseTo(power);
    }

    /**
     * Raises this unit to a power.
     *
     * @param power
     *            The power.
     * @return The result of raising this unit to the power.
     * @throws RaiseException
     *             Can't raise this unit to a power.
     */
    protected abstract Unit myRaiseTo(int power) throws RaiseException;

    /*
     * Default implementation.
     *
     * @see ucar.units.Unit#log(double)
     */
    public Unit log(final double base) {
        return LogarithmicUnit.getInstance(this, base);
    }

    /**
     * Provides support for converting numeric values from this unit to another
     * unit.
     */
    protected static class MyConverter extends ConverterImpl {
        private final DerivableUnit fromUnit;
        private final DerivableUnit toUnit;

        protected MyConverter(final Unit fromUnit, final Unit toUnit)
                throws ConversionException {
            super(fromUnit, toUnit);
            if (!(fromUnit instanceof DerivableUnit)
                    || !(toUnit instanceof DerivableUnit)) {
                throw new ConversionException(fromUnit, toUnit);
            }
            this.fromUnit = (DerivableUnit) fromUnit;
            this.toUnit = (DerivableUnit) toUnit;
        }

        public double convert(final double amount) {
            double output;
            try {
                output = toUnit.fromDerivedUnit(fromUnit.toDerivedUnit(amount));
            }
            catch (final ConversionException e) {
                output = 0;
            } // can't happen because isCompatible() vetted
            return output;
        }

        public float[] convert(final float[] input, final float[] output) {
            try {
                toUnit.fromDerivedUnit(fromUnit.toDerivedUnit(input, output),
                        output);
            }
            catch (final ConversionException e) {
            } // can't happen because isCompatible() vetted
            return output;
        }

        public double[] convert(final double[] input, final double[] output) {
            try {
                toUnit.fromDerivedUnit(fromUnit.toDerivedUnit(input, output),
                        output);
            }
            catch (final ConversionException e) {
            } // can't happen because isCompatible() vetted
            return output;
        }
    }

    /**
     * Gets a Converter for converting numeric values from this unit to another,
     * compatible unit.
     *
     * @param outputUnit
     *            The unit to which to convert the numeric values.
     * @return A converter of values from this unit to the other unit.
     * @throws ConversionException
     *             The units aren't convertible.
     */
    public Converter getConverterTo(final Unit outputUnit)
            throws ConversionException {
        return new MyConverter(this, outputUnit);
    }

    /**
     * Converts a numeric value from this unit to another unit.
     *
     * @param amount
     *            The numeric value.
     * @param outputUnit
     *            The unit to which to convert the numeric value.
     * @return The numeric value in the output unit.
     * @throws ConversionException
     *             The units aren't convertible.
     */
    public float convertTo(final float amount, final Unit outputUnit)
            throws ConversionException {
        return (float) convertTo((double) amount, outputUnit);
    }

    /**
     * Converts a numeric value from this unit to another unit.
     *
     * @param amount
     *            The numeric value.
     * @param outputUnit
     *            The unit to which to convert the numeric value.
     * @return The numeric value in the output unit.
     * @throws ConversionException
     *             The units aren't convertible.
     */
    public double convertTo(final double amount, final Unit outputUnit)
            throws ConversionException {
        return getConverterTo(outputUnit).convert(amount);
    }

    /**
     * Converts numeric values from this unit to another unit.
     *
     * @param amounts
     *            The numeric values.
     * @param outputUnit
     *            The unit to which to convert the numeric values.
     * @return The numeric values in the output unit in allocated space.
     * @throws ConversionException
     *             The units aren't convertible.
     */
    public float[] convertTo(final float[] amounts, final Unit outputUnit)
            throws ConversionException {
        return convertTo(amounts, outputUnit, new float[amounts.length]);
    }

    /**
     * Converts numeric values from this unit to another unit.
     *
     * @param amounts
     *            The numeric values.
     * @param outputUnit
     *            The unit to which to convert the numeric values.
     * @return The numeric values in the output unit in allocated space.
     * @throws ConversionException
     *             The units aren't convertible.
     */
    public double[] convertTo(final double[] amounts, final Unit outputUnit)
            throws ConversionException {
        return convertTo(amounts, outputUnit, new double[amounts.length]);
    }

    /**
     * Converts numeric values from this unit to another unit.
     *
     * @param input
     *            The input numeric values.
     * @param outputUnit
     *            The unit to which to convert the numeric values.
     * @param output
     *            The output numeric values. May be the same array as the input
     *            values.
     * @return The numeric values in the output unit.
     * @throws ConversionException
     *             The units aren't convertible.
     */
    public float[] convertTo(final float[] input, final Unit outputUnit,
            final float[] output) throws ConversionException {
        return getConverterTo(outputUnit).convert(input, output);
    }

    /**
     * Converts numeric values from this unit to another unit.
     *
     * @param input
     *            The input numeric values.
     * @param outputUnit
     *            The unit to which to convert the numeric values.
     * @param output
     *            The output numeric values. May be the same array as the input
     *            values.
     * @return The numeric values in the output unit.
     * @throws ConversionException
     *             The units aren't convertible.
     */
    public double[] convertTo(final double[] input, final Unit outputUnit,
            final double[] output) throws ConversionException {
        return getConverterTo(outputUnit).convert(input, output);
    }

    /**
     * Indicates if numeric values in this unit are convertible with another
     * unit.
     *
     * @param that
     *            The other unit.
     * @return true if and only if numeric values in this unit are
     *         convertible the other unit.
     */
    public boolean isCompatible(final Unit that) {
        // jeffm: for some reason just calling getDerivedUnit().equals(...)
        // with jikes 1.1.7 as the compiler causes the jvm to crash.
        // The Unit u1=... does not crash.
        final Unit u1 = getDerivedUnit();
        return u1.equals(that.getDerivedUnit());
        // return getDerivedUnit().equals(that.getDerivedUnit());
    }

    /**
     * Returns the hash code of this instance.
     *
     * @return The hash code of this instance.
     */
    @Override
    public abstract int hashCode();

    /**
     * Indicates if two string are equal.
     *
     * @param s1
     *            One string. May be null.
     * @param s2
     *            The other string. May be null.
     * @return true if an only if both strings are
     *         null or both strings are identical.
     */
    static private boolean equals(final String s1, final String s2) {
        return (s1 == null && s2 == null)
                || (s1 != null && s2 != null && s1.equals(s2));
    }

    /**
     * Indicates if two string are equal (ignoring case).
     *
     * @param s1
     *            One string. May be null.
     * @param s2
     *            The other string. May be null.
     * @return true if an only if both strings are
     *         null or both strings are identical (ignoring case).
     */
    static private boolean equalsIgnoreCase(final String s1,
            final String s2) {
        return (s1 == null && s2 == null)
                || (s1 != null && s2 != null && s1.equalsIgnoreCase(s2));
    }

    /**
     * Returns the string representation of this unit.
     *
     * @return The string representation of this unit.
     */
    @Override
    public String toString() {
        final String string = getSymbol();
        return string != null
                ? string
                : getName();
    }

    /**
     * Returns a label for a quantity in this unit.
     *
     * @param quantityID
     *            The identifier for the quantity (e.g. "altitude").
     * @return The appropriate label (e.g. "altitude/m").
     */
    public String makeLabel(final String quantityID) {
        final StringBuilder buf = new StringBuilder(quantityID);
        if (quantityID.contains(" ")) {
            buf.insert(0, '(').append(')');
        }
        buf.append('/');
        final int start = buf.length();
        buf.append(toString());
        if (buf.substring(start).indexOf(' ') != -1) {
            buf.insert(start, '(').append(')');
        }
        return buf.toString();
    }
}


package ucar.nc2.ogc.waterml;

import net.opengis.waterml.x20.MonitoringPointType;
import ucar.nc2.ft.StationTimeSeriesFeature;
import ucar.nc2.ogc.MarshallingUtil;
import ucar.nc2.ogc.gml.NcCodeWithAuthorityType;
import ucar.nc2.ogc.gml.NcStringOrRefType;
import ucar.nc2.ogc.spatialsampling.NcShapeType;

/**
 * Created by cwardgar on 2014/02/26.
 */
public abstract class NcMonitoringPointType {
    // wml2:Collection/wml2:observationMember/om:OM_Observation/om:featureOfInterest/wml2:MonitoringPoint
    public static MonitoringPointType initMonitoringPointType(
            MonitoringPointType monitoringPoint, StationTimeSeriesFeature stationFeat) {
        // @gml:id
        String id = MarshallingUtil.createIdForType(MonitoringPointType.class);
        monitoringPoint.setId(id);

        // gml:identifier
        NcCodeWithAuthorityType.initIdentifier(monitoringPoint.addNewIdentifier(), stationFeat);

        // gml:description
        NcStringOrRefType.initDescription(monitoringPoint.addNewDescription(), stationFeat);
        if (monitoringPoint.getDescription().getStringValue() == null ||
                monitoringPoint.getDescription().getStringValue().isEmpty()) {
            monitoringPoint.unsetDescription();
        }

        // sam:sampledFeature
        monitoringPoint.addNewSampledFeature();
        monitoringPoint.setNilSampledFeatureArray(0);  // Set the "sam:sampledFeature" we just added to nil.

        // sams:shape
        NcShapeType.initShape(monitoringPoint.addNewShape(), stationFeat);

        return monitoringPoint;
    }

    private NcMonitoringPointType() { }
}


/*
 * Copyright (c) 1998-2018 John Caron and University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */

package ucar.nc2.ft2.simpgeometry.adapter;

import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;

import ucar.nc2.constants.AxisType;
import ucar.nc2.dataset.*;
import ucar.nc2.Dimension;
import ucar.nc2.dataset.CoordinateAxis;
import ucar.nc2.ft2.simpgeometry.*;

/**
 * Simple Geometry Coordinate System / Enhanced Dataset Implementation
 * Forked from ucar.nc2.ft2.coverage.adapter.GridCS
 *
 * @author John
 * @author wchen@usgs.gov
 * @since 8/9/2018
 */
public class SimpleGeometryCS {

  private List<CoordinateAxis> simpleGeometryX, simpleGeometryY, simpleGeometryZ, simpleGeometryID;
  SimpleGeometryCSBuilder builder;

  public SimpleGeometryCS(SimpleGeometryCSBuilder builder) {
    this.builder = builder;
    simpleGeometryX = new ArrayList<CoordinateAxis>(); simpleGeometryY = new ArrayList<CoordinateAxis>();
    simpleGeometryZ = new ArrayList<CoordinateAxis>(); simpleGeometryID = new ArrayList<CoordinateAxis>();

    for(CoordinateAxis axis : builder.getSgAxes()) {

    	// Look for simple geometry axes and add them
    	if(axis.getAxisType().equals(AxisType.SimpleGeometryX)) simpleGeometryX.add(axis);
    	else if (axis.getAxisType().equals(AxisType.SimpleGeometryY)) simpleGeometryY.add(axis);
    	else if(axis.getAxisType().equals(AxisType.SimpleGeometryZ)) simpleGeometryZ.add(axis);
    	else if(axis.getAxisType().equals(AxisType.SimpleGeometryID)) simpleGeometryID.add(axis);

    }
  }

  /**
   * Given a variable name, returns the type of geometry which that variable is holding
   *
   * @param name name of the variable
   * @return geometry type associated with that variable
   */
  public GeometryType getGeometryType(String name) {
	return this.builder.getGeometryType(name);
  }

  /**
   * Get a list of all simple geometry X axes.
   *
   * @return list of simple geometry X axes
   */
  public List<CoordinateAxis> getSimpleGeometryX() {
	return this.simpleGeometryX;
  }

  /**
   * Get a list of all simple geometry Y axes
   *
   * @return list of simple geometry Y axes
   */
  public List<CoordinateAxis> getSimpleGeometryY() {
	return this.simpleGeometryY;
  }

  /**
   * Get a list of all simple geometry Z axes.
   *
   * @return list of simple geometry Z axes.
   */
  public List<CoordinateAxis> getSimpleGeometryZ() {
	return this.simpleGeometryZ;
  }

  /**
   * Get a list of all simple geometry ID axes.
   * Simple Geometry ID axes are used for indexing into simple geometry variables.
   *
   * @return list of simple geometry ID axes
   */
  public List<CoordinateAxis> getSimpleGeometryID() {
	return this.simpleGeometryID;
  }

  /**
   * Get a list of all dimensions in this dataset.
   *
   * @return list of dimensions.
   */
  public List<Dimension> getDimensions(){
	  return builder.getDimensions();
  }

  /**
   * Given a Variable name and a geometry index
   * returns a Polygon
   *
   * @param name of the data variable
   * @param index within the variable
   *
   * @return polygon with all associated data, null if not found
   */
  public Polygon getPolygon(String name, int index) {
	 return builder.getPolygon(name, index);
  }

  /**
   * Given a Variable name and a beginning index and end index, returns a list of
   * polygon (inclusive on both sides)
   *
   * @param name
   * @param indexBegin
   * @param indexEnd
   * @return
   */
  public List<Polygon> getPolygons(String name, int indexBegin, int indexEnd) {
	  return builder.getPolygons(name, indexBegin, indexEnd);
  }

  /**
   * Given a Variable name and a geometry index
   * returns a Line
   *
   * @param name of the data variable
   * @param index within the variable
   *
   * @return line with all associated data, null if not found
   */
  public Line getLine(String name, int index) {
	 return builder.getLine(name, index);
  }

  /**
   * Given a Variable name and a beginning index and end index, returns a list of
   * lines (inclusive on both sides)
   *
   * @param name
   * @param indexBegin
   * @param indexEnd
   * @return
   */
  public List<Line> getLines(String name, int indexBegin, int indexEnd) {
	  return builder.getLines(name, indexBegin, indexEnd);
  }

  /**
   * Given a Variable name and a geometry index
   * returns a Point
   *
   * @param name of the data variable
   * @param index within the variable
   * @return point with all associated data, null if not found
   */
  public Point getPoint(String name, int index) {
	 return builder.getPoint(name,index);
  }

  /**
   * Given a Variable name and a beginning index and end index
   * returns a list of points (inclusive on both sides)
   *
   * @param name of the data variable
   * @param indexBegin within the variable
   * @param indexEnd within the variable
   * @return a list of points with associated data
   */
  public List<Point> getPoints(String name, int indexBegin, int indexEnd) {
	  return builder.getPoints(name, indexBegin, indexEnd);
  }

    public static SimpleGeometryCS makeSGCoordSys(Formatter sbuff, CoordinateSystem cs, VariableEnhanced v) {
        if (sbuff != null) {
            sbuff.format(" ");
            v.getNameAndDimensions(sbuff, false, true);
            sbuff.format(" check CS %s: ", cs.getName());
        }

        SimpleGeometryCS gcs = new SimpleGeometryCS(new SimpleGeometryCSBuilder(null, cs, null));
        return gcs;
    }

}