// ********************************************************************** // // <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/rpf/RpfCacheManager.java,v $ // $RCSfile: RpfCacheManager.java,v $ // $Revision: 1.5 $ // $Date: 2005/12/09 21:09:05 $ // $Author: dietrick $ // // ********************************************************************** package com.bbn.openmap.layer.rpf; import java.io.Serializable; import java.util.List; import com.bbn.openmap.omGraphics.OMGraphicList; import com.bbn.openmap.proj.Projection; import com.bbn.openmap.proj.coords.LatLonPoint; import com.bbn.openmap.util.Debug; /** * The RpfCacheManager is the object you need if you want to retrieve RPF files. * You set it up with a RpfFrameProvider, and it gives that provider to the * RpfCacheHandler it creates. The RpfCacheManager then handles working with the * RpfCacheHandlers to get subframes to display. * <P> * * RPF data comes with a Table of Contents, which sits at the root of the RPF * file system structure and contains information about the frame files. When * the RpfFrameProvider gets created, it creates an array of RpfTocHandlers. * These Table of Contents readers know how to take a geographic area and figure * out which frames and subframes are needed to put on the screen. An array of * RpfTocHandlers are needed in case there are many places where there is RPF * data. * <P> * * The RpfCacheManager also manages objects called RpfCacheHandlers. Cache * handlers take the information from a frame provider, and create a subframe * cache for that zone and map type. The situation gets pretty tricky when the * screen has the equator and/or the dateline on it, and a different cache * handler is needed for each quadrant of the earth. This situation is * relatively rare, though, and the RpfCacheManager automatically checks for * these situations and creates the cache handlers needed. * <P> * There are two calls to the Cache that you need to use. The constructor sets * up the cache with the location of the data. The getRectangle() call returns * an OMGraphicList of objects to draw, that cover the area asked for. */ public class RpfCacheManager implements Serializable { private static final long serialVersionUID = 1L; /** * The size of the smaller caches, when more cachehandlers are needed to * cover the equator and the dateline. Lowered from 20 to try to conserve * memory. */ public final static int SMALL_CACHE_SIZE = 10; /** A box is a earth quadrant. */ public final static int MAX_NUM_BOXES = 4; /** * The cache handlers needed to cover the screen. Need one for each earth * quadrant, and for each RPF directory, in case coverage is spread out over * different sources. */ protected transient RpfCacheHandler[] caches = new RpfCacheHandler[MAX_NUM_BOXES]; /** The place to look for for image data. */ protected RpfFrameProvider frameProvider; /** * Contains information about displaying the RPF data. Also passed to the * RpfTocHandlers to determine chart selection. * * @see RpfViewAttributes */ protected RpfViewAttributes viewAttributes; /** * The size of the aux caches, which are used when the map crosses the * equator or dateline. */ protected int auxCacheSize; public RpfCacheManager() { } /** * Constructor that lets you set the RPF frame provider * * @param fp the object supplying the data. */ public RpfCacheManager(RpfFrameProvider fp) { this(fp, new RpfViewAttributes()); } /** * Constructor that lets you set the RPF frame provider, the view attributes * and the subframe cache size. * * @param rfp the object supplying the data. * @param rva the view attributes for the images. */ public RpfCacheManager(RpfFrameProvider rfp, RpfViewAttributes rva) { this(rfp, rva, RpfCacheHandler.SUBFRAME_CACHE_SIZE, SMALL_CACHE_SIZE); } /** * Constructor that lets you set the RPF frame provider, the view attributes * and the subframe cache sizes. * * @param rfp the object supplying the data. * @param rva the view attributes for the images. * @param mainCacheSize the number of subframes held in the large main * cache. * @param auxSubframeCacheSize the number of subframes held in the aux * caches. */ public RpfCacheManager(RpfFrameProvider rfp, RpfViewAttributes rva, int mainCacheSize, int auxSubframeCacheSize) { frameProvider = rfp; viewAttributes = rva; caches[0] = new RpfCacheHandler(rfp, rva, mainCacheSize); auxCacheSize = auxSubframeCacheSize; } // public void finalize() { // Debug.message("gc", "RpfCacheManager: getting GC'd"); // } /** * Reset the caches in the RpfCacheHandlers. */ public void clearCaches() { for (int i = 0; i < caches.length; i++) { if (caches[i] != null) { caches[i].clearCache(); } } } /** * Set the view attributes for the layer. The frame provider view attributes * are updated, and the cache is cleared. * * @param rva the RpfViewAttributes used for the layer. */ public void setViewAttributes(RpfViewAttributes rva) { viewAttributes = rva; for (int i = 0; i < caches.length; i++) { if (caches[i] != null) { caches[i].setViewAttributes(viewAttributes); } } } /** * Get the view attributes or the layer. * * @return RpfViewAttributes. */ public RpfViewAttributes getViewAttributes() { return viewAttributes; } /** * Set the RpfFrameProvider for the layer. Clears out the cache, and the * frame provider gets the RpfViewAttributes held by the layer. * * @param fp the frame provider. */ public void setFrameProvider(RpfFrameProvider fp) { frameProvider = fp; for (int i = 0; i < caches.length; i++) { if (caches[i] != null) { caches[i].setFrameProvider(fp); } } } /** * Return RpfFrameProvider used by the layer. */ public RpfFrameProvider getFrameProvider() { return frameProvider; } /** * Returns the Vector containing RpfCoverageBoxes from the primary * RpfCacheHandler. The Vector is the same that was returned to the cache * handler from the RpfFrameProvider as a result of the last setCache call. * These provide rudimentary knowledge about what is being displayed. * * @return Vector of RpfCoverageBoxes. */ public List<RpfCoverageBox> getCoverageBoxes() { return caches[0].getCoverageBoxes(); } /** * The call to the cache that lets you choose what kind of information is * returned. This function also figures out what part of the earth is * covered on the screen, and creates auxiliary cache handlers as needed. * The CADRG projection held inside the view attributes, used by the * RpfTocHandlers, is set here. If the projection passed in is not CADRG, * and the caller doesn't care, then a new CADRG projection is created. * * @param proj the projection of the screen. */ public synchronized OMGraphicList getRectangle(Projection proj) { float[] lat = new float[3]; float[] lon = new float[3]; // This should be checked by the caller. // if (!(proj instanceof CADRG)) { // if (viewAttributes.requireProjection) { // return new OMGraphicList(); // } else { // viewAttributes.proj = // new CADRG((LatLonPoint) proj.getCenter(new LatLonPoint.Float()), // proj.getScale(), proj.getWidth(), // proj.getHeight()); // } // } else { // viewAttributes.proj = (CADRG) proj; // } // Need to update the view attributes of the frame provider if // it is remote. if (frameProvider != null && frameProvider.needViewAttributeUpdates()) { frameProvider.setViewAttributes(viewAttributes); } // Hand off coordinates and scale to RpfCacheHandler. // Then the RpfCacheHandler will figure out the frames to load // and add to the display list. // This next bit of mumbo jumbo is to handle the equator and // dateline: Worst case, crossing both, treat each area // separately, so it is the same as handling four requests for // data - above and below the equator, and left and right of // the dateline. Normal case, there is only one box. Two // boxes if crossing only one of the boundaries. int xa = 2; int ya = 2; int lat_minus = 2; int lon_minus = 2; // Set up checks for equator and dateline LatLonPoint ll1 = (LatLonPoint) proj.getUpperLeft(); LatLonPoint ll2 = (LatLonPoint) proj.getLowerRight(); lat[0] = ll1.getLatitude(); lon[0] = ll1.getLongitude(); lat[1] = ll2.getLatitude(); lon[1] = ll2.getLongitude(); lat[2] = ll2.getLatitude(); lon[2] = ll2.getLongitude(); if (lon[0] > 0 && lon[2] < 0) { lon[1] = -179.999f; // put a little breather on the // dateline lon_minus = 1; } if (lat[0] > 0 && lat[2] < 0) { lat[1] = -0.0001f; // put a little breather on the // equator lat_minus = 1; } if (Debug.debugging("rpf")) { Debug.output("RpfCacheManager - for:"); Debug.output("\tlat[0] " + lat[0]); Debug.output("\tlon[0] " + lon[0]); Debug.output("\tlat[1] " + lat[1]); Debug.output("\tlon[1] " + lon[1]); Debug.output("\tlat[2] " + lat[2]); Debug.output("\tlon[2] " + lon[2]); Debug.output("\tlat_minus = " + lat_minus); Debug.output("\tlon_minus = " + lon_minus); } /* * Worst case, there are four boxes on the screen. Best case, there is * one. The things that create boxes and dictates how large they are are * the equator and the dateline. When the screen straddles one or both * of these lat/lon lines, lon_minus and lat_minus get adjusted, causing * two or four different calls to the tochandler to get the data * above/below the equator, and left/right of the dateline. Plus, each * path gets checked until the required boxes are filled. */ OMGraphicList list = new OMGraphicList(); // Normal (maybe) box[0] gets filled every time - bottom right // box. caches[0].getSubframes(lat[ya - lat_minus], lon[xa - lon_minus], lat[ya], lon[xa], proj, list); if (Debug.debugging("rpf")) Debug.output("RpfCacheManager: main (1) cache used."); // Dateline split if (lon_minus == 1) { if (caches[1] == null) { caches[1] = new RpfCacheHandler(frameProvider, viewAttributes, auxCacheSize); } caches[1].getSubframes(lat[ya - lat_minus], lon[0], lat[ya], -1f * lon[1], proj, list); // -1 // to // make // it // 180 if (Debug.debugging("rpf")) Debug.output("-- second cache used"); } else { caches[1] = null; } // Equator Split if (lat_minus == 1) { if (caches[2] == null) { caches[2] = new RpfCacheHandler(frameProvider, viewAttributes, auxCacheSize); } caches[2].getSubframes(lat[0], lon[xa - lon_minus], -1f * lat[1], // flip // breather lon[xa], proj, list); if (Debug.debugging("rpf")) Debug.output("-- third cache used"); } else { caches[2] = null; } // Both!! if (lon_minus == 1 && lat_minus == 1) { if (caches[3] == null) { caches[3] = new RpfCacheHandler(frameProvider, viewAttributes, auxCacheSize); } // Flip breather to make it 180, not -180. caches[3].getSubframes(lat[0], lon[0], -1f * lat[1], -1f * lon[1], proj, list); if (Debug.debugging("rpf")) Debug.output("-- fourth cache used"); } else { caches[3] = null; } return list; } }