// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/layer/location/csv/CSVLinkHandler.java,v $
// $RCSfile: CSVLinkHandler.java,v $
// $Revision: 1.5 $
// $Date: 2005/08/09 18:17:08 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.layer.location.csv;
/* Java Core */
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import com.bbn.openmap.layer.location.Link;
import com.bbn.openmap.layer.location.Location;
import com.bbn.openmap.layer.location.URLRasterLocation;
import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.util.ColorFactory;
import com.bbn.openmap.util.DataOrganizer;
import com.bbn.openmap.util.PropUtils;
import com.bbn.openmap.util.quadtree.QuadTree;
/**
* The CSVLinkHandler is designed to let you put data on the map based on
* information from a Comma Separated Value(CSV) file. It's assumed that the
* each row in the file refers to two locations, and that a link is to be shown
* between the two locations. It has the same basic properties as the
* CSVLocationHandler, with allowances for extra lat/lon per record to handle
* the other side of the link/line.
*
* <P>
* The individual fields must not have leading whitespace.
*
* <P>
* The locationFile property should contain a URL referring to the file. This
* can take the form of file:/myfile.csv for a local file or
* http://somehost.org/myfile.csv for a remote file
*
* If there is a lat/lon index, and a lat2/lon2 index, then the links' endpoints
* are in the link file.
* <P>
*
* The Link CSV file has to have certain fields, and the column number of those
* fields are set in the properties:
*
* <pre>
*
* # latitude and longitude indexes of the link end points. The first two are the same as CSVLocationHandler.
* linkMarkerName.latIndex=column_number
* linkMarkerName.lonIndex=column_number
* linkMarkerName.lat2Index=column_number
* linkMarkerName.lon2Index=column_number
*
* #plus all the other optional properties. The marker parameters will work on the link OMGraphics.
*
* </pre>
*
* <p>
* TODO: update the quadtree used to instead use a
* com.bbn.openmap.geo.ExtentIndex, so that lines that bisect the map will
* appear. Right now, one of the endpoints of the line has to be in the map
* window in order for the link to be displayed, and that's not quite right.
*/
public class CSVLinkHandler extends CSVLocationHandler {
// //////////////////////
// Link Variables
/**
* Property to use to designate the column of the link file to use as the
* latitude of end "2".
*/
public static final String Lat2IndexProperty = "lat2Index";
/**
* Property to use to designate the column of the link file to use as the
* longitude of end "2".
*/
public static final String Lon2IndexProperty = "lon2Index";
/** The names of the various link types on the map. Not used. */
/** Index of column in CSV to use as latitude2 of link. */
protected int lat2Index = -1;
/** Index of column in CSV to use as longitude2 of link. */
protected int lon2Index = -1;
/**
* The default constructor for the Layer. All of the attributes are set to
* their default values.
*/
public CSVLinkHandler() {
}
/**
* The properties and prefix are managed and decoded here, for the standard
* uses of the CSVLinkHandler.
*
* @param prefix string prefix used in the properties file for this layer.
* @param properties the properties set in the properties file.
*/
public void setProperties(String prefix, java.util.Properties properties) {
super.setProperties(prefix, properties);
String realPrefix = PropUtils.getScopedPropertyPrefix(this);
lat2Index = PropUtils.intFromProperties(properties, realPrefix + Lat2IndexProperty, lat2Index);
lon2Index = PropUtils.intFromProperties(properties, realPrefix + Lon2IndexProperty, lon2Index);
}
/**
* PropertyConsumer method, to fill in a Properties object, reflecting the
* current values of the layer. If the layer has a propertyPrefix set, the
* property keys should have that prefix plus a separating '.' prepended to
* each property key it uses for configuration.
*
* @param props a Properties object to load the PropertyConsumer properties
* into.
* @return Properties object containing PropertyConsumer property values. If
* getList was not null, this should equal getList. Otherwise, it
* should be the Properties object created by the PropertyConsumer.
*/
public Properties getProperties(Properties props) {
props = super.getProperties(props);
String prefix = PropUtils.getScopedPropertyPrefix(this);
props.put(prefix + Lat2IndexProperty, (lat2Index != -1 ? Integer.toString(lat2Index) : ""));
props.put(prefix + Lon2IndexProperty, (lon2Index != -1 ? Integer.toString(lon2Index) : ""));
return props;
}
/**
* Method to fill in a Properties object with values reflecting the
* properties able to be set on this PropertyConsumer. The key for each
* property should be the raw property name (without a prefix) with a value
* that is a String that describes what the property key represents, along
* with any other information about the property that would be helpful
* (range, default value, etc.). This method takes care of the basic
* LocationHandler parameters, so any LocationHandlers that extend the
* AbstractLocationHandler should call this method, too, before adding any
* specific properties.
*
* @param list a Properties object to load the PropertyConsumer properties
* into. If getList equals null, then a new Properties object should
* be created.
* @return Properties object containing PropertyConsumer property values. If
* getList was not null, this should equal getList. Otherwise, it
* should be the Properties object created by the PropertyConsumer.
*/
public Properties getPropertyInfo(Properties list) {
list = super.getPropertyInfo(list);
list.remove(LatIndexProperty);
list.remove(LonIndexProperty);
list.put(LatIndexProperty, "The column index, in the location file, of the first node latitude.");
list.put(LonIndexProperty, "The column index, in the location file, of the first node longitude.");
list.put(Lat2IndexProperty, "The column index, in the location file, of the second node latitude.");
list.put(Lon2IndexProperty, "The column index, in the location file, of the second node longitude.");
return list;
}
protected boolean checkIndexSettings() {
if (latIndex == -1 || lonIndex == -1 || lat2Index == -1 || lon2Index == -1) {
logger.warning("CSVLocationHandler: createData(): Index properties for Lat/Lon/Name are not set properly! lat index:"
+ latIndex + ", lon index:" + lonIndex);
return false;
}
if (logger.isLoggable(Level.FINE)) {
logger.fine("CSVLinkHandler: Reading File:" + locationFile + " lat1Index: " + latIndex
+ " lon1Index: " + lonIndex + " lat2Index: " + lat2Index + " lon2Index: "
+ lon2Index);
}
return true;
}
/**
* Provides the palette widgets to control the options of showing maps, or
* attribute text.
* <P>
* In this case, the palette widget only contains one button, which reloads
* the data files for the layer.
* <p>
*
* @return Component object representing the palette widgets.
*/
public Component getGUI() {
JButton rereadFilesButton;
JCheckBox showCSVLinkCheck;
showCSVLinkCheck = new JCheckBox("Show Links", isShowLocations());
showCSVLinkCheck.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
JCheckBox locationCheck = (JCheckBox) ae.getSource();
setShowLocations(locationCheck.isSelected());
if (logger.isLoggable(Level.FINE)) {
logger.fine("CSVLocationHandler::actionPerformed showLocations is "
+ isShowLocations());
}
getLayer().repaint();
}
});
rereadFilesButton = new JButton("Re-Read Data File");
rereadFilesButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Re-reading Locations file");
}
quadtree = null;
getLayer().doPrepare();
}
});
Box box = Box.createVerticalBox();
box.add(showCSVLinkCheck);
box.add(rereadFilesButton);
return box;
}
/**
* This is the method called by create data with a row's worth of
* information stuffed in the record List. The indexes set in the properties
* should describe what each entry is.
*
* @param recordList a record/row of data from the csv file.
* @param qt the Quadtree to add the Location object, created from the row
* contents.
*/
protected void createLocation(List recordList, QuadTree<Location> qt) {
String name = tokenToString(recordList, nameIndex, "");
double lat = tokenToDouble(recordList, latIndex, 0.0);
double lon = tokenToDouble(recordList, lonIndex, 0.0, eastIsNeg);
String iconURL = tokenToString(recordList, iconIndex, defaultIconURL);
double lat2 = tokenToDouble(recordList, lat2Index, 0.0);
double lon2 = tokenToDouble(recordList, lon2Index, 0.0, eastIsNeg);
Link link = new Link(lat, lon, lat2, lon2, "No details");
getLocationDrawingAttributes().setTo(link);
link.setLocationHandler(CSVLinkHandler.this);
// What we really want to do is get the
// locationDrawingAttributes and set them on the link.
if (logger.isLoggable(Level.FINE)) {
logger.fine("CSVLinkHandler: " + link.getDetails());
}
qt.put(lat, lon, link);
qt.put(lat2, lon2, link);
qt.put(lat, lon, createLocation(lat, lon, name, iconURL, recordList));
}
}