// ********************************************************************** // // <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/dted/DTEDCacheHandler.java,v $ // $RCSfile: DTEDCacheHandler.java,v $ // $Revision: 1.7 $ // $Date: 2009/02/25 22:34:04 $ // $Author: dietrick $ // // ********************************************************************** package com.bbn.openmap.layer.dted; import java.awt.geom.Point2D; import com.bbn.openmap.MoreMath; import com.bbn.openmap.omGraphics.OMGraphic; import com.bbn.openmap.omGraphics.OMRaster; import com.bbn.openmap.proj.EqualArc; import com.bbn.openmap.proj.LLXY; import com.bbn.openmap.proj.Projection; import com.bbn.openmap.proj.coords.LatLonPoint; import com.bbn.openmap.util.Debug; /** * The DTEDCacheHandler controls the real cache of DTED frames. It is managed by * the DTEDCacheManager, and the manager asks it for frames. The * DTEDCacheHandler goes to its cache for the images, but it also manages the * configuration of the frames, and figures out what frames are needed, given a * projection. */ public class DTEDCacheHandler { /** Default frame cache size. */ public final static int FRAME_CACHE_SIZE = 20; /** Subframe pixel height and width. */ public final static int SF_PIXEL_HW = 200; /** The DTED level 0, 1 directory paths. */ protected String[] paths; /** The real frame cache. */ protected DTEDFrameCache frameCache; protected int frameCacheSize = -1; // No limit. /** The colors used by the frames to create the images. */ protected DTEDFrameColorTable colortable; // numColors, reduced // colortable // Setting up the screen... LatLonPoint ulCoords, lrCoords; double frameUp, frameDown, frameLeft, frameRight; double xPixInterval, yPixInterval; // degrees/pixel int numXSubframes, numYSubframes; int lastSubframeWidth, lastSubframeHeight; int currentFrameCacheSize = -10; // guarantees that it // will changed first // time. // Returning the images... boolean firstImageReturned = true; double frameLon = 0.0; double frameLat = 0.0; int subx = 0; int suby = 0; boolean newframe = false; DTEDSubframedFrame frame = null; /** A description of the drawing attributes of the images. */ protected DTEDFrameSubframeInfo dfsi = new DTEDFrameSubframeInfo(DTEDFrameSubframe.NOSHADING, DTEDFrameSubframe.DEFAULT_BANDHEIGHT, DTEDFrameSubframe.LEVEL_0, DTEDFrameSubframe.DEFAULT_SLOPE_ADJUST); public DTEDCacheHandler() { this(null, DTEDFrameColorTable.DTED_COLORS, DTEDFrameColorTable.DEFAULT_OPAQUENESS, FRAME_CACHE_SIZE); } public DTEDCacheHandler(String[] dataPaths, int numColors, int opaque) { this(dataPaths, numColors, opaque, -1); } public DTEDCacheHandler(String[] dataPaths, int numColors, int opaqueness, int subframe_cache_size) { colortable = new DTEDFrameColorTable(numColors, opaqueness, true); setFrameCacheSize(subframe_cache_size); paths = dataPaths; frameCache = new DTEDFrameCache(dataPaths, frameCacheSize); if (Debug.debugging("dted")) { Debug.output("DTEDCacheHandler: Created with cache size of " + frameCacheSize); } } /** * Normally, the cache grows and shrinks as appropriate according to the * number of frames needed to cover the screen. If you want to limit the * size it can grow, set the size. If it's negative, then there will be no * limit. */ public void setFrameCacheSize(int size) { if (size <= 0) { frameCacheSize = FRAME_CACHE_SIZE; } else { frameCacheSize = size; } } /** * Get the limit imposed on the number of frames used in the cache. */ public int getFrameCacheSize() { return frameCacheSize; } /** * Get an elevation at a point. Always uses the cache to load the frame and * get the data. */ public int getElevation(float lat, float lon) { return frameCache.getElevation(lat, lon); } /** Setting the subframe attributes. */ public void setSubframeInfo(DTEDFrameSubframeInfo new_dfsi) { dfsi = new_dfsi; if (dfsi.viewType == DTEDFrameSubframe.COLOREDSHADING) colortable.setGreyScale(false); else colortable.setGreyScale(true); } /** * The method to call to let the cache handler know what the projection * looks like so it can figure out which frames (and subframes) will be * needed. * * @param proj the EqualArc projection of the screen. */ public void setProjection(EqualArc proj) { setProjection(proj, ((Point2D) proj.getUpperLeft()).getY(), ((Point2D) proj.getUpperLeft()).getX(), ((Point2D) proj.getLowerRight()).getY(), ((Point2D) proj.getLowerRight()).getX()); } /** * The method to call to let the cache handler know what the projection * looks like so it can figure out which frames (and subframes) will be * needed. Should be called when the CacheHandler is dealing with just a * part of the map, such as when the map covers the dateline or equator. * * @param proj the EqualArc projection of the screen. * @param lat1 latitude of the upper left corner of the window, in decimal * degrees. * @param lon1 longitude of the upper left corner of the window, in decimal * degrees. * @param lat2 latitude of the lower right corner of the window, in decimal * degrees. * @param lon2 longitude of the lower right corner of the window, in decimal * degrees. */ public void setProjection(Projection proj, double lat1, double lon1, double lat2, double lon2) { ulCoords = new LatLonPoint.Double(lat1, lon1); lrCoords = new LatLonPoint.Double(lat2, lon2); firstImageReturned = true; // upper lat of top frame of the screen // lower lat of bottom frame of the screen // left lon of left frame of the screen // upper lon of right frame of the screen frameUp = Math.floor((double) lat1); frameDown = Math.floor((double) lat2); frameLeft = Math.floor((double) lon1); frameRight = Math.ceil((double) lon2); if (Debug.debugging("dted")) Debug.output("frameUp = " + frameUp + ", frameDown = " + frameDown + ", frameLeft = " + frameLeft + ", frameRight = " + frameRight); int numFramesNeeded; // Limit the size of the cache, if desired. if (frameCacheSize > 0) { numFramesNeeded = frameCacheSize; if (Debug.debugging("dteddetail")) { Debug.output("DTEDCacheHandler: frameCacheSize remains at: " + numFramesNeeded); } } else { // calculate how many frames should be in the cache... numFramesNeeded = (int) (Math.abs(frameUp - frameDown) * Math.abs(frameRight - frameLeft) * 2); } EqualArc eaProj = null; boolean isEqualArcProj = proj instanceof EqualArc; if (isEqualArcProj) { eaProj = (EqualArc) proj; } else { eaProj = LLXY.convertProjection(proj); } double xpi = 360 / eaProj.getXPixConstant(); double ypi = 90 / eaProj.getYPixConstant(); if (!MoreMath.approximately_equal(xPixInterval, xpi) || !MoreMath.approximately_equal(yPixInterval, ypi)) { // Screen attributes changed!!!! xPixInterval = xpi; yPixInterval = ypi; // While it changed... dfsi.xPixInterval = xPixInterval; dfsi.yPixInterval = yPixInterval; // Trap for funky values... if (xpi == 0 || ypi == 0) { numXSubframes = 0; numYSubframes = 0; return; } int frame_width = (int) Math.ceil(1.0 / xpi); int frame_height = (int) Math.ceil(1.0 / ypi); /* * There is some weird projection parameter stuff going on when the * projection is not equal arc, the subframe placement isn't quite * being set up right. Some subframes are getting misplaced. To work * around this, since I don't have time/money to really look at it, * we're going to make one subframe for non-equal-arc projections * and let the OMWarpingImage handle it. The DTEDFrameSubframe has * also been modified to make the bounds of the subframe the entire * image for non-EA projections. */ if (!isEqualArcProj) { numXSubframes = 1; numYSubframes = 1; lastSubframeHeight = frame_height; lastSubframeWidth = frame_width; } else { // Even number of subframes in frame numXSubframes = frame_width / SF_PIXEL_HW; lastSubframeWidth = SF_PIXEL_HW; numYSubframes = frame_height / SF_PIXEL_HW; lastSubframeHeight = SF_PIXEL_HW; if (frame_width % SF_PIXEL_HW != 0) { lastSubframeWidth = frame_width - (numXSubframes * SF_PIXEL_HW); numXSubframes++; } if (frame_height % SF_PIXEL_HW != 0) { lastSubframeHeight = frame_height - (numYSubframes * SF_PIXEL_HW); numYSubframes++; } } currentFrameCacheSize = numFramesNeeded; frameCache.resizeCache(numFramesNeeded, numXSubframes, numYSubframes); if (Debug.debugging("dteddetail")) { Debug.output("DTEDCacheHandler: frameCacheSize set to: " + numFramesNeeded); } if (Debug.debugging("dted")) Debug.output("***** Screen Parameters Changed! \n" + " Frame width (pix) = " + frame_width + "\n" + " Frame height (pix) = " + frame_height + "\n" + " Num x subframes = " + numXSubframes + "\n" + " Num y subframes = " + numYSubframes + "\n" + " last sf width = " + lastSubframeWidth + "\n" + " last sf height = " + lastSubframeHeight + "\n" + " X pix interval = " + xpi + "\n" + " Y pix interval = " + ypi + "\n"); } else if (Math.abs(numFramesNeeded - currentFrameCacheSize) > numFramesNeeded / 2) { currentFrameCacheSize = numFramesNeeded; frameCache.resizeCache(numFramesNeeded); if (Debug.debugging("dteddetail")) { Debug.output("DTEDCacheHandler: frameCacheSize set to: " + numFramesNeeded); } } } /** * Returns the next OMRaster image. When setProjection() is called, the * cache sets the projection parameters it needs, and also resets this * popping mechanism. When this mechanism is reset, you can keep calling * this method to get another subframe image. When it returns a null value, * it is done. It will automatically skip over window frames it doesn't * have, and return the next one it does have. It traverses from the top * left to right frames, and top to bottom for each column of frames. It * handles all the subframes for a frame at one time. * * @param proj current projection. * @return OMGraphic image, projected if not null. */ public OMGraphic getNextImage(Projection proj) { OMGraphic subframe = null; // Subframe coordinates and height and width // upper left, lower right double sf_ullat, sf_ullon, sf_lrlat, sf_lrlon; long sf_width = SF_PIXEL_HW; long sf_height = SF_PIXEL_HW; if (Debug.debugging("dted")) Debug.output("--- DTEDCacheHandler: getNextImage:"); while (true) { if (firstImageReturned == true) { frameLon = frameLeft; frameLat = frameDown; subx = 0; suby = 0; newframe = true; firstImageReturned = false; } else { if (frame != null && subx < numXSubframes) { // update statics to look for next subframe if (suby < numYSubframes - 1) suby++; else { suby = 0; subx++; } } else if (frameLon < frameRight) { // update statics to look for next frame subx = 0; suby = 0; if (frameLat < frameUp) frameLat++; else { frameLat = frameDown; frameLon++; } newframe = true; } else { // bounds exceeded, all done return (OMRaster) null; } } if (newframe && frameLon < frameRight) { if (Debug.debugging("dted")) Debug.output(" gni: Getting new frame Lat = " + frameLat + " Lon = " + frameLon); frame = frameCache.get(frameLat, frameLon, dfsi.dtedLevel); } // Figure out subframe lat/lon and height/width if (frame != null) { newframe = false; if (subx == (numXSubframes - 1)) sf_width = lastSubframeWidth; if (suby == (numYSubframes - 1)) sf_height = lastSubframeHeight; // width/height degrees are spacers - degrees to // subframe within the frame. sf_height/width_degrees // are the lat/lon of the frame corner. double sf_width_degrees = (double) sf_width * xPixInterval; double sf_height_degrees = (double) sf_height * yPixInterval; double width_degrees = (double) SF_PIXEL_HW * xPixInterval; double height_degrees = (double) SF_PIXEL_HW * yPixInterval; sf_ullat = (double) (frameLat + 1.0) - ((double) suby * height_degrees); sf_ullon = (double) frameLon + ((double) subx * width_degrees); sf_lrlat = (double) (frameLat + 1.0) - ((double) suby * height_degrees) - sf_height_degrees; sf_lrlon = (double) frameLon + ((double) subx * width_degrees) + sf_width_degrees; if ((ulCoords.getY() > sf_lrlat && lrCoords.getY() < sf_ullat) && (ulCoords.getX() < sf_lrlon && lrCoords.getX() > sf_ullon) && subx < numXSubframes) { dfsi.height = (int) sf_height; dfsi.width = (int) sf_width; dfsi.lon = (float) sf_ullon; dfsi.lat = (float) sf_ullat; dfsi.subx = subx; dfsi.suby = suby; if (Debug.debugging("dteddetail")) { Debug.output(" gni: Looking for Subframe " + subx + ", " + suby); } subframe = frame.getSubframeImage(dfsi, colortable, proj); if (subframe != null) { if (Debug.debugging("dted")) { Debug.output(" gni: Subframe " + subx + ", " + suby + " found :)"); } return subframe; } } else if (Debug.debugging("dteddetail")) { Debug.output(" gni: Subframe " + subx + ", " + suby + " didn't meet screen criteria"); } } sf_width = SF_PIXEL_HW; sf_height = SF_PIXEL_HW; } } /** * Get the colortable being used to color the frames. * * @return DTEDFrameColorTable */ public DTEDFrameColorTable getColortable() { return colortable; } /** * Set the DTEDFrameColorTable used by the handler. If you pass in a null * value, a default colortable will be inserted. * * @param colorT */ public void setColortable(DTEDFrameColorTable colorT) { if (colorT == null) { colortable = new DTEDFrameColorTable(DTEDFrameColorTable.DTED_COLORS, DTEDFrameColorTable.DEFAULT_OPAQUENESS, true); } else { colortable = colorT; } } }