/*
 * MapViewer.java
 *
 * Created on Jun 30, 2009, 4:14:25 PM
 */
package usda.weru.gis.gui;

import org.locationtech.jts.geom.Coordinate;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Collections;
import java.util.List;
import javax.swing.JLabel;
import systems.uom.common.USCustomary;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.apache.log4j.Logger;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.map.FeatureLayer;

import usda.weru.gis.latlong.LatLong;
import usda.weru.gis.GISData;
import usda.weru.gis.GISLookup;
import usda.weru.gis.GISUtil;
import usda.weru.gis.data.CropManagementZone;
import usda.weru.gis.data.Territory;
import usda.weru.util.About;
import usda.weru.util.ConfigData;

/**
 *
 * @author Joseph A. Levin <joelevin@weru.ksu.edu>
 */

public class MapViewer extends javax.swing.JFrame {

    boolean initialized;
    private static final long serialVersionUID = 1L;
    private final MapController c_controller;
    private LatLong c_selectedLatLong;
    public static final String PROP_SELECTEDLATLONG = "MapViewerSelectedLatLong";
    public static final String PROP_RESETLATLONG = "MapViewerResetLatLong";
    private FeatureLayer layerCurrentLocationMarker;
    private static Rectangle c_restoreBounds;
    private int c_restoreState;    
    private LayerTableModel layerTableModel;
    private int currentZoomLevel;
    private int zoomLevelCnt;
    LatLong currentLocation;
    LatLong initialLocation;
    private static final Logger logger = Logger.getLogger(MapViewer.class);
    ShowPositionDataSwingWorker c_positionData;

    MapViewerLayersContent mapLayersContent;

    protected DefaultFeatureCollection prismLayerCollection;
    protected FeatureLayer prismLayer;
    
