// ********************************************************************** // // <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/CoverageTable.java,v $ // $Revision: 1.13 $ $Date: 2005/12/09 21:08:58 $ $Author: dietrick $ // ********************************************************************** package com.bbn.openmap.layer.vpf; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; import com.bbn.openmap.io.BinaryFile; import com.bbn.openmap.io.FormatException; import com.bbn.openmap.layer.vpf.VPFAutoFeatureGraphicWarehouse.FeaturePriorityHolder; import com.bbn.openmap.omGraphics.OMGraphic; import com.bbn.openmap.omGraphics.OMGraphicList; import com.bbn.openmap.proj.coords.LatLonPoint; import com.bbn.openmap.util.Debug; /** * Encapsulate a VPF coverage directory. This class handles requests that happen * for a particular coverage type (political boundary, road, etc.) for a * particular library (north america, browse, etc.). */ public class CoverageTable { protected final static Logger logger = Logger.getLogger("com.bbn.openmap.layer.vpf.CoverageTable"); /** our coverage type - such as "po", "bnd", "hydro" */ final public String covtype; /** the directory for our coverage type */ final protected String tablepath; /** a table to cache int.vdt information */ final private Map<CoverageIntVdt, String> intvdtrec = new HashMap<CoverageIntVdt, String>(); /** a table to cache char.vdt information */ final private Map<CoverageCharVdt, String> charvdtrec = new HashMap<CoverageCharVdt, String>(); /** hack - used by EdgeTable */ public int cachedLineSchema[] = null; /** hack - used by AreaTable */ public int cachedAreaSchema[] = null; /** hack - used by TextTable */ public int cachedTextSchema[] = null; /** hack - used by nodetable */ public int cachedEPointSchema[] = null; /** hack - used by nodetable */ public int cachedCPointSchema[] = null; /** featureclasses used for the line feature type */ public FeatureClassInfo lineinfo[] = new FeatureClassInfo[0]; /** featureclasses used for the area feature type */ public FeatureClassInfo areainfo[] = new FeatureClassInfo[0]; /** featureclasses used for the text feature type */ public FeatureClassInfo textinfo[] = new FeatureClassInfo[0]; /** featureclasses used for the entity node feature type */ public FeatureClassInfo epointinfo[] = new FeatureClassInfo[0]; /** featureclasses used for the connected node feature type */ public FeatureClassInfo cpointinfo[] = new FeatureClassInfo[0]; /** * Feature classes to look up FeatureClassInfo via feature name. */ protected Hashtable<String, FeatureClassInfo> featureTypes = new Hashtable<String, FeatureClassInfo>(); protected Hashtable<String, FeatureClassRec> featureTypeInfo; /** do we need to append a '.' to three-character file names */ public boolean appendDot = false; /** * Need this in case we have to go from the coverage type-<feature * type-<tile */ protected CoverageAttributeTable cat; /** hackage for the antarctica polygon in DCW browse coverage */ final public boolean doAntarcticaWorkaround; public static final char EDGE_FEATURETYPE = 'L'; public static final char AREA_FEATURETYPE = 'A'; public static final char TEXT_FEATURETYPE = 'T'; public static final char UPOINT_FEATURETYPE = 'P'; public static final char EPOINT_FEATURETYPE = 'E'; public static final char CPOINT_FEATURETYPE = 'N'; public static final char COMPLEX_FEATURETYPE = 'C'; public static final char SKIP_FEATURETYPE = 'S'; /** * Construct a CoverageTable object. Data is expected to be in a directory * called path/covtype. * * @param path the path to the parent directory of where our data resides * @param covtype the subdirectory name for the coverage data */ public CoverageTable(String path, String covtype) { this.covtype = covtype; tablepath = path + "/" + covtype + "/"; doAntarcticaWorkaround = (tablepath.indexOf("browse") >= 0); internSchema(); loadIntVDT(); loadCharVDT(); featureTypeInfo = getFeatureTypeInfo(); } /** * Construct a CoverageTable object. Data is expected to be in a directory * called path/covtype. * * @param path the path to the parent directory of where our data resides * @param covtype the subdirectory name for the coverage data * @param cat the CoverageAttributeTable reference, in case we need to * backtrack the tiles through the feature tables. */ public CoverageTable(String path, String covtype, CoverageAttributeTable cat) { this(path, covtype); this.cat = cat; } /** required column names for char.vdt and int.vdt files */ public final static String VDTColumnNames[] = { Constants.VDT_TABLE, Constants.VDT_ATTRIBUTE, Constants.VDT_VALUE, Constants.VDT_DESC }; /** expected schema types for int.vdt files */ public final static char intVDTschematype[] = { DcwColumnInfo.VPF_COLUMN_INT, DcwColumnInfo.VPF_COLUMN_TEXT, DcwColumnInfo.VPF_COLUMN_TEXT, DcwColumnInfo.VPF_COLUMN_INT_OR_SHORT, DcwColumnInfo.VPF_COLUMN_TEXT }; /** expected schema lengths for int.vdt files */ public final static int intVDTschemalength[] = { 1, -1, -1, 1, -1 }; private void loadIntVDT() { try { String vdt = tablepath + Constants.intVDTTableName; if (BinaryFile.exists(vdt)) { DcwRecordFile intvdt = new DcwRecordFile(vdt); int cols[] = intvdt.lookupSchema(VDTColumnNames, true, intVDTschematype, intVDTschemalength, false); for (List<Object> l = new ArrayList<Object>(intvdt.getColumnCount()); intvdt.parseRow(l);) { String tab = (String) l.get(cols[0]); String attr = (String) l.get(cols[1]); int val = ((Number) l.get(cols[2])).intValue(); String desc = ((String) l.get(cols[3])).intern(); intvdtrec.put(new CoverageIntVdt(tab, attr, val), desc); } intvdt.close(); } } catch (FormatException f) { } } /** expected schema types for char.vdt files */ public final static char charVDTschematype[] = { DcwColumnInfo.VPF_COLUMN_INT, DcwColumnInfo.VPF_COLUMN_TEXT, DcwColumnInfo.VPF_COLUMN_TEXT, DcwColumnInfo.VPF_COLUMN_TEXT, DcwColumnInfo.VPF_COLUMN_TEXT }; /** expected schema lengths for char.vdt files */ public final static int charVDTschemalength[] = { 1, -1, -1, -1, -1 }; private void loadCharVDT() { try { String vdt = tablepath + Constants.charVDTTableName; if (BinaryFile.exists(vdt)) { DcwRecordFile charvdt = new DcwRecordFile(vdt); int cols[] = charvdt.lookupSchema(VDTColumnNames, true, charVDTschematype, charVDTschemalength, false); for (List<Object> l = new ArrayList<Object>(charvdt.getColumnCount()); charvdt.parseRow(l);) { String tab = (String) l.get(cols[0]); String attr = (String) l.get(cols[1]); String val = (String) l.get(cols[2]); String desc = ((String) l.get(cols[3])).intern(); charvdtrec.put(new CoverageCharVdt(tab, attr, val), desc); } charvdt.close(); } } catch (FormatException f) { } } private FeatureClassInfo[] internSchema(FeatureClassInfo[] fti, String foreign_key, String tablename) throws FormatException { FeatureClassInfo rv[] = new FeatureClassInfo[fti.length + 1]; System.arraycopy(fti, 0, rv, 0, fti.length); rv[fti.length] = new FeatureClassInfo(this, foreign_key.intern(), tablepath, tablename.intern()); return rv; } /** the columns of the fcs file we are interested in */ private static final String[] fcsColumns = { Constants.FCS_FEATURECLASS, Constants.FCS_TABLE1, Constants.FCS_TABLE1KEY, Constants.FCS_TABLE2, Constants.FCS_TABLE2KEY }; /** the columns we need in fcs for tiling for DCW */ private static final String[] fcsColumnsDCW = { Constants.FCS_FEATURECLASS, Constants.FCS_TABLE1, Constants.DCW_FCS_TABLE1KEY, Constants.FCS_TABLE2, Constants.DCW_FCS_TABLE2KEY }; /** * This method reads the feature class schema (fcs) file to discover the * inter-table relations (joins, in database parlance). As a side effect, * this method also sets the appendDot member. */ private void internSchema() { internSchema(false); } /** * This method reads the feature class schema (fcs) file to discover the * inter-table relations (joins, in database parlance). As a side effect, * this method also sets the appendDot member. The DCW option refers to if * the DCW column names should be used, for DCW data. This is only true if a * problem occurs, and then this method is called recursively. */ private void internSchema(boolean DCW) { // Figure out how files names should be constructed... boolean addSlash = true; // if (tablepath.endsWith(File.separator)) { if (tablepath.endsWith("/") || tablepath.endsWith(File.separator)) { addSlash = false; } try { String filename = tablepath + (addSlash ? "/" : "") + "fcs"; if (!BinaryFile.exists(filename)) { filename += "."; appendDot = true; } DcwRecordFile fcs = new DcwRecordFile(filename); int[] fcscols = fcs.lookupSchema(DCW ? fcsColumnsDCW : fcsColumns, true); for (List<Object> fcsrec = new ArrayList<Object>(fcs.getColumnCount()); fcs.parseRow(fcsrec);) { String feature_class = (String) fcsrec.get(fcscols[0]); String table1 = (String) fcsrec.get(fcscols[1]); String foreign_key = (String) fcsrec.get(fcscols[2]); String table2 = (String) fcsrec.get(fcscols[3]); String primary_key = (String) fcsrec.get(fcscols[4]); internSchema(feature_class.toLowerCase(), table1.toLowerCase(), foreign_key.toLowerCase(), table2.toLowerCase(), primary_key.toLowerCase()); } fcs.close(); } catch (FormatException f) { if (!DCW) { internSchema(true); } else { System.out.println("CoverageTable: " + f.getMessage()); } } } private void internSchema(String feature_class, String table1, String foreign_key, String table2, String primary_key) { try { if (table1.equals("fac")) { areainfo = internSchema(areainfo, foreign_key, table2); } else if (table1.equals("edg")) { lineinfo = internSchema(lineinfo, foreign_key, table2); } else if (table1.equals("end")) { epointinfo = internSchema(epointinfo, foreign_key, table2); } else if (table1.equals("cnd")) { cpointinfo = internSchema(cpointinfo, foreign_key, table2); } else if (table1.equals("txt")) { textinfo = internSchema(textinfo, foreign_key, table2); } else if (table1.startsWith(feature_class) && (foreign_key.equals("end_id") || foreign_key.equals("cnd_id") || foreign_key.equals("fac_id") || foreign_key.equals("edg_id") || foreign_key.equals("txt_id"))) { if (Debug.debugging("vpf")) { Debug.output("CoverageTable: Found entry for: " + feature_class + ": " + table1 + "|" + foreign_key + "|" + table2 + "|" + primary_key); } FeatureClassInfo featureClass = new FeatureClassInfo(this, foreign_key.intern(), tablepath.intern(), table1.intern(), table2.intern(), foreign_key.intern()); featureClass.close(false); // releases file descriptors featureTypes.put(feature_class.intern(), featureClass); } else { // nothing else that we care about for now // symbol.rat could show up here, for example } } catch (FormatException f) { System.out.println("internSchema: " + f.getMessage()); } } /** * Get the path for this coverage */ public String getDataPath() { return tablepath; } /** * Returns all the feature classes */ public Map<String, FeatureClassInfo> getFeatureClasses() { return Collections.unmodifiableMap(featureTypes); } /** * Returns the FeatureClassInfo object corresponding to the feature type. * Returns null if the featureType doesn't exist. * * @return the feature class object for the feature type * @param featureType the name of the feature to get */ public FeatureClassInfo getFeatureClassInfo(String featureType) { return featureTypes.get(featureType); } public String getDescription(String t, String a, int v) { CoverageIntVdt civ = new CoverageIntVdt(t, a, v); return intvdtrec.get(civ); } public String getDescription(String t, String a, String v) { CoverageCharVdt civ = new CoverageCharVdt(t, a, v); return charvdtrec.get(civ); } private String getDescription(List<Object> id, FeatureClassInfo fti[], MutableInt ret) { if ((fti == null) || (fti.length == 0)) { return null; } StringBuffer foo = null; for (int i = 0; i < fti.length; i++) { String desc = fti[i].getDescription(id, ret); if (desc != null) { if (foo == null) { foo = new StringBuffer(desc); } else { foo.append(";; ").append(desc); } } } return ((foo == null) ? null : foo.toString()); } public String getLineDescription(List<Object> lineid, MutableInt retval) { return getDescription(lineid, lineinfo, retval); } public String getTextDescription(List<Object> textid, MutableInt retval) { return getDescription(textid, textinfo, retval); } public String getEPointDescription(List<Object> pointid, MutableInt retval) { return getDescription(pointid, epointinfo, retval); } public String getCPointDescription(List<Object> pointid, MutableInt retval) { return getDescription(pointid, cpointinfo, retval); } public String getAreaDescription(List<Object> areaid, MutableInt retval) { return getDescription(areaid, areainfo, retval); } public void setCoverateAttributeTable(CoverageAttributeTable cat) { this.cat = cat; } public CoverageAttributeTable getCoverageAttributeTable() { return cat; } /** * Given a tile directory, go through the entries in the edg/fac/txt files, * and send those entries to the warehouse. The warehouse will check their * feature names with the feature names given to it in its properties, and * eliminate the ones that it shouldn't draw. */ public void drawTile(TileDirectory drawtd, VPFGraphicWarehouse warehouse, LatLonPoint ll1, LatLonPoint ll2, float dpplat, float dpplon) { if (Debug.debugging("vpf.tile")) { Debug.output("Drawtile for " + drawtd); } TableHolder tables = new TableHolder(this); tables.drawTile(drawtd, warehouse, ll1, ll2, dpplat, dpplon); } /** * This function uses the warehouse to get a list of features, and then * looks in the featureList to see what feature tables handle those * features. Using the appropriate feature table, the function then tracks * down the tile that contains that feature, and the feature index into that * tile file, and then contacts the warehouse to get that feature created * into a graphic. */ public boolean drawFeatures(VPFFeatureWarehouse warehouse, LatLonPoint ll1, LatLonPoint ll2, float dpplat, float dpplon) { boolean didSomething = false; TableHolder tables = new TableHolder(this); // First, find the list of features, and iterate through // them. Use each one to go through it's feature table, if it // exists. Then, from the feature table (perhaps check the // FACC code, too), get the tile number and feature ID. // Access the CoverageAttributeTable to get the tile (using a // table from above), and check if it is within bounds. If it // is, seek to get the feature. Once the feature has been // read, contact the warehouse to use the row to build a // graphic. Hold on to the tile in case (and it's likely) // that the next feature desired is in the same tile). If it // isn't, then dump the tile. // Should sort the feature types by area, text and then lines, // to do them in that order. List<String> featureList = warehouse.getFeatures(); for (String currentFeature : featureList) { // Figure out if the feature should be rendered, depending // on what the warehouse settings are (drawedges, // drawareas, drawtext). char featureType = whatFeatureType(warehouse, currentFeature); if (featureType == SKIP_FEATURETYPE) { // Blow off this feature type. continue; } if (Debug.debugging("vpf")) { Debug.output("CoverageTable getting " + currentFeature + " features"); } // Get the feature class for this feature type. FeatureClassInfo fci = getFeatureClassInfo(currentFeature); int fciFeatureIDCol = fci.whatColumn(DcwRecordFile.ID_COLUMN_NAME); // Don't know why currentFeature would be null, but reports from // users demonstrate that it might be. if ((fci == null) || (cat == null) || currentFeature == null) { continue; // don't have enough info to proceed // in an untiled coverage, we could probably work // without the cat } // This looks like where most features get processed. if (drawFeaturesFromThematicIndex(fci, warehouse, tables, ll1, ll2, dpplat, dpplon, currentFeature, featureType)) { didSomething = true; continue; } // couldn't use the tile_id thematic index, so just parse // the whole file. This looks like the lessor case, trying to do // something if the thematic index didn't work. boolean needToFindOurselves = true; TilingAdapter fciTilingAdapter = fci.getTilingAdapter(); if (fciTilingAdapter == null) { // no way to find primitives continue; } TileDirectory currentTile = null; // There are going to be a variable number of columns. // We're interested in the f_code, tile_id, and the // primitive id (fci independent depending on type). int oldTileID = -2; // -1 is "untiled" tile_id // int faccIndex = fci.getFaccIndex() // OK, now we are looking in the Feature class file. try { int getrow = 1; for (List<Object> fcirow = new ArrayList<Object>(); fci.getRow(fcirow, getrow++);) { if (Debug.debugging("vpfdetail")) { Debug.output("CoverageTable new feature " + fcirow); } int tileID = fciTilingAdapter.getTileId(fcirow); // String facc = (String)fcirow.get(faccIndex); // With tileID, find the tile and figure out if it // is needed. if (tileID != oldTileID) { tables.close(); if (Debug.debugging("vpf.tile")) { Debug.output("CoverageTable.drawFeatures(): opening new tile (" + tileID + ")"); } currentTile = (tileID == -1) ? new TileDirectory() : cat.getTileWithID(tileID); if (currentTile == null) { Debug.error("VPFLayer|CoverageTable.drawFeatures: null tile from bogus ID (" + tileID + ") from " + fci.filename + ", skipping..."); continue; } if ((tileID == -1) || currentTile.inRegion(ll1.getLatitude(), ll2.getLatitude(), ll2.getLongitude(), ll1.getLongitude())) { // We should only be in here once, I // think, if the tile IDs are all stored // in order. If the tile IDs are laid out // in the FCI in jumbled order, only the // first group of features will be // gathered because the tile will appear // cached. String libraryname = ""; if (cat != null) { libraryname = cat.libraryname; } if (!warehouse.needToFetchTileContents(libraryname, currentFeature, currentTile)) { if (Debug.debugging("vpf")) { Debug.output("CoverageTable: Loaded Cached List for " + currentFeature + " and " + currentTile.getPath()); } continue; } if (Debug.debugging("vpf.tile")) { Debug.output("CoverageTable: Drawing " + featureType + " features for " + currentTile); } tables.setTables(featureType, currentTile); // Only need to do this once for a new // fci... if (needToFindOurselves) { needToFindOurselves = false; tables.findYourself(fci); } } else { tables.close(); } oldTileID = tileID; } // If currentTile == null, then the tile // wasn't found, or it is outside the area of // interest. The tables in the TableHolder // (tables) will all be null, and the tables // drawFeature will return false... int primitiveID = fciTilingAdapter.getPrimId(fcirow); int featureID = ((Number) fcirow.get(fciFeatureIDCol)).intValue(); if (tables.drawFeature(primitiveID, warehouse, ll1, ll2, dpplat, dpplon, currentFeature, featureID) != null) { didSomething = true; } } } catch (FormatException f) { if (Debug.debugging("vpf.FormatException")) { Debug.output("CoverageTable: Creating table: " + f.getClass() + " " + f.getMessage()); } } fci.close(); } tables.close(); return didSomething; } /** * This function gets the thematic index from the FeatureClassInfo object, * and uses it to look up the tiles that contain the currentFeature. Then, * that tile is checked to see if it is on the map. If it is, then that row * in the thematic index is read to get the feature id numbers. The feature * table is referenced for the feature ID number in the tile, and then the * feature is drawn. * * @param fci the FeatureClassInfo (feature table) * @param warehouse the VPFFeatureGraphicWarehouse to use to draw the * graphics. * @param ll1 the upper left corner of the map. * @param ll2 the lower right corner of the map. * @param dpplat degrees per pixel latitude direction. * @param dpplon degrees per pixel longitude direction. * @param currentFeature the feature string (roadl) * @param featureType the CoverageTable letter representation of the feature * type. */ protected boolean drawFeaturesFromThematicIndex(FeatureClassInfo fci, VPFFeatureWarehouse warehouse, TableHolder tables, LatLonPoint ll1, LatLonPoint ll2, float dpplat, float dpplon, String currentFeature, char featureType) { if (!fci.initThematicIndex(tablepath)) { return false; } List<Object> v = new ArrayList<Object>(); // hold fci row contents try { int primitiveIdColIndex = fci.getTilePrimitiveIdColIndex(); DcwThematicIndex thematicIndex = fci.getThematicIndex(); Object[] indexes = thematicIndex.getValueIndexes(); fci.reopen(1); // We just know that these values are tile IDs. for (int i = 0; i < indexes.length; i++) { int tileID = VPFUtil.objectToInt(indexes[i]); TileDirectory currentTile = cat.getTileWithID(tileID); if (currentTile == null) { Debug.error("VPFLayer|CoverageTable.drawFeatures: null tile from bogus ID (" + tileID + ") from " + fci.filename + ", skipping..."); continue; } if (currentTile.inRegion(ll1.getLatitude(), ll2.getLatitude(), ll2.getLongitude(), ll1.getLongitude())) { tables.setTables(featureType, currentTile); tables.findYourself(fci); int[] featureID = thematicIndex.get(indexes[i]); if (Debug.debugging("vpf.tile")) { Debug.output("Drawing " + featureID.length + " " + featureType + " features for " + tileID + " " + currentTile); } String libraryname = cat.libraryname; if (!warehouse.needToFetchTileContents(libraryname, currentFeature, currentTile)) { if (Debug.debugging("vpf")) { Debug.output("Loaded Cached List for " + currentFeature + " and " + currentTile.getPath()); } continue; } for (int j = 0; j < featureID.length; j++) { // featurePrimID is the unique ID of this feature. The // primitiveID looks like it's the fac_id of the feature // in the tile. int featurePrimID = featureID[j]; if (!fci.getRow(v, featurePrimID)) { // couldn't get row for some reason continue; } int primitiveID = VPFUtil.objectToInt(v.get(primitiveIdColIndex)); // for attribute retrieval, we need the featurePrimID. tables.drawFeature(primitiveID, warehouse, ll1, ll2, dpplat, dpplon, currentFeature, featurePrimID); } tables.close(); } else { if (Debug.debugging("vpf.tile")) { Debug.output("Skipping " + featureType + " features for " + tileID + ", not on map"); } } } return true; } catch (FormatException f) { if (Debug.debugging("vpf.FormatException")) { Debug.output("CoverageTable.DFFTI: Format Exception creating features: " + f.getClass() + " " + f.getMessage()); } return false; } finally { fci.close(); } } /** * This is a method specifically designed for the * VPFAutoFeatureGraphicWarehouse, and the CoverageTable knows to check with * the warehouse and use the PriorityHolders to fetch features. * * @param warehouse VPFAutoFeatureGraphicWarehouse * @param ll1 upper left of coverage area * @param ll2 lower right of coverage area * @param dpplat degrees/pixel vertically * @param dpplon degrees/pixel horizontally * @param omgList The OMGraphicList to add OMGraphics, representing * features. * @throws FormatException if something goes wrong reading files, this * exception will be thrown. */ public void getFeatures(VPFAutoFeatureGraphicWarehouse warehouse, LatLonPoint ll1, LatLonPoint ll2, double dpplat, double dpplon, OMGraphicList omgList) throws FormatException { // The map of feature names versus their table information Map<String, FeatureClassInfo> featureInfo = getFeatureClasses(); TableHolder tables = new TableHolder(this); // Loop through the features, one by one. for (Entry<String, FeatureClassInfo> entry : featureInfo.entrySet()) { String featureName = entry.getKey(); FeatureClassInfo fci = entry.getValue(); if (fci == null) { continue; } int fciFeatureIDCol = fci.whatColumn(DcwRecordFile.ID_COLUMN_NAME); char featureType = whatFeatureType(warehouse, featureName); fci.checkInit(); if (logger.isLoggable(Level.FINE)) { logger.fine(" for " + featureName + ": " + fci.getDescription()); } /** * The features are made up of various FACC codes, that more * precisely categorize what each feature is. We're going to look at * each entry of this particular feature type, and use the * PriorityHolders from the warehouse to determine when it gets * drawn. */ int faccIndex = fci.getFaccIndex(); if (faccIndex < 0) { continue; } int primitiveIDIndex = fci.getTilePrimitiveIdColIndex(); int tileIDIndex = fci.getTileIdIndex(); TileDirectory currentTile = null; boolean needToFindOurselves = true; TilingAdapter fciTilingAdapter = fci.getTilingAdapter(); if (fciTilingAdapter == null) { // no way to find primitives continue; } // There are going to be a variable number of columns. // We're interested in the f_code, tile_id, and the // primitive id (fci independent depending on type). int oldTileID = -2; // -1 is "untiled" tile_id int getrow = 1; /** * fcirow holds all the information for a particular feature. The * fci lets you know what each column represents. */ for (List<Object> fcirow = new ArrayList<Object>(); fci.getRow(fcirow, getrow++);) { String facc = (String) fcirow.get(faccIndex); /** * Get the list of FeaturePriorityHolders that correspond to * this particlar facc. This list is just an organizational tool * so we don't have to run through all of the FPHs. */ List<FeaturePriorityHolder> list = warehouse.faccLookup.get(facc); if (list != null) { boolean foundMatch = false; for (FeaturePriorityHolder ph : list) { /** * Checking to see if the attributes for a particular * feature match this particular FPH. */ if (ph.matches(facc, fci, fcirow)) { foundMatch = true; if (logger.isLoggable(Level.FINE)) { logger.fine("+++ MATCH FOUND for " + facc + " tileid:" + fcirow.get(tileIDIndex) + ", primID:" + fcirow.get(primitiveIDIndex)); } // Now we have the PriorityHolder that should // receive the OMGraphic created for this feature. // Use the CoverageTable to create it via the // warehouse, if (logger.isLoggable(Level.FINER)) { logger.finer("CoverageTable new feature " + fcirow); } int tileID = fciTilingAdapter.getTileId(fcirow); // With tileID, find the tile and figure out if it // is needed. if (tileID != oldTileID) { tables.close(); if (logger.isLoggable(Level.FINER)) { logger.finer("opening new tile (" + tileID + ")"); } currentTile = (tileID == -1) ? new TileDirectory() : cat.getTileWithID(tileID); if (currentTile == null) { if (logger.isLoggable(Level.FINE)) { logger.warning("VPFLayer|CoverageTable.drawFeatures: null tile from bogus ID (" + tileID + ") from " + fci.filename + ", skipping..."); } continue; } if ((tileID == -1) || currentTile.inRegion(ll1.getLatitude(), ll2.getLatitude(), ll2.getLongitude(), ll1.getLongitude())) { // We should only be in here once, I // think, if the tile IDs are all stored // in order. If the tile IDs are laid out // in the FCI in jumbled order, only the // first group of features will be // gathered because the tile will appear // cached. String libraryname = ""; if (cat != null) { libraryname = cat.libraryname; } if (!warehouse.needToFetchTileContents(libraryname, featureName, currentTile)) { if (Debug.debugging("vpf")) { Debug.output("CoverageTable: Loaded Cached List for " + featureName + " and " + currentTile.getPath()); } continue; } if (Debug.debugging("vpf.tile")) { Debug.output("CoverageTable: Drawing " + featureType + " features for " + currentTile); } try { tables.setTables(featureType, currentTile); } catch (FormatException fe) { System.out.println("missing tile, let's ignore that"); continue; } // Only need to do this once for a new // fci... if (needToFindOurselves) { needToFindOurselves = false; tables.findYourself(fci); } } else { tables.close(); } oldTileID = tileID; } // If currentTile == null, then the tile // wasn't found, or it is outside the area of // interest. The tables in the TableHolder // (tables) will all be null, and the tables // drawFeature will return false... int primitiveID = fciTilingAdapter.getPrimId(fcirow); int featureID = ((Number) fcirow.get(fciFeatureIDCol)).intValue(); OMGraphic omg = tables.drawFeature(primitiveID, warehouse, ll1, ll2, dpplat, dpplon, featureName, featureID); if (omg != null) { warehouse.handleInformationForOMGraphic(omg, fci, fcirow); ph.add(omg); if (logger.isLoggable(Level.FINEST)) { StringBuffer pout = new StringBuffer(); for (Object obj : fcirow) { pout.append(obj).append(','); } logger.finest(pout.toString()); } } } else { // NOTE, this else statement is just for checking // buoys. // if (ph.facc.equals(facc)) { // logger.info("something is getting blown off"); // ph.matches(facc, fci, fcirow); // } } // And add it to the ph omgraphic list for // retrieval a little bit later. } if (!foundMatch) { if (logger.isLoggable(Level.FINE)) { logger.fine("--- NO MATCH FOUND for " + facc + ", type:" + featureType + ", tileid:" + fcirow.get(tileIDIndex) + ", primID:" + fcirow.get(primitiveIDIndex)); } } } else if (warehouse.debugFacc == null) { if (logger.isLoggable(Level.FINE)) { logger.info("didn't find facc list for: " + facc); } } } fci.close(); } tables.close(); } protected OMGraphic getOMGraphicForFeature() { OMGraphic omg = null; return omg; } /** * Given a feature type name, figure out if the warehouse thinks it should * *NOT* be drawn. * * @param warehouse the warehouse to build the graphics. * @param featureName the VPF name of the feature (polbndl, for example). * @return SKIP_FEATURETYPE if the feature should not be drawn. */ protected char whatFeatureType(VPFWarehouse warehouse, String featureName) { // Test for the feature kind (edge, area, text, points) and // don't continue if that type is not needed. char featureType = SKIP_FEATURETYPE; // Get the feature class for this feature type. FeatureClassInfo fci = getFeatureClassInfo(featureName); if (fci == null) { return featureType; } char type = fci.getFeatureType(); if ((type == AREA_FEATURETYPE && warehouse.drawAreaFeatures()) || (type == TEXT_FEATURETYPE && warehouse.drawTextFeatures()) || (type == EDGE_FEATURETYPE && warehouse.drawEdgeFeatures()) || (type == EPOINT_FEATURETYPE && warehouse.drawEPointFeatures()) || (type == CPOINT_FEATURETYPE && warehouse.drawCPointFeatures())) { featureType = type; } return featureType; } /** * Feature Type Information read from VPF fca files. */ public static class FeatureClassRec { /** the name of the feature class */ final public String feature_class; /** the type of the feature */ final public char type; /** a short text description */ final public String description; /** * Construct an instance of the class * * @param fclass the feature class name * @param type the feature type * @param desc the feature description */ public FeatureClassRec(String fclass, char type, String desc) { feature_class = fclass; this.type = type; description = desc; } } /** * Returns a map from feature name to FeatureClassRec */ public Hashtable<String, FeatureClassRec> getFeatureTypeInfo() { if (featureTypeInfo == null) { featureTypeInfo = new Hashtable<String, FeatureClassRec>(); String path = getDataPath(); boolean addSlash = true; // if (path.endsWith(File.separator)) { if (path.endsWith("/") || path.endsWith(File.separator)) { addSlash = false; } String fca = path + (addSlash ? "/" : "") + "fca"; if (!BinaryFile.exists(fca)) { fca = fca + "."; } if (BinaryFile.exists(fca)) { try { DcwRecordFile fcadesc = new DcwRecordFile(fca); int fclass = fcadesc.whatColumn("fclass"); int type = fcadesc.whatColumn("type"); int descr = fcadesc.whatColumn("descr"); for (ArrayList<Object> al = new ArrayList<Object>(fcadesc.getColumnCount()); fcadesc.parseRow(al);) { String fname = ((String) al.get(fclass)).toLowerCase().intern(); char ftype = ((String) al.get(type)).charAt(0); String fdesc = (String) al.get(descr); FeatureClassRec fcr = new FeatureClassRec(fname, ftype, fdesc); featureTypeInfo.put(fname, fcr); } fcadesc.close(); } catch (FormatException fe) { // nevermind, skip it } } } return featureTypeInfo; } public static void main(String[] args) { if (args.length != 5) { System.out.println("This main() is just assorted test code."); System.out.println("Usage: java classname librarypath coveragename"); System.out.println(" tablename attribute value"); System.out.println("Result: Prints the corresponding value in int.vdt"); } else { CoverageTable ct = new CoverageTable(args[0], args[1]); String desc = ct.getDescription(args[2], args[3], Integer.parseInt(args[4])); System.out.println(desc); } } } /** * The TableHolder is a utility class that manages the EdgeTable, TextTable and * AreaTable that are needed by the CoverageTable to use the warehouse to create * graphics. */ class TableHolder { EdgeTable edg = null; TextTable tft = null; AreaTable aft = null; NodeTable ent = null; NodeTable cnt = null; /** Used as a preallocated list to read feature tables. */ List<Object> primitiveVector = new ArrayList<Object>(); CoverageTable coverageTable; /** * Construct the TableHandler with the CoverageTable it is helping. */ protected TableHolder(CoverageTable ct) { coverageTable = ct; } /** * When drawing features (CoverageTable.drawFeatures()), sets up the * TableHolder tables so that the right types are used. * * @param featureType from the CoverageTable, either AREA_FEATURETYPE, * EDGE_FEATURETYPE or TEXT_FEATURETYPE. * @param tile the tile directory that needs to be used when fetching * graphics from the appropriate files. */ protected void setTables(char featureType, TileDirectory tile) throws FormatException { if (featureType == CoverageTable.EDGE_FEATURETYPE) { edg = new EdgeTable(coverageTable, tile); } if (featureType == CoverageTable.TEXT_FEATURETYPE) { tft = new TextTable(coverageTable, tile); } if (featureType == CoverageTable.AREA_FEATURETYPE) { aft = new AreaTable(coverageTable, null, tile); edg = null; } if (featureType == CoverageTable.EPOINT_FEATURETYPE) { ent = new NodeTable(coverageTable, tile, true); } if (featureType == CoverageTable.CPOINT_FEATURETYPE) { cnt = new NodeTable(coverageTable, tile, false); } if (CoverageTable.logger.isLoggable(Level.FINE)) { int activeTableCount = 0; if (edg != null) activeTableCount++; if (tft != null) activeTableCount++; if (aft != null) activeTableCount++; if (ent != null) activeTableCount++; if (cnt != null) activeTableCount++; if (activeTableCount > 1) { CoverageTable.logger.warning("TableHolder has more than one feature type"); } } } /** * Should be called once per FeatureClassInfo, after the tables have been * set. Lets the tables figure out which columns to use as an index. */ protected void findYourself(FeatureClassInfo fci) { if (aft != null) { fci.findYourself(aft); } else if (tft != null) { fci.findYourself(tft); } else if (edg != null) { fci.findYourself(edg); } else if (ent != null) { fci.findYourself(ent); } } /** * Should be called once per feature, after the tables have been set * (setTables()), and findYourself() has been called. The appropriate table * will use the warehouse to create proper OMGraphic. */ protected OMGraphic drawFeature(int primitiveID, VPFFeatureWarehouse warehouse, LatLonPoint ll1, LatLonPoint ll2, double dpplat, double dpplon, String currentFeature, int featurePrimID) throws FormatException { if (aft != null || tft != null || edg != null || ent != null || cnt != null) { // OK, now check to see what table is being // used. if the tile is being reused, the // table will be reused. if ((aft != null) && aft.getRow(primitiveVector, primitiveID)) { return aft.drawFeature(warehouse, dpplat, dpplon, ll1, ll2, primitiveVector, currentFeature, featurePrimID); } if ((tft != null) && tft.getRow(primitiveVector, primitiveID)) { return tft.drawFeature(warehouse, dpplat, dpplon, ll1, ll2, primitiveVector, currentFeature, featurePrimID); } if ((ent != null) && ent.getRow(primitiveVector, primitiveID)) { return ent.drawFeature(warehouse, dpplat, dpplon, ll1, ll2, primitiveVector, currentFeature, featurePrimID); } if ((cnt != null) && cnt.getRow(primitiveVector, primitiveID)) { return cnt.drawFeature(warehouse, dpplat, dpplon, ll1, ll2, primitiveVector, currentFeature, featurePrimID); } if ((edg != null) && edg.getRow(primitiveVector, primitiveID)) { return edg.drawFeature(warehouse, dpplat, dpplon, ll1, ll2, primitiveVector, currentFeature, featurePrimID); } } return null; } /** * Only call once per tile. It will parse all the needed data in the tile. * Does not require setTables() or findYourself(). */ protected void drawTile(TileDirectory tile, VPFGraphicWarehouse warehouse, LatLonPoint ll1, LatLonPoint ll2, float dpplat, float dpplon) { boolean drawedge = warehouse.drawEdgeFeatures(); boolean drawtext = warehouse.drawTextFeatures(); boolean drawarea = warehouse.drawAreaFeatures(); boolean drawepoint = warehouse.drawEPointFeatures(); boolean drawcpoint = warehouse.drawCPointFeatures(); close(); try { if (drawedge || drawarea) { edg = new EdgeTable(coverageTable, tile); } } catch (FormatException f) { if (Debug.debugging("vpf.FormatException")) { Debug.output("EdgeTable: " + f.getClass() + " " + f.getMessage()); } } try { if (drawtext) { tft = new TextTable(coverageTable, tile); } } catch (FormatException f) { if (Debug.debugging("vpf.FormatException")) { Debug.output("TextTable: " + f.getClass() + " " + f.getMessage()); } } try { if (drawepoint) { ent = new NodeTable(coverageTable, tile, true); } } catch (FormatException f) { if (Debug.debugging("vpf.FormatException")) { Debug.output("NodeTable: " + f.getClass() + " " + f.getMessage()); } } try { if (drawcpoint) { cnt = new NodeTable(coverageTable, tile, false); } } catch (FormatException f) { if (Debug.debugging("vpf.FormatException")) { Debug.output("NodeTable: " + f.getClass() + " " + f.getMessage()); } } try { if (drawarea && (edg != null)) { aft = new AreaTable(coverageTable, edg, tile); } } catch (FormatException f) { if (Debug.debugging("vpf.FormatException")) { Debug.output("AreaTable: " + f.getClass() + " " + f.getMessage()); } } if ((aft != null) && drawarea) { for (int i = 0; i < coverageTable.areainfo.length; i++) { coverageTable.areainfo[i].findYourself(aft); } aft.drawTile(warehouse, dpplat, dpplon, ll1, ll2); } if ((tft != null) && drawtext) { for (int i = 0; i < coverageTable.textinfo.length; i++) { coverageTable.textinfo[i].findYourself(tft); } tft.drawTile(warehouse, dpplat, dpplon, ll1, ll2); } if ((edg != null) && drawedge) { for (int i = 0; i < coverageTable.lineinfo.length; i++) { coverageTable.lineinfo[i].findYourself(edg); } edg.drawTile(warehouse, dpplat, dpplon, ll1, ll2); } if ((ent != null) && drawepoint) { for (int i = 0; i < coverageTable.epointinfo.length; i++) { coverageTable.epointinfo[i].findYourself(ent); } ent.drawTile(warehouse, dpplat, dpplon, ll1, ll2); } if ((cnt != null) && drawcpoint) { for (int i = 0; i < coverageTable.cpointinfo.length; i++) { coverageTable.cpointinfo[i].findYourself(cnt); } cnt.drawTile(warehouse, dpplat, dpplon, ll1, ll2); } // if (Debug.On && Debug.debugging("vpf.tile")) // Debug.output(drawtd.toString() + " " + edgecount[0] + // " polys with " + edgecount[1] + // " points (cumulative)\n" + // drawtd.toString() + " " + textcount[0] + // " texts with " + textcount[1] + // " points (cumulative)\n" + // drawtd.toString() + " " + areacount[0] + // " areas with " + areacount[1] + // " points (cumulative)"); close(); } /** * Close any of these tables that may be in use. */ protected void close() { if (Debug.debugging("vpf.tile")) { Debug.output("CoverageTable closing tile tables"); } if (edg != null) { edg.close(); } if (tft != null) { tft.close(); } if (aft != null) { aft.close(); } if (ent != null) { ent.close(); } if (cnt != null) { cnt.close(); } aft = null; tft = null; edg = null; ent = null; cnt = null; } } /** * A utility class used to map information from a VPF feature table to its * associated value in an int.vdt file. */ class CoverageIntVdt { /** the name of the table we are looking up (table is interned) */ final String table; /** * the name of the attribute we are looking up (attribute is interned) */ final String attribute; /** the integer value we are looking up */ final int value; /** * Construct a new object * * @param t value for the table member * @param a the value for the attribute member * @param v the value for the value member */ public CoverageIntVdt(String t, String a, int v) { table = t.toLowerCase().intern(); attribute = a.toLowerCase().intern(); value = v; } /** * Override the equals method. Two CoverageIntVdts are equal if and only iff * their respective table, attribute and value members are equal. */ public boolean equals(Object o) { if (o == null) { return false; } if (getClass() != o.getClass()) { return false; } final CoverageIntVdt civ = (CoverageIntVdt) o; // we can use == rather than String.equals(String) since // table and attribute are interned. return ((table == civ.table) && (attribute == civ.attribute) && (value == civ.value)); } /** * Override hashcode. Compute a hashcode based on our member values, rather * than our (base class) object identity. */ public int hashCode() { return ((table.hashCode() ^ attribute.hashCode()) ^ value); } } /** * A utility class used to map information from a VPF feature table to its * associated value in an char.vdt file. */ class CoverageCharVdt { /** the name of the table we are looking up (table is interned) */ final String table; /** * the name of the attribute we are looking up (attribute is interned) */ final String attribute; /** the character value we are looking up (value is interned) */ final String value; /** * Construct a new object * * @param t value for the table member * @param a the value for the attribute member * @param v the value for the value member */ public CoverageCharVdt(String t, String a, String v) { table = t.toLowerCase().intern(); attribute = a.toLowerCase().intern(); value = v.intern(); } /** * Override the equals method. Two CoverageIntVdts are equal if and only iff * their respective table, attribute and value members are equal. */ public boolean equals(Object o) { if (o == null) { return false; } if (getClass() != o.getClass()) { return false; } final CoverageCharVdt civ = (CoverageCharVdt) o; // we can use == rather than String.equals(String) since // table, attribute, and value are interned. return ((table == civ.table) && (attribute == civ.attribute) && (value == civ.value)); } /** * Override hashcode. Compute a hashcode based on our member values, rather * than our (base class) object identity. */ public int hashCode() { return ((table.hashCode() ^ attribute.hashCode()) ^ value.hashCode()); } }