// ********************************************************************** // // <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/OMScalingIcon.java,v $ // $RCSfile: OMScalingIcon.java,v $ // $Revision: 1.10 $ // $Date: 2009/01/21 01:24:41 $ // $Author: dietrick $ // // ********************************************************************** package com.bbn.openmap.omGraphics; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Point; import java.awt.Rectangle; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import java.io.Serializable; import javax.swing.ImageIcon; import com.bbn.openmap.proj.Projection; import com.bbn.openmap.util.Debug; /** * This is an extension to OMScalingRaster that scales an icon. The icon is * automatically centered over the lat/lon location. The offsets push the icon * away from the lat/lon. * * @see OMScalingRaster */ public class OMScalingIcon extends OMScalingRaster implements Serializable { protected float baseScale; protected float maxScale = Float.MAX_VALUE; protected float minScale = 0f; /** * Shortcut flag for position/scaleTo/rendering for images that don't need * scaling. Preserves rendering quality. */ protected boolean noScalingRequired = false; /** * Construct a blank OMRaster, to be filled in with set calls. */ public OMScalingIcon() { super(); } // /////////////////////////////////// INT PIXELS - DIRECT // COLORMODEL /** * Creates an OMRaster images, Lat/Lon placement with a direct colormodel * image. * * @param centerLat latitude of the top of the image. * @param centerLon longitude of the left side of the image. * @param offsetX horizontal pixel offset of icon (positive pushes east). * @param offsetY vertical pixel offset of icon (positive pushes south). * @param w width of the image, in pixels. * @param h height of the image, in pixels. * @param pix color values for the pixels. * @param baseScale the scale where the icon will be show regular size. * @see #setPixel */ public OMScalingIcon(double centerLat, double centerLon, int offsetX, int offsetY, int w, int h, int[] pix, float baseScale) { super(centerLat, centerLon, 0f, 0f, w, h, pix); setX(offsetX); setY(offsetY); this.baseScale = baseScale; } // //////////////////////////////////// IMAGEICON /** * Create an OMRaster, Lat/Lon placement with an ImageIcon. * * @param centerLat latitude of the top of the image. * @param centerLon longitude of the left side of the image. * @param offsetX horizontal pixel offset of icon (positive pushes east). * @param offsetY vertical pixel offset of icon (positive pushes south). * @param ii ImageIcon used for the image. * @param baseScale the scale where the icon will be show regular size. */ public OMScalingIcon(double centerLat, double centerLon, int offsetX, int offsetY, ImageIcon ii, float baseScale) { this(centerLat, centerLon, offsetX, offsetY, ii.getImage(), baseScale); } /** * Create an OMRaster, Lat/Lon placement with an ImageIcon. Doesn't scale, * because baseScale, minScale and maxScale are all set to the same number * (4000000). * * @param centerLat latitude of the top of the image. * @param centerLon longitude of the left side of the image. * @param ii ImageIcon used for the image. */ public OMScalingIcon(double centerLat, double centerLon, ImageIcon ii) { this(centerLat, centerLon, ii.getImage()); } /** * Create an OMRaster, Lat/Lon placement with an Image. * * @param centerLat latitude of the top of the image. * @param centerLon longitude of the left side of the image. * @param offsetX horizontal pixel offset of icon (positive pushes east). * @param offsetY vertical pixel offset of icon (positive pushes south). * @param ii Image used for the image. * @param baseScale the scale where the icon will be show regular size. */ public OMScalingIcon(double centerLat, double centerLon, int offsetX, int offsetY, Image ii, float baseScale) { super(); setRenderType(OMGraphic.RENDERTYPE_LATLON); setColorModel(COLORMODEL_IMAGEICON); lat = centerLat; lon = centerLon; setImage(ii); setX(offsetX); setY(offsetY); this.baseScale = baseScale; } /** * Create an OMRaster, Lat/Lon placement with an ImageIcon. Doesn't scale, * because baseScale, minScale and maxScale are all set to the same number * (4000000). * * @param centerLat latitude of the top of the image. * @param centerLon longitude of the left side of the image. * @param image ImageIcon used for the image. */ public OMScalingIcon(double centerLat, double centerLon, Image image) { this(centerLat, centerLon, 0, 0, image, 4000000); setMaxScale(4000000); setMinScale(4000000); } // //////////////////////////////////// BYTE PIXELS with // COLORTABLE /** * Lat/Lon placement with a indexed colormodel, which is using a colortable * and a byte array to construct the int[] pixels. * * @param centerLat latitude of the top of the image. * @param centerLon longitude of the left side of the image. * @param offsetX horizontal pixel offset of icon (positive pushes east). * @param offsetY vertical pixel offset of icon (positive pushes south). * @param w width of the image, in pixels. * @param h height of the image, in pixels. * @param bytes colortable index values for the pixels. * @param colorTable color array corresponding to bytes * @param trans transparency of image. * @param baseScale the scale where the icon will be show regular size. * @see #setPixel */ public OMScalingIcon(float centerLat, float centerLon, int offsetX, int offsetY, int w, int h, byte[] bytes, Color[] colorTable, int trans, float baseScale) { super(centerLat, centerLon, 0f, 0f, w, h, bytes, colorTable, trans); setX(offsetX); setY(offsetY); this.baseScale = baseScale; } /** * Since the image doesn't necessarily need to be regenerated when it is * merely moved, raster objects have this function, called from generate() * and when a placement attribute is changed. * * @return true if enough information is in the object for proper placement. * @param proj projection of window. */ protected boolean position(Projection proj) { if (proj == null) { Debug.error("OMScalingIcon: null projection in position!"); return false; } if (bitmap == null) { // Debug.error("OMScalingIcon: null sourceImage in position!"); // XXX: For now fail silently. return false; } float shrinkScale = proj.getScale(); if (shrinkScale > maxScale) { shrinkScale = maxScale; } if (shrinkScale < minScale) { shrinkScale = minScale; } noScalingRequired = baseScale == shrinkScale; float scaleFactor = baseScale / shrinkScale; point1 = (Point) proj.forward(lat, lon, new Point()); point2 = (Point) proj.forward(lat, lon, new Point()); int halfImageWidth = width / 2; int halfImageHeight = height / 2; // Mindful of pixel offset icons int myX = getX(); int myY = getY(); double p1x = point1.getX(); double p1y = point1.getY(); double newP1x = p1x + (scaleFactor * (myX - halfImageWidth)); double newP1y = p1y + (scaleFactor * (myY - halfImageHeight)); point1.setLocation((int) newP1x, (int) newP1y); double p2x = point2.getX(); double p2y = point2.getY(); double newP2x = p2x + (scaleFactor * (myX + halfImageWidth)); double newP2y = p2y + (scaleFactor * (myY + halfImageHeight)); point2.setLocation((int) newP2x, (int) newP2y); setNeedToReposition(false); return true; } public boolean isOnMap(Projection proj) { generate(proj); // Should only generate if needed... Shape shape = getShape(); if (shape == null) { return false; } Point2D p1 = proj.forward(proj.getUpperLeft()); Point2D p2 = proj.forward(proj.getLowerRight()); int h = (int) (p2.getY() - p1.getY()); int w = (int) (p2.getX() - p1.getX()); Rectangle mapRect = new Rectangle((int) p1.getX(), (int) p1.getY(), w, h); return mapRect.intersects(shape.getBounds()); } /** * Over-riding this so we don't clip rotated icons near the edge of the map. * Just display icons as whole. */ protected void scaleTo(Projection thisProj) { if (DEBUG) { logger.fine("OMScalingRaster: scaleTo()"); } if (bitmap == null) { if (DEBUG) { logger.fine("scaleTo() source image is null"); } return; } if (noScalingRequired) { return; } // Get image projection rectangle Rectangle projRect = new Rectangle(); projRect.setLocation(point1); projRect.setSize(point2.x - point1.x, point2.y - point1.y); Rectangle sourceRect = new Rectangle(); sourceRect.width = width; sourceRect.height = height; // Now we have everything we need to sort out this new projection. // boolean currentVisibility = isVisible(); clipRect = null; if (!projRect.isEmpty()) { // Assume will need whole image, set the clipRect so it's // on the map, somewhere. // If big enough to see if ((projRect.width >= 1) && (projRect.height >= 1)) { // check width and height of clipRect, in case it got // rounded down to zero. if (sourceRect.width <= 0) { sourceRect.width = 1; } if (sourceRect.height <= 0) { sourceRect.height = 1; } // Now we can grab the bit we want out of the source // and scale it to fit the intersection. // Calc width adjustment double widthAdj = (double) projRect.width / (double) sourceRect.width; // Calc height adjustment double heightAdj = (double) projRect.height / (double) sourceRect.height; // Create the transform AffineTransform xform = new AffineTransform(); // Specify scaling xform.setToScale(widthAdj, heightAdj); // Create the transform op. this.scalingXFormOp = new AffineTransformOp(xform, getScaleTransformType()); } } } /** * Render the image at the given pixel location. This method should be * overridden for special Image handling. * * @param g the Graphics object to render the image into. Assumes this is a * derivative of the Graphics passed into the OMGraphic, and can be * modified without worrying about passing settings on to other * OMGraphics. * @param image the image to render. * @param loc the pixel location of the image. */ protected void renderImage(Graphics g, Image image, Point loc) { if (image != null) { if (DEBUG) { logger.fine("drawing icon image at " + loc.x + ", " + loc.y); } if (noScalingRequired) { g.drawImage(image, loc.x, loc.y, null); return; } if (g instanceof Graphics2D) { if (image instanceof BufferedImage) { ((Graphics2D) g).drawImage((BufferedImage) image, scalingXFormOp, loc.x, loc.y); } else { int dx1 = loc.x; int dy1 = loc.y; int dx2 = point2.x; int dy2 = point2.y; ((Graphics2D) g).drawImage(image, dx1, dy1, dx2, dy2, 0, 0, width, height, this); } } // else what? Never seen this test fail with Java2D } else if (DEBUG) { logger.fine("ignoring null bitmap image"); } } public void setBaseScale(float bs) { baseScale = bs; } public float getBaseScale() { return baseScale; } /** * Set the scale that limits how small an icon will shrink. Should be a * number larger than the base scale. If the map scale gets larger than this * number, the icon will stop shrinking. */ public void setMaxScale(float ms) { maxScale = ms; } public float getMaxScale() { return maxScale; } /** * Set the scale that limits how big an icon should grow. Should be a number * smaller than the base scale. If the map scale gets smaller than this * number, the icon will stop growing. */ public void setMinScale(float ms) { minScale = ms; } public float getMinScale() { return minScale; } public void restore(OMGeometry source) { super.restore(source); if (source instanceof OMScalingIcon) { OMScalingIcon icon = (OMScalingIcon) source; this.baseScale = icon.baseScale; this.maxScale = icon.maxScale; this.minScale = icon.minScale; } } }