// ********************************************************************** // // <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/omGraphics/OMRangeRings.java,v $ // $RCSfile: OMRangeRings.java,v $ // $Revision: 1.8 $ // $Date: 2009/02/25 22:34:03 $ // $Author: dietrick $ // // ********************************************************************** package com.bbn.openmap.omGraphics; import java.awt.Graphics; import java.text.DecimalFormat; import java.text.NumberFormat; import com.bbn.openmap.proj.Length; import com.bbn.openmap.proj.Projection; import com.bbn.openmap.proj.coords.LatLonPoint; import com.bbn.openmap.util.DeepCopyUtil; /** * An object that manages a series of range circles. It is really an OMCircle * that manages a set of inner circles and an OMPOint. The location of these * inner circles depend on two new variables, the interval and intervalUnits. If * the intervalUnits are null, then the interval represents the number of inner * circles, not including the outer ring and the innermost point, that are * spaced evenly between them. If the intervalUnits are not null, then the * interval represents the number of intervalUnits where inner circles are * placed. For example, if the intervalUnits is Length.MILE, and the interval is * 5, then inner circles will be placed every 5 miles. If the intervalUnits is * null, then there will be 5 inner circles drawn between the center point and * the outer ring. * * @see OMCircle */ public class OMRangeRings extends OMCircle { /** The inner ring of circles. */ protected OMCircle[] subCircles = null; /** The labels for the circles. */ protected OMText[] labels = null; /** By default, there are 3 inner rings, 4 total. */ public final static int DEFAULT_INTERVAL = 4; /** * The number of rings, or the unit interval, depending on whether * intervalUnits is null or not. */ protected int interval = DEFAULT_INTERVAL; /** The unit object specifying the interval meaning. */ protected Length intervalUnits = null; /** * The DrawingAttributes object used to reflect the outer circle properties * to the inner circles. Only used in render(), and allocated to save * repeated allocation during render. */ protected DrawingAttributes drawingAttributes = new DrawingAttributes(); /** The center point of the range rings. */ protected OMPoint centerPoint; /** The default format. */ public final static DecimalFormat DEFAULT_FORMAT = new DecimalFormat(); /** Formatting for the labels with units. */ protected NumberFormat form = DEFAULT_FORMAT; protected boolean drawLabels = true; /** * Used for UndoEvents. Otherwise, don't use this unless you set all other * parameters befitting an OMGraphic of such resplendent information. */ public OMRangeRings() { } /** * Creates an OMRangeRings with a Lat-lon center and a lat-lon axis. * Rendertype is RENDERTYPE_LATLON. * * @param latPoint latitude of center point, decimal degrees * @param lonPoint longitude of center point, decimal degrees * @param radius distance in decimal degrees (converted to radians * internally). */ public OMRangeRings(double latPoint, double lonPoint, double radius) { this(new LatLonPoint.Double(latPoint, lonPoint), radius, Length.DECIMAL_DEGREE, -1); } /** * Create an OMRangeRings with a lat/lon center and a physical distance * radius. Rendertype is RENDERTYPE_LATLON. * * @param latPoint latitude of center of circle in decimal degrees * @param lonPoint longitude of center of circle in decimal degrees * @param radius distance * @param units com.bbn.openmap.proj.Length object. */ public OMRangeRings(double latPoint, double lonPoint, double radius, Length units) { this(new LatLonPoint.Double(latPoint, lonPoint), radius, units, -1); } /** * Create an OMRangeRings with a lat/lon center and a physical distance * radius. Rendertype is RENDERTYPE_LATLON. * * @param latPoint latitude of center of circle in decimal degrees * @param lonPoint longitude of center of circle in decimal degrees * @param radius distance * @param units com.bbn.openmap.proj.Length object specifying units. * @param nverts number of vertices for the poly-circle (if < 3, value is * generated internally) */ public OMRangeRings(double latPoint, double lonPoint, double radius, Length units, int nverts) { this(new LatLonPoint.Double(latPoint, lonPoint), radius, units, nverts); } /** * Create an OMRangeRings with a lat/lon center and a physical distance * radius. Rendertype is RENDERTYPE_LATLON. * * @param center LatLon center of circle * @param radius distance * @param units com.bbn.openmap.proj.Length object specifying units for * distance. * @param nverts number of vertices for the poly-circle(if < 3, value is * generated internally) */ public OMRangeRings(LatLonPoint center, double radius, Length units, int nverts) { super(center, radius, units, nverts); centerPoint = createCenterPoint(); form.setMaximumFractionDigits(2); } protected OMPoint createCenterPoint() { return new OMPoint(center.getY(), center.getX()); } /** * Set the interval. If the interval units are null, then this interval * represents the number of circles within the external, defined circle. If * the interval units are not null, then this interval represents the unit * intervals where range rings are placed. */ public void setInterval(int interval) { this.interval = interval; setNeedToRegenerate(true); } /** Convenience method to set both at one time. */ public void setInterval(int interval, Length units) { setInterval(interval); setIntervalUnits(units); } /** * Get the interval number. */ public int getInterval() { return interval; } /** * Set the interval units. If this is null, then the interval value will * represent the number of rings drawn within the defined outer ring. If this * is not null, then it represents the units of the interval where the range * rings are drawn. */ public void setIntervalUnits(Length units) { intervalUnits = units; setNeedToRegenerate(true); } /** * Get the interval units. */ public Length getIntervalUnits() { return intervalUnits; } /** * Flag for whether the rings should be labeled. */ public void setDrawLabels(boolean dl) { drawLabels = dl; } public boolean getDrawLabels() { return drawLabels; } /** * Set the format for the number labels. If null, the default will be used. * This only applies to the labels with units. */ public void setFormat(java.text.NumberFormat nf) { if (nf != null) { form = nf; } else { form = DEFAULT_FORMAT; } } /** * Get the format used for the labeling of unit rings. */ public java.text.NumberFormat getFormat() { return form; } /** * Set the radius. This is meaningful only if the render type is * RENDERTYPE_LATLON. Note that while the radius is specified as decimal * degrees, it only means the distance along the ground that that number of * degrees represents at the equator, *NOT* a radius of a number of degrees * around a certain location. There is a difference. * * @param radius float radius in decimal degrees */ public void setRadius(float radius) { setRadius(radius, Length.DECIMAL_DEGREE); } /** * Set the radius with units. This is meaningful only if the render type is * RENDERTYPE_LATLON. * * @param radius float radius * @param units Length specifying unit type. */ public void setRadius(float radius, Length units) { this.radius = units.toRadians(radius); setNeedToRegenerate(true); } /** * Take the interval and intervalUnits, and then create the proper inner * circles. */ public OMCircle[] createCircles() { OMCircle[] circles; OMText[] t; int i; double rad; String value; if (intervalUnits == null) { int noUnitInterval = interval - 1; circles = new OMCircle[noUnitInterval]; t = new OMText[noUnitInterval]; for (i = 0; i < noUnitInterval; i++) { rad = (i + 1) * radius / (noUnitInterval + 1); circles[i] = new OMCircle(LatLonPoint.getDouble(center), rad, Length.RADIAN, -1); value = ((i + 1) + "/" + (noUnitInterval + 1)); t[i] = new OMText(center.getY() + Length.DECIMAL_DEGREE.fromRadians(rad), center.getX(), value, OMText.JUSTIFY_CENTER); } } else { double realDistanceInterval = intervalUnits.toRadians(interval); int number = (int) (radius / realDistanceInterval); circles = new OMCircle[number]; t = new OMText[number + 1]; for (i = 0; i < number; i++) { rad = (i + 1) * realDistanceInterval; circles[i] = new OMCircle(LatLonPoint.getDouble(center), rad, Length.RADIAN, -1); value = (form.format((double) (i + 1) * interval) + " " + intervalUnits.getAbbr()); t[i] = new OMText(center.getY() + Length.DECIMAL_DEGREE.fromRadians(rad), center.getX(), value, OMText.JUSTIFY_CENTER); } value = (form.format((double) intervalUnits.fromRadians(radius)) + " " + intervalUnits.getAbbr()); t[i] = new OMText(center.getY() + Length.DECIMAL_DEGREE.fromRadians(radius), center.getX(), value, OMText.JUSTIFY_CENTER); } labels = t; return circles; } /** * Prepare the circles for rendering. * * @param proj Projection * @return true if generate was successful */ public boolean generate(Projection proj) { if (getNeedToRegenerate() == true) { if (interval > 0) { subCircles = createCircles(); } else { subCircles = null; } } centerPoint = createCenterPoint(); centerPoint.generate(proj); setRenderType(RENDERTYPE_LATLON); // Can't be anything else. int i; if (subCircles != null) { for (i = 0; i < subCircles.length; i++) { subCircles[i].generate(proj); labels[i].generate(proj); } // do the one for the outer ring if there are units. if (labels.length > i) { labels[i].generate(proj); } } return super.generate(proj); } /** * Paint the circles. * * @param g Graphics context to render into */ public void render(Graphics g) { super.render(g); drawingAttributes.setFrom(this); if (subCircles != null) { // Draw from the larger to the smaller, so the lines of // the smaller circles will appear on top of the bigger // ones. for (int i = subCircles.length - 1; i >= 0; i--) { drawingAttributes.setTo(subCircles[i]); drawingAttributes.setTo(labels[i]); labels[i].setLinePaint(drawingAttributes.getLinePaint()); subCircles[i].render(g); if (drawLabels) { labels[i].render(g); } } // do the one for the outer ring if there are units. if (labels.length > subCircles.length && drawLabels) { drawingAttributes.setTo(labels[subCircles.length]); labels[subCircles.length].setLinePaint(drawingAttributes.getLinePaint()); labels[subCircles.length].render(g); } } if (centerPoint != null) { drawingAttributes.setTo(centerPoint); centerPoint.render(g); } } /** * Return the shortest distance from the circle to an XY-point. * * @param x X coordinate of the point. * @param y Y coordinate fo the point. * @return float distance from circle to the point */ public float distance(double x, double y) { float dist = normalizeDistanceForLineWidth(super.distance(x, y)); // Not sure whether the inner circles should be queried for // distance measurements. float tmpDist; // if (dist != 0 && subCircles != null) { // for (int i = 0; i < subCircles.length; i++) { // tmpDist = subCircles[i].distance(x, y); // if (tmpDist == 0) return tmpDist; // if (tmpDist < dist) { // dist = tmpDist; // } // } // } tmpDist = centerPoint.distance(x, y); if (tmpDist < dist) { dist = tmpDist; } return dist; } public void restore(OMGeometry source) { super.restore(source); if (source instanceof OMRangeRings) { OMRangeRings rings = (OMRangeRings) source; this.subCircles = DeepCopyUtil.deepCopy(rings.subCircles); this.labels = DeepCopyUtil.deepCopy(rings.labels); this.interval = rings.interval; this.intervalUnits = rings.intervalUnits; this.centerPoint = DeepCopyUtil.deepCopy(rings.centerPoint); this.form = (NumberFormat) rings.form.clone(); this.drawLabels = true; } } }