// **********************************************************************
//
// <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/RpfCacheHandler.java,v $
// $RCSfile: RpfCacheHandler.java,v $
// $Revision: 1.12 $
// $Date: 2006/10/04 14:46:13 $
// $Author: dietrick $
//
// **********************************************************************
/**
* Modifications :
*
* 1. Changed getSubframeFromOtherTOC(): changed offsets and prevent caching
* from other TOCs
*/
/*
* Some of the ideas for this code is based on source code provided by
* The MITRE Corporation, through the browse application source code.
* Many thanks to Nancy Markuson who provided BBN with the software,
* and Theron Tock, who wrote the software, and to Daniel Scholten,
* who revised it - (c) 1994 The MITRE Corporation for those parts,
* and used/distributed with permission. Namely, the subframe caching
* mechanism is the part that has been modified.
*/
package com.bbn.openmap.layer.rpf;
import java.awt.Point;
import java.awt.geom.Point2D;
import java.util.List;
import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.omGraphics.OMGraphicList;
import com.bbn.openmap.omGraphics.OMRasterObject;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.util.Debug;
/**
* The RpfCacheHandler handles everything to do with the decompressed subframes,
* which is what gets put up on the screen. It interfaces with the
* RpfFrameProvider to get the information about the data. It also is usually
* handled by the RpfCacheManager.
*/
public class RpfCacheHandler {
/* Lowered from 128 - used too much memory */
public final static int SUBFRAME_CACHE_SIZE = 20;
/* # CHUM descriptor string */
public final static int MAX_NUM_DESC = 20;
/* # CHUM descriptor string */
public final static int MAX_DESC_LEN = 512;
public static final int DEFAULT_SUBFRAMEBUFFER = 5;
/* DKS fix chum description problem : */
/** subframe status constant. */
public final static int NOT_CACHED = -1;
/** subframe status constant. */
public final static int NOT_PRESENT = -2;
/** Subframe scaling for map scales that don't match chart scale. */
protected int scalingHeight = RpfSubframe.PIXEL_EDGE_SIZE;
/** Subframe scaling for map scales that don't match chart scale. */
protected int scalingWidth = RpfSubframe.PIXEL_EDGE_SIZE;
/** The subframe cache. */
protected SubframeCache cache;
/**
* The current description of the TOC entry that currently applies.
*/
protected List<RpfCoverageBox> coverageBoxes;
/**
* The array of indexes for subframes contained in the RpfTocEntry.
*/
protected byte[][] subframeIndex;
/**
* The array of version markers for subframes contained in the RpfTocEntry.
*/
protected byte[][] subframeVersion;
/** The size of the subframe cache. */
protected int subframeCacheSize = SUBFRAME_CACHE_SIZE;
/**
* Description of how the frames should be constructed and displayed.
*/
protected RpfViewAttributes viewAttributes;
/** The place to look for for image data. */
protected RpfFrameProvider frameProvider;
/** The upper left subframe index on screen. */
protected Point start = new Point();
/** The lower right subframe index on screen. */
protected Point end = new Point();
/**
* A flag to let the cache manager know that the subframes needed for the
* map make sense.
*/
protected boolean goodData = false;
/**
* The subframe cache is mapped by a 2D matrix based on the number of
* subframes that will fit in the RpfCoverageBox area. The subframeBuffer is
* used to put additional subframe entries in the matrix, so that subframes
* retrieved outside of the RpfCoverageBox area can still be cached. The
* subframeBuffer refers to the number of subframes added on each side of
* the matrix.
*/
protected int subframeBuffer = DEFAULT_SUBFRAMEBUFFER;
/**
* Used in setCache to see if new coverage is needed with a projection
* change.
*/
// private float lastScaleDifference = -1f;
protected boolean DEBUG_RPF = false;
protected boolean DEBUG_RPFDETAIL = false;
/** The entire subframe cache */
static public class SubframeCache {
RpfSubframe[] subframe;
int LRU_head, LRU_tail;
public SubframeCache(int numSubframes) {
subframe = new RpfSubframe[numSubframes];
}
}
/**
* Constructor for a main cache, with the full size cache.
*/
public RpfCacheHandler(RpfFrameProvider provider, RpfViewAttributes rva) {
this(provider, rva, SUBFRAME_CACHE_SIZE);
}
/**
* Constructor for an auxiliary cache, with a settable cache size.
*/
public RpfCacheHandler(RpfFrameProvider provider, RpfViewAttributes rva, int subframe_cache_size) {
DEBUG_RPF = Debug.debugging("rpf");
DEBUG_RPFDETAIL = Debug.debugging("rpfdetail");
frameProvider = provider;
viewAttributes = rva;
updateViewAttributes();
if (subframe_cache_size > Byte.MAX_VALUE) {
subframeCacheSize = Byte.MAX_VALUE;
} else if (subframe_cache_size >= 0) {
subframeCacheSize = subframe_cache_size;
}
initCache(true); // subframe cache, and it's new
if (DEBUG_RPF) {
Debug.output("RpfCacheHandler: Created with cache size of " + subframeCacheSize);
}
}
// public void finalize() {
// Debug.message("gc", "RpfCacheHandler: getting GC'd");
// }
/**
* 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;
updateViewAttributes();
clearCache();
}
/**
* Get the view attributes or the layer.
*
* @return RpfViewAttributes.
*/
public RpfViewAttributes getViewAttributes() {
if (viewAttributes == null) {
viewAttributes = new RpfViewAttributes();
}
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;
if (frameProvider != null) {
frameProvider.setViewAttributes(getViewAttributes());
}
clearCache();
}
/**
* Return RpfFrameProvider used by the layer.
*/
public RpfFrameProvider getFrameProvider() {
return frameProvider;
}
/**
* This only needs to be called if the frame provider is not local. In that
* case, updates to the view attributes object will not be reflected on the
* server side. This will update the parameters.
*/
public void updateViewAttributes() {
if (frameProvider != null) {
frameProvider.setViewAttributes(getViewAttributes());
}
}
/**
* Returns the Vector containing RpfCoverageBoxes that was returned 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 coverageBoxes;
}
/**
* Called to prepare the cache for subframes that will fit into the next
* request. The subframe entry from the TOC is known and tracked, and if it
* changes, the frame cache gets tossed and recreated via
* setScreenSubframes.
*
* @param ullat NW latitude.
* @param ullon NW longitude.
* @param lrlat SE latitude.
* @param lrlon SE longitude
* @param proj projection to use for zone decisions.
*/
public synchronized void setCache(float ullat, float ullon, float lrlat, float lrlon,
Projection proj) {
if (DEBUG_RPF) {
Debug.output("RpfCacheHandler: Need new Coverage.");
}
if (frameProvider != null) {
coverageBoxes = frameProvider.getCoverage(ullat, ullon, lrlat, lrlon, proj);
} else {
coverageBoxes = null;
}
// See if anything came back...
if (coverageBoxes == null || coverageBoxes.isEmpty()) {
// Guess not.
goodData = false;
return;
}
// The percent coverage should be greater than zero here.
// That should be checked by the RpfTocHandler.
// Base the cache off the coverage in the first box. It's
// supposed to have the best coverage.
RpfCoverageBox currentBox = coverageBoxes.get(0);
resetSubframeIndex(currentBox.verticalSubframes(), currentBox.horizontalSubframes());
initCache(false);
start = currentBox.startIndexes;
end = currentBox.endIndexes;
goodData = true;
// Set the backup indexes, just in case.
for (int i = 1; i < coverageBoxes.size(); i++) {
coverageBoxes.get(i).setPercentCoverage(ullat, ullon, lrlat, lrlon);
}
if (DEBUG_RPF) {
Debug.output("RpfCachehandler: ####################");
Debug.output("" + currentBox);
Debug.output(" Starting point " + start);
Debug.output(" Ending point " + end);
}
// Figure out how much to scale the cached images. This would
// be one of the big problems if we were going to merge
// different data types.
if (getViewAttributes().scaleImages) {
// Do the work for a great scaling factor here...
// Need to figure how much this will change for this scale
// chart at this screen scale
// Reference at 0, 0
Point2D refllpt = (Point2D) proj.getUpperLeft();
refllpt.setLocation(refllpt.getX() + (float) currentBox.subframeLonInterval, refllpt.getY()
- (float) currentBox.subframeLatInterval);
Point refpt = (Point) proj.forward(refllpt, new Point());
scalingWidth = refpt.x;
scalingHeight = refpt.y;
} else {
scalingWidth = RpfSubframe.PIXEL_EDGE_SIZE;
scalingHeight = RpfSubframe.PIXEL_EDGE_SIZE;
}
}
protected OMGraphicList getSubframes(float ullat, float ullon, float lrlat, float lrlon,
Projection proj, OMGraphicList omGraphics) {
setCache(ullat, ullon, lrlat, lrlon, proj);
int subframeRunningCount = 0;
if (omGraphics == null) {
omGraphics = new OMGraphicList();
}
for (int subx = start.x; subx <= end.x; subx++) {
for (int suby = start.y; suby <= end.y; suby++) {
// /////
RpfSubframe subframe = getCached(subx, suby, subframeRunningCount);
// /////
if (subframe == null) {
if (Debug.debugging("rpf")) {
Debug.output("RpfCacheManager: checking other TOCs for subframe.");
}
subframe = getSubframeFromOtherTOC(subx, suby, subframeRunningCount);
}
if (subframe != null) {
OMGraphic image = subframe.getImage(proj);
if (image != null) {
image.setSelected(viewAttributes.showInfo);
omGraphics.add(image);
if (Debug.debugging("rpf")) {
Debug.output("RpfCacheManager: Adding subframe " + subx + ", " + suby);
}
}
} else {
if (Debug.debugging("rpf")) {
Debug.output("RpfCacheManager: subframe " + subx + ", " + suby + " empty");
}
}
subframeRunningCount++;
}
}
return omGraphics;
}
/**
* Resets the indicators in the subframe cache, so that none of the current
* contents will be used - they'll have to be loaded with data first. The
* cache management is also set for the current main RpfCoverageBox.
*/
protected void resetSubframeIndex(int vertFrames, int horizFrames) {
if (subframeCacheSize > 0) {
/* Allocate the indices into the subframe cache */
int matrixheight = (vertFrames * 6) + (subframeBuffer * 2);
int matrixwidth = (horizFrames * 6) + (subframeBuffer * 2);
subframeIndex = new byte[matrixheight][matrixwidth];
subframeVersion = new byte[matrixheight][matrixwidth];
clearCache();
} else {
subframeIndex = null;
subframeVersion = null;
}
}
/**
* Clear the subframes in the cache, marking them as NOT_CACHED.
*/
public void clearCache() {
if (subframeIndex != null && subframeVersion != null) {
/* Initialize the subframe indices */
for (int i = 0; i < subframeIndex.length; i++) {
for (int j = 0; j < subframeIndex[0].length; j++) {
subframeIndex[i][j] = NOT_CACHED;
subframeVersion[i][j] = -1;
}
}
}
// TODO see if this makes a difference
initCache(false);
}
/**
* Return true if the cache handler knows about good data in the current
* situation.
*/
public boolean getGoodData() {
return goodData;
}
// /////////////////////////////////////////////////////////
// SUBFRAME CACHE HANDLING
// /////////////////////////////////////////////////////////
protected void initCache(boolean newCache) {
int i;
// Don't have a cache.
if (subframeCacheSize <= 0) {
cache = null;
return;
}
if (newCache || cache == null) {
cache = new SubframeCache(subframeCacheSize);
}
cache.LRU_head = 0;
cache.LRU_tail = subframeCacheSize - 1;
for (i = 0; i < subframeCacheSize; i++) {
if (newCache) {
try {
cache.subframe[i] = new RpfSubframe();
} catch (java.lang.OutOfMemoryError oome) {
Debug.error("RpfCacheHandler: \n\tRan out of memory allocating the image cache.\tConsider increasing the java memory heap using the -Xmx option.");
cache = null;
subframeCacheSize = i;
if (DEBUG_RPF) {
Debug.output("RpfCacheHandler: resetting cache size to "
+ subframeCacheSize);
}
initCache(true);
return;
}
}
RpfSubframe subframe = cache.subframe[i];
subframe.version = 0;
// Here's where I messed up - forgot to hook up the ends
// of the chain...
if (i < subframeCacheSize - 1) {
subframe.nextSubframe = i + 1;
} else {
subframe.nextSubframe = 0;
}
if (i > 0) {
subframe.prevSubframe = i - 1;
} else {
subframe.prevSubframe = subframeCacheSize - 1;
}
}
}
/**
* Get the index of the least recently used entry from the subframe cache.
*/
protected int getLRU() {
if (cache != null) {
return cache.LRU_tail;
} else {
return NOT_CACHED;
}
}
protected void freeCache(int index) {
if (cache == null) {
return;
}
if (index == cache.LRU_tail) {
return;
} else if (index == cache.LRU_head) {
cache.LRU_head = cache.subframe[cache.LRU_head].nextSubframe;
} else {
int next = cache.subframe[index].nextSubframe;
int prev = cache.subframe[index].prevSubframe;
cache.subframe[next].prevSubframe = prev;
cache.subframe[prev].nextSubframe = next;
}
cache.subframe[cache.LRU_tail].nextSubframe = index;
cache.subframe[index].prevSubframe = cache.LRU_tail;
cache.LRU_tail = index;
}
/**
* Mark a cache entry as being recently used.
*/
protected void referenceCache(int index) {
if (cache == null) {
return;
}
/* First unlink the cache entry from the list */
if (index == cache.LRU_head) {
return;
} else if (index == cache.LRU_tail) {
cache.LRU_tail = cache.subframe[cache.LRU_tail].prevSubframe;
} else {
int next = cache.subframe[index].nextSubframe;
int prev = cache.subframe[index].prevSubframe;
cache.subframe[next].prevSubframe = prev;
cache.subframe[prev].nextSubframe = next;
}
/* Now add the entry as the most recently referenced */
cache.subframe[cache.LRU_head].prevSubframe = index;
cache.subframe[index].nextSubframe = cache.LRU_head;
cache.LRU_head = index;
}
/**
* Find out the size of the subframe cache. From the start and end indexes,
* you can figure out the number of subframes the map needs. If that number
* is bigger than this cache size, you'll need to use the getCached that
* lets you supply the subframe number that you are requesting, so that the
* RpfCacheHandler knows when to stop caching subframes during a retrival..
* Otherwise, the cache will overwrite data and subframes will not show up
* on the map.
*/
public int getCacheSize() {
return subframeCacheSize;
}
/**
* Get a subframe from one of the other RpfCoverageBoxes. Keep going through
* them until there is a subframe returned, or if there's nothing. Use this
* method when you are sure that the subframe cache is big enough to handle
* all the subframes on the map.
*
* @param x the x index of subframe in the FIRST RpfCoverageBox space -
* translation needed.
* @param y the y index of subframe in the FIRST RpfCoverageBox space -
* translation needed.
*/
protected RpfSubframe getSubframeFromOtherTOC(int x, int y) {
return getSubframeFromOtherTOC(x, y, -1);
}
/**
* Get a subframe from one of the other RpfCoverageBoxes. Keep going through
* them until there is a subframe returned, or if there nothing. If you are
* not sure that the number of subframes that go on the map is less than or
* equal to the size of the subframe cache, then use this method to provide
* a running count of how many subframes you've already called for to use in
* the current map. If this number gets bigger than the cache size, then the
* RpfCacheHandler will keep fetching data without storing the extra
* subframes in the cache. Otherwise, the previous images in the cache would
* be replaced before they were painted, and they would not appear on the
* map. If subframeCount is less than subframe size, then the latest
* retrieved subframe will be stored in the cache.
*
* @param x the x index of subframe in the FIRST RpfCoverageBox space -
* translation needed.
* @param y the y index of subframe in the FIRST RpfCoverageBox space -
* translation needed.
* @param subframeCount a running count of the number of subframes retrieved
* so far for the current map. Should be used it there is concern
* that the number of subframes needed for the map is greater than
* the size of the subframe.
*/
protected RpfSubframe getSubframeFromOtherTOC(int x, int y, int subframeCount) {
int size = coverageBoxes.size();
RpfCoverageBox currentBox = null;
// Decision to never cache if it's coming from another TOC.
// Problems arose in areas that had 3 coverage boxes
// converging.
// They kept writing over each others' cache.
boolean cacheIt = false;
RpfSubframe ret = null;
int index = 0;
// There isn't anything else to check.
if (size < 2) {
return null;
} else {
/* If beyond the cache boundary, don't cache it. */
if (subframeIndex == null || y < 0 || x < 0 || y >= subframeIndex.length
|| x >= subframeIndex[0].length || subframeCount >= subframeCacheSize) {
cacheIt = false;
}
for (int i = 1; i < size; i++) {
try {
currentBox = (RpfCoverageBox) coverageBoxes.get(i);
} catch (ArrayIndexOutOfBoundsException aioobe) {
return null;
}
// Changed offsets because they were
// incorrect, and this was preventing other RCBs from
// finding the box
int offsetX = x - start.x;
int offsetY = y - start.y;
// previous values were:
// int offsetX = start.x - x;
// int offsetY = start.y - y;
int newX = currentBox.startIndexes.x + offsetX;
int newY = currentBox.startIndexes.y + offsetY;
if (cacheIt) {
/*
* Subframe isn't cached; allocate new entry and decompress
* it
*/
index = getLRU();
if (index < 0 || index >= subframeCacheSize
|| subframeCount >= subframeCacheSize) {
ret = null;
} else {
referenceCache(index);
RpfSubframe subframe = cache.subframe[index];
// DFD - not sure we need to do this. It seems like it's
// more important to make sure the version changes to
// some other value that matches what's in the subframe,
// rather than keep it stuck on MAX_VALUE.
// if (subframe.version < Byte.MAX_VALUE) {
// subframe.version++;
// }
subframeIndex[y][x] = (byte) index;
subframeVersion[y][x] = subframe.version;
ret = cache.subframe[index];
}
}
if (ret == null) {
try {
ret = new RpfSubframe();
} catch (java.lang.OutOfMemoryError oome) {
Debug.error("RpfCacheHandler: Out of memory! No subframe for you! Next up!");
return null;
}
}
if (loadSubframe(ret, currentBox, newX, newY)) {
return ret;
} else if (cacheIt) {
freeCache(index);
subframeIndex[y][x] = NOT_PRESENT;
}
}
}
return null;
}
/**
* Get a subframe from the cache if possible, otherwise allocate a new cache
* entry and decompress it. Each cache entry has a version number that is
* incremented whenever it is replaced by a new subframe. This ensures the
* replacement is detected. Use this method when you are sure that the
* subframe cache is big enough to handle all the subframes on the map.
*
* @param cbx the x index of subframe in the rcbIndex A.TOC space.
* @param cby the y index of subframe in the rcbIndex A.TOC space.
*/
protected RpfSubframe getCached(int cbx, int cby) {
return getCached(cbx, cby, -1);
}
/**
* Get a subframe from the cache if possible, otherwise allocate a new cache
* entry and decompress it. Each cache entry has a version number that is
* incremented whenever it is replaced by a new subframe. This ensures the
* replacement is detected. If you are not sure that the number of subframes
* that go on the map is less than or equal to the size of the subframe
* cache, then use this method to provide a running count of how many
* subframes you've already called for to use in the current map. If this
* number gets bigger than the cache size, then the RpfCacheHandler will
* keep fetching data without storing the extra subframes in the cache.
* Otherwise, the previous images in the cache would be replaced before they
* were painted, and they would not appear on the map. If subframeCount is
* less than subframe size, then the latest retrieved subframe will be
* stored in the cache.
*
* @param cbx the x index of subframe in the rcbIndex A.TOC space.
* @param cby the y index of subframe in the rcbIndex A.TOC space.
* @param subframeCount a running count of the number of subframes retrieved
* so far for the current map. Should be used if there is concern
* that the number of subframes needed for the map is greater than
* the size of the cache.
*/
protected RpfSubframe getCached(int cbx, int cby, int subframeCount) {
RpfSubframe ret;
RpfCoverageBox currentBox = null;
RpfViewAttributes viewAttributes = getViewAttributes();
// x, y are the subframe indexes in the cache matrix
int x = cbx + subframeBuffer;
int y = cby + subframeBuffer;
/* If beyond the image boundary, forget it */
if (subframeIndex != null
&& (coverageBoxes == null || coverageBoxes.isEmpty() || y < 0 || x < 0
|| y >= subframeIndex.length || x >= subframeIndex[0].length)) {
return null;
}
try {
currentBox = (RpfCoverageBox) coverageBoxes.get(0);
} catch (IndexOutOfBoundsException aioobe) {
return null;
}
int index = NOT_CACHED;
if (subframeIndex != null) {
index = subframeIndex[y][x];
}
if (index == NOT_PRESENT) {
return null;
} else if (index != NOT_CACHED && cache != null
&& cache.subframe[index].version == subframeVersion[y][x]
&& subframeCount < subframeCacheSize) {
/* We found it and it's ours; return the cached image */
referenceCache(index);
ret = cache.subframe[index];
if (DEBUG_RPF) {
Debug.output("RpfCacheHandler: found subframe " + x + ", " + y + " in cache.");
}
ret.setTransparent(viewAttributes.opaqueness);
// Check to see if the attribute text has even been
// retrieved from the RpfFrameProvider. If it hasn't, and
// needs to be, get it.
if (frameProvider != null && viewAttributes.showInfo
&& (ret.getAttributeText() == null || ret.getAttributeText().length() == 0)) {
// It's needed but not here.
ret.setAttributeText(frameProvider.getSubframeAttributes(currentBox.tocNumber, currentBox.entryNumber, x, y));
}
return ret;
} else {
/*
* Subframe isn't cached; allocate new entry and decompress it
*/
index = getLRU();
// Meet the requirements for not caching...
if (index < 0 || index >= subframeCacheSize || subframeCount >= subframeCacheSize) {
try {
ret = new RpfSubframe();
if (DEBUG_RPF) {
Debug.output("RpfCacheHandler: using uncached subframe.");
}
} catch (java.lang.OutOfMemoryError oome) {
Debug.error("RpfCacheHandler: Out of memory! No subframe for you! Next up!");
return null;
}
} else { // or set the cache for the new subframe
referenceCache(index);
// DFD - I'm not sure it ever gets to the point where version
// gets bigger than a byte can hold, and we don't
// want the byte overrunning, do we? Then again, does it matter,
// if the frame version number matches the array setting for it,
// even
// if it's negative?
cache.subframe[index].version++;
subframeIndex[y][x] = (byte) index;
subframeVersion[y][x] = cache.subframe[index].version;
ret = cache.subframe[index];
}
if (loadSubframe(ret, currentBox, cbx, cby)) {
return ret;
} else {
freeCache(index);
if (subframeIndex != null) {
subframeIndex[y][x] = NOT_PRESENT;
}
}
}
return null;
}
/**
* Contacts the frame provider to put the subframe image in the RpfSubframe.
*
* @param subframe the RpfSubframe to load the image data into.
* @param coverageBox that has toc and entry numbers to use.
* @param x the coveragebox x index for the subframe.
* @param y the coveragebox y index for the subframe.
* @return true if successful.
*/
protected boolean loadSubframe(RpfSubframe subframe, RpfCoverageBox coverageBox, int x, int y) {
boolean good = false;
int[] pixels = null;
if (frameProvider == null) {
Debug.message("rpf", "RpfCacheHandler.loadSubframes(): null frameProvider");
return false;
}
RpfViewAttributes viewAttributes = getViewAttributes();
subframe.opaqueness = viewAttributes.opaqueness;
if (viewAttributes.colorModel == OMRasterObject.COLORMODEL_DIRECT) {
pixels = frameProvider.getSubframeData(coverageBox.tocNumber, coverageBox.entryNumber, x, y);
if (pixels != null) {
subframe.setPixels(pixels);
good = true;
}
} else if (viewAttributes.colorModel == OMRasterObject.COLORMODEL_INDEXED) {
RpfIndexedImageData riid = frameProvider.getRawSubframeData(coverageBox.tocNumber, coverageBox.entryNumber, x, y);
if (riid != null && riid.imageData != null && riid.colortable != null) {
subframe.setBitsAndColors(riid.imageData, riid.colortable);
subframe.setTransparent(viewAttributes.opaqueness);
good = true;
}
} else {
Debug.error("RpfCacheHandler: Frame Provider colormodel not handled.");
return false;
}
if (good) {
// LOAD UP the geographic stuff into
// cache.subframe[index].image
double lat, lon, lat2, lon2;
double xlloffset, ylloffset;
ylloffset = (double) (y * coverageBox.subframeLatInterval);
xlloffset = (double) (x * coverageBox.subframeLonInterval);
lat = coverageBox.nw_lat - ylloffset;
lon = coverageBox.nw_lon + xlloffset;
lat2 = lat - coverageBox.subframeLatInterval;
lon2 = lon + coverageBox.subframeLonInterval;
String data;
if (viewAttributes != null
&& (viewAttributes.autofetchAttributes || viewAttributes.showInfo)) {
data = frameProvider.getSubframeAttributes(coverageBox.tocNumber, coverageBox.entryNumber, x, y);
} else {
data = "";
}
if (DEBUG_RPFDETAIL) {
Debug.output("Attribute data for subframe " + x + ", " + y + ":\n" + data);
}
// fill in the information for the subframe.
subframe.setLocation(lat, lon, lat2, lon2);
subframe.setAttributeText(data);
return true;
} else {
subframe.setAttributeText("");
}
return false;
}
}