// **********************************************************************
//
// <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/corba/com/bbn/openmap/layer/specialist/vpf/VPFSpecialist.java,v $
// $RCSfile: VPFSpecialist.java,v $
// $Revision: 1.4.2.1 $
// $Date: 2004/10/14 18:26:33 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.layer.specialist.vpf;
import com.bbn.openmap.CSpecialist.*;
import com.bbn.openmap.LatLonPoint;
import com.bbn.openmap.MoreMath;
import com.bbn.openmap.layer.specialist.*;
import com.bbn.openmap.layer.util.http.HttpServer;
import com.bbn.openmap.layer.vpf.*;
import com.bbn.openmap.util.Debug;
import java.io.File;
import java.util.Hashtable;
import java.util.StringTokenizer;
/**
* Implement the Specialist interface so that we can serve graphics to
* OpenMap via CORBA.
* <p>
*
* In the properties file for OpenMap, you can set parameters that
* determine what coverages and features are sent back to the
* CSpecLayer. Specifically, these parameters are listed in the
* staticArgs property as (key, value) pairs. For political boundaries
* and coastlines, for instance:
* <p>
*
* staticArgs=(coverageType, bnd)(featureTypes, edge area) (edge,
* polbndl coastl) (area, polbnda) (lineColor, FFFF0000) (fillColor,
* FFAAAA66) (draw, edge area)
*
* <p>
*
* The (draw, edge area) pairing should be used if the user won't have
* access to the palette for the CSpecLayer. The palette controls
* whether certain feature types are drawn, and by default, areas are
* not and need to be turned on in the palette. If you set the draw
* key, the feature types listed will be forced to be returned if they
* exist. The keys used here should match the featureTypes list, or
* not, depending on what you want.
*
* <p>
*
* The contents of this staticArgs line should reflect the properties
* that can be set via the VPFLayer properties. See those javadocs for
* more details.
*/
public class VPFSpecialist extends Specialist {
private String dcwpath;
private Hashtable checkboxes = new Hashtable();
private LibrarySelectionTable lst;
private Hashtable comphash = new Hashtable();
public static final String prefix = "VPFSpec";
public static final float altCovScale = 30000000f;
/**
* default constructor is called when we're loading the class
* directly into OpenMap.
*/
public VPFSpecialist() {
super("VPFSpecialist", (short) 2, true);
//startHttpServer(0);
initDcwFiles();
}
public VPFSpecialist(LibrarySelectionTable libtab) {
super("VPFSpecialist", (short) 2, true);
//startHttpServer(0);
lst = libtab;
}
public VPFSpecialist(File dcwDataPath) {
super("VPFSpecialist", (short) 2, true);
//startHttpServer(0);
dcwpath = dcwDataPath.getAbsolutePath();
initDcwFiles();
}
public VPFSpecialist(String name) {
super(name, (short) 2, true);
}
public java.util.Properties processTokens(String args, StringBuffer cov,
StringBuffer altcov,
boolean usealt) {
java.util.StringTokenizer tok = new java.util.StringTokenizer(args, ")");
String token = null;
java.util.Properties props = new java.util.Properties();
// First see if there are parens, otherwise just straight
// coverage types
while (true) {
try {
token = tok.nextToken();
token = token.trim();
if (token.startsWith("(")) {
// Get the name
// Remove the open paren
token = token.substring(1, token.length());
int comma = token.indexOf(",");
String key = token.substring(0, comma);
String value = token.substring(comma + 1, token.length());
// Remove whitespace
key = key.trim();
value = value.trim();
// Need to set cov or altcov or else props later
if (key.equals("coverageType")) {
// Coverage types require special handling,
// since
// coverage types get passed back to drawTile
// routine
// Check to see if one coverage or two
int spacedelim = value.indexOf(" ");
if (spacedelim == -1) {
// One coverage
cov.delete(0, cov.length());
cov = cov.append(value);
altcov = altcov.delete(0, altcov.length());
altcov.append("");
} else {
// Two coverages
cov.delete(0, cov.length());
cov = cov.append(value.substring(0, spacedelim));
altcov = altcov.delete(0, altcov.length());
altcov = altcov.append(value.substring(spacedelim + 1,
value.length()));
}
// Set the property
props.setProperty(prefix + "." + key, value);
} else {
// Not a coverage type, so don't have to
// fool with cov and altcov
// Need to check for alt key prefix
// !! Assumes coverageType has been processed
// first
// If there is an alternate coverage and it's
// required, use it
if (usealt == true && !altcov.equals("")) {
if (key.startsWith("alt")) {
key = key.substring(3, key.length()); // 3 is
// "alt",
// remove
// it
props.setProperty(prefix + "." + key, value);
} else {
// Need alt, not an alt prefix
continue;
}
} else {
// Alt not necessary
if (key.startsWith("alt")) {
// alt key, don't want it
continue;
} else {
props.setProperty(prefix + "." + key, value);
}
}
}
} else {
// This case is for old style static args, when
// they
// were not (key,value) format
// Find out if there is an alternate coverage
int spacedelim = token.indexOf(" ");
if (spacedelim == -1) {
// No alternate coverage
cov.delete(0, cov.length());
cov = cov.append(token);
altcov = altcov.delete(0, altcov.length());
altcov.append("");
} else {
// Yes, alternate coverage
cov.delete(0, cov.length());
cov = cov.append(token.substring(0, spacedelim));
cov = altcov.delete(0, altcov.length());
altcov = altcov.append(token.substring(spacedelim + 1,
token.length()));
}
}
} catch (java.util.NoSuchElementException nee) {
break;
}
}
return props;
}
public UGraphic[] fillRectangle(
com.bbn.openmap.CSpecialist.CProjection p,
com.bbn.openmap.CSpecialist.LLPoint ll1,
com.bbn.openmap.CSpecialist.LLPoint ll2,
java.lang.String staticArgs,
org.omg.CORBA.StringHolder dynamicArgs,
com.bbn.openmap.CSpecialist.GraphicChange notifyOnChange,
String uniqueID) {
try {
LatLonPoint newll1 = new LatLonPoint(ll1.lat, ll1.lon);
LatLonPoint newll2 = new LatLonPoint(ll2.lat, ll2.lon);
/*
* If the we are zoomed out so that we _might_ have the
* entire world on the screen, then let's check to see if
* east and west are approximately equal. If they are,
* change them to -180 and +180 so that we get the entire
* world without having trouble with the floating point
* rounding errors that allow west to be slightly less
* than east (by .00001) and confuse our clipping routines
* into thinking that there should be nothing on the
* screen since nothing falls in that .00001 slice of the
* world.
*/
if ((p.scale > 100000000)
&& MoreMath.approximately_equal(ll1.lon, ll2.lon, .01)) {
newll1.setLongitude(-180.0f);
newll2.setLongitude(180.0f);
}
forgetComps(uniqueID);
Debug.message("vpfspecialist", "fillRectangle.. " + staticArgs);
Hashtable dynArgs = parseDynamicArgs(dynamicArgs.value);
VPFSpecialistGraphicWarehouse warehouse = new VPFSpecialistGraphicWarehouse();
CheckButton buttons[] = null;
SCheckBox s = (SCheckBox) checkboxes.get(uniqueID);
if (s == null) {
buttons = new CheckButton[3];
Debug.message("vpfspecialist", "default buttons");
buttons[0] = new CheckButton("Edges", false);
buttons[1] = new CheckButton("Text", false);
buttons[2] = new CheckButton("Area", false);
} else {
Debug.message("vpfspecialist", "palette buttons");
buttons = s.buttons();
}
boolean showEdges = (getHashedValueAsBoolean(dynArgs, DynArgEdges) || buttons[0].checked);
warehouse.setEdgeFeatures(showEdges);
boolean showText = (getHashedValueAsBoolean(dynArgs, DynArgText) || buttons[1].checked);
warehouse.setTextFeatures(showText);
boolean showAreas = (getHashedValueAsBoolean(dynArgs, DynArgArea) || buttons[2].checked);
warehouse.setAreaFeatures(showAreas);
// Changeable String objects for processTokens call
StringBuffer retcov = new StringBuffer();
StringBuffer retaltcov = new StringBuffer("");
// Now need to know in advance if we are using an
// alternate coverage
// for arguments
boolean usealt;
if (p.scale >= altCovScale)
usealt = true;
else
usealt = false;
java.util.Properties props = processTokens(staticArgs,
retcov,
retaltcov,
usealt);
if (Debug.debugging("vpfspecialist")) {
Debug.output("VPFSpecialist: with prefix " + prefix
+ ", properties: " + props);
}
// After processing properties, make sure they're
// available
warehouse.setProperties(prefix, props);
// Trim whitespace on coverages
String cov = retcov.toString().trim();
String altcov = retaltcov.toString().trim();
// If got nothing back, null this object (hack to convert
// StringBuffer to String
if (altcov.equals("")) {
Debug.message("vpfspecialist", "Altcov = null");
altcov = null;
}
//warehouse.showPointFeatures(false);
// tokenize the staticArgs. should be coverage type
// followed by alternate coverage type.
// Check both dynamic args and palette values when
// deciding what to draw.
lst.drawTile(p.scale,
p.width,
p.height,
((altcov != null) && (p.scale >= altCovScale)) ? altcov
: cov,
warehouse,
newll1,
newll2);
UGraphic[] retlist = warehouse.packGraphics();
if (Debug.debugging("vpfspecialist")) {
Debug.output("retlist.size(): " + retlist.length);
}
// DFD - I've commented the comphash method because I
// can't figure out where it is used. In the
// MATT Specialists, we processed mouse events on
// particular objects, and allowed things to happen on the
// server side. For this specialist, the receiveGesture
// method doesn't do anything, and we risk keeping all
// these objects around if the client dies before coming
// back to clean up.
// comphash.put(uniqueID, warehouse.getComps());
warehouse = null; // clean up.
Debug.message("vpfspecialist", "returning from fillRectangle");
return retlist;
} catch (Throwable t) {
t.printStackTrace();
return new UGraphic[0];
}
}
private synchronized void forgetComps(String uniqueID) {
Comp[] oldcomps = (Comp[]) comphash.remove(uniqueID);
if (oldcomps != null) {
Debug.message("vpfspecialist", "Releasing comps");
// Not sure how to do this in POA, but shouldn't be
// a problem because we aren't adding the comp objects to
// the graphics in the warehouse either.
// for (int i = 0; i < oldcomps.length; i++) {
// if (boa != null)
// boa.deactivate_obj(oldcomps[i]);
// }
}
}
public void receiveGesture(MouseEvent gesture, String uniqueID) {
// System.out.println("Gesture away...");
// addInfoText("Some text string");
}
public void makePalette(WidgetChange notifyOnChange, String staticArgs,
org.omg.CORBA.StringHolder dynamicArgs,
String uniqueID) {
clearPalette();
CheckButton buttons[] = new CheckButton[3];
buttons[0] = new CheckButton("Edges", false);
buttons[1] = new CheckButton("Text", false);
buttons[2] = new CheckButton("Area", false);
SCheckBox cb = new UsefulCheckbox("Feature Types:", buttons);
checkboxes.put(uniqueID, cb);
addPalette(cb.widget());
}
public void parseArgs(String[] args) {
for (int i = 0; i < args.length; i++) {
if (args[i].equalsIgnoreCase("-dcwpath")
|| args[i].equalsIgnoreCase("-datapath")) {
// dcwpath = new File(args[++i]);
dcwpath = args[++i];
}
if (args[i].equalsIgnoreCase("-verbose")) {
Debug.put("vpfspecialist");
}
}
super.parseArgs(args);
initDcwFiles();
}
public void initDcwFiles() {
if (dcwpath == null) {
dcwpath = "/usr/local/matt/data/dcw";
}
try {
// Parse the string to get all the possible paths...
String[] paths = parsePaths(dcwpath);
if (paths != null) {
lst = new LibrarySelectionTable();
for (int i = 0; i < paths.length; i++) {
System.out.println("VPFSpecialist: adding " + paths[i]
+ " to server");
lst.addDataPath(paths[i]);
}
}
} catch (com.bbn.openmap.io.FormatException f) {
throw new java.lang.IllegalArgumentException(f.getMessage());
}
}
/**
* Take a string that represents a bunch of path names separated
* by ";", and return an array of Strings.
*/
public String[] parsePaths(String path) {
String[] ret = null;
String tok = ";";
if (path != null) {
if (Debug.debugging("vpfspecialist")) {
System.out.println("VPFSpecialist: parsing path string: "
+ path);
}
try {
StringTokenizer token = new StringTokenizer(path, tok);
int numPaths = token.countTokens();
ret = new String[numPaths];
for (int i = 0; i < numPaths; i++) {
ret[i] = token.nextToken();
}
return ret;
} catch (java.util.NoSuchElementException e) {
e.printStackTrace();
}
}
return ret;
}
public void printHelp() {
System.err.println("usage: java <specialist> -ior <file> -datapath <path1;path2;path3>");
}
public void signOff(String uniqueID) {
forgetComps(uniqueID);
if (Debug.debugging("vpfspecialist")) {
Debug.output("Client |" + uniqueID + "| going away...");
}
}
/**
* Start an HttpServer on a port to listen for table requests
*
* @param port the port to bind. 0 picks an arbitrary port
* @return the HttpServer that got constructed, <code>null</code>
* if there was a problem.
*/
public static HttpServer startHttpServer(int port) {
try {
HttpServer server = new HttpServer(port, true);
server.addHttpRequestListener(new TableListener());
return server;
} catch (java.io.IOException e) {
Debug.output("Unable to start http server:");
return null;
}
}
public final static String DynArgEdges = "edges";
public final static String DynArgText = "text";
public final static String DynArgArea = "area";
/**
* Parses dynamic args passed by specialist client. A
* <code>Hashtable</code> is returned as a unified holder of all
* dynamic arguments.
*/
public static Hashtable parseDynamicArgs(String args) {
Hashtable dynArgs = new Hashtable();
if (args != null) {
String lowerArgs = args.toLowerCase();
dynArgs.put(DynArgEdges,
new Boolean(lowerArgs.indexOf(DynArgEdges) != -1));
dynArgs.put(DynArgText,
new Boolean(lowerArgs.indexOf(DynArgText) != -1));
dynArgs.put(DynArgArea,
new Boolean(lowerArgs.indexOf(DynArgArea) != -1));
}
return dynArgs;
}
/**
* If <code>arg</code> maps to a <code>Boolean</code> in the
* Hashtable, that value is returned, <code>false</code>
* otherwise.
*
* @param dynArgs the Hashtable to look in
* @param arg the argument to return
*/
public static boolean getHashedValueAsBoolean(Hashtable dynArgs, String arg) {
Object obj = dynArgs.get(arg);
if (obj == null) {
return false;
} else if (obj instanceof Boolean) {
return ((Boolean) obj).booleanValue();
} else {
return false;
}
}
public static void main(String[] args) {
Debug.init(System.getProperties());
Debug.message("vpfspecialist", "VPFSpecialist starting up");
int port = 0;
startHttpServer(port);
// Create the specialist server
VPFSpecialist srv = new VPFSpecialist("VPFSpecialist");
srv.start(args);
}
}