//**********************************************************************
//
//<copyright>
//
//BBN Technologies
//10 Moulton Street
//Cambridge, MA 02138
//(617) 873-8000
//
//Copyright (C) BBNT Solutions LLC. All rights reserved.
//
//</copyright>
//**********************************************************************
//
//$Source:
///cvs/darwars/ambush/aar/src/com/bbn/ambush/mission/MissionHandler.java,v
//$
//$RCSfile: ImageTileLayer.java,v $
//$Revision: 1.1.4.4 $
//$Date: 2007/01/22 16:34:07 $
//$Author: dietrick $
//
//**********************************************************************
package com.bbn.openmap.layer.imageTile;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import java.util.Properties;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.ListCellRenderer;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileFilter;
import com.bbn.openmap.Environment;
import com.bbn.openmap.I18n;
import com.bbn.openmap.LatLonPoint;
import com.bbn.openmap.MapBean;
import com.bbn.openmap.MapHandler;
import com.bbn.openmap.dataAccess.image.ErrImageTile;
import com.bbn.openmap.dataAccess.image.ImageReader;
import com.bbn.openmap.dataAccess.image.ImageReaderLoader;
import com.bbn.openmap.dataAccess.image.ImageTile;
import com.bbn.openmap.event.LayerStatusEvent;
import com.bbn.openmap.gui.LayerControlButtonPanel;
import com.bbn.openmap.gui.LayersPanel;
import com.bbn.openmap.layer.OMGraphicHandlerLayer;
import com.bbn.openmap.omGraphics.DrawingAttributes;
import com.bbn.openmap.omGraphics.OMColor;
import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.omGraphics.OMGraphicList;
import com.bbn.openmap.proj.Proj;
import com.bbn.openmap.tools.icon.BasicIconPart;
import com.bbn.openmap.tools.icon.IconPart;
import com.bbn.openmap.tools.icon.IconPartList;
import com.bbn.openmap.tools.icon.OMIconFactory;
import com.bbn.openmap.util.ComponentFactory;
import com.bbn.openmap.util.PropUtils;
/**
* The ImageTileLayer is a layer that manages georeferenced images over a map.
* The layer uses ImageReaders to figure out how to load images from a file,
* create an ImageTile object from the image data, and deduce where the
* ImageTile should be located from the information provided with/in the image
* data.
* <P>
*
* ImageReaderLoader objects are held by the layer to assist in finding the
* appropriate ImageReader for an image file.
* <P>
*
* The properties for this layer are:
*
* <pre>
* # semi-colon separated paths to image files or directories containing images
* imageTileLayer.imageFilePath=path/to/file1;path/to/directory;path/to/file2
*
* # optional - image cache size specifies how many images will be held in memory for fast retrieval.
* imageTileLayer.imageCacheSize=20
*
* # optional - image cutoff scale specifies the scale that images will not load when the projection is zoomed out from it.
* imageTileLayer.imageCutoffScale=1000000
*
* # optional - image Reader loaders specify which image files are handled
* imageTileLayer.iamgeReaderLoaders=geotiff
* imageTileLayer.geotiff=com.bbn.openmap.dataAccess.image.geotiff.GeoTIFFImageReader.Loader
*
* # optional - Drawing attributes properties for image highlighting
* imageTileLayer.lineWidth=2
* imageTileLayer.selectColor=FFFFFF00
* </pre>
*
* @author dietrick
*
*/
public class ImageTileLayer extends OMGraphicHandlerLayer {
public static Logger logger = Logger.getLogger("com.bbn.openmap.layer.imageTile.ImageTileLayer");
public final static String ImageFilePathProperty = "imageFilePath";
public final static String ImageReaderLoadersProperty = "imageReaderLoaders";
public final static String ImageCacheSizeProperty = "imageCacheSize";
public final static String ImageCutoffRatioProperty = "imageCutoffRatio";
protected String SHOW_TILES_TITLE;
protected String HIDE_TILES_TITLE;
protected Vector filePaths;
protected Vector imageReaderLoaders;
protected ImageTile.Cache imageCache;
protected DrawingAttributes selectedDrawingAttributes = DrawingAttributes.getDefaultClone();
/**
* Default constructor for layer, initializes tile cache.
*
*/
public ImageTileLayer() {
configureImageReaderLoaders();
imageCache = new ImageTile.Cache();
SHOW_TILES_TITLE = i18n.get(ImageTileLayer.class,
"showTilesButton",
"Show");
HIDE_TILES_TITLE = i18n.get(ImageTileLayer.class,
"hideTilesButton",
"Hide");
}
/**
* PropertyConsumer interface method.
*/
public void setProperties(String prefix, Properties props) {
super.setProperties(prefix, props);
selectedDrawingAttributes.setProperties(prefix, props);
prefix = PropUtils.getScopedPropertyPrefix(prefix);
filePaths = PropUtils.parseMarkers(props.getProperty(prefix
+ ImageFilePathProperty), ";");
imageCache.resetCache(PropUtils.intFromProperties(props, prefix
+ ImageCacheSizeProperty, imageCache.getCacheSize()));
imageCache.setCutoffScaleRatio(PropUtils.floatFromProperties(props, prefix
+ ImageCutoffRatioProperty, imageCache.getCutoffScaleRatio()));
String imageReaderLoaderString = props.getProperty(prefix
+ ImageReaderLoadersProperty);
if (imageReaderLoaders == null) {
imageReaderLoaders = new Vector();
}
if (imageReaderLoaderString != null) {
imageReaderLoaders.clear();
Vector idls = PropUtils.parseSpacedMarkers(imageReaderLoaderString);
for (Iterator it = idls.iterator(); it.hasNext();) {
String idlMarkerName = (String) it.next();
String idlClassName = props.getProperty(prefix + idlMarkerName);
Object obj = ComponentFactory.create(idlClassName);
if (obj != null && obj instanceof ImageReaderLoader) {
imageReaderLoaders.add((ImageReaderLoader) obj);
}
}
}
}
/**
* Internal callback method for subclasses to use to be able to configure
* imageReaderLoader Vector with specific ImageReaderLoaders. By default,
* loads GeoTIFFImageReader.Loader.
*/
protected void configureImageReaderLoaders() {
imageReaderLoaders = new Vector();
ImageReaderLoader idl = (ImageReaderLoader) ComponentFactory.create("com.bbn.openmap.dataAccess.image.geotiff.GeoTIFFImageReaderLoader");
if (idl != null) {
imageReaderLoaders.add(idl);
} else {
logger.warning("ImageTileLayer needs JAI installed in order to use GeoTIFF Image Reader.");
}
idl = (ImageReaderLoader) ComponentFactory.create("com.bbn.openmap.dataAccess.image.WorldFileImageReaderLoader");
if (idl != null) {
imageReaderLoaders.add(idl);
} else {
logger.warning("ImageTileLayer needs JAI installed in order to use World File Image Reader.");
}
}
/**
* PropertyConsumer interface method.
*/
public Properties getProperties(Properties props) {
props = super.getProperties(props);
selectedDrawingAttributes.getProperties(props);
String prefix = PropUtils.getScopedPropertyPrefix(this);
OMGraphicList list = getList();
if (list != null) {
StringBuffer buf = null;
for (Iterator it = list.iterator(); it.hasNext();) {
if (buf == null) {
buf = new StringBuffer();
} else {
buf.append(";");
}
ImageTile imageTile = (ImageTile) it.next();
String filePath = (String) imageTile.getAttribute(FILE_PATH_ATTRIBUTE);
if (filePath != null) {
buf.append(filePath);
}
}
props.put(prefix + ImageFilePathProperty, buf.toString());
}
if (imageReaderLoaders != null) {
int count = 0;
StringBuffer sbuf = null;
for (Iterator it = imageReaderLoaders.iterator(); it.hasNext(); count++) {
ImageReaderLoader idl = (ImageReaderLoader) it.next();
props.put(prefix + "idl" + count, idl.getClass().getName());
if (sbuf == null) {
sbuf = new StringBuffer("idl" + count);
} else {
// Space separated for parsing on input
sbuf.append(" idl" + count);
}
}
if (sbuf != null) {
props.put(prefix + ImageReaderLoadersProperty, sbuf.toString());
}
}
props.put(prefix + ImageCacheSizeProperty,
Integer.toString(imageCache.getCacheSize()));
props.put(prefix + ImageCutoffRatioProperty,
Float.toString(imageCache.getCutoffScaleRatio()));
return props;
}
/**
* PropertyConsumer interface method.
*/
public Properties getPropertyInfo(Properties props) {
props = super.getPropertyInfo(props);
selectedDrawingAttributes.getPropertyInfo(props);
PropUtils.setI18NPropertyInfo(i18n,
props,
ImageTileLayer.class,
ImageFilePathProperty,
"Images",
"A list of images or directories to display (separated by ;).",
"com.bbn.openmap.util.propertyEditor.MultiDirFilePropertyEditor");
PropUtils.setI18NPropertyInfo(i18n,
props,
ImageTileLayer.class,
ImageCacheSizeProperty,
"Cache Size",
"Number of images to keep in cache.",
null);
PropUtils.setI18NPropertyInfo(i18n,
props,
ImageTileLayer.class,
ImageCutoffRatioProperty,
"Cutoff Scale",
"Projection scale where larger values won't cause images to be loaded and displayed.",
null);
String dummyMarker = PropUtils.getDummyMarkerForPropertyInfo(getPropertyPrefix(),
null);
PropUtils.setI18NPropertyInfo(i18n,
props,
ImageTileLayer.class,
dummyMarker,
"Highlight Settings",
"Settings for annototations on highlighted images.",
"com.bbn.openmap.omGraphics.DrawingAttributesPropertyEditor");
props.put(initPropertiesProperty, ImageFilePathProperty + " "
+ ImageCacheSizeProperty + " " + ImageCutoffRatioProperty + " "
+ dummyMarker);
return props;
}
/**
* OMGraphicHandlerLayer method called when projection changes or when
* doPrepare() is called. If the interal OMGraphicList is null the image
* file paths will be used to read image files.
*/
public synchronized OMGraphicList prepare() {
OMGraphicList list = getList();
if (list == null) {
list = new OMGraphicList();
setList(list);
Thread loadThread = new Thread() {
public void run() {
loadImages();
fireStatusUpdate(LayerStatusEvent.FINISH_WORKING);
}
};
loadThread.start();
} else {
list.generate(getProjection());
}
return list;
}
/**
* Gets the filePaths and loads the images found in those places. Should be
* called in a non-AWT thread.
*
* @return OMGraphicList retrieved from getList(), or a new list of that
* list is null.
*/
protected OMGraphicList loadImages() {
clearImageTileList();
OMGraphicList ret = getList();
if (ret == null) {
ret = new OMGraphicList();
setList(ret);
} else {
ret.clear();
}
if (filePaths != null) {
for (Iterator it = filePaths.iterator(); it.hasNext();) {
loadImage((String) it.next(), ret);
}
}
return ret;
}
/**
* If filePath is a file, the ImageReaderLoaders are used to try to load
* and place the image. If filePath is a directory, this method is called
* for each file contained within. ImageTile objects are created from the
* image files.
*
* @param filePath
* @param ret The OMGraphicList to add any ImageTiles to.
*/
protected void loadImage(String filePath, OMGraphicList ret) {
File file = new File(filePath);
if (file.exists() && file.isDirectory()) {
String[] files = file.list();
for (int i = 0; i < files.length; i++) {
loadImage(filePath + "/" + files[i], ret);
}
} else {
fireStatusUpdate(LayerStatusEvent.START_WORKING);
try {
URL fileURL = PropUtils.getResourceOrFileOrURL(filePath);
if (fileURL != null) {
if (imageReaderLoaders != null) {
ImageTile imageTile = null;
for (Iterator it = imageReaderLoaders.iterator(); it.hasNext();) {
ImageReaderLoader idl = (ImageReaderLoader) it.next();
if (idl.isLoadable(filePath)) {
ImageReader id = idl.getImageReader(fileURL);
ImageTile tmpImageTile = id.getImageTile(imageCache);
if (imageTile == null) {
imageTile = tmpImageTile;
} else if (tmpImageTile != null
&& imageTile instanceof ErrImageTile) {
imageTile = tmpImageTile;
}
if (imageTile != null
&& !(imageTile instanceof ErrImageTile)) {
break;
}
}
}
// Need to check for null in case none of the
// ImageReaders could handle the file.
if (imageTile != null) {
addImageToLists(imageTile, ret, fileURL);
}
} else {
logger.warning("ImageReaders not configured in "
+ getName() + " ImageTileLayer.");
}
} else {
logger.warning("Can't get URL from " + filePath);
}
} catch (MalformedURLException murle) {
}
}
}
/**
* A method to handle a newly created ImageTile object from the loadImage
* method.
*
* @param imageTile The new ImageTile
* @param ret An OMGraphicList to add the ImageTile to.
* @param fileURL A URL describing the location of the source image file.
*/
protected void addImageToLists(ImageTile imageTile, OMGraphicList ret,
URL fileURL) {
imageTile.generate(getProjection());
ret.add(imageTile);
addImageTileToList(imageTile);
imageTile.putAttribute(FILE_PATH_ATTRIBUTE, fileURL.getPath());
// Probably need to check for the last slash
// and grab that part.
imageTile.putAttribute(NAME_ATTRIBUTE, fileURL.getFile());
selectedDrawingAttributes.setTo(imageTile);
// Let's just assume that we're working with
// the main list here at the top level, and we
// can paint the images we have.
repaint();
if (resultsList != null) {
resultsList.repaint();
}
}
public final static String NAME_ATTRIBUTE = "NAME";
public final static String FILE_PATH_ATTRIBUTE = "FILE_PATH";
protected JPanel itPanel;
protected JList resultsList;
protected DefaultListModel listModel;
protected ListManager listManager;
JButton showHideButton;
JButton gotoButton;
JToggleButton locateButton;
ImageControlButtonPanel icbp;
/**
* Gets the gui controls associated with the layer.
*
* @return IPanel for layer controls.
*/
public Component getGUI() {
if (itPanel == null) {
itPanel = new JPanel();
GridBagConstraints c = new GridBagConstraints();
GridBagLayout gridbag = new GridBagLayout();
itPanel.setLayout(gridbag);
icbp = new ImageControlButtonPanel();
gridbag.setConstraints(icbp, c);
itPanel.add(icbp);
resultsList = new JList(getListModel());
resultsList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
listManager = new ListManager();
resultsList.addListSelectionListener(listManager);
resultsList.addMouseListener(listManager);
resultsList.addMouseMotionListener(listManager);
resultsList.setCellRenderer(new ImageListCellRenderer());
JScrollPane listScrollPane = new JScrollPane(resultsList);
listScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
listScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
c.fill = GridBagConstraints.BOTH;
c.weightx = 1.0f;
c.weighty = 1.0f;
c.insets = new Insets(5, 5, 5, 5);
c.gridwidth = GridBagConstraints.REMAINDER;
gridbag.setConstraints(listScrollPane, c);
itPanel.add(listScrollPane);
JPanel buttonPanel = new JPanel();
GridBagLayout bGridbag = new GridBagLayout();
GridBagConstraints bc = new GridBagConstraints();
showHideButton = new JButton(HIDE_TILES_TITLE);
showHideButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
showHideTiles(((JButton) ae.getSource()).getText(),
getSelectedTiles());
}
});
bGridbag.setConstraints(showHideButton, bc);
buttonPanel.add(showHideButton);
gotoButton = new JButton(i18n.get(ImageTileLayer.class,
"gotoButton",
"Go To"));
gotoButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
goTo(getSelectedTiles());
}
});
bGridbag.setConstraints(gotoButton, bc);
buttonPanel.add(gotoButton);
locateButton = new JToggleButton(i18n.get(ImageTileLayer.class,
"locateButton",
"Highlight"));
locateButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
JToggleButton jtb = (JToggleButton) ae.getSource();
setSelection(getSelectedTiles(), jtb.isSelected());
}
});
bGridbag.setConstraints(locateButton, bc);
buttonPanel.add(locateButton);
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1.0f;
c.weighty = 0f;
c.insets = new Insets(0, 5, 5, 5);
gridbag.setConstraints(buttonPanel, c);
itPanel.add(buttonPanel);
setGUIButtonEnableState(false);
}
return itPanel;
}
/**
* A modified LayerControlButtonPanel that is used to control which image
* files are available and their display order relative to each other on the
* image stack.
*
* @author dietrick
*/
class ImageControlButtonPanel extends LayerControlButtonPanel {
public ImageControlButtonPanel() {
super();
add = new JButton(addgif);
add.setActionCommand(LayersPanel.LayerAddCmd);
add.setToolTipText(i18n.get(ImageTileLayer.class,
"addImage",
I18n.TOOLTIP,
"Add images(s)"));
add.addActionListener(ImageControlButtonPanel.this);
add(add);
// Fix the tooltips:
delete.setToolTipText(i18n.get(ImageTileLayer.class,
"deleteImage",
I18n.TOOLTIP,
"Remove image(s)"));
delete.setEnabled(true);
top.setToolTipText(i18n.get(ImageTileLayer.class,
"moveImageToTop",
I18n.TOOLTIP,
"Move selected image(s) to top"));
up.setToolTipText(i18n.get(ImageTileLayer.class,
"moveImageUp",
I18n.TOOLTIP,
"Move selected image(s) up"));
down.setToolTipText(i18n.get(ImageTileLayer.class,
"moveImageDown",
"Move selected image(s) down"));
bottom.setToolTipText(i18n.get(ImageTileLayer.class,
"moveImageToBottom",
I18n.TOOLTIP,
"Move selected image(s) to bottom"));
}
public void actionPerformed(ActionEvent ae) {
String cmd = ae.getActionCommand();
if (cmd == LayersPanel.LayerAddCmd) {
addNewImagesWithFileChooser();
} else if (cmd == LayersPanel.LayerRemoveCmd) {
removeImages(getSelectedTiles());
} else if (cmd == LayersPanel.LayerDownCmd) {
moveOneSlotToBottom(getSelectedTiles());
ImageTileLayer.this.repaint();
} else if (cmd == LayersPanel.LayerBottomCmd) {
moveToBottom(getSelectedTiles());
ImageTileLayer.this.repaint();
} else if (cmd == LayersPanel.LayerTopCmd) {
moveToTop(getSelectedTiles());
ImageTileLayer.this.repaint();
} else if (cmd == LayersPanel.LayerUpCmd) {
moveOneSlotToTop(getSelectedTiles());
ImageTileLayer.this.repaint();
}
}
public void setGUIButtonEnableState(boolean somethingSelected) {
delete.setEnabled(somethingSelected);
top.setEnabled(somethingSelected);
up.setEnabled(somethingSelected);
down.setEnabled(somethingSelected);
bottom.setEnabled(somethingSelected);
}
public void setGUIDeleteButtonEnableState(boolean state) {
delete.setEnabled(state);
}
}
/**
* Changes the visibility setting on all ImageTile objects.
*
* @param visible
*/
protected void setVisibilityOnAllTiles(boolean visible) {
OMGraphicList list = getList();
if (list != null) {
for (Iterator it = list.iterator(); it.hasNext();) {
((OMGraphic) it.next()).setVisible(visible);
}
repaint();
}
}
/**
* Action method called when the show/hide button is pressed.
*
* @param text if SHOW_TILES_TITLE, tiles made visible.
* @param selectedTiles2
*/
protected void showHideTiles(String text, ImageTile[] selectedTiles2) {
boolean isVisible = (text == SHOW_TILES_TITLE);
for (int i = 0; i < selectedTiles2.length; i++) {
selectedTiles2[i].setVisible(isVisible);
}
checkShowHideStatus();
repaint();
if (resultsList != null) {
resultsList.repaint();
}
}
/**
* Move all the selected tiles down one space.
*
* @param selectedTiles2
*/
protected void moveOneSlotToBottom(ImageTile[] selectedTiles2) {
OMGraphicList list = getList();
if (list != null && selectedTiles != null && selectedTiles.length > 0) {
for (int i = selectedTiles2.length - 1; i >= 0; i--) {
ImageTile tile = selectedTiles2[i];
list.moveIndexedOneToBottom(list.indexOf(tile));
}
rebuildListModel();
}
}
/**
* Move all of the selected tiles to the bottom of the stack.
*
* @param selectedTiles2
*/
protected void moveToBottom(ImageTile[] selectedTiles2) {
OMGraphicList list = getList();
if (list != null && selectedTiles != null && selectedTiles.length > 0) {
for (int i = 0; i < selectedTiles2.length; i++) {
ImageTile tile = selectedTiles2[i];
list.moveIndexedToBottom(list.indexOf(tile));
}
rebuildListModel();
}
}
/**
* Move all of the selected tiles up one space.
*
* @param selectedTiles2
*/
protected void moveOneSlotToTop(ImageTile[] selectedTiles2) {
OMGraphicList list = getList();
if (list != null && selectedTiles != null && selectedTiles.length > 0) {
for (int i = 0; i < selectedTiles2.length; i++) {
ImageTile tile = selectedTiles2[i];
list.moveIndexedOneToTop(list.indexOf(tile));
}
rebuildListModel();
}
}
/**
* Move all of the selected tiles to the top of the stack.
*
* @param selectedTiles2
*/
protected void moveToTop(ImageTile[] selectedTiles2) {
OMGraphicList list = getList();
if (list != null && selectedTiles != null && selectedTiles.length > 0) {
for (int i = selectedTiles2.length - 1; i >= 0; i--) {
ImageTile tile = selectedTiles2[i];
list.moveIndexedToTop(list.indexOf(tile));
}
rebuildListModel();
}
}
/**
* MapBean is used to reset the projection of the map over the selected
* images.
*/
protected MapBean mapBean;
/**
* Figure out where the images are and move the MapBean over them.
*
* @param selectedTiles2
*/
protected void goTo(ImageTile[] selectedTiles2) {
if (mapBean == null) {
MapHandler bc = (MapHandler) getBeanContext();
if (bc != null) {
mapBean = (MapBean) bc.get(com.bbn.openmap.MapBean.class);
}
}
if (mapBean != null) {
if (selectedTiles != null && selectedTiles.length > 0) {
Rectangle2D rec = null;
for (int i = selectedTiles2.length - 1; i >= 0; i--) {
ImageTile tile = selectedTiles2[i];
if (rec == null) {
rec = new Rectangle2D.Float(tile.getLRLon(), tile.getLRLat(), 0f, 0f);
rec.add(tile.getULLon(), tile.getULLat());
} else {
rec.add(tile.getULLon(), tile.getULLat());
rec.add(tile.getLRLon(), tile.getLRLat());
}
}
if (rec != null) {
LatLonPoint center = new LatLonPoint(rec.getCenterY(), rec.getCenterX());
LatLonPoint anchor1 = new LatLonPoint(rec.getMaxY(), rec.getMinX());
LatLonPoint anchor2 = new LatLonPoint(rec.getMinY(), rec.getMaxX());
Proj proj = (Proj) mapBean.getProjection();
float scale = com.bbn.openmap.proj.ProjMath.getScale(anchor1,
anchor2,
proj);
if (logger.isLoggable(Level.FINE)) {
logger.info("Images cover " + anchor1 + " to "
+ anchor2 + ", scale adjusted to " + scale);
}
proj.setCenter(center);
proj.setScale(scale);
mapBean.setProjection(proj);
}
}
}
}
/**
* Note the provided tiles as being highlighted. Selection, in this case,
* means the OMGraphic selection.
*
* @param selectedTiles2
*/
protected void select(ImageTile[] selectedTiles2) {
setSelection(selectedTiles2, true);
}
/**
* Note the provided tiles as being highlighted or not. Selection, in this
* case, means the OMGraphic selection.
*
* @param selectedTiles2
*/
protected void setSelection(ImageTile[] selectedTiles2, boolean isSelected) {
for (int i = 0; i < selectedTiles2.length; i++) {
selectedTiles2[i].setSelected(isSelected);
}
repaint();
}
/**
* Un-highlight all of the tiles.
*/
public void deselect() {
OMGraphicList list = getList();
if (list != null) {
for (Iterator it = list.iterator(); it.hasNext();) {
((OMGraphic) it.next()).setSelected(false);
}
repaint();
}
}
/**
* Take the drawing attributes held by the layer and push the settings on
* all of the ImageTiles.
*/
public void resetSelectAttributes() {
OMGraphicList list = getList();
if (list != null) {
for (Iterator it = list.iterator(); it.hasNext();) {
selectedDrawingAttributes.setTo((OMGraphic) it.next());
}
repaint();
}
}
/**
* Remove the selected tiles from the image stack. Asks the user for
* confirmation.
*
* @param selectedTiles2
*/
protected void removeImages(ImageTile[] selectedTiles2) {
ImageTile[] selectedTiles = getSelectedTiles();
if (selectedTiles != null && selectedTiles.length > 0) {
String confirmStringMulti = i18n.get(ImageTileLayer.class,
"removeConfirmMultiple",
"Are you sure you want to remove these images from the layer?");
String confirmStringSolo = i18n.get(ImageTileLayer.class,
"removeConfirmSolo",
"Are you sure you want to remove this image from the layer?");
String confirmTitleString = i18n.get(ImageTileLayer.class,
"removeConfirmTitle",
"Remove Images?");
int answer = JOptionPane.showConfirmDialog(this,
(selectedTiles.length == 1 ? confirmStringSolo
: confirmStringMulti),
confirmTitleString,
JOptionPane.YES_NO_OPTION);
if (answer == JOptionPane.YES_OPTION) {
OMGraphicList list = getList();
if (list != null) {
for (int i = 0; i < selectedTiles.length; i++) {
ImageTile selectedTile = selectedTiles[i];
list.remove(selectedTile);
((DefaultListModel) getListModel()).removeElement(selectedTile);
}
if (resultsList != null) {
resultsList.repaint();
}
repaint();
}
}
}
}
/**
* Asks the user to choose a new file or directory to load. The
* ImageReaderLoaders are consulted to only allow files that can be handled
* to be selectable.
*/
protected void addNewImagesWithFileChooser() {
// Need to get File Chooser, and allow the user to add a directory or
// file. We could even set up filters to check the ImageReaderLoaders
// available to this Layer and limit file selection based on matches. We
// should also report to the user what files were loaded using a dialog
// window,
// or we could simply select the new images and scroll the list to make
// those images visible.
File startingPoint = new File(Environment.get("lastchosendirectory",
System.getProperty("user.home")));
JFileChooser chooser = new JFileChooser(startingPoint);
String title = i18n.get(ImageTileLayer.class,
"addImagesWindowTitle",
"Add Images");
chooser.setDialogTitle(title);
chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
chooser.setFileFilter(new ImageLoaderFileFilter(imageReaderLoaders));
String acceptButtonText = i18n.get(ImageTileLayer.class,
"acceptButtonText",
"Add");
int state = chooser.showDialog(null, acceptButtonText);
try {
// only bother trying to read the file if there is one
// for some reason, the APPROVE_OPTION said it was a
// boolean during compile and didn't work in this next
// statement
if ((state != JFileChooser.CANCEL_OPTION)
&& (state != JFileChooser.ERROR_OPTION)) {
String newFile = chooser.getSelectedFile().getCanonicalPath();
int dirIndex = newFile.lastIndexOf(File.separator);
if (dirIndex >= 0) {
// store the selected file for later
Environment.set("lastchosendirectory", newFile.substring(0,
dirIndex));
}
OMGraphicList list = getList();
if (list == null) {
list = new OMGraphicList();
setList(list);
}
LoadImageThread lit = new LoadImageThread(newFile, list);
lit.start();
}
} catch (IOException ioe) {
JOptionPane.showMessageDialog(null,
ioe.getMessage(),
"Error picking file",
JOptionPane.ERROR_MESSAGE);
ioe.printStackTrace();
}
}
/**
* A special Thread subclass to handle image loading, so it's not managed by
* the AWT thread.
*
* @author dietrick
*/
class LoadImageThread extends Thread {
String fileToOpen;
OMGraphicList listToAddTo;
public LoadImageThread(String fto, OMGraphicList ltat) {
fileToOpen = fto;
listToAddTo = ltat;
}
public void run() {
loadImage(fileToOpen, listToAddTo);
fireStatusUpdate(LayerStatusEvent.FINISH_WORKING);
}
}
/**
* Set the GUI button state to be enabled or not based on something on the
* list being selected.
*
* @param somethingSelected whether something is selected.
*/
protected void setGUIButtonEnableState(boolean somethingSelected) {
if (icbp != null) {
icbp.setGUIButtonEnableState(somethingSelected);
showHideButton.setEnabled(somethingSelected);
gotoButton.setEnabled(somethingSelected);
locateButton.setEnabled(somethingSelected);
}
}
/**
* The ListModel used by the JList, displaying the images.
*
* @return
*/
protected synchronized ListModel getListModel() {
if (listModel == null) {
listModel = new DefaultListModel();
}
return listModel;
}
/**
* Add an ImageTile to the list model.
*
* @param tile
*/
protected void addImageTileToList(ImageTile tile) {
((DefaultListModel) getListModel()).addElement(tile);
}
/**
* Clear the list model.
*/
protected void clearImageTileList() {
((DefaultListModel) getListModel()).clear();
}
/**
* Remove an ImageTile from the ListModel.
*
* @param tile
* @return
*/
protected boolean removeImageTileFromList(ImageTile tile) {
return ((DefaultListModel) getListModel()).removeElement(tile);
}
/**
* Rebuild the list model contents based on the ImageTiles contained on the
* OMGraphicList.
*
*/
protected void rebuildListModel() {
DefaultListModel dlm = (DefaultListModel) getListModel();
OMGraphicList list = getList();
int[] selectedIndicies = null;
boolean checkForIndicies = false;
if (list != null) {
if (selectedTiles != null && selectedTiles.length > 0) {
selectedIndicies = new int[selectedTiles.length];
checkForIndicies = true;
}
int tileCount = 0;
for (Iterator it = list.iterator(); it.hasNext(); tileCount++) {
ImageTile imageTile = (ImageTile) it.next();
if (checkForIndicies) {
for (int i = 0; i < selectedTiles.length; i++) {
if (imageTile == selectedTiles[i]) {
for (int j = 0; j < selectedIndicies.length; j++) {
selectedIndicies[j] = tileCount + j;
}
checkForIndicies = false;
}
}
}
}
// Causes value changed() to be called, which then unsets selected
// tiles. So we need to find out which tiles were selected above,
// and then set them again later.
dlm.clear();
for (Iterator it = list.iterator(); it.hasNext(); tileCount++) {
dlm.addElement(it.next());
}
}
if (resultsList != null) {
if (selectedIndicies != null) {
resultsList.setSelectedIndices(selectedIndicies);
}
resultsList.repaint();
}
}
/**
* The ImageTiles currently selected on the list in the GUI.
*/
protected ImageTile[] selectedTiles;
/**
* @return the ImageTile[] of tiles currently selected in the GUI.
*/
protected ImageTile[] getSelectedTiles() {
return selectedTiles;
}
/**
* Set the ImageTile[] of tiles currently selected in the GUI.
*
* @param sTiles
*/
protected void setSelectedTiles(ImageTile[] sTiles) {
selectedTiles = sTiles;
boolean allTilesDefective = areAllTilesDefective(sTiles);
setGUIButtonEnableState(sTiles != null && sTiles.length > 0
&& !allTilesDefective);
if (allTilesDefective && icbp != null) {
icbp.setGUIDeleteButtonEnableState(allTilesDefective);
}
checkShowHideStatus();
}
protected boolean areAllTilesDefective(ImageTile[] sTiles) {
boolean allTilesDefective = false;
if (sTiles != null && sTiles.length > 0) {
allTilesDefective = true;
for (int i = 0; i < sTiles.length; i++) {
if (!(sTiles[i] instanceof ErrImageTile)) {
allTilesDefective = false;
break;
}
}
}
return allTilesDefective;
}
/**
* Checks the selected tiles from the visible list and tallies their
* visibility. If all of the tiles are invisible, the GUI button will allow
* them to be made visible. If any of them are visible, all of them can be
* made invisible before the button will change them to the visible.
*
*/
public void checkShowHideStatus() {
ImageTile[] sTiles = getSelectedTiles();
boolean anyTilesVisible = (sTiles == null || sTiles.length == 0);
if (sTiles != null) {
for (int i = 0; i < sTiles.length; i++) {
anyTilesVisible = sTiles[i].isVisible() || anyTilesVisible;
}
}
showHideButton.setText(anyTilesVisible ? HIDE_TILES_TITLE
: SHOW_TILES_TITLE);
}
/**
* A list selection listener object for the JList.
*
* @author dietrick
*/
class ListManager implements ListSelectionListener, MouseListener,
MouseMotionListener {
public ListManager() {
}
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting() == false && resultsList != null) {
// Reset the location selection;
locateButton.setSelected(false);
deselect();
int[] indicies = resultsList.getSelectedIndices();
ImageTile[] selectedTiles = new ImageTile[indicies.length];
if (indicies.length > 0) {
ListModel listModel = getListModel();
for (int i = 0; i < indicies.length; i++) {
selectedTiles[i] = (ImageTile) listModel.getElementAt(indicies[i]);
}
}
setSelectedTiles(selectedTiles);
}
}
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
public void mousePressed(MouseEvent e) {
checkMouseSelection(e);
}
public void mouseReleased(MouseEvent e) {
checkMouseSelection(e);
}
protected void checkMouseSelection(MouseEvent e) {
int selectedIndex = getResultListIndex(e);
if (selectedIndex < 0) {
resultsList.clearSelection();
}
}
public void mouseDragged(MouseEvent e) {
// TODO Auto-generated method stub
}
public void mouseMoved(MouseEvent e) {
int selectedIndex = getResultListIndex(e);
if (selectedIndex >= 0) {
Object it = getListModel().getElementAt(selectedIndex);
if (it instanceof ErrImageTile) {
resultsList.setToolTipText(((ErrImageTile) it).getProblemMessage());
return;
}
}
resultsList.setToolTipText(null);
}
}
/**
* find out which list object was moused.
*
* @param e
* @return
*/
protected int getResultListIndex(MouseEvent e) {
int index = -1;
if (resultsList != null) {
double height = getResultsListCellHeight();
if (height == 0) {
return index;
}
int nIndex = e.getY() / (int) height;
if (nIndex < getListModel().getSize()) {
index = nIndex;
}
}
return index;
}
/**
* Get the pixel height of each cell in the JList.
*
* @return
*/
protected double getResultsListCellHeight() {
double height = 0;
if (resultsList != null) {
int rlFVI = resultsList.getFirstVisibleIndex();
Rectangle bounds = resultsList.getCellBounds(rlFVI, rlFVI);
if (bounds != null) {
height = bounds.getHeight();
}
}
return height;
}
public static int buttonSize = 16;
public static ImageIcon warningImage;
public static ImageIcon invisibleImage;
protected static void initIcons() {
DrawingAttributes blackDa = new DrawingAttributes();
DrawingAttributes invisDa = new DrawingAttributes();
invisDa.setLinePaint(OMColor.clear);
invisDa.setFillPaint(OMColor.clear);
DrawingAttributes yellowDa = new DrawingAttributes();
yellowDa.setLinePaint(OMColor.yellow);
yellowDa.setFillPaint(OMColor.yellow);
IconPart ip = new BasicIconPart(new Rectangle2D.Double(0, 0, 100, 100));
ip.setRenderingAttributes(invisDa);
invisibleImage = OMIconFactory.getIcon(buttonSize, buttonSize, ip);
IconPartList ipl = new IconPartList();
Polygon triangle = new Polygon(new int[] { 50, 90, 10, 50 }, new int[] {
10, 90, 90, 10 }, 4);
BasicIconPart bip = new BasicIconPart(triangle);
bip.setRenderingAttributes(yellowDa);
ipl.add(bip);
bip = new BasicIconPart(triangle);
bip.setRenderingAttributes(yellowDa);
ipl.add(bip);
bip = new BasicIconPart(triangle);
bip.setRenderingAttributes(blackDa);
ipl.add(bip);
bip = new BasicIconPart(new Line2D.Double(49, 35, 49, 65));
bip.setRenderingAttributes(blackDa);
ipl.add(bip);
bip = new BasicIconPart(new Line2D.Double(49, 75, 49, 77));
bip.setRenderingAttributes(blackDa);
ipl.add(bip);
bip = new BasicIconPart(new Line2D.Double(51, 35, 51, 65));
bip.setRenderingAttributes(blackDa);
ipl.add(bip);
bip = new BasicIconPart(new Line2D.Double(51, 75, 51, 77));
bip.setRenderingAttributes(blackDa);
ipl.add(bip);
warningImage = OMIconFactory.getIcon(buttonSize, buttonSize, ipl);
}
/**
* Renders the JList cells.
*/
public static class ImageListCellRenderer extends JPanel implements
ListCellRenderer {
protected int buttonSize = 16;
protected JLabel label = new JLabel();
protected JLabel statusMark = new JLabel();
public static Color fontColor = Color.BLACK;
public static Color altFontColor = Color.BLACK;
public static Color selectColor = Color.GRAY;
public static Color notVisibleColor = new Color(100, 100, 100);
public static Color regularBackgroundColor = Color.WHITE;
public ImageListCellRenderer() {
if (warningImage == null) {
initIcons();
}
setOpaque(true);
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
setLayout(gridbag);
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1.0f;
gridbag.setConstraints(label, c);
this.add(label);
c.fill = GridBagConstraints.NONE;
c.weightx = 0f;
gridbag.setConstraints(statusMark, c);
this.add(statusMark);
Font f = label.getFont();
f = new Font(f.getName(), f.getStyle(), f.getSize() - 1);
label.setFont(f);
setPreferredSize(new Dimension(20, buttonSize));
}
public Component getListCellRendererComponent(JList list, Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
if (value instanceof ImageTile) {
ImageTile imageTile = (ImageTile) value;
label.setText((String) imageTile.getAttribute(NAME_ATTRIBUTE));
if (!isSelected) {
label.setForeground(imageTile.isVisible() ? fontColor
: notVisibleColor);
}
if (value instanceof ErrImageTile) {
statusMark.setIcon(warningImage);
} else {
statusMark.setIcon(invisibleImage);
}
}
setBackground(isSelected ? selectColor : regularBackgroundColor);
return this;
}
}
/**
* File filter created based on what the ImageReaders can handle.
*
* @author dietrick
*/
class ImageLoaderFileFilter extends FileFilter {
Vector imageReaderLoaders;
public ImageLoaderFileFilter(Vector imgDcdrLdrs) {
imageReaderLoaders = imgDcdrLdrs;
}
public boolean accept(File f) {
if (f.isDirectory()) {
return true;
}
if (imageReaderLoaders != null) {
for (Iterator it = imageReaderLoaders.iterator(); it.hasNext();) {
if (((ImageReaderLoader) it.next()).isLoadable(f.getName())) {
return true;
}
}
}
return false;
}
public String getDescription() {
String description = i18n.get(ImageTileLayer.class,
"fileFilterDescription",
"Image File Formats Supported by Layer");
return description;
}
}
}