    public MapViewer() {
        initialized = false;
        setIconImage (About.getWeruIconImage());
        initComponents();
        positionWingen.setVisible(false);
        demoWingenLabel.setVisible(false);
        positionCligen.setVisible(false);
        demoCligenLabel.setVisible(false);
        
        InternalZoomAction zoomAction = new InternalZoomAction();
        addMouseWheelListener(zoomAction);
        
        // loading, restore size and location if possible
        Rectangle bounds;
        int state;
        synchronized (MapViewer.class) {
            bounds = c_restoreBounds;
            state = c_restoreState;
        }
        if (bounds != null) {
            setBounds(bounds);
            setExtendedState(state);
        }

        // when a window is closing, save its state
        addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent e) {
                c_restoreBounds = getBounds();
                c_restoreState = getExtendedState();
            }
        });

        c_controller = mapPane.getMapLayer();

        // selection handler
        MouseAdapter selection = new InternalSelectionHandler();
        c_controller.addMouseListener(selection);
        c_controller.addMouseMotionListener(selection);
        c_controller.setViewer(this);
        
        jSzoomBar.setValue( ConfigData.getIntParm(ConfigData.viewerDefaultZoom, 4));
        
        layerTableModel = null;
        mapLayersContent = null;
        
        initialLocation = null;
        c_selectedLatLong = null;
        initializeZoomBar();
        setZoomLevels(10);
        
        Dimension viewerInitialSize = new Dimension (
                ConfigData.getIntParm(ConfigData.viewerInitialSizeWidth, 900),
                ConfigData.getIntParm(ConfigData.viewerInitialSizeHeight, 600));
        setSize(viewerInitialSize);
        showWindowSize(viewerInitialSize);
   }
    
    public void setZoomBarExternal(int newZoom) {
        if(newZoom != currentZoomLevel &&
           newZoom >= jSzoomBar.getMinimum() &&
           newZoom <= jSzoomBar.getMaximum()) {
            
            jSzoomBar.setValue(newZoom);
            jSzoomBar.repaint();
        }
    }
    
    public int getViewerCurrentZoomVal () {
        return currentZoomLevel;
    }
    
    public void zoomInc (int inc) {
        int newZoom;
        
        newZoom = currentZoomLevel + (inc * this.jSzoomBar.getMinorTickSpacing());
        currentZoomLevel = (currentZoomLevel < 0) ? 0 : currentZoomLevel;
        currentZoomLevel = (currentZoomLevel > 100) ? 100 : currentZoomLevel;
        jSzoomBar.setValue(newZoom);
    }
    
    private void initializeZoomBar() {
        jSzoomBar.setValue(currentZoomLevel);
        jtfZoomVal.setText(Integer.toString(currentZoomLevel));
        jSzoomBar.setLabelTable(jSzoomBar.createStandardLabels(10));
        jSzoomBar.setPaintLabels(true);
    }
    
    @Override
    public void setVisible(boolean b) {
        super.setVisible(b);
        if (b) {
            // halt the preload to give quicker user response
            mapPane.getMapLayer().haltpreloadMapCache();
            
            if (!initialized) {
                initContent();
            }
            c_controller.setRenderEnabled(b);
            if (b) {
                initialLocation = currentLocation;
            }
        }
    }    
    
    public void initContent () {

        setupLayerTable ();
        mapLayersContent = new MapViewerLayersContent(this, layerTableModel);
        mapPane.getMapLayer().setContent(mapLayersContent);
        layerTable.setModel(layerTableModel);

        if (mapPane.getMapLayer().setRenderParms()) {
            mapPane.getMapLayer().preloadMapCache();
        }
        initialized = true;
    }
    
    public MapViewerLayersContent getMapLayersContent () {
        return mapLayersContent;
    }
       
    public boolean checkServerAccessibility () {
        return true;
    }
    
    private void setupLayerTable () {
        if (layerTableModel == null) {
            layerTableModel = new LayerTableModel(this);
            layerTable.setModel(layerTableModel);
            // size the left column
            layerTable.getColumnModel().getColumn(0).setMaxWidth(45);
        }
    }
    
    /**
     * creates the red crosshairs at the center of the map view
     * @param location the center of the map viewer
     */
    public void setCurrentLocationMarker(final LatLong location) {
        currentLocation = location;
        if (initialLocation == null) {
            initialLocation = currentLocation;
        }
        FeatureLayer old = layerCurrentLocationMarker;
        layerCurrentLocationMarker = mapLayersContent.createLatLongMarkerLayer(location);
        if (old != null) {
            mapPane.getMapLayer().getContent().removeLayer(old);
            layerCurrentLocationMarker.setVisible(old.isVisible());
        } else {
            layerCurrentLocationMarker.setVisible(true);
        }

        mapPane.getMapLayer().getContent().addLayer(layerCurrentLocationMarker);
        
        ShowPositionData(location);
        
        mapLayersContent.updatePrismDataLocationLayer();
    }

    public MapController getMapController() {
        return mapPane.getMapLayer();
    }

    public LatLong getSelectedLatLong() {
        return c_selectedLatLong;
    }

    private void setSelectedLatLong(LatLong latlong) {
        setSelectedLatLong (latlong, false);
    }

    private void setSelectedLatLong(LatLong latlong, boolean force) {
        LatLong old;
        if (force) {
            old = null;
        } else {
            old = c_selectedLatLong;
        }
        c_selectedLatLong = latlong;
        firePropertyChange(PROP_SELECTEDLATLONG, old, c_selectedLatLong);
    }

    public void setSelectedLatLongExternal(LatLong latlong) {
        if (latlong != c_selectedLatLong) {
            c_selectedLatLong = latlong;
            initialLocation = latlong;
            c_controller.setCenter(GISUtil.toCoordinate(c_selectedLatLong));
        }
    }
    
    protected void setLoadingGraphicVisible (boolean enabled) {
        jLabelLoading.setVisible(enabled);
        jProgBarLoading.setVisible(enabled);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jb_cancel = new javax.swing.JButton();
        jb_reset = new javax.swing.JButton();
        jLabel3 = new javax.swing.JLabel();
        jb_done = new javax.swing.JButton();
        tf_windowSize = new javax.swing.JFormattedTextField();
        splitPanel = new javax.swing.JSplitPane();
        sidePanel = new javax.swing.JPanel();
        tableScrollPanel = new javax.swing.JScrollPane();
        layerTable = new javax.swing.JTable();
        demoCountryLabel = new javax.swing.JLabel();
        demoStateLabel = new javax.swing.JLabel();
        demoCountyLabel = new javax.swing.JLabel();
        demoCMZLabel = new javax.swing.JLabel();
        demoWingenLabel = new javax.swing.JLabel();
        demoCligenLabel = new javax.swing.JLabel();
        positionCMZ = new javax.swing.JLabel();
        positionCountry = new javax.swing.JLabel();
        positionState = new javax.swing.JLabel();
        positionCounty = new javax.swing.JLabel();
        positionWingen = new javax.swing.JLabel();
        positionCligen = new javax.swing.JLabel();
        demoPositionLabel = new javax.swing.JLabel();
        positionLatLon = new javax.swing.JLabel();
        mainPanel = new javax.swing.JPanel();
        statusBar = new javax.swing.JPanel();
        latLonField = new javax.swing.JFormattedTextField();
        jLabel1 = new javax.swing.JLabel();
        zoomLabel = new javax.swing.JLabel();
        jSzoomBar = new javax.swing.JSlider();
        jProgBarLoading = new javax.swing.JProgressBar();
        jLabelLoading = new javax.swing.JLabel();
        jtfZoomVal = new javax.swing.JTextField();
        jLabel2 = new javax.swing.JLabel();
        mapPane = new usda.weru.gis.gui.MapPane();

        java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("usda/weru/gis/gui/Bundle"); // NOI18N
        setTitle(bundle.getString("MapViewer.title")); // NOI18N
        setPreferredSize(new java.awt.Dimension(900, 600));
        addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseEntered(java.awt.event.MouseEvent evt) {
                formMouseEntered(evt);
            }
        });
        addComponentListener(new java.awt.event.ComponentAdapter() {
            public void componentResized(java.awt.event.ComponentEvent evt) {
                formComponentResized(evt);
            }
        });
        addWindowStateListener(new java.awt.event.WindowStateListener() {
            public void windowStateChanged(java.awt.event.WindowEvent evt) {
                formWindowStateChanged(evt);
            }
        });

        jb_cancel.setFont(new java.awt.Font("Dialog", 0, 12)); // NOI18N
        jb_cancel.setText(org.openide.util.NbBundle.getMessage(MapViewer.class, "MapViewer.jb_cancel.text")); // NOI18N
        jb_cancel.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jb_cancelActionPerformed(evt);
            }
        });

        jb_reset.setFont(new java.awt.Font("Dialog", 0, 12)); // NOI18N
        jb_reset.setText(org.openide.util.NbBundle.getMessage(MapViewer.class, "MapViewer.jb_reset.text")); // NOI18N
        jb_reset.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jb_resetActionPerformed(evt);
            }
        });

        jLabel3.setFont(new java.awt.Font("Dialog", 0, 12)); // NOI18N
        jLabel3.setText(org.openide.util.NbBundle.getMessage(MapViewer.class, "MapViewer.jLabel3.text")); // NOI18N

        jb_done.setFont(new java.awt.Font("Dialog", 0, 12)); // NOI18N
        jb_done.setText(org.openide.util.NbBundle.getMessage(MapViewer.class, "MapViewer.jb_done.text")); // NOI18N
        jb_done.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jb_doneActionPerformed(evt);
            }
        });

        tf_windowSize.setEditable(false);
        tf_windowSize.setFormatterFactory(new LatLonFormatterFactory());
        tf_windowSize.setHorizontalAlignment(javax.swing.JTextField.CENTER);
        tf_windowSize.setText(bundle.getString("MapViewer.tf_windowSize.text")); // NOI18N
        tf_windowSize.setFont(new java.awt.Font("Dialog", 0, 10)); // NOI18N
        tf_windowSize.setPreferredSize(new java.awt.Dimension(30, 20));

        splitPanel.setDividerLocation(200);

        tableScrollPanel.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);

        layerTable.setFont(new java.awt.Font("Dialog", 0, 10)); // NOI18N
        layerTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {

            },
            new String [] {

            }
        ));
        layerTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_LAST_COLUMN);
        layerTable.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                layerTableMouseClicked(evt);
            }
        });
        tableScrollPanel.setViewportView(layerTable);

        demoCountryLabel.setFont(demoCountryLabel.getFont().deriveFont(demoCountryLabel.getFont().getStyle() | java.awt.Font.BOLD));
        demoCountryLabel.setText(bundle.getString("MapViewer.demoCountryLabel.text")); // NOI18N

        demoStateLabel.setFont(demoStateLabel.getFont().deriveFont(demoStateLabel.getFont().getStyle() | java.awt.Font.BOLD));
        demoStateLabel.setText(bundle.getString("MapViewer.demoStateLabel.text")); // NOI18N

        demoCountyLabel.setFont(demoCountyLabel.getFont().deriveFont(demoCountyLabel.getFont().getStyle() | java.awt.Font.BOLD));
        demoCountyLabel.setText(bundle.getString("MapViewer.demoCountyLabel.text")); // NOI18N

        demoCMZLabel.setFont(demoCMZLabel.getFont().deriveFont(demoCMZLabel.getFont().getStyle() | java.awt.Font.BOLD));
        demoCMZLabel.setText(bundle.getString("MapViewer.demoCMZLabel.text")); // NOI18N

        demoWingenLabel.setFont(demoWingenLabel.getFont().deriveFont(demoWingenLabel.getFont().getStyle() | java.awt.Font.BOLD));
        demoWingenLabel.setText(bundle.getString("MapViewer.demoWingenLabel.text")); // NOI18N

        demoCligenLabel.setFont(demoCligenLabel.getFont().deriveFont(demoCligenLabel.getFont().getStyle() | java.awt.Font.BOLD));
        demoCligenLabel.setText(bundle.getString("MapViewer.demoCligenLabel.text")); // NOI18N

        positionCMZ.setFont(positionCMZ.getFont().deriveFont(positionCMZ.getFont().getStyle() & ~java.awt.Font.BOLD));
        positionCMZ.setText(bundle.getString("MapViewer.positionCMZ.text")); // NOI18N

        positionCountry.setFont(positionCountry.getFont().deriveFont(positionCountry.getFont().getStyle() & ~java.awt.Font.BOLD));
        positionCountry.setText(bundle.getString("MapViewer.positionCountry.text")); // NOI18N

        positionState.setFont(positionState.getFont().deriveFont(positionState.getFont().getStyle() & ~java.awt.Font.BOLD));
        positionState.setText(bundle.getString("MapViewer.positionState.text")); // NOI18N

        positionCounty.setFont(positionCounty.getFont().deriveFont(positionCounty.getFont().getStyle() & ~java.awt.Font.BOLD));
        positionCounty.setText(bundle.getString("MapViewer.positionCounty.text")); // NOI18N

        positionWingen.setFont(positionWingen.getFont().deriveFont(positionWingen.getFont().getStyle() & ~java.awt.Font.BOLD));
        positionWingen.setText(bundle.getString("MapViewer.positionWingen.text")); // NOI18N

        positionCligen.setFont(positionCligen.getFont().deriveFont(positionCligen.getFont().getStyle() & ~java.awt.Font.BOLD));
        positionCligen.setText(bundle.getString("MapViewer.positionCligen.text")); // NOI18N

        demoPositionLabel.setFont(demoPositionLabel.getFont().deriveFont(demoPositionLabel.getFont().getStyle() | java.awt.Font.BOLD));
        demoPositionLabel.setText(bundle.getString("MapViewer.demoPositionLabel.text")); // NOI18N

        positionLatLon.setFont(positionLatLon.getFont().deriveFont(positionLatLon.getFont().getStyle() & ~java.awt.Font.BOLD));
        positionLatLon.setText(bundle.getString("MapViewer.positionLatLon.text")); // NOI18N

        javax.swing.GroupLayout sidePanelLayout = new javax.swing.GroupLayout(sidePanel);
        sidePanel.setLayout(sidePanelLayout);
        sidePanelLayout.setHorizontalGroup(
            sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(sidePanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(sidePanelLayout.createSequentialGroup()
                        .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(demoCountryLabel)
                            .addComponent(demoStateLabel)
                            .addComponent(demoCMZLabel)
                            .addComponent(demoCountyLabel))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(positionCounty, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(positionCMZ, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(positionState, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(positionCountry, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
                    .addGroup(sidePanelLayout.createSequentialGroup()
                        .addComponent(demoPositionLabel)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(positionLatLon, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                    .addGroup(sidePanelLayout.createSequentialGroup()
                        .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(demoWingenLabel)
                            .addComponent(demoCligenLabel))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(positionCligen, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(positionWingen, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
                    .addComponent(tableScrollPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
                .addContainerGap())
        );
        sidePanelLayout.setVerticalGroup(
            sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(sidePanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(demoPositionLabel)
                    .addComponent(positionLatLon))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(demoCountryLabel)
                    .addComponent(positionCountry))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(demoStateLabel)
                    .addComponent(positionState))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(demoCountyLabel)
                    .addComponent(positionCounty))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(demoCMZLabel)
                    .addComponent(positionCMZ))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(demoWingenLabel)
                    .addComponent(positionWingen))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(sidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(demoCligenLabel)
                    .addComponent(positionCligen))
                .addGap(18, 18, 18)
                .addComponent(tableScrollPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 460, Short.MAX_VALUE)
                .addContainerGap())
        );

        splitPanel.setLeftComponent(sidePanel);

        statusBar.setBorder(javax.swing.BorderFactory.createEtchedBorder());
        statusBar.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
        statusBar.setFont(new java.awt.Font("Ubuntu", 0, 10)); // NOI18N

        latLonField.setEditable(false);
        latLonField.setFormatterFactory(new LatLonFormatterFactory());
        latLonField.setHorizontalAlignment(javax.swing.JTextField.CENTER);
        latLonField.setText(bundle.getString("MapViewer.latLonField.text")); // NOI18N
        latLonField.setFont(new java.awt.Font("Dialog", 0, 10)); // NOI18N
        latLonField.setPreferredSize(new java.awt.Dimension(30, 20));

        jLabel1.setFont(new java.awt.Font("Dialog", 0, 12)); // NOI18N
        jLabel1.setText(org.openide.util.NbBundle.getMessage(MapViewer.class, "MapViewer.jLabel1.text")); // NOI18N

        zoomLabel.setFont(new java.awt.Font("Dialog", 0, 12)); // NOI18N
        zoomLabel.setText(org.openide.util.NbBundle.getMessage(MapViewer.class, "MapViewer.zoomLabel.text")); // NOI18N
        zoomLabel.setName(""); // NOI18N

        jSzoomBar.setFont(new java.awt.Font("Dialog", 0, 10)); // NOI18N
        jSzoomBar.setMajorTickSpacing(10);
        jSzoomBar.setMinorTickSpacing(10);
        jSzoomBar.setPaintTicks(true);
        jSzoomBar.setSnapToTicks(true);
        jSzoomBar.setToolTipText(org.openide.util.NbBundle.getMessage(MapViewer.class, "MapViewer.jSzoomBar.toolTipText")); // NOI18N
        jSzoomBar.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
        jSzoomBar.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                jSzoomBarStateChanged(evt);
            }
        });

        jProgBarLoading.setIndeterminate(true);
        jProgBarLoading.setName(""); // NOI18N

        jLabelLoading.setFont(new java.awt.Font("Dialog", 0, 12)); // NOI18N
        jLabelLoading.setText(org.openide.util.NbBundle.getMessage(MapViewer.class, "MapViewer.jLabelLoading.text")); // NOI18N

        jtfZoomVal.setFont(new java.awt.Font("Dialog", 0, 10)); // NOI18N
        jtfZoomVal.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
        jtfZoomVal.setText(org.openide.util.NbBundle.getMessage(MapViewer.class, "MapViewer.jtfZoomVal.text")); // NOI18N
        jtfZoomVal.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseExited(java.awt.event.MouseEvent evt) {
                jtfZoomValMouseExited(evt);
            }
        });
        jtfZoomVal.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jtfZoomValActionPerformed(evt);
            }
        });

        jLabel2.setFont(new java.awt.Font("Dialog", 0, 10)); // NOI18N
        jLabel2.setText(org.openide.util.NbBundle.getMessage(MapViewer.class, "MapViewer.jLabel2.text")); // NOI18N

        javax.swing.GroupLayout statusBarLayout = new javax.swing.GroupLayout(statusBar);
        statusBar.setLayout(statusBarLayout);
        statusBarLayout.setHorizontalGroup(
            statusBarLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(statusBarLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(statusBarLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(statusBarLayout.createSequentialGroup()
                        .addGap(12, 12, 12)
                        .addComponent(jtfZoomVal, javax.swing.GroupLayout.PREFERRED_SIZE, 42, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(1, 1, 1)
                        .addComponent(jLabel2))
                    .addComponent(zoomLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 86, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jSzoomBar, javax.swing.GroupLayout.PREFERRED_SIZE, 238, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(32, 32, 32)
                .addComponent(jLabel1)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(statusBarLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(latLonField, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addGroup(statusBarLayout.createSequentialGroup()
                        .addComponent(jLabelLoading)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jProgBarLoading, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addContainerGap(161, Short.MAX_VALUE))
        );
        statusBarLayout.setVerticalGroup(
            statusBarLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(statusBarLayout.createSequentialGroup()
                .addGap(3, 3, 3)
                .addGroup(statusBarLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jSzoomBar, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
                    .addGroup(statusBarLayout.createSequentialGroup()
                        .addGroup(statusBarLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(latLonField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabel1)
                            .addComponent(zoomLabel))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(statusBarLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(statusBarLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                .addComponent(jtfZoomVal, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addComponent(jLabel2))
                            .addComponent(jProgBarLoading, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabelLoading))
                        .addGap(0, 0, Short.MAX_VALUE)))
                .addContainerGap())
        );

        mapPane.setBackground(new java.awt.Color(255, 255, 255));
        mapPane.setOpaque(true);

        javax.swing.GroupLayout mainPanelLayout = new javax.swing.GroupLayout(mainPanel);
        mainPanel.setLayout(mainPanelLayout);
        mainPanelLayout.setHorizontalGroup(
            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(mainPanelLayout.createSequentialGroup()
                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(mainPanelLayout.createSequentialGroup()
                        .addGap(30, 30, 30)
                        .addComponent(mapPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                    .addComponent(statusBar, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addContainerGap())
        );
        mainPanelLayout.setVerticalGroup(
            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, mainPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(mapPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(statusBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );

        splitPanel.setRightComponent(mainPanel);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(splitPanel)
                .addContainerGap())
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addGap(223, 223, 223)
                .addComponent(jLabel3)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(tf_windowSize, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(54, 54, 54)
                .addComponent(jb_reset)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(jb_cancel)
                .addGap(18, 18, 18)
                .addComponent(jb_done)
                .addGap(25, 25, 25))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addComponent(splitPanel)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jb_done)
                    .addComponent(jb_cancel)
                    .addComponent(jb_reset)
                    .addComponent(tf_windowSize, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel3))
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void jSzoomBarStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jSzoomBarStateChanged
        int newZoomValue;
        newZoomValue = jSzoomBar.getValue();
        
        if (newZoomValue != currentZoomLevel) {
            currentZoomLevel = newZoomValue;
            int idx = newZoomValue/this.jSzoomBar.getMinorTickSpacing();
            c_controller.setZoom(idx);

            
            jtfZoomVal.setText(Integer.toString(newZoomValue));
            jSzoomBar.setValue(newZoomValue);
        }
    }//GEN-LAST:event_jSzoomBarStateChanged

    public void setZoomLevels (int levels) {
        zoomLevelCnt = levels;
        int level = jSzoomBar.getValue();
        int tickSize = 100/zoomLevelCnt;
        jSzoomBar.setMinorTickSpacing(tickSize);
        jSzoomBar.setValue(level);
    }
    
    private void layerTableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_layerTableMouseClicked

    }//GEN-LAST:event_layerTableMouseClicked

    private void jtfZoomValActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jtfZoomValActionPerformed
        String s = jtfZoomVal.getText();
        int newZoomValue = Integer.decode(s);
        if (newZoomValue != currentZoomLevel) {
            this.jSzoomBar.setValue(newZoomValue);
        }
    }//GEN-LAST:event_jtfZoomValActionPerformed

    private void jtfZoomValMouseExited(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jtfZoomValMouseExited
        String s = jtfZoomVal.getText();
        int newZoomValue = Integer.decode(s);
        if (newZoomValue != currentZoomLevel) {
            this.jSzoomBar.setValue(newZoomValue);
        }
    }//GEN-LAST:event_jtfZoomValMouseExited

    private void formComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentResized
        Dimension size = getSize();
        showWindowSize(size);
    }//GEN-LAST:event_formComponentResized

    private void formWindowStateChanged(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowStateChanged
        int j = 1;
    }//GEN-LAST:event_formWindowStateChanged

    private void jb_resetActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jb_resetActionPerformed
        setSelectedLatLong(initialLocation);
        c_controller.setCenter(GISUtil.toCoordinate(initialLocation));
        firePropertyChange(PROP_RESETLATLONG, null, c_selectedLatLong);
    }//GEN-LAST:event_jb_resetActionPerformed

    private void jb_cancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jb_cancelActionPerformed
        setSelectedLatLong(initialLocation);
        c_controller.setCenter(GISUtil.toCoordinate(initialLocation));
        firePropertyChange(PROP_RESETLATLONG, null, c_selectedLatLong);
        
        setVisible(false);
    }//GEN-LAST:event_jb_cancelActionPerformed

    private void jb_doneActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jb_doneActionPerformed
        
        setSelectedLatLong(c_selectedLatLong, true);
        setVisible(false);
    }//GEN-LAST:event_jb_doneActionPerformed

    private void formMouseEntered(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseEntered
    }//GEN-LAST:event_formMouseEntered

    private void showWindowSize (Dimension size) {
        String s = size.width + " , " + size.height;
        tf_windowSize.setText(s);
    }
    
    
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                
                new MapViewer().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel demoCMZLabel;
    private javax.swing.JLabel demoCligenLabel;
    private javax.swing.JLabel demoCountryLabel;
    private javax.swing.JLabel demoCountyLabel;
    private javax.swing.JLabel demoPositionLabel;
    private javax.swing.JLabel demoStateLabel;
    private javax.swing.JLabel demoWingenLabel;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabelLoading;
    private javax.swing.JProgressBar jProgBarLoading;
    protected javax.swing.JSlider jSzoomBar;
    private javax.swing.JButton jb_cancel;
    private javax.swing.JButton jb_done;
    private javax.swing.JButton jb_reset;
    private javax.swing.JTextField jtfZoomVal;
    private javax.swing.JFormattedTextField latLonField;
    private javax.swing.JTable layerTable;
    private javax.swing.JPanel mainPanel;
    private usda.weru.gis.gui.MapPane mapPane;
    private javax.swing.JLabel positionCMZ;
    private javax.swing.JLabel positionCligen;
    private javax.swing.JLabel positionCountry;
    private javax.swing.JLabel positionCounty;
    private javax.swing.JLabel positionLatLon;
    private javax.swing.JLabel positionState;
    private javax.swing.JLabel positionWingen;
    private javax.swing.JPanel sidePanel;
    private javax.swing.JSplitPane splitPanel;
    private javax.swing.JPanel statusBar;
    private javax.swing.JScrollPane tableScrollPanel;
    private javax.swing.JFormattedTextField tf_windowSize;
    private javax.swing.JLabel zoomLabel;
    // End of variables declaration//GEN-END:variables

    private class InternalSelectionHandler extends MouseAdapter {

        private Coordinate translate(MouseEvent e) {
            Coordinate center = c_controller.getCenter();
            double zoom = c_controller.getZoomFactor();
            double cx = e.getX() - (mapPane.getWidth() / 2);
            double cy = e.getY() - (mapPane.getHeight() / 2);
            double dx = cx / zoom;
            double dy = cy / zoom;
            return new Coordinate(center.x + dx, center.y - dy);
            
        }

        @Override
        public void mouseClicked(MouseEvent e) {
            if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) {
                // getClickCount() == 1 was already called, so don't repeat what it did.
                setVisible(false);
            } else if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 1) {
                Coordinate select = translate(e);
                c_controller.setCenter(select);
                LatLong latlong = GISUtil.toLatLong(select);
                setSelectedLatLong(latlong);
            }
        }

        @Override
        public void mouseExited(MouseEvent e) {
            latLonField.setValue(c_controller.getCenter());
        }
        
        @Override
        public void mouseMoved(MouseEvent e) {
            Coordinate coord = translate(e);
            latLonField.setValue(coord);
        }
    }
    

    /**
     *
     * @param latlong
     */
    public synchronized void ShowPositionData(LatLong latlong) {
        if (c_positionData != null) {
            c_positionData.cancel(false);
        }

        c_positionData = new ShowPositionDataSwingWorker(latlong);
        c_positionData.execute();

    }
    
    private class InternalZoomAction extends MouseAdapter {
        @Override
        public void mouseWheelMoved(MouseWheelEvent e) {
           //change zoomStatus
        }
    }

    private class ShowPositionDataSwingWorker extends SwingWorker<Object, Runnable> {

        private final LatLong c_latlong;

        public ShowPositionDataSwingWorker(LatLong latlong) {
            c_latlong = latlong;
        }

        @Override
        protected Object doInBackground() throws Exception {
            if (isCancelled()) {
                return null;
            }
            publish(positionCountry, "");
            publish(positionState, "");
            publish(positionCounty, "");
            publish(demoCountyLabel, "County:");
            publish(positionCMZ, "");
            //publish(positionSoils, "");
            publish(positionWingen, "");
            publish(positionCligen, "");

            //position
            try {
                NumberFormat format = new DecimalFormat("0.00000");
                publish(positionLatLon, format.format(c_latlong.longitudeValue(USCustomary.DEGREE_ANGLE))
                        + ", " + format.format(c_latlong.latitudeValue(USCustomary.DEGREE_ANGLE)));
                if (isCancelled()) {
                    return null;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (isCancelled()) {
                return null;
            }
            //end position

            //territory
            List<Territory> territories = lookupValues(Territory.class);
            boolean hasState = false;
            String countryName = "";
            for (Territory territory : territories) {
                switch (territory.getType()) {
                    case Country:
                        publish(positionCountry, territory.getDisplayName());
                        countryName = territory.getDisplayName();
                        break;
                    case State:
                        publish(positionState, territory.getDisplayName());
                        hasState = true;
                        break;
                    case County:
                        publish(positionCounty, territory.getDisplayName());
                        publish(demoCountyLabel, territory.getTypeDescription() + ":");
                        break;
                }
            }
            if (!hasState) {
                publish(positionState, countryName);
            }
            if (isCancelled()) {
                return null;
            }
            //end territory

            //CMZ
            CropManagementZone cmz = lookupValue(CropManagementZone.class);
            if (cmz != null) {
                float cmzNum = cmz.getId();
                if (cmzNum - Math.floor(cmzNum) < .08) {
                    publish(positionCMZ, Integer.toString((int)cmzNum));
                } else {
                    publish(positionCMZ, Float.toString(cmzNum));
                }
            } else {
                publish(positionCMZ, "");
            }
            if (isCancelled()) {
                return null;
            }
            //end cmz

            return null;
        }

        private <T> T lookupValue(Class<T> type) {
            GISData data = GISData.getInstance();
            GISLookup<T> lookup = data.getLookup(type);
            List<T> results = null;
            if (lookup != null) {
                results = lookup.lookup(c_latlong);
            }

            if (results != null && !results.isEmpty()) {
                return results.get(0);
            } else {
                return null;
            }

        }

        private <T> List<T> lookupValues(Class<T> type) {
            GISData data = GISData.getInstance();
            GISLookup<T> lookup = data.getLookup(type);
            List<T> results = null;
            if (lookup != null) {
                results = lookup.lookup(c_latlong);
            }
            if (results != null) {
                return results;
            } else {
                return Collections.emptyList();
            }
        }

        private void publish(final JLabel label, final String text) {
            Runnable runnable = new Runnable() {

                @Override
                public void run() {
                    label.setText(text);
                }
            };
            publish(runnable);
        }

        @Override
        protected void process(List<Runnable> chunks) {
            
            for (Runnable runnable : chunks) {
                if (ShowPositionDataSwingWorker.this.isCancelled()) {
                    return;
                }
                runnable.run();
            }
        }
    }
    
    public void resetCachePaths () {
        c_controller.updateDataSources();
    }
    
    public void refreshLayerTable () {
        layerTable.repaint();
    }
}
