// ********************************************************************** // // <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/DTEDCoverageManager.java,v $ // $RCSfile: DTEDCoverageManager.java,v $ // $Revision: 1.6 $ // $Date: 2005/12/09 21:09:05 $ // $Author: dietrick $ // // ********************************************************************** package com.bbn.openmap.layer.dted; /* Java Core */ import java.awt.Component; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import com.bbn.openmap.Environment; import com.bbn.openmap.I18n; import com.bbn.openmap.PropertyConsumer; import com.bbn.openmap.io.BinaryFile; import com.bbn.openmap.layer.OMGraphicHandlerLayer; import com.bbn.openmap.omGraphics.DrawingAttributes; import com.bbn.openmap.omGraphics.OMGraphic; import com.bbn.openmap.omGraphics.OMGraphicList; import com.bbn.openmap.omGraphics.OMRect; import com.bbn.openmap.proj.Projection; import com.bbn.openmap.util.PropUtils; import com.bbn.openmap.util.wanderer.Wanderer; import com.bbn.openmap.util.wanderer.WandererCallback; /** * A DTEDCoverageManager knows how to look at DTED data and figure out what * coverage is available. */ public class DTEDCoverageManager extends OMGraphicList implements PropertyConsumer { protected I18n i18n = Environment.getI18n(); public static Logger logger = Logger.getLogger("com.bbn.openmap.layer.dted.DTEDCoverageManager"); protected String[] paths; /** The default line color for level 0. */ public final static String DEFAULT_LEVEL0_COLOR_STRING = "CE4F3F"; // redish /** The default line color for level 1. */ public final static String DEFAULT_LEVEL1_COLOR_STRING = "339159"; // greenish /** The default line color for level 2. */ public final static String DEFAULT_LEVEL2_COLOR_STRING = "0C75D3"; // bluish public final static String COVERAGE_FILE_PROPERTY = "coverageFile"; /** Coverage for level 0, 1, 2 frames */ protected boolean[][][] coverage = null; /** * CoverageDataFile object handling pre-cached coverage */ protected CoverageDataFile coverageFile = null; protected DrawingAttributes[] attributes = null; protected OMGraphicList[] levelRects = null; public DTEDCoverageManager(String[] paths) { this.paths = paths; attributes = new DrawingAttributes[3]; attributes[0] = DrawingAttributes.getDefaultClone(); attributes[0].setLinePaint(PropUtils.parseColor(DEFAULT_LEVEL0_COLOR_STRING)); attributes[1] = DrawingAttributes.getDefaultClone(); attributes[1].setLinePaint(PropUtils.parseColor(DEFAULT_LEVEL1_COLOR_STRING)); attributes[2] = DrawingAttributes.getDefaultClone(); attributes[2].setLinePaint(PropUtils.parseColor(DEFAULT_LEVEL2_COLOR_STRING)); levelRects = new OMGraphicList[3]; levelRects[0] = new OMGraphicList(); levelRects[1] = new OMGraphicList(); levelRects[2] = new OMGraphicList(); } public void reset() { coverage = null; clear(); } /** * The method that cycles through all the paths, looking for the frames. * This takes time, so it's only done when a coverage file can't be found. * * @param paths paths to the level 0, 1 and 2 dted root directory. */ public boolean[][][] checkOutCoverage(String[] paths) { if (paths == null || paths.length == 0) { logger.warning("No paths for DTED data given."); return null; } logger.fine("checking out DTED at paths:"); for (int d1 = 0; d1 < paths.length; d1++) { if (logger.isLoggable(Level.FINE)) { logger.fine(" " + paths[d1]); } if (!BinaryFile.exists(paths[d1])) { paths[d1] = null; logger.fine(" - path invalid, ignoring."); } } CoverageWandererCallback callback = new CoverageWandererCallback(); Wanderer wanderer = new Wanderer(callback); for (int pathNum = 0; pathNum < paths.length; pathNum++) { wanderer.handleEntry(new File(paths[pathNum])); } return callback.getCoverage(); } /** * Method organizes the query based on the projection, and returns the * applicable rectangles representing the frame coverage. If the coverage * spans over the date line, then two queries are performed, one for each * side of the date line. * * @param proj the projection of the screen * @return an array of lists, one for each level of dted data. */ public OMGraphicList getCoverageRects(Projection proj) { if (coverage == null) { if (coverageFile != null) { coverage = coverageFile.readCoverage(); } if (coverage == null) { logger.fine("Scanning for frames - This could take several minutes!"); coverage = checkOutCoverage(paths); if (coverageFile != null) { coverageFile.writeFile(coverage); } } } if (isEmpty()) { getCoverageRects(-180, -90, 179, 89, OMGraphic.LINETYPE_RHUMB, proj); } else { generate(proj); } return this; } /** * Get a percentage value of how much of the map is covered for a * projection. * * @param proj * @return float[] with percentages, float[0] is level 0 coverage, 1 is * level 1, 2 is level 2. */ public float[] getCoverage(Projection proj) { float[] ret = new float[3]; if (coverage != null) { Point pnt1 = new Point(); Point pnt2 = new Point(); int height = proj.getHeight(); int width = proj.getWidth(); // Number frames possible on map int total = 0; for (int x = -180; x < 180; x++) { for (int y = -90; y < 89; y++) { proj.forward((float) y, (float) x, pnt1); proj.forward((float) (y + 1), (float) (x + 1), pnt2); double x1 = pnt1.getX(); double y1 = pnt1.getY(); double x2 = pnt2.getX(); double y2 = pnt2.getY(); boolean someX = (x1 >= 0 && x1 <= width) || (x2 >= 0 && x2 <= width); boolean someY = (y1 >= 0 && y1 <= height) || (y2 >= 0 && y2 <= height); boolean onMap = someX && someY; if (onMap) { int xIndex = x + 180; int yIndex = y + 90; total++; if (coverage[0][yIndex][xIndex]) ret[0] += 1f; if (coverage[1][yIndex][xIndex]) ret[1] += 1f; if (coverage[2][yIndex][xIndex]) ret[2] += 1f; } } } logger.info("Total frames: " + total + " " + ret[0] + ", " + ret[1] + ", " + ret[2]); ret[0] = ret[0] / total * 100f; ret[1] = ret[1] / total * 100f; ret[2] = ret[2] / total * 100f; } return ret; } /** * Method looks at the coverage arrays, and returns the applicable * rectangles representing the frame coverages. * * @param startx the western-most longitude. * @param starty the southern-most latitude. * @param endx the eastern-most longitude. * @param endy the northern-most latitude. * @param lineType the type of line to use on the rectangles - Cylindrical * projections can use straight lines, but other projections should * use Rhumb lines. * @return an array of lists, one for each level of dted data. */ public OMGraphicList getCoverageRects(int startx, int starty, int endx, int endy, int lineType, Projection proj) { clear(); OMRect rect; for (int level = 0; level < 3; level++) { OMGraphicList rectangles = levelRects[level]; rectangles.clear(); rectangles.setVague(true); for (int lat = starty; lat <= endy && lat < 90; lat++) { for (int lon = startx; lon <= endx && lon < 180; lon++) { if (coverage[level][lat + 90][lon + 180]) { double offset = level * .1; double up = lat + offset; double left = lon + offset; double down = lat + 1.0 - offset; double right = lon + 1.0 - offset; rect = new OMRect(up, left, down, right, lineType); attributes[level].setTo(rect); rect.generate(proj); rectangles.add(rect); } } } add(rectangles); } return this; } ////// PropertyConsumer methods protected String prefix; public Properties getProperties(Properties getList) { if (getList == null) { getList = new Properties(); } for (DrawingAttributes atts : attributes) { atts.getProperties(getList); } String prefix = PropUtils.getScopedPropertyPrefix(this); if (coverageFile != null) { getList.put(prefix + COVERAGE_FILE_PROPERTY, coverageFile.getAbsolutePath()); } if (paths != null) { StringBuilder sBuilder = new StringBuilder(); for (String path : paths) { if (sBuilder.length() != 0) { sBuilder.append(";"); } sBuilder.append(path); } getList.put(prefix + DTEDLayer.DTEDPathsProperty, sBuilder.toString()); } return getList; } public Properties getPropertyInfo(Properties list) { return list; } public String getPropertyPrefix() { return prefix; } public void setProperties(Properties setList) { setProperties(null, setList); } public void setProperties(String prefix, Properties setList) { setPropertyPrefix(prefix); prefix = PropUtils.getScopedPropertyPrefix(prefix); attributes[0].setProperties(prefix + "0", setList); attributes[1].setProperties(prefix + "1", setList); attributes[2].setProperties(prefix + "2", setList); String coverageFileString = setList.getProperty(prefix + COVERAGE_FILE_PROPERTY); if (coverageFileString != null) { coverageFile = new CoverageDataFile(coverageFileString); } } public void setPropertyPrefix(String prefix) { this.prefix = prefix; } ////// end of PropertyConsumer methods /** * @return the coverageFile */ public CoverageDataFile getCoverageFile() { return coverageFile; } /** * @param coverageFile the coverageFile to set */ public void setCoverageFile(CoverageDataFile coverageFile) { this.coverageFile = coverageFile; } protected JPanel panel; public Component getGUI(final OMGraphicHandlerLayer layer) { if (panel == null) { panel = new JPanel(); GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); c.gridx = 0; panel.setLayout(gridbag); ActionListener aListener = new ActionListener() { public void actionPerformed(ActionEvent ae) { int level = Integer.parseInt(ae.getActionCommand()); levelRects[level].setVisible(((JCheckBox) ae.getSource()).isSelected()); layer.doPrepare(); } }; for (int level = 0; level < 3; level++) { JPanel pane = new JPanel(); String interString = i18n.get(DTEDCoverageManager.class, "level" + level + "title", "Level " + level + ": "); pane.add(new JLabel(interString)); String showString = i18n.get(DTEDCoverageManager.class, "show", "Show"); JCheckBox jcb = new JCheckBox(showString, levelRects[level].isVisible()); jcb.addActionListener(aListener); jcb.setActionCommand(Integer.toString(level)); pane.add(jcb); pane.add(attributes[level].getGUI()); c.gridy = level; gridbag.setConstraints(pane, c); panel.add(pane); } } return panel; } /** * WandererCallback class that provides coverage array based on existance of * DTED frames. * * @author dietrick */ static class CoverageWandererCallback implements WandererCallback { boolean[][][] cov; protected int curLon = Integer.MAX_VALUE; CoverageWandererCallback() { cov = new boolean[3][180][360]; } public boolean handleDirectory(File directory) { String name = directory.getName().toLowerCase(); char hemi = name.charAt(0); if (name.length() == 4 && (hemi == 'e' || hemi == 'w')) { try { // Get the longitude index right, use hemi to set the +/-, // and // then add 180 to get indexy. curLon = (hemi == 'w' ? -1 : 1) * Integer.parseInt(name.substring(1)) + 180; } catch (NumberFormatException nfe) { curLon = Integer.MAX_VALUE; logger.warning("Can't process " + name); } } return true; } public boolean handleFile(File file) { if (curLon != Integer.MAX_VALUE) { String name = file.getName().toLowerCase(); char hemi = name.charAt(0); char level = name.charAt(name.length() - 1); if (name.length() == 7 && name.charAt(name.length() - 4) == '.' && (hemi == 'n' || hemi == 's')) { try { int curLat = (hemi == 's' ? -1 : 1) * Integer.parseInt(name.substring(1, name.length() - 4)) + 90; if (level == '0') { cov[0][curLat][curLon] = true; } else if (level == '1') { cov[1][curLat][curLon] = true; } else if (level == '2') { cov[2][curLat][curLon] = true; } } catch (NumberFormatException nfe) { logger.warning("Can't process " + name); } } } return true; } boolean[][][] getCoverage() { return cov; } } /** * Inner class that handles reading and writing coverage cache file. * * @author dietrick */ public static class CoverageDataFile { File coverageFile = null; public CoverageDataFile(String path) { coverageFile = new File(path); } public boolean exists() { return coverageFile != null && coverageFile.exists(); } public String getAbsolutePath() { if (coverageFile != null) { return coverageFile.getAbsolutePath(); } return ""; } public boolean[][][] readCoverage() { if (exists()) { try { RandomAccessFile raf = new RandomAccessFile(coverageFile, "rw"); boolean[][][] coverage = new boolean[3][180][360]; for (int level = 0; level < 3; level++) { for (int y = 0; y < 180; y++) { for (int x = 0; x < 360; x++) { coverage[level][y][x] = raf.readBoolean(); } } } raf.close(); return coverage; } catch (FileNotFoundException e) { } catch (IOException ioe) { } } return null; } public void writeFile(boolean[][][] coverage) { try { RandomAccessFile raf = new RandomAccessFile(coverageFile, "rw"); for (int level = 0; level < 3; level++) { for (int y = 0; y < 180; y++) { for (int x = 0; x < 360; x++) { raf.writeBoolean(coverage[level][y][x]); } } } raf.close(); } catch (FileNotFoundException e) { } catch (IOException ioe) { } catch (ArrayIndexOutOfBoundsException aioobe) { } } } }