// ********************************************************************** // // <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/vpf/LibrarySelectionTable.java,v $ // $Revision: 1.13 $ $Date: 2005/12/09 21:08:57 $ $Author: dietrick $ // ********************************************************************** package com.bbn.openmap.layer.vpf; import java.io.File; import java.io.FileFilter; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.logging.Level; import com.bbn.openmap.io.BinaryFile; import com.bbn.openmap.io.FormatException; import com.bbn.openmap.proj.coords.LatLonPoint; /** * Reads the VPF LibraryAttribute table and constructs CoverageAttributeTables * for each of the library coverages (north america, browse, etc) that exist. * * <p> * NOTE: This class maintains a whole bunch of cached information, and also * hangs onto references to classes that cache even more information. When using * this class, you are much better off sharing an instance of this class, rather * than creating multiple instantiations of it for the same VPF data directory. * * @see CoverageAttributeTable */ public class LibrarySelectionTable { /** cutoff scale for browse coverage. */ public final static int DEFAULT_BROWSE_CUTOFF = 31000000; protected int BROWSE_CUTOFF = DEFAULT_BROWSE_CUTOFF; /** * the names of the VPF libraries listed in the library attribute table */ // private String libraryname[] = null; //library [i] /** the bounding rectangle of the respective libraries */ private Map<String, float[]> boundrec = new HashMap<String, float[]>();// bounding // rect // as // [W,S,E,N] /** the CoverageAttributeTables corresponding to the different libs */ private Map<String, CoverageAttributeTable> CATs = new HashMap<String, CoverageAttributeTable>(); /** * A list of libraries in the same order as their data paths were added. */ private List<String> orderedLibraryNameList = new ArrayList<String>(); /** the names of the lat columns */ final private static String LATColumns[] = { Constants.LAT_LIBNAME, Constants.LAT_XMIN, Constants.LAT_YMIN, Constants.LAT_XMAX, Constants.LAT_YMAX }; /** the expected schema types for the library attribute table */ final private static char LATschematype[] = { 'T', 'F', 'F', 'F', 'F' }; /** the expected schema lengths for the library attribute table */ final private static int LATschemalength[] = { -1 /* 8 */, 1, 1, 1, 1 }; /** the name of the database */ private String databaseName; /** the database description of itself */ private String databaseDesc; /** * Construct a LibrarySelectionTable without a path to data. */ public LibrarySelectionTable() { } /** * Construct a LibrarySelectionTable with a path to data. * * @param vpfpath the path to the base data directory; the file opened is * <code>vpfpath</code> /lat. * @exception FormatException some error was encountered while trying to * handle the file. */ public LibrarySelectionTable(String vpfpath) throws FormatException { addDataPath(vpfpath); } /** * Construct a LibrarySelectionTable with a path to data. * * @param vpfpaths the paths to the data directories; the file opened is * <code>vpfpath</code> /lat. * @exception FormatException some error was encountered while trying to * handle the file. */ public LibrarySelectionTable(String vpfpaths[]) throws FormatException { for (int i = 0; i < vpfpaths.length; i++) { addDataPath(vpfpaths[i]); } } /** * Set the cutoff scale where if the map scale number is larger (smaller * overall map scale), the coverage won't be returned. For example, if the * scale cutoff is 30000000, if the map scale is 1:31000000, no map data * will be returned. */ public void setCutoffScale(int scale) { BROWSE_CUTOFF = scale; } /** * Get the cutoff scale where data will be retrieved. */ public int getCutoffScale() { return BROWSE_CUTOFF; } /** * add a path to LibrarySelectionTable. Adding different types of VPF * libraries to the same LST is likely to cause trouble. (e.g. it would be * bad to add both DCW and VMAP paths to the same LST. adding each DCW disk * separately is why this method exists.) * * @param vpfpath the path to the base DCW directory; the file opened is * <code>vpfpath</code> /lat. * @exception FormatException some error was encountered while trying to * handle the file. */ public void addDataPath(String vpfpath) throws FormatException { VPFLayer.logger.fine("LST.addDataPath(" + vpfpath + ")"); // Figure out how files names should be constructed... boolean addSlash = true; if (vpfpath.endsWith("/") || vpfpath.endsWith(File.separator)) { addSlash = false; } String latf = vpfpath + (addSlash ? "/" : "") + "lat"; String dhtf = vpfpath + (addSlash ? "/" : "") + "dht"; if (!BinaryFile.exists(latf)) { latf += "."; dhtf += "."; } DcwRecordFile latrf = new DcwRecordFile(latf); DcwRecordFile dhtrf = new DcwRecordFile(dhtf); List<Object> databaseVec = dhtrf.parseRow(); int dcol = dhtrf.whatColumn("database_name"); if (dcol != -1) { databaseName = (String) databaseVec.get(dcol); } dcol = dhtrf.whatColumn("database_desc"); if (dcol != -1) { databaseDesc = (String) databaseVec.get(dcol); } dhtrf.close(); dhtrf = null; int latcols[] = latrf.lookupSchema(LATColumns, true, LATschematype, LATschemalength, false); final List<String> libDirectories = searchLibraryDirectories(vpfpath); VPFLayer.logger.fine("lst.adp: looked up schema"); for (final List l = new ArrayList(latrf.getColumnCount()); latrf.parseRow(l);) { final String lname = ((String) l.get(latcols[0])).toLowerCase(); if (!libDirectories.contains(lname.toUpperCase())) { continue; } final float br[] = new float[] { ((Float) l.get(latcols[1])).floatValue(), ((Float) l.get(latcols[2])).floatValue(), ((Float) l.get(latcols[3])).floatValue(), ((Float) l.get(latcols[4])).floatValue() }; try { CoverageAttributeTable table = new CoverageAttributeTable(vpfpath, lname); CATs.put(lname, table); boundrec.put(lname, br); orderedLibraryNameList.add(lname); if (VPFLayer.logger.isLoggable(Level.FINE)) { VPFLayer.logger.fine(lname + " " + br[0] + " " + br[1] + " " + br[2] + " " + br[3]); } } catch (FormatException fe) { if (VPFLayer.logger.isLoggable(Level.FINER)) { VPFLayer.logger.finer( "*****\nVPFLayer.LST: Couldn't create CoverageAttributeTable for " + vpfpath + " " + lname + " " + fe.getMessage() + "\n--- Not a problem if you have multiple paths, and " + lname + " is included in another path ---\n*****"); fe.printStackTrace(); } else { VPFLayer.logger .fine("VPFLayer.LST: CAT discrepancy (run with finer logging level for more details)"); } } } latrf.close(); latrf = null; } private List<String> searchLibraryDirectories(final String vpfpath) { final ArrayList<String> subDirLibWithCat = new ArrayList<String>(); final File vpfDirectory = new File(vpfpath); final File[] vpfSubDirectory = vpfDirectory.listFiles(new FileFilter() { public boolean accept(final File file) { return file.isDirectory(); } }); for (int i = 0; i < vpfSubDirectory.length; i++) { final File subDir = vpfSubDirectory[i]; String cat = subDir + "/cat"; if (BinaryFile.exists(cat)) { subDirLibWithCat.add(subDir.getName().toUpperCase()); continue; } cat = cat + "."; if (BinaryFile.exists(cat)) { subDirLibWithCat.add(subDir.getName().toUpperCase()); } } return subDirLibWithCat; } /** * Return the list of libraries that this database has. * * @return the list of libraries. for DCW, this is typically NOAMER, BROWSE, * etc. */ public List<String> getLibraryNames() { return new ArrayList<String>(orderedLibraryNameList); } /** * Return the name of the database we are reading from. */ public String getDatabaseName() { return databaseName; } /** * Return the description of the database we are reading from. */ public String getDatabaseDescription() { return databaseDesc; } /** * Return the coverage attribute table (list of coverages available for the * given library) for the given library name. * * @param library the name of the library to get the CAT for * @return the CoverageAttributeTable requested (null if the library * requested doesn't exist in the database) * @exception FormatException exceptions from opening the CAT for the * library */ public CoverageAttributeTable getCAT(String library) throws FormatException { return CATs.get(library); } /** * */ public void drawTile(int scale, int screenwidth, int screenheight, String covname, VPFGraphicWarehouse warehouse, LatLonPoint ll1, LatLonPoint ll2) { if (VPFLayer.logger.isLoggable(Level.FINE)) { VPFLayer.logger.fine("Library selection table coverage: " + covname); VPFLayer.logger.fine("Library selection table - edges: " + warehouse.drawEdgeFeatures()); VPFLayer.logger.fine("Library selection table - text: " + warehouse.drawTextFeatures()); VPFLayer.logger.fine("Library selection table - areas: " + warehouse.drawAreaFeatures()); VPFLayer.logger.fine("Warehouse: " + warehouse); VPFLayer.logger.fine("Warehouse: cutoff scale " + BROWSE_CUTOFF); } // handle Dateline if ((scale < BROWSE_CUTOFF) && (ll1.getLongitude() > ll2.getLongitude())) { drawTile(scale, screenwidth, screenheight, covname, warehouse, ll1, new LatLonPoint.Float(ll2.getLatitude(), 180f - .00001f)/* * 180 * - * epsilon */); drawTile(scale, screenwidth, screenheight, covname, warehouse, new LatLonPoint.Float(ll1.getLatitude(), -180f), ll2); return; } if (VPFLayer.logger.isLoggable(Level.FINE)) { VPFLayer.logger.fine("LST.drawTile() with scale of " + scale); } float dpplat = Math.abs((ll1.getLatitude() - ll2.getLatitude()) / screenheight); float dpplon = Math.abs((ll1.getLongitude() - ll2.getLongitude()) / screenwidth); int inArea = 0; CoverageTable redrawUntiled = null; for (CoverageAttributeTable cat : CATs.values()) { if (VPFLayer.logger.isLoggable(Level.FINE)) { VPFLayer.logger.fine("LST: checking library: " + cat.getLibraryName()); } if (!warehouse.checkLibraryForUsage(cat.getLibraryName())) { continue; } warehouse.resetForCAT(); List<TileDirectory> tiles = cat.tilesInRegion(ll1.getLatitude(), ll2.getLatitude(), ll2.getLongitude(), ll1.getLongitude()); if (tiles == null) { redrawUntiled = cat.getCoverageTable(covname); } else if (cat.isTiledData() && (scale < BROWSE_CUTOFF)) { if (!tiles.isEmpty()) { CoverageTable c = cat.getCoverageTable(covname); if (c == null) { if (VPFLayer.logger.isLoggable(Level.FINE)) { VPFLayer.logger.fine("|LST.drawTile(): Couldn't get coverage table for " + covname + " " + cat.getLibraryName()); } continue; } if (VPFLayer.logger.isLoggable(Level.FINE)) { VPFLayer.logger.fine("Using coverage table for " + covname + " " + cat.getLibraryName()); } inArea++; for (TileDirectory tileDirectory : tiles) { c.drawTile(tileDirectory, warehouse, ll1, ll2, dpplat, dpplon); } } } } if ((redrawUntiled != null) && (inArea == 0)) { if (VPFLayer.logger.isLoggable(Level.FINE)) { VPFLayer.logger.fine("LST drawing untiled browse data"); } redrawUntiled.drawTile(new TileDirectory(), warehouse, ll1, ll2, dpplat, dpplon); } } /** * */ public void drawFeatures(int scale, int screenwidth, int screenheight, String covname, VPFFeatureWarehouse warehouse, LatLonPoint ll1, LatLonPoint ll2) { if (VPFLayer.logger.isLoggable(Level.FINE)) { VPFLayer.logger.fine("LST.drawFeatures(): Coverage name: " + covname); VPFLayer.logger.fine("Library selection table - edges: " + warehouse.drawEdgeFeatures()); VPFLayer.logger.fine("Library selection table - text: " + warehouse.drawTextFeatures()); VPFLayer.logger.fine("Library selection table - areas: " + warehouse.drawAreaFeatures()); VPFLayer.logger.fine("Warehouse: " + warehouse); } // handle Dateline if ((scale < BROWSE_CUTOFF) && (ll1.getLongitude() > ll2.getLongitude())) { drawFeatures(scale, screenwidth, screenheight, covname, warehouse, ll1, new LatLonPoint.Float(ll2.getLatitude(), 180f - .00001f)// 180-epsilon ); drawFeatures(scale, screenwidth, screenheight, covname, warehouse, new LatLonPoint.Float(ll1.getLatitude(), -180f), ll2); return; } if (VPFLayer.logger.isLoggable(Level.FINE)) { VPFLayer.logger.fine("LST.drawFeatures() with scale of " + scale); } float dpplat = Math.abs((ll1.getLatitude() - ll2.getLatitude()) / screenheight); float dpplon = Math.abs((ll1.getLongitude() - ll2.getLongitude()) / screenwidth); int inArea = 0; CoverageTable redrawUntiled = null; for (CoverageAttributeTable cat : CATs.values()) { if (!warehouse.checkLibraryForUsage(cat.getLibraryName())) { continue; } if (cat.isTiledCoverage() && scale < BROWSE_CUTOFF) { CoverageTable c = cat.getCoverageTable(covname); if (c == null) { if (VPFLayer.logger.isLoggable(Level.FINE)) { VPFLayer.logger.fine("LST.getFeatures(): Couldn't get coverage table for " + covname + " " + cat.getLibraryName()); } continue; } if (VPFLayer.logger.isLoggable(Level.FINE)) { VPFLayer.logger.fine("Using coverage table for " + covname + " " + cat.getLibraryName()); } c.drawFeatures(warehouse, ll1, ll2, dpplat, dpplon); inArea++; } else { // Set up to draw browse coverage, or non-tiled coverage if (VPFLayer.logger.isLoggable(Level.FINE)) { VPFLayer.logger.fine("LST.drawTile(): Scale too small (probably) or no tiles in region."); } redrawUntiled = cat.getCoverageTable(covname); if (redrawUntiled != null) { redrawUntiled.drawFeatures(warehouse, ll1, ll2, dpplat, dpplon); } } } // Moved this code up into the redrawUntiled section directly above // this. It looks like the code wanted to restrict how many un-tiled // coverage attribute tables would be consulted, but for certain kinds // of VPF data that gets distributed as a bunch of little libraries, // this seems to limit rendering to only one small area. // if ((redrawUntiled != null) && (inArea == 0)) { // redrawUntiled.drawFeatures(warehouse, ll1, ll2, dpplat, dpplon); // } } /** * Given a string for a coverage type or feature type, return the * description for that string. Return null if the code string isn't found. * * @param coverageOrFeatureType string ID for coverage or Feature type. */ public String getDescription(String coverageOrFeatureType) throws FormatException { boolean DEBUG = VPFLayer.logger.isLoggable(Level.FINE); if (DEBUG) VPFLayer.logger.fine("LST.getDescription: " + coverageOrFeatureType); for (String libraryName : getLibraryNames()) { CoverageAttributeTable cat = getCAT(libraryName); if (cat == null) { continue; } String[] coverages = cat.getCoverageNames(); for (int j = 0; j < coverages.length; j++) { String covname = coverages[j]; if (coverageOrFeatureType.equalsIgnoreCase(covname)) { if (DEBUG) VPFLayer.logger.fine("** Matches coverage " + covname); return cat.getCoverageDescription(covname); } else { if (DEBUG) VPFLayer.logger.fine(" Checking in coverage table " + covname); CoverageTable ct = cat.getCoverageTable(covname); Hashtable<String, CoverageTable.FeatureClassRec> info = ct.getFeatureTypeInfo(); for (CoverageTable.FeatureClassRec fcr : info.values()) { String name = fcr.feature_class; if (coverageOrFeatureType.equalsIgnoreCase(name)) { if (DEBUG) VPFLayer.logger.fine("** Found feature " + name); return fcr.description; } if (DEBUG) VPFLayer.logger.fine(" checked " + name); } } } } if (DEBUG) VPFLayer.logger.fine("-- No matches found."); return null; } /** * Just a test main to parse vpf datafiles param args files to parse, plus * other command line flags * * @param args command line arguments args[0] is a path to the VPF root */ public static void main(String[] args) { String dcwbase = null; if (args.length > 0) { dcwbase = args[0]; try { LibrarySelectionTable lst = new LibrarySelectionTable(dcwbase); System.out.println("Database Name " + lst.getDatabaseName()); for (String libraryName : lst.getLibraryNames()) { System.out.println("Library " + libraryName); lst.getCAT(libraryName); } } catch (FormatException f) { System.err.println("*****************************************"); System.err.println("*---------------------------------------*"); System.err.println("Format error in dealing with LST"); System.err.println(f.getMessage()); System.err.println("*---------------------------------------*"); System.err.println("*****************************************"); } } else { System.out.println("Need a path to the VPF lat. file"); } } }