// ********************************************************************** // // <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/image/ImageMaster.java,v $ // $RCSfile: ImageMaster.java,v $ // $Revision: 1.6 $ // $Date: 2005/12/09 21:09:09 $ // $Author: dietrick $ // // ********************************************************************** package com.bbn.openmap.image; import java.awt.Color; import java.awt.geom.Point2D; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.Hashtable; import java.util.Properties; import java.util.StringTokenizer; import java.util.Vector; import com.bbn.openmap.MapBean; import com.bbn.openmap.proj.Mercator; import com.bbn.openmap.proj.Proj; import com.bbn.openmap.proj.Projection; import com.bbn.openmap.proj.ProjectionFactory; import com.bbn.openmap.util.Debug; import com.bbn.openmap.util.PropUtils; /** * The ImageMaster is an organizer for running the ImageServer to create one or * more images. It relies on a properties file, which sets up a series of * entries for an ImageServer. Each entry has parameters for setting up a * projection for an image, a parameters for a URL for the ImageServer to use to * set up the layers for an image, and a parameter to set the name and path of * the output image file. * <P> * Each map entry in the ImageServer has parameters for the projection and layer * properties to use for the map image, and the size, location and format of the * output image. */ public class ImageMaster { /** Property for space separated image servers to be created. */ public static final String ImageServersProperty = "servers"; /** * Property for the properties file holding property for a particular image. */ public static final String ServerPropertiesProperty = "properties"; /** Property for an image's projection type. */ public static final String ImageProjectionProperty = "imageProjection"; /** Property for an image's projection center latitude. */ public static final String ImageLatitudeProperty = "imageLatitude"; /** Property for an image's projection center longitude. */ public static final String ImageLongitudeProperty = "imageLongitude"; /** Property for an image's projection scale. */ public static final String ImageScaleProperty = "imageScale"; /** Property for an image's height. */ public static final String ImageHeightProperty = "imageHeight"; /** Property for an image's width. */ public static final String ImageWidthProperty = "imageWidth"; /** Property for the image's background color. */ public static final String ImageBackgroundColorProperty = "imageBackgroundColor"; /** Property for an image's output name. */ public static final String ImageNameProperty = "outputName"; /** Property for scaling the width of image after creation. */ public static final String ScaleToWidthProperty = "scaleToWidth"; /** Property for scaling the height of image after creation. */ public static final String ScaleToHeightProperty = "scaleToHeight"; /** Property for an output log file. */ public static final String OutputLogFileProperty = "outputLogFile"; /** Property for an error log file. */ public static final String ErrorLogFileProperty = "errorLogFile"; /** * Hashtable of instantiated layers across servers, to reduce duplication of * same layers. */ protected Hashtable instantiatedLayers = new Hashtable(); ImageMasterHelper[] helpers; /** Create with properties. */ public ImageMaster(Properties props) { setProperties(props); } /** Create with properties file. */ public ImageMaster(String propertiesFile) { Properties props = new Properties(); loadProperties(props, propertiesFile); setProperties(props); } /** Create with properties file URL. */ public ImageMaster(URL propertiesURL) { Properties props = new Properties(); loadProperties(props, propertiesURL); setProperties(props); } /** * Loads properties from a java resource. This will load the named resource * identifier into the given properties instance. * * @param props the Properties instance to receive the properties * @param resourceName the name of the resource to load * @return true if all's well. */ protected boolean loadPropertiesFromResource(Properties props, String resourceName) { InputStream propsIn = getClass().getResourceAsStream(resourceName); if (propsIn == null) { if (Debug.debugging("imagemaster")) { Debug.error("Unable to locate resources: " + resourceName); } return false; } else { try { props.load(propsIn); return true; } catch (java.io.IOException e) { Debug.error("ImageMaster: Caught IOException loading resources: " + resourceName); return false; } } } /** * Loads properties from a java resource. This will load the named resource * identifier into the given properties instance. * * @param props the Properties instance to receive the properties * @param url the url to load * @return true if all's well. */ public boolean loadProperties(Properties props, URL url) { try { InputStream propsIn = url.openStream(); props.load(propsIn); return true; } catch (java.io.IOException e) { Debug.error("ImageMaster: Caught IOException loading resources: " + url); return false; } } /** * Load the named file from the named directory into the given * <code>Properties</code> instance. If the file is not found a warning is * issued. If an IOException occurs, a fatal error is printed and the * application will exit. * * @param file the name of the file * @return true if all's well. */ public boolean loadProperties(Properties props, String file) { File propsFile = new File(file); try { InputStream propsStream = new FileInputStream(propsFile); props.load(propsStream); return true; } catch (java.io.FileNotFoundException e) { Debug.error("ImageMaster.loadProperties(): Unable to read configuration file \"" + propsFile + "\""); } catch (java.io.IOException e) { Debug.error("ImageMaster.loadProperties(): Caught IO Exception reading configuration file \"" + propsFile + "\" \n" + e); } return false; } /** * Set the properties for the ImageMaster, which also gets all the * ImageMasterHelpers created. */ public void setProperties(Properties properties) { helpers = setImageServers(properties); } /** Start the ImageMaster to go through the ImageMasterHelpers. */ public void run() { doNext(); } /** * This causes the ImageMaster to look through the list of * ImageMasterHelpers and launch the next one that hasn't been completed. It * will cause the program to exit if there is nothing more to do. */ protected void doNext() { for (int i = 0; i < helpers.length; i++) { if (!helpers[i].complete) { helpers[i].create(); return; } } System.exit(0); } /** * Creates the ImageMasterHelper array from an ImageMaster properties * object. After this method is called, call run() to start the servers on * their creative ways. * * @param properties the ImageMaster properties. * @return ImageMasterHelper array. */ public ImageMasterHelper[] setImageServers(Properties properties) { String serversValue = properties.getProperty(ImageServersProperty); if (Debug.debugging("imagemaster")) { Debug.output("ImageMaster.setImageServers(): servers = \"" + serversValue + "\""); } if (serversValue == null) { Debug.error("ImageMaster.setImageServers(): No property \"" + ImageServersProperty + "\" found in application properties."); return new ImageMasterHelper[0]; } // Divide up the names ... StringTokenizer tokens = new StringTokenizer(serversValue, " "); Vector serverNames = new Vector(); while (tokens.hasMoreTokens()) { serverNames.addElement(tokens.nextToken()); } if (Debug.debugging("imagemaster")) { Debug.output("ImageMaster.setImageServers(): " + serverNames); } int nServerNames = serverNames.size(); ImageMasterHelper[] masterHelpers = new ImageMasterHelper[nServerNames]; for (int i = 0; i < nServerNames; i++) { String serverName = (String) serverNames.elementAt(i); masterHelpers[i] = new ImageMasterHelper(serverName, properties, this); } return masterHelpers; } /** * Start up and go. */ public static void main(String[] args) { Debug.init(); ImageMaster master = null; for (int i = 0; i < args.length; i++) { if (args[i].equalsIgnoreCase("-file")) { master = new ImageMaster(args[++i]); } else if (args[i].equalsIgnoreCase("-url")) { String url = null; try { url = args[++i]; master = new ImageMaster(new URL(url)); } catch (MalformedURLException mue) { Debug.output("ImageMaster: Malformed URL: " + url); master = null; } } else if (args[i].equalsIgnoreCase("-masterprops")) { printMasterProps(); } else if (args[i].equalsIgnoreCase("-serverprops")) { printServerProps(); } else if (args[i].equalsIgnoreCase("-h")) { printHelp(); } } if (master != null) { master.run(); } else { printHelp(); } } /** * <b>printHelp </b> should print a usage statement which reflects the * command line needs of the ImageServer. */ public static void printHelp() { Debug.output(""); Debug.output("usage: java com.bbn.openmap.image.ImageMaster [-file <properties file> || -url <properties file>] [-masterprops || -serverprops"); Debug.output(" -file requires a complete path to a ImageMaster properties file."); Debug.output(" -url requires an URL to a ImageMaster properties file."); Debug.output(" -masterprops prints out an example of a ImageMaster properties file."); Debug.output(" -serverprops prints out an example of a ImageServer properties file."); Debug.output(""); System.exit(1); } /** * Prints an example of the ImageMaster properties file. */ public static void printMasterProps() { Debug.output(""); Debug.output("#################################################"); Debug.output("# Properties file for the ImageMaster"); Debug.output("# List of unique server nicknames (your choice)."); Debug.output("servers=<server1> <server2> <server3> <etc>"); Debug.output(""); Debug.output("# URL of server1 properties"); Debug.output("# If this is not included, it is assumed that "); Debug.output("# the ImageServer properties reside in the "); Debug.output("# ImageMaster properties file."); Debug.output("server1.properties=http://<url to server1 properties>"); Debug.output("# Projection type of server1 image."); Debug.output("server1.imageProjection=mercator"); Debug.output("# Center latitude of server1 image."); Debug.output("server1.imageLatitude=40f"); Debug.output("# Center longitude of server1 image."); Debug.output("server1.imageLongitude=-72f"); Debug.output("# Projection scale of server1 image."); Debug.output("server1.imageScale=20000000"); Debug.output("# Pixel height of server1 image."); Debug.output("server1.imageHeight=640"); Debug.output("# Pixel width of server1 image."); Debug.output("server1.imageWidth=480"); Debug.output("# ARGB representation of the map background color (default is a saucy blue)"); Debug.output("server1.imageBackgroundColor=ffffffff"); Debug.output("# Complete path to server1 image output."); Debug.output("server1.outputName=<path to output file>"); Debug.output(""); Debug.output("# Repeat for each server listed in the servers property"); Debug.output("#################################################"); Debug.output(""); } /** * Print the ImageServer properties file, referenced by the ImageMaster * properties file. */ public static void printServerProps() { Debug.output(""); Debug.output("#################################################"); Debug.output("# Properties for ImageServer"); Debug.output("# List of unique layer nicknames to use for the image (your choice)."); Debug.output("# server1 is the name specified in the ImageMaster properties."); Debug.output("server1.imageServer.layers=<layer1> <layer2> <etc>"); Debug.output("# Classname of object to determine image format."); Debug.output("server1.imageServer.formatter=<classname of ImageFormatter>"); Debug.output(""); Debug.output("layer1.class=<com.bbn.openmap.layer.ShapeLayer"); Debug.output("layer1.prettyName=ShapeLayer"); Debug.output("# Continue with layer specific properties. See each layer's documentation or source for more details."); Debug.output(""); Debug.output("# Continue for each layer listed in the imageServer.layers property."); Debug.output("#################################################"); Debug.output(""); } /** * The ImageMasterHelper contains an ImageServer, and acts like the * ImageReceiver to create the Image file when the bits are ready. */ public class ImageMasterHelper implements ImageReceiver { public ImageServer iServer; public String outputFileName; public boolean complete = false; public ImageMaster iMaster; public Proj proj; public String outputLogFileName; public String errorLogFileName; public int scaleToWidth = -1; public int scaleToHeight = -1; public ImageMasterHelper(String prefix, Properties props, ImageMaster master) { String propPrefix = prefix + "."; float scale = PropUtils.floatFromProperties(props, propPrefix + ImageScaleProperty, 20000000f); int height = PropUtils.intFromProperties(props, propPrefix + ImageHeightProperty, 480); int width = PropUtils.intFromProperties(props, propPrefix + ImageWidthProperty, 640); float longitude = PropUtils.floatFromProperties(props, propPrefix + ImageLongitudeProperty, -71f); float latitude = PropUtils.floatFromProperties(props, propPrefix + ImageLatitudeProperty, 42f); String projType = props.getProperty(propPrefix + ImageProjectionProperty); String uniquePropsURL = props.getProperty(propPrefix + ServerPropertiesProperty); scaleToWidth = PropUtils.intFromProperties(props, propPrefix + ScaleToWidthProperty, -1); scaleToHeight = PropUtils.intFromProperties(props, propPrefix + ScaleToHeightProperty, -1); outputFileName = props.getProperty(propPrefix + ImageNameProperty); outputLogFileName = props.getProperty(propPrefix + OutputLogFileProperty); errorLogFileName = props.getProperty(propPrefix + ErrorLogFileProperty); if (outputLogFileName != null && errorLogFileName != null) { if (outputLogFileName.equalsIgnoreCase(errorLogFileName)) { Debug.setLog(new File(outputFileName), true); } else { Debug.directErrors(errorLogFileName, true, true); Debug.directOutput(new File(outputLogFileName), true); } } else { if (errorLogFileName != null) { Debug.directErrors(errorLogFileName, true, true); } if (outputLogFileName != null) { Debug.directOutput(new File(outputLogFileName), true); } } ProjectionFactory projectionFactory = getProjectionFactory(); Class<? extends Projection> projClass = projectionFactory.getProjClassForName(projType); if (projClass == null) { projClass = Mercator.class; } proj = (Proj) projectionFactory.makeProjection(projClass, new Point2D.Float(latitude, longitude), scale, width, height); // Set the background color of the map Color background = (Color) PropUtils.parseColorFromProperties(props, propPrefix + ImageBackgroundColorProperty, MapBean.DEFAULT_BACKGROUND_COLOR); iMaster = master; Properties uniqueProps; // if there isn't a unique properties file designated for // the imageserver layers, assume that the layer properties // reside in the ImageMaster properties file. if (uniquePropsURL != null) { uniqueProps = new Properties(); try { loadProperties(uniqueProps, new URL(uniquePropsURL)); } catch (MalformedURLException mue) { Debug.error("ImageMaster: Malformed URL for server properties: " + uniquePropsURL); uniqueProps = null; } } else { uniqueProps = props; } if (uniqueProps != null && outputFileName != null) { iServer = new ImageServer(propPrefix, uniqueProps, instantiatedLayers); iServer.setBackground(background); } } /** * Override this method if you want to change the available projections * from the defaults. * * @return the ProjectionFactory with projections */ public ProjectionFactory getProjectionFactory() { return ProjectionFactory.loadDefaultProjections(); } /** * Start the ImageServer on it's creative journey. */ public void create() { receiveImageData(iServer.createImage(proj, scaleToWidth, scaleToHeight)); } /** * Receive the bytes from a image. ImageReceiver interface function. * * @param imageBytes the formatted image.. */ public void receiveImageData(byte[] imageBytes) { writeDataFile(outputFileName, imageBytes); complete = true; iMaster.doNext(); } /** * Write the image to a file. * * @param fileName the file name to write the image into. * @param imageData the image data to put in the file. */ public void writeDataFile(String fileName, byte[] imageData) { try { Debug.message("imagemaster", "ImageMasterHelper: Writing image file " + fileName); FileOutputStream binFile = new FileOutputStream(fileName); binFile.write(imageData); binFile.close(); } catch (IOException ioe) { Debug.error("ImageMasterHelper: Error writing image file " + fileName); } } } }