// ********************************************************************** // // <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/RpfCoverageBox.java,v $ // $RCSfile: RpfCoverageBox.java,v $ // $Revision: 1.8 $ // $Date: 2006/12/13 16:45:24 $ // $Author: dietrick $ // // ********************************************************************** /** * Modifications : * * 1. Changed getId() to make more unique * 2. Changed horizontalSubframes() and verticalSubframes(); need to round up * in certain cases. */ package com.bbn.openmap.layer.rpf; import java.awt.Point; import com.bbn.openmap.util.Debug; /** * The RpfCoverageBox describes the coverage provided by a RpfTocEntry * within a table of contents file. This should be enough information * that lets you tell what you need to ask for the proper subframes to * put on a screen. */ public class RpfCoverageBox { public double nw_lat; public double nw_lon; public double se_lat; public double se_lon; /** Lat degrees/subframe; vertInterval*256. */ public double subframeLatInterval; /** Lon degrees/subframe; horizInterval*256. */ public double subframeLonInterval; /** Two letter code for chart type. */ public String chartCode; /** * The starting index for coverage subframes. If it's null, it * hasn't been figured out yet. */ public Point startIndexes; /** * The ending index for coverage subframes. If it's null, it * hasn't been figured out yet. */ public Point endIndexes; /** * For the coverage queries, the CADRG zone becomes important. * This is not the zone from the RpfTocEntry - its the translated * zone for use with the CADRG projection. */ public int zone; /** * The TOC number that a frame provider can use to get to the * right entry number. Used internally. */ public int tocNumber; /** * The RpfTocEntry index to use to get more information about the * frame files to use to get data for a subframe. Used internally. */ public int entryNumber; /** The scale of the maps of this coverage rectangle. */ public float scale; /** * Of the number of subframes that can fit on the screen, the * percentage of them that are on the screen. Should be set via * setPercentCoverage, unless you are copying all values as well. */ public float percentCoverage; /** A semi unique string descriptor. */ protected String id; public String toString() { StringBuffer s = new StringBuffer(); s.append(" nw_lat ").append(nw_lat).append(", nw_lon ").append(nw_lon).append("\n"); s.append(" se_lat ").append(se_lat).append(", se_lon ").append(se_lon).append("\n"); s.append(" chart code ").append(chartCode).append("\n"); s.append(" scale (parsed and decoded) ").append(scale).append("\n"); s.append(" vertical subframes ").append(verticalSubframes()).append("\n"); s.append(" horizontal subframes ").append(horizontalSubframes()).append("\n"); s.append(" percent coverage ").append(percentCoverage); return s.toString(); } /** * Modified to make the semi-unique ID more unique. The former * method of generating these ID numbers was causing some coverage * boxes that are geographically close to each other to have the * same ID, which resulted in caching problems. In particular, if * two non-identical boxes had the same ID, they could invalidate * each others' caches in certain regions. Modified these IDs so * that the numbers would be more unique. This was a major cause * of gaps at zone boundaries. * * @return a unique ID for the coverage box */ public String getID() { if (id == null) { // This should be enough to tell that the box is not the // same. Even if it isn't the same, the subframes from // this source should be the same. // NOTE: added +se_lat+se_lon because of // uniqueness problems id = tocNumber + entryNumber + nw_lat + nw_lon + se_lat + se_lon + chartCode; } return id; } /** * The number of subframes vertically within this coverage box. * * NOTE: Empirically noticed that # of subframes sometimes * underreported because these cast to an int. Rounding would * sometimes overreport, however. Observed that a tolerance of * .000001 would define when to round up. This appeared to be * causing gaps at zone boundaries, since a coverage box could * think it doesn't have a certain subframe, when it actually * does. */ public int verticalSubframes() { double value = Math.abs((nw_lat - se_lat) / subframeLatInterval); int lower = (int) value; int upper = lower + 1; if (upper - value < .000001) { return upper; } else return lower; } /** * The number of subframes horizontally within this coverage box. * * NOTE: Empirically noticed that # of subframes sometimes * underreported because these cast to an int. Rounding would * sometimes overreport, however. Observed that a tolerance of * .000001 would define when to round up. This appeared to be * causing gaps at zone boundaries, since a coverage box could * think it doesn't have a certain subframe, when it actually * does. */ public int horizontalSubframes() { double value = Math.abs((se_lon - nw_lon) / subframeLonInterval); int lower = (int) value; int upper = lower + 1; if (upper - value < .000001) { return upper; } else return lower; } /** * This is only good for a preliminary check to see of the * boundaries are within the range of each other. * * @return how many of the edges of this coverage box fall within * the queried box. */ public int setBoundaryHits(double ullat, double ullon, double lrlat, double lrlon) { int boundaryHits = 0; if (lrlat < nw_lat) boundaryHits++; if (ullat > se_lat) boundaryHits++; if (lrlon > nw_lon) boundaryHits++; if (ullon < se_lon) boundaryHits++; if (ullat < nw_lat) boundaryHits++; if (lrlat > se_lat) boundaryHits++; if (ullon > nw_lon) boundaryHits++; if (lrlon < se_lon) boundaryHits++; return boundaryHits; } /** * The percentage of subframes that actually fill the queried * rectangle, compared to the number of subframes that could fit. * As a bonus, the start and end suframe indexes are set. * * @return the percentage of coverage over the queried rectangle. */ public float setPercentCoverage(double ullat, double ullon, double lrlat, double lrlon) { startIndexes = new Point(); endIndexes = new Point(); return setPercentCoverage(ullat, ullon, lrlat, lrlon, startIndexes, endIndexes); } /** * The percentage of subframes that actually fill the queried * rectangle, compared to the number of subframes that could fit. * As a bonus, the start and end suframe indexes are set. * * @return the percentage of coverage over the queried rectangle. */ public float setPercentCoverage(double ullat, double ullon, double lrlat, double lrlon, Point start, Point end) { startIndexes = start; endIndexes = end; // Set the subframes that are on the screen, in the matrix of // subframes that make up the overall boundary rectangle. // Upper left is 0, 0 double tempInterval = (ullon - nw_lon) / subframeLonInterval; start.x = (int) tempInterval; if (tempInterval < 0 && tempInterval < (double) start.x) start.x--; tempInterval = (nw_lat - ullat) / subframeLatInterval; start.y = (int) tempInterval; if (tempInterval < 0 && tempInterval < (double) start.y) start.y--; tempInterval = (lrlon - nw_lon) / subframeLonInterval; end.x = (int) tempInterval; if (tempInterval < 0 && tempInterval < (double) end.x) end.x--; tempInterval = (nw_lat - lrlat) / subframeLatInterval; end.y = (int) tempInterval; if (tempInterval < 0 && tempInterval < (double) end.y) end.y--; //(int) (Math.abs(lrlon - ullon)/subframeLonInterval); int num_horiz_subframes = horizontalSubframes(); //(int) (Math.abs(ullat - lrlat)/subframeLatInterval); int num_vert_subframes = verticalSubframes(); if ((start.y >= 0 || end.y >= 0) && (start.x >= 0 || end.x >= 0) && (start.x < num_horiz_subframes || end.x < num_horiz_subframes) && (start.y < num_vert_subframes || end.y < num_vert_subframes)) { // So now here, either the lesser side is less than zero, // the greater side is greater than the Max. int left = start.x < 0 ? 0 : start.x; int right = end.x >= num_horiz_subframes ? (num_horiz_subframes - 1) : end.x; int top = start.y < 0 ? 0 : start.y; int bottom = end.y >= num_vert_subframes ? (num_vert_subframes - 1) : end.y; percentCoverage = ((float) ((Math.abs(right - left) + 1f) * (Math.abs(bottom - top) + 1f)) / (float) ((Math.abs(end.x - start.x) + 1f) * (Math.abs(end.y - start.y) + 1))) * 100f; if (percentCoverage > 100f) percentCoverage = 100f; if (Debug.debugging("rpf")) { System.out.println("Calculated percentage = " + percentCoverage + " <= " + ((Math.abs(right - left) + 1) * (Math.abs(bottom - top) + 1)) + " subframes / " + ((Math.abs(end.x - start.x) + 1) * (Math.abs(end.y - start.y) + 1)) + " subframes\n (" + right + " - " + left + ") * (" + bottom + " - " + top + ") / (" + end.x + " - " + start.x + ") * (" + end.y + " - " + start.y + ")"); } } else { percentCoverage = 0f; } return percentCoverage; } /** Return the percent coverage of the last queried rectangle. */ public float getPercentCoverage() { return percentCoverage; } /** * Location within box. True if it is; */ public boolean within(float lat, float lon) { double y = (double) lat; double x = (double) lon; return (y < nw_lat && y > se_lat && x < se_lon && x > nw_lon); } /** Reset the coverage percentage and scale difference. */ public void reset() { percentCoverage = 0f; scale = 0; } }