// ********************************************************************** // // <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/rpf/corba/CRFPClient.java,v $ // $RCSfile: CRFPClient.java,v $ // $Revision: 1.6 $ // $Date: 2005/12/09 21:09:15 $ // $Author: dietrick $ // // ********************************************************************** package com.bbn.openmap.layer.rpf.corba; import java.awt.Point; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.Vector; import javax.imageio.ImageIO; import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.MemoryCacheImageInputStream; import com.bbn.openmap.Environment; import com.bbn.openmap.layer.rpf.RpfCoverageBox; import com.bbn.openmap.layer.rpf.RpfFrameProvider; import com.bbn.openmap.layer.rpf.RpfIndexedImageData; import com.bbn.openmap.layer.rpf.RpfViewAttributes; import com.bbn.openmap.layer.rpf.corba.CRpfFrameProvider.CRFPCADRGProjection; import com.bbn.openmap.layer.rpf.corba.CRpfFrameProvider.CRFPCoverageBox; import com.bbn.openmap.layer.rpf.corba.CRpfFrameProvider.CRFPViewAttributes; import com.bbn.openmap.layer.rpf.corba.CRpfFrameProvider.LLPoint; import com.bbn.openmap.layer.rpf.corba.CRpfFrameProvider.RawImage; import com.bbn.openmap.layer.rpf.corba.CRpfFrameProvider.Server; import com.bbn.openmap.layer.rpf.corba.CRpfFrameProvider.ServerHelper; import com.bbn.openmap.omGraphics.OMColor; import com.bbn.openmap.proj.CADRG; import com.bbn.openmap.proj.Projection; import com.bbn.openmap.util.Debug; import com.bbn.openmap.util.PropUtils; /** * An implementation of the RpfFrameProvider interface that uses CORBA to get * the subframe data via a server. The image data is transmitted in jpeg format. * This class requires the sunw package that handles jpeg encoding/decoding. * <P> * * The client can connect to the server in two different ways. The client can * locate the server using an IOR file that the server has written. This IOR * file is read using an URL. The server can also be located using the CORBA * naming service. The name should be in a three part fomat <ROOT name>/ * <PART2>/ <PART3>. The root name has to be known by the nameserver and the * entire string has to be used by the server on startup. If both the IOR and * name string are set, the IOR is the thing that gets used. */ public class CRFPClient implements RpfFrameProvider { /** The property specifying the IOR URL. */ public static final String iorUrlProperty = "ior"; /** The name of the server, using the name service. */ public static final String nameProperty = "name"; /** The property specifying the initial JPEG quality. */ public static final String JPEGQualityProperty = "jpegQuality"; /** The CRFPServer. */ protected transient Server server = null; /** The string used for the CORBA naming service. */ protected String naming = null; /** The URL used for the IOR, to connect to the server that way. */ protected URL iorURL = null; private String clientID = Environment.generateUniqueString(); /** * The compression quality of the images. Lower quality images are smaller. */ public float jpegQuality = .8f; /** * We'll set up the connection to the server when it's needed, but not here. */ public CRFPClient() { } /** * Set the JPEG quality parameter for subframe transfer. * * @param jq number between 0 and 1, should be between .4 and .8. Anything * else is a waste. */ public void setJpegQuality(float jq) { jpegQuality = jq; } /** * Get the quality setting for JPEG subframe retrieval. * * @return float reflecting JPEG quality. */ public float getJpegQuality() { return jpegQuality; } /** * Set the name used for the CORBA naming service. */ public void setNaming(String CORBAName) { naming = CORBAName; } /** * Get the name used for the CORBA naming service. */ public String getNaming() { return naming; } /** * If you want to connect to the server using an ior, set the URL where it * is located. */ public void setIorURL(URL iorurl) { iorURL = iorurl; } /** * Get the URL for the ior. */ public URL getIorURL() { return iorURL; } /** * Get the clientID string that is used by the server to keep track of * clients. This string in internally generated. */ public String getClientID() { return clientID; } /** * Set all the RPF properties from a properties object. */ public void setProperties(String prefix, java.util.Properties properties) { prefix = PropUtils.getScopedPropertyPrefix(prefix); jpegQuality = PropUtils.floatFromProperties(properties, prefix + JPEGQualityProperty, .8f); String url = properties.getProperty(prefix + iorUrlProperty); if (url != null) { try { iorURL = PropUtils.getResourceOrFileOrURL(url); } catch (MalformedURLException e) { throw new IllegalArgumentException("\"" + url + "\"" + " is malformed."); } } naming = properties.getProperty(prefix + nameProperty); } /** * When the client is deleted, it should sign off from the server, so that * it can free up it's cache for it. */ protected void dispose() { if (Debug.debugging("crfp")) { Debug.output("CRFPClient.finalize(): calling shutdown"); } try { if (server != null) { server.signoff(clientID); } server = null; } catch (org.omg.CORBA.SystemException e) { Debug.error("CRFPClient.finalize(): " + e); } catch (Throwable t) { Debug.error("CRFPClient.finalize(): " + t); } } /** * Returns true because the view attributes should be set if they change at * the RpfCacheHandler/RpfCacheManager. */ public boolean needViewAttributeUpdates() { return true; } /** * Set the RpfViewAttribute object parameters, which describes alot about * what you'll be asking for later. * * @param rva the view attributes. */ public void setViewAttributes(RpfViewAttributes rva) { Server serv = getServer(); if (serv == null || rva == null) { return; } try { serv.setViewAttributes(new CRFPViewAttributes((short) rva.numberOfColors, (short) rva.opaqueness, rva.scaleImages, rva.imageScaleFactor, rva.chartSeries), clientID); Debug.message("crfp", "CRFPClient: setting attributes."); } catch (org.omg.CORBA.SystemException e) { handleCORBAError(e); } } /** * Given a projection that describes a map or geographical area, return * RpfCoverageBoxes that let you know how to locate and ask for * RpfSubframes. * * @param ullat NW latitude. * @param ullon NW longitude * @param lrlat SE latitude * @param lrlon SE longitude * @param p a CADRG projection */ public Vector getCoverage(double ullat, double ullon, double lrlat, double lrlon, Projection p) { CRFPCoverageBox[] boxes; Server serv = getServer(); if (serv == null) return new Vector(); Point2D center = p.getCenter(); LLPoint llpoint = new LLPoint((float) center.getY(), (float) center.getX()); CADRG cadrg = CADRG.convertProjection(p); CRFPCADRGProjection proj = new CRFPCADRGProjection(llpoint, (short) cadrg.getHeight(), (short) cadrg.getWidth(), cadrg.getScale(), (short) cadrg.getZone()); Debug.message("crfp", "CRFPClient: getting coverage from server."); try { boxes = serv.getCoverage(ullat, ullon, lrlat, lrlon, proj, clientID); return translateCRFPCoverageBoxes(boxes); } catch (org.omg.CORBA.SystemException e) { handleCORBAError(e); } return new Vector(); } /** * Given a projection that describes a map or geographical area, return * RpfCoverageBoxes that let you know what bounding boxes of data are * available. * * @param ullat NW latitude. * @param ullon NW longitude * @param lrlat SE latitude * @param lrlon SE longitude * @param p a CADRG projection */ public Vector getCatalogCoverage(double ullat, double ullon, double lrlat, double lrlon, Projection p, String chartSeriesCode) { CRFPCoverageBox[] boxes; Server serv = getServer(); if (serv == null) return new Vector(); Point2D center = p.getCenter(); LLPoint llpoint = new LLPoint((float) center.getY(), (float) center.getX()); CADRG cadrg = CADRG.convertProjection(p); CRFPCADRGProjection proj = new CRFPCADRGProjection(llpoint, (short) cadrg.getHeight(), (short) cadrg.getWidth(), cadrg.getScale(), (short) cadrg.getZone()); Debug.message("crfp", "CRFPClient: getting catalog coverage from server."); try { boxes = serv.getCatalogCoverage(ullat, ullon, lrlat, lrlon, proj, chartSeriesCode, clientID); return translateCRFPCoverageBoxes(boxes); } catch (org.omg.CORBA.SystemException e) { handleCORBAError(e); } return new Vector(); } /** * Given an area and a two-letter chart series code, find the percentage of * coverage on the map that that chart series can offer. If you want * specific coverage information, use the getCatalogCoverage call. * * @see #getCatalogCoverage(float ullat, float ullon, float lrlat, float * lrlon, Projection p, String chartSeriesCode) */ public float getCalculatedCoverage(double ullat, double ullon, double lrlat, double lrlon, Projection p, String chartSeries) { if (chartSeries.equalsIgnoreCase(RpfViewAttributes.ANY)) { return 0f; } Vector results = getCatalogCoverage(ullat, ullon, lrlat, lrlon, p, chartSeries); int size = results.size(); if (size == 0) { return 0f; } // Now interpret the results and figure out the real total // percentage coverage for the chartSeries. First need to // figure out the current size of the subframes. Then create // a boolean matrix of those subframes that let you figure out // how many of them are available. Calculate the percentage // off that. // int pZone = p.getZone(); int i, x, y; double frameLatInterval = Double.MAX_VALUE; double frameLonInterval = Double.MAX_VALUE; RpfCoverageBox rcb; for (i = 0; i < size; i++) { rcb = (RpfCoverageBox) results.elementAt(i); if (rcb.subframeLatInterval < frameLatInterval) { frameLatInterval = rcb.subframeLatInterval; } if (rcb.subframeLonInterval < frameLonInterval) { frameLonInterval = rcb.subframeLonInterval; } } if (frameLatInterval == Double.MAX_VALUE || frameLonInterval == Double.MAX_VALUE) { return 0.0f; } int numHFrames = (int) Math.ceil((lrlon - ullon) / frameLonInterval); int numVFrames = (int) Math.ceil((ullat - lrlat) / frameLatInterval); boolean[][] coverage = new boolean[numHFrames][numVFrames]; for (i = 0; i < size; i++) { rcb = (RpfCoverageBox) results.elementAt(i); if (rcb.percentCoverage == 100) { return 1.0f; } for (y = 0; y < numVFrames; y++) { for (x = 0; x < numHFrames; x++) { // degree location of indexs float yFrameLoc = (float) (lrlat + (y * frameLatInterval)); float xFrameLoc = (float) (ullon + (x * frameLonInterval)); if (coverage[x][y] == false) { if (rcb.within(yFrameLoc, xFrameLoc)) { coverage[x][y] = true; } } } } } float count = 0; for (y = 0; y < numVFrames; y++) { for (x = 0; x < numHFrames; x++) { if (coverage[x][y] == true) { // System.out.print("X"); count++; } else { // System.out.print("."); } } // Debug.output(""); } return count / (float) (numHFrames * numVFrames); } /** * Convert CRFPCoverageBox[] to vector of RpfCoverageBox. * * @param boxes CRFPCoverageBox[]. * @return java.util.Vector */ protected Vector translateCRFPCoverageBoxes(CRFPCoverageBox[] boxes) { Vector vector = new Vector(); for (int i = 0; i < boxes.length; i++) { CRFPCoverageBox box = boxes[i]; RpfCoverageBox rcb = new RpfCoverageBox(); rcb.nw_lat = box.nw_lat; rcb.nw_lon = box.nw_lon; rcb.se_lat = box.se_lat; rcb.se_lon = box.se_lon; rcb.subframeLatInterval = box.subframeLatInterval; rcb.subframeLonInterval = box.subframeLonInterval; rcb.chartCode = box.chartCode; rcb.startIndexes = new Point(box.startIndexes.x, box.startIndexes.y); rcb.endIndexes = new Point(box.endIndexes.x, box.endIndexes.y); rcb.tocNumber = (int) box.tocNumber; rcb.entryNumber = (int) box.entryNumber; rcb.scale = box.scale; rcb.percentCoverage = box.percentCoverage; rcb.zone = box.zone; vector.addElement(rcb); } return vector; } /** * Given the indexes to a certain RpfTocEntry within a certain A.TOC, find * the frame/subframe data, decompress it, and return image pixels. The * tocNumber and entryNumber are given within the RpfCoverageBox received * from a getCoverage call. With the CORBA implementation, we are assuming * that the byte array is an encoded jpeg image. * * @param tocNumber the toc id for a RpfTocHandler for a particular frame * provider. * @param entryNumber the RpfTocEntry id for a RpfTocHandler for a * particular frame provider. * @param x the horizontal subframe index, from the left side of a boundary * rectangle of the entry. * @param y the vertical subframe index, from the top side of a boundary * rectangle of the entry. * @see #getCoverage(float ullat, float ullon, float lrlat, float lrlon, * CADRG p) * @return integer pixel data. */ public int[] getSubframeData(int tocNumber, int entryNumber, int x, int y) { Server serv = getServer(); if (serv == null) return null; byte[] jpegData; Debug.message("crfp", "CRFPClient: getting subframe data from server."); try { jpegData = serv.getSubframeData((short) tocNumber, (short) entryNumber, (short) x, (short) y, jpegQuality, clientID); if (Debug.debugging("crfpdetail")) { Debug.output("CRFPClient: got subframe data length " + jpegData.length); } // Need to check for the corba rendition of an allowable // null image (length 0) if (jpegData.length == 0) return null; ImageInputStream iis = new MemoryCacheImageInputStream(new ByteArrayInputStream(jpegData)); BufferedImage bi = ImageIO.read(iis); int height = bi.getHeight(); int width = bi.getWidth(); int[] pixels = bi.getRGB(0, 0, width, height, null, 0, width); return pixels; } catch (IOException ioe) { Debug.error("CRFPClient: IOException decoding jpeg bytes"); } catch (org.omg.CORBA.SystemException e) { handleCORBAError(e); } return null; } public RpfIndexedImageData getRawSubframeData(int tocNumber, int entryNumber, int x, int y) { Server serv = getServer(); if (serv == null) return null; Debug.message("crfp", "CRFPClient: getting raw subframe data from server."); try { RawImage ri = serv.getRawSubframeData((short) tocNumber, (short) entryNumber, (short) x, (short) y, clientID); // Need to check for the corba rendition of an allowable // null image (length 0) if (ri.imagedata.length == 0 || ri.colortable.length == 0) { return null; } RpfIndexedImageData riid = new RpfIndexedImageData(); riid.imageData = ri.imagedata; riid.colortable = new OMColor[ri.colortable.length]; for (int i = 0; i < riid.colortable.length; i++) { riid.colortable[i] = new OMColor(ri.colortable[i]); } return riid; } catch (org.omg.CORBA.SystemException e) { handleCORBAError(e); return null; } } /** * Given the indexes to a certain RpfTocEntry within a certain A.TOC, find * the frame and return the attribute information. The tocNumber and * entryNumber are given within the RpfCoverageBox received from a * getCoverage call. * * @param tocNumber the toc id for a RpfTocHandler for a particular frame * provider. * @param entryNumber the RpfTocEntry id for a RpfTocHandler for a * particular frame provider. * @param x the horizontal subframe index, from the left side of a boundary * rectangle of the entry. * @param y the vertical subframe index, from the top side of a boundary * rectangle of the entry. * @see #getCoverage(float ullat, float ullon, float lrlat, float lrlon, * Projection p) * @return string. */ public String getSubframeAttributes(int tocNumber, int entryNumber, int x, int y) { Server serv = getServer(); if (serv == null) return ""; Debug.message("crfp", "CRFPClient: getting subframe attributes from server."); try { return serv.getSubframeAttributes((short) tocNumber, (short) entryNumber, (short) x, (short) y, clientID); } catch (org.omg.CORBA.SystemException e) { handleCORBAError(e); } return ""; } // ////////////// Corba management /** * get the server proxy. * * @return Server server or null if error. * */ public Server getServer() { if (server == null) initServer(); return server; } /** * bind to the server. * */ private void initServer() { String ior = null; org.omg.CORBA.Object object = null; com.bbn.openmap.util.corba.CORBASupport cs = new com.bbn.openmap.util.corba.CORBASupport(); try { object = cs.readIOR(iorURL); server = ServerHelper.narrow(object); } catch (IOException ioe) { if (Debug.debugging("crfp")) { Debug.output("CRFPClient.initServer() IO Exception with ior: " + iorURL); } server = null; return; } if (server == null) { object = cs.resolveName(naming); if (object != null) { server = ServerHelper.narrow(object); if (Debug.debugging("crfp")) { Debug.output("Have a RPF server:"); Debug.output("*** Server: is a " + server.getClass().getName() + "\n" + server); } } } if (Debug.debugging("crfp")) { if (server == null) { Debug.error("CRFPClient.initServer: null server!\n IOR=" + ior + "\n Name = " + naming); } else { Debug.output("CRFPClient: server is golden."); } } } protected void handleCORBAError(org.omg.CORBA.SystemException e) { // don't freak out if we were only interrupted... if (e.toString().indexOf("InterruptedIOException") != -1) { Debug.error("CRFPClient server communication interrupted!"); } else { Debug.error("CRFPClient caught CORBA exception: " + e + "\n" + "CRFPClient Exception class: " + e.getClass().getName() + "\n" + e.getMessage()); e.printStackTrace(); } server = null;// dontcha just love CORBA? reinit later } }