Java tutorial
/* Copyright (C) 2015, University of Kansas Center for Research * * Specify Software Project, specify@ku.edu, Biodiversity Institute, * 1345 Jayhawk Boulevard, Lawrence, Kansas, 66045, USA * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package edu.ku.brc.specify.ui; import java.awt.Dimension; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Vector; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import com.jgoodies.forms.builder.PanelBuilder; import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; import edu.ku.brc.af.core.RecordSetFactory; import edu.ku.brc.af.core.db.DBTableIdMgr; import edu.ku.brc.af.core.db.DBTableInfo; import edu.ku.brc.af.ui.forms.formatters.UIFieldFormatterIFace; import edu.ku.brc.dbsupport.RecordSetIFace; import edu.ku.brc.services.mapping.LatLonPlacemarkIFace; import edu.ku.brc.services.mapping.LatLonPoint; import edu.ku.brc.specify.conversion.BasicSQLUtils; import edu.ku.brc.specify.datamodel.CollectingEvent; import edu.ku.brc.specify.datamodel.CollectionObject; import edu.ku.brc.specify.datamodel.Locality; import edu.ku.brc.specify.datamodel.Taxon; import edu.ku.brc.specify.tasks.DataEntryTask; import edu.ku.brc.specify.tasks.RecordSetTask; import edu.ku.brc.ui.CommandAction; import edu.ku.brc.ui.CommandDispatcher; import edu.ku.brc.ui.CustomDialog; import edu.ku.brc.ui.IconManager; import edu.ku.brc.ui.UIHelper; import gov.nasa.worldwind.event.SelectEvent; import gov.nasa.worldwind.event.SelectListener; // ZZZ import gov.nasa.worldwind.examples.LineBuilder; import gov.nasa.worldwind.geom.Position; import gov.nasa.worldwind.layers.LayerList; import gov.nasa.worldwind.layers.MarkerLayer; import gov.nasa.worldwind.layers.RenderableLayer; import gov.nasa.worldwind.render.GlobeAnnotation; import gov.nasa.worldwind.render.Polyline; import gov.nasa.worldwind.render.markers.BasicMarker; //ZZZ import gov.nasa.worldwind.util.GeometryMath; //ZZZ import gov.nasa.worldwind.view.OrbitView; /** * @author rods * * @code_status Alpha * * Sep 26, 2009 * */ public class WorldWindSearchPanel extends JPanel { protected static final int[] TABLE_IDS = { CollectionObject.getClassTableId(), Locality.getClassTableId(), CollectingEvent.getClassTableId(), Taxon.getClassTableId() }; protected static final int MAP_WIDTH = 500; protected static final int MAP_HEIGHT = 500; protected static DBTableInfo[] TABLE_INFO; protected WorldWindPanel wwPanel; protected Vector<LatLonPoint> availPoints = new Vector<LatLonPoint>(); protected Vector<LatLonPoint> selectedPoints = new Vector<LatLonPoint>(); protected Vector<Position> containedWWPoints = new Vector<Position>(); protected List<Position> polygonWWPoints = new Vector<Position>(); protected HashSet<Integer> topIdHash = new HashSet<Integer>(); protected HashSet<Integer> botIdHash = new HashSet<Integer>(); protected JLabel topLbl; protected JList dbObjList; protected JList recSetList; protected JComboBox typeCBX; protected JButton searchBtn; protected JButton startBtn; protected JButton endBtn; protected JButton clearBtn; protected JButton dwnBtn; protected JButton upBtn; protected JButton selectAllBtn; protected JButton deselectAllBtn; protected JButton rsBtn; protected JButton fmBtn; // Polygon protected boolean isCreatingPolygon = false; protected Point prevPoint = null; // Map Selection protected Position lastClickPos = null; // ZZZ protected LineBuilder lineBuilder; protected RenderableLayer lineLayer; protected Polyline polyline = new Polyline(); /** * */ public WorldWindSearchPanel() { super(); TABLE_INFO = new DBTableInfo[TABLE_IDS.length]; for (int i = 0; i < TABLE_IDS.length; i++) { TABLE_INFO[i] = DBTableIdMgr.getInstance().getInfoById(TABLE_IDS[i]); } createUI(); } /** * Creates the UI. */ protected void createUI() { CellConstraints cc = new CellConstraints(); lineLayer = new RenderableLayer(); upBtn = UIHelper.createIconBtn("Green Arrow Up", "", new ActionListener() { @Override public void actionPerformed(ActionEvent e) { moveItems(recSetList, botIdHash, dbObjList, topIdHash); } }); dwnBtn = UIHelper.createIconBtn("Green Arrow Down", "", new ActionListener() { @Override public void actionPerformed(ActionEvent e) { moveItems(dbObjList, topIdHash, recSetList, botIdHash); boolean hasItems = recSetList.getModel().getSize() > 0; rsBtn.setEnabled(hasItems); fmBtn.setEnabled(hasItems); } }); selectAllBtn = UIHelper.createI18NButton("SELECTALL"); selectAllBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { dbObjList.clearSelection(); dbObjList.getSelectionModel().setSelectionInterval(0, dbObjList.getModel().getSize()); } }); deselectAllBtn = UIHelper.createI18NButton("DESELECTALL"); deselectAllBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { dbObjList.clearSelection(); } }); PanelBuilder upDwnPB = new PanelBuilder(new FormLayout("f:p:g,p,f:p:g,p,10px,p,f:p:g,p,f:p:g", "p")); //upDwnPB.add(selectAllBtn, cc.xy(2,1)); upDwnPB.add(dwnBtn, cc.xy(4, 1)); upDwnPB.add(upBtn, cc.xy(6, 1)); //upDwnPB.add(deselectAllBtn, cc.xy(7,1)); dbObjList = new JList(new DefaultListModel()); dbObjList.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); //wpList.setCellRenderer(new WPListCellRenderer()); dbObjList.addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { upBtn.setEnabled(false); dwnBtn.setEnabled(dbObjList.getSelectedIndex() != -1); } }); recSetList = new JList(new DefaultListModel()); recSetList.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); //trkList.setCellRenderer(new TrkListCellRenderer()); recSetList.addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { boolean isSel = recSetList.getSelectedIndex() != -1; dwnBtn.setEnabled(false); upBtn.setEnabled(isSel); } }); rsBtn = UIHelper.createIconBtn("Record_Set", IconManager.IconSize.Std24, "", null); fmBtn = UIHelper.createIconBtn("FormEdit", IconManager.IconSize.Std24, "", null); PanelBuilder toolsBtnPB = new PanelBuilder(new FormLayout("f:p:g,2px,p,2px,p", "p")); toolsBtnPB.add(fmBtn, cc.xy(3, 1)); toolsBtnPB.add(rsBtn, cc.xy(5, 1)); PanelBuilder leftPB = new PanelBuilder( new FormLayout("min(p;250px),10px,f:p:g", "p,2px,f:p:g, 4px,p,4px, p,2px,f:p:g, 2px,p")); int y = 1; leftPB.add(topLbl = UIHelper.createLabel("Available Points"), cc.xy(1, y)); y += 2; // I18N leftPB.add(UIHelper.createScrollPane(dbObjList, true), cc.xy(1, y)); y += 2; leftPB.add(upDwnPB.getPanel(), cc.xy(1, y)); y += 2; leftPB.add(UIHelper.createLabel("Selected Objects"), cc.xy(1, y)); y += 2; leftPB.add(UIHelper.createScrollPane(recSetList, true), cc.xy(1, y)); y += 2; leftPB.add(toolsBtnPB.getPanel(), cc.xy(1, y)); y += 2; wwPanel = new WorldWindPanel(); wwPanel.setPreferredSize(new Dimension(MAP_WIDTH, MAP_HEIGHT)); wwPanel.getWorld().addSelectListener(new ClickAndGoSelectListener(wwPanel.getWorld(), MarkerLayer.class)); wwPanel.getWorld().addSelectListener(getWWSelectListener()); wwPanel.getWorld().getInputHandler().addMouseListener(getWWMouseAdapter()); PanelBuilder rightPB = new PanelBuilder(new FormLayout("f:p:g", "f:p:g")); // ZZZ lineBuilder = new LineBuilder(wwPanel.getWorld(), lineLayer, polyline); rightPB.add(wwPanel, cc.xy(1, 1)); y += 2; polyline.setAntiAliasHint(Polyline.ANTIALIAS_NICEST); polyline.setLineWidth(1.5); polyline.setFollowTerrain(true); startBtn = UIHelper.createI18NButton("Start"); endBtn = UIHelper.createI18NButton("End"); searchBtn = UIHelper.createI18NButton("SEARCH"); clearBtn = UIHelper.createI18NButton("Clear"); typeCBX = UIHelper.createComboBox(TABLE_INFO); PanelBuilder btnPB = new PanelBuilder(new FormLayout("f:p:g,p,f:p:g,p,f:p:g,p,f:p:g,p,f:p:g,p,f:p:g", "p")); btnPB.add(typeCBX, cc.xy(2, 1)); btnPB.add(startBtn, cc.xy(4, 1)); btnPB.add(endBtn, cc.xy(6, 1)); btnPB.add(searchBtn, cc.xy(8, 1)); btnPB.add(clearBtn, cc.xy(10, 1)); PanelBuilder pb = new PanelBuilder(new FormLayout("p,10px,f:p:g", "f:p:g, 4px, p"), this); pb.add(leftPB.getPanel(), cc.xy(1, 1)); pb.add(rightPB.getPanel(), cc.xy(3, 1)); pb.add(btnPB.getPanel(), cc.xyw(1, 3, 3)); pb.setDefaultDialogBorder(); startBtn.setEnabled(true); endBtn.setEnabled(false); searchBtn.setEnabled(false); clearBtn.setEnabled(false); rsBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { RecordSetIFace rs = createRecordSet(); if (rs != null) { CommandDispatcher.dispatch(new CommandAction(RecordSetTask.RECORD_SET, "Save", null, null, rs)); } } }); fmBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { RecordSetIFace rs = createRecordSet(); if (rs != null) { CommandDispatcher .dispatch(new CommandAction(DataEntryTask.DATA_ENTRY, DataEntryTask.EDIT_DATA, rs)); } } }); searchBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { doSearch(); } }); startBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { polygonWWPoints.clear(); isCreatingPolygon = true; searchBtn.setEnabled(false); endBtn.setEnabled(true); startBtn.setEnabled(false); // ZZZ lineBuilder.setArmed(true); } }); endBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { polyline.setClosed(true); isCreatingPolygon = false; searchBtn.setEnabled(true); endBtn.setEnabled(false); startBtn.setEnabled(false); searchBtn.setEnabled(true); clearBtn.setEnabled(true); //polygonWWPoints.add(polygonWWPoints.get(0)); //createPolyline(); // ZZZ lineBuilder.setArmed(false); } }); clearBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { doClear(true); } }); typeCBX.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { doPickedNewObjType(); } }); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { typeCBX.setSelectedIndex(0); } }); } /** * @return the RecordSet of chosen items. */ private RecordSetIFace createRecordSet() { RecordSetIFace rs = null; DefaultListModel model = (DefaultListModel) recSetList.getModel(); if (model.getSize() > 0) { rs = RecordSetFactory.getInstance().createRecordSet(); rs.setDbTableId(TABLE_IDS[typeCBX.getSelectedIndex()]); for (int i = 0; i < model.getSize(); i++) { LatLonPoint llp = (LatLonPoint) model.get(i); rs.addItem(llp.getLocId()); } } return rs; } /** * Moves selected items from one list to the other. * @param srcList * @param srcHash * @param dstList * @param dstHash */ private void moveItems(final JList srcList, final HashSet<Integer> srcHash, final JList dstList, final HashSet<Integer> dstHash) { int inx = srcList.getSelectedIndex(); if (inx > -1) { DefaultListModel srcModel = (DefaultListModel) srcList.getModel(); DefaultListModel dstModel = (DefaultListModel) dstList.getModel(); int[] indexes = srcList.getSelectedIndices(); ArrayList<LatLonPoint> llpList = new ArrayList<LatLonPoint>(indexes.length); for (int selInx : indexes) { LatLonPoint llp = (LatLonPoint) srcModel.get(selInx); llpList.add(llp); if (!dstHash.contains(llp.getLocId())) { dstModel.addElement(llp); dstHash.add(llp.getLocId()); } } for (LatLonPoint llp : llpList) { srcModel.removeElement(llp); srcHash.remove(llp.getLocId()); } } } /** * */ private void doClear(final boolean doClearPolygon) { if (doClearPolygon) { LayerList layers = wwPanel.getWorld().getModel().getLayers(); int inx = layers.indexOf(lineLayer); layers.remove(inx); } wwPanel.getMarkerLayer().clearList(); wwPanel.getWorld().redrawNow(); wwPanel.reset(); if (doClearPolygon) { polyline = new Polyline(); polyline.setAntiAliasHint(Polyline.ANTIALIAS_NICEST); polyline.setLineWidth(1.5); polyline.setFollowTerrain(true); lineLayer = new RenderableLayer(); // ZZZ lineBuilder = new LineBuilder(wwPanel.getWorld(), lineLayer, polyline); //WorldWindPanel.insertAfterPlacenames(wwPanel.getWorld(), lineLayer); } ((DefaultListModel) dbObjList.getModel()).removeAllElements(); topIdHash.clear(); clearBtn.setEnabled(false); startBtn.setEnabled(true); } /** * */ private void doPickedNewObjType() { boolean hadItems = dbObjList.getModel().getSize() > 0; doClear(false); topLbl.setText(typeCBX.getSelectedItem().toString()); DefaultListModel model = (DefaultListModel) recSetList.getModel(); model.removeAllElements(); botIdHash.clear(); rsBtn.setEnabled(false); fmBtn.setEnabled(false); if (hadItems) { searchBtn.setEnabled(true); startBtn.setEnabled(false); } } /** * */ private void doSearch() { double topMin = Double.MAX_VALUE; double topMax = Double.MIN_VALUE; double botMin = Double.MAX_VALUE; double botMax = -1000.0; ArrayList<Position> pntList = new ArrayList<Position>(); for (Position p : polyline.getPositions()) { double lat = p.getLatitude().getDegrees(); double lon = p.getLongitude().getDegrees(); if (lat <= topMin) { topMin = lat; } if (lat >= topMax) { topMax = lat; } if (lon <= botMin) { botMin = lon; } if (lon >= botMax) { botMax = lon; } pntList.add(p); } String orderBy = null; StringBuilder sb = new StringBuilder(); switch (typeCBX.getSelectedIndex()) { case 0: sb.append(doColObjSearchSQL()); orderBy = "CatalogNumber"; break; case 1: sb.append(getLocalitySearchSQL()); orderBy = "LocalityName"; break; case 2: sb.append(doCollEventSearchSQL()); orderBy = "StartDate"; break; case 3: sb.append(doTaxonSearchSQL()); orderBy = "FullName"; break; } String whereSQL = " Latitude1 >= %10.5f AND Latitude1 <= %10.5f AND Longitude1 >= %10.5f AND Longitude1 <= %10.5f"; sb.append(String.format(whereSQL, topMin, topMax, botMin, botMax)); sb.append(orderBy != null ? (" ORDER BY " + orderBy) : ""); //System.err.println(sb.toString()); availPoints.clear(); DefaultListModel model = (DefaultListModel) dbObjList.getModel(); model.removeAllElements(); topIdHash.clear(); List<LatLonPlacemarkIFace> markers = new Vector<LatLonPlacemarkIFace>(); pntList.add(pntList.get(0)); Polyline polygon = new Polyline(pntList); polygon.setClosed(true); UIFieldFormatterIFace fldFmt = null; if (typeCBX.getSelectedIndex() == 0) { fldFmt = DBTableIdMgr.getFieldFormatterFor(CollectionObject.class, "catalogNumber"); } int index = 0; Vector<Object[]> pnts = BasicSQLUtils.query(sb.toString()); for (Object[] row : pnts) { double lat = ((BigDecimal) row[1]).doubleValue(); double lon = ((BigDecimal) row[2]).doubleValue(); Position pos = Position.fromDegrees(lat, lon, 0.0); // ZZZ // if (GeometryMath.isLocationInside(pos, polygon.getPositions())) // { // LatLonPoint llp = new LatLonPoint((Integer)row[0], lat, lon); // String title; // if (row[3] != null) // { // title = (fldFmt != null ? fldFmt.formatToUI(row[3]) : row[3]).toString(); // } else // { // title = "N/A"; // } // llp.setTitle(title); // llp.setIndex(index++); // availPoints.add(llp); // markers.add(llp); // model.addElement(llp); // topIdHash.add(llp.getLocId()); // } } if (markers.size() > 0) { wwPanel.placeMarkers(markers, null); clearBtn.setEnabled(true); searchBtn.setEnabled(false); } else { doClear(true); clearBtn.setEnabled(true); startBtn.setEnabled(false); } } /** * @return */ private String getLocalitySearchSQL() { return "SELECT LocalityID, Latitude1, Longitude1, LocalityName FROM locality WHERE %s GROUP BY LocalityID ORDER BY LocalityName"; } /** * @return */ private String doColObjSearchSQL() { return "SELECT co.CollectionObjectID, l.Latitude1, l.Longitude1, co.CatalogNumber FROM locality l INNER JOIN collectingevent ce ON l.LocalityID = ce.LocalityID " + "INNER JOIN collectionobject co ON ce.CollectingEventID = co.CollectingEventID WHERE %s ORDER BY CatalogNumber"; } /** * @return */ private String doCollEventSearchSQL() { return "SELECT ce.CollectingEventID, l.Latitude1, l.Longitude1, ce.StartDate FROM locality l " + "INNER JOIN collectingevent ce ON l.LocalityID = ce.LocalityID WHERE %s GROUP BY ce.CollectingEventID ORDER BY StartDate"; } /** * @return */ private String doTaxonSearchSQL() { return "SELECT t.TaxonID, l.Latitude1, l.Longitude1, t.FullName " + "FROM locality l INNER JOIN collectingevent ce ON l.LocalityID = ce.LocalityID " + "INNER JOIN collectionobject co ON ce.CollectingEventID = co.CollectingEventID " + "INNER JOIN determination d ON co.CollectionObjectID = d.CollectionObjectID " + "INNER JOIN taxon t ON d.TaxonID = t.TaxonID WHERE d.IsCurrent = TRUE AND %s GROUP BY t.TaxonID ORDER BY FullName"; } /** * @return */ private SelectListener getWWSelectListener() { return new SelectListener() { @Override public void selected(SelectEvent event) { if (event.getEventAction().equals(SelectEvent.LEFT_CLICK)) { // This is a left click if (event.hasObjects() && event.getTopPickedObject().hasPosition()) { lastClickPos = wwPanel.getWorld().getCurrentPosition(); // There is a picked object with a position // ZZZ // if (wwPanel.getWorld().getView() instanceof OrbitView) // { // if (event.getTopObject().getClass().equals(BasicMarker.class)) // { // int inx = wwPanel.getMarkers().indexOf(event.getTopObject()); // if (inx > -1) // { // //resultsTable.setRowSelectionInterval(inx, inx); // } // } else if (event.getTopObject().getClass().equals(GlobeAnnotation.class)) // { // int inx = wwPanel.getAnnotations().indexOf(event.getTopObject()); // if (inx > -1) // { // //resultsTable.setRowSelectionInterval(inx, inx); // } // } // } } } } }; } /** * @return */ private MouseAdapter getWWMouseAdapter() { return new MouseAdapter() { @Override public void mouseClicked(final MouseEvent e) { super.mouseClicked(e); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { // ZZZ // if (!lineBuilder.isArmed()) // { // Position pos = wwPanel.getWorld().getCurrentPosition(); // if (!pos.equals(lastClickPos)) // { // // } // } } }); } }; } /** * Converts a list of LatLonPoints to a List of WW Positions. * @param pnts LatLonPoint list * @return list of Positions */ protected List<Position> createPolygonFromPoints(final Vector<LatLonPoint> pnts) { ArrayList<Position> polygonList = new ArrayList<Position>(); for (LatLonPoint p : pnts) { Position pos = Position.fromDegrees(p.getLatitude(), p.getLongitude(), 0.0); polygonList.add(pos); } return polygonList; } /** * */ /*protected void createContainedPoints() { List<LatLonPlacemarkIFace> markers = new Vector<LatLonPlacemarkIFace>(); for (LatLonPoint p : availPoints) { Position pos = Position.fromDegrees(p.getLatitude(), p.getLongitude(), 0.0); if (GeometryMath.isLocationInside(pos, polyline.getPositions())) { markers.add(p); } } wwPanel.placeMarkers(markers, null); }*/ //------------------------------------------------------------------------------------ //-- static methods //------------------------------------------------------------------------------------ /** * @return */ public static CustomDialog getDlg() { WorldWindSearchPanel panel = new WorldWindSearchPanel(); CustomDialog dlg = new CustomDialog(null, "", true, panel); dlg.setVisible(true); return dlg; } }