/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2008-2011, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.swing.tool; import java.awt.Cursor; import java.awt.Point; import java.awt.Toolkit; import java.util.WeakHashMap; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.ImageIcon; import org.geotools.geometry.DirectPosition2D; import org.geotools.map.Layer; import org.geotools.map.MapContent; import org.geotools.swing.locale.LocaleUtils; import org.geotools.swing.dialog.JTextReporter; import org.geotools.swing.dialog.TextReporterListener; import org.geotools.swing.event.MapMouseEvent; import org.geotools.util.logging.Logging; /** * A cursor tool to retrieve information about features that the user clicks * on with the mouse. It works with {@linkplain InfoToolHelper} objects which do * the work of querying feature data. * <p> * Feature information is displayed on screen using a {@linkplain JTextReporter} * dialog. If you want to access the displayed text programmatically you can * override the {@linkplain #onReporterUpdated()} method as shown here: * <pre><code> * InfoTool tool = new InfoTool() { * @Override * public void onReporterUpdated() { * String text = getTextReporterConnection().getText(); * // do something with text * } * }; * </code></pre> * * @author Michael Bedward * @since 2.6 * @source $URL$ * @version $URL$ */ public class InfoTool extends CursorTool implements TextReporterListener { private static final Logger LOGGER = Logging.getLogger("org.geotools.swing"); /** The tool name */ public static final String TOOL_NAME = LocaleUtils.getValue("CursorTool", "Info"); /** Tool tip text */ public static final String TOOL_TIP = LocaleUtils.getValue("CursorTool", "InfoTooltip"); /** Cursor */ public static final String CURSOR_IMAGE = "/org/geotools/swing/icons/mActionIdentify.png"; /** Cursor hotspot coordinates */ public static final Point CURSOR_HOTSPOT = new Point(0, 0); /** Icon for the control */ public static final String ICON_IMAGE = "/org/geotools/swing/icons/mActionIdentify.png"; private Cursor cursor; private WeakHashMap<Layer, InfoToolHelper> helperTable; private JTextReporter.Connection textReporterConnection; /** * Constructor */ public InfoTool() { Toolkit tk = Toolkit.getDefaultToolkit(); ImageIcon cursorIcon = new ImageIcon(getClass().getResource(CURSOR_IMAGE)); cursor = tk.createCustomCursor(cursorIcon.getImage(), CURSOR_HOTSPOT, TOOL_TIP); helperTable = new WeakHashMap<Layer, InfoToolHelper>(); } /** * Respond to a mouse click by querying each of the {@code Layers}. The * details of features lying within the threshold distance of the mouse * position are reported on screen using a {@code JTextReporter} dialog. * <p> * <b>Implementation note:</b> An instance of {@code InfoToolHelper} is created * and cached for each of the {@code Layers}. The helpers are created using * reflection to avoid direct references to grid coverage classes here that would * required JAI (Java Advanced Imaging) to be on the classpath even when only * vector layers are being used. * * @param ev mouse event * * @see JTextReporter * @see InfoToolHelper */ @Override public void onMouseClicked(MapMouseEvent ev) { DirectPosition2D pos = ev.getWorldPos(); createReporter(); report(pos); MapContent content = getMapPane().getMapContent(); final int nlayers = content.layers().size(); int n = 0; for (Layer layer : content.layers()) { if (layer.isSelected()) { InfoToolHelper helper = null; String layerName = layer.getTitle(); if (layerName == null || layerName.length() == 0) { layerName = layer.getFeatureSource().getName().getLocalPart(); } if (layerName == null || layerName.length() == 0) { layerName = layer.getFeatureSource().getSchema().getName().getLocalPart(); } helper = helperTable.get(layer); if (helper == null) { helper = InfoToolHelperLookup.getHelper(layer); if (helper == null) { LOGGER.log(Level.WARNING, "InfoTool cannot query {0}", layer.getClass().getName()); return; } helper.setMapContent(content); helper.setLayer(layer); } try { InfoToolResult result = helper.getInfo(pos); textReporterConnection.append(layerName + "\n"); textReporterConnection.append(result.toString(), 4); if (++n < nlayers) { textReporterConnection.append("\n"); } } catch (Exception ex) { LOGGER.log(Level.WARNING, "Unable to query layer {0}", layerName); } } } textReporterConnection.appendSeparatorLine(10, '-'); textReporterConnection.appendNewline(); } /** * Gets the connection to the text reporter displayed by this tool. Returns * {@code null} if the reporter dialog is not currently displayed. The * connection should not be stored because it will expire when the reporter * dialog is closed. * <p> * This method was added for unit test purposes but may be useful for * applications wishing to access the feature data report text, e.g. by * overriding {@linkplain #onReporterUpdated()}. * * @return the text reporter connection or {@code null} if the reporter dialog * is not currently displayed */ public JTextReporter.Connection getTextReporterConnection() { return textReporterConnection; } /** * Writes the mouse click position to a {@code JTextReporter} * * @param pos mouse click position in world coordinates */ private void report(DirectPosition2D pos) { textReporterConnection.append(String.format("Pos x=%.4f y=%.4f\n", pos.x, pos.y)); } /** * Creates and shows a {@code JTextReporter}. Does nothing if the * reporter is already active. */ private void createReporter() { if (textReporterConnection == null) { textReporterConnection = JTextReporter.showDialog( "Feature info", null, JTextReporter.DEFAULT_FLAGS, 20, 40); textReporterConnection.addListener(this); } } @Override public Cursor getCursor() { return cursor; } /** * {@inheritDoc} * * @return Always returns false */ @Override public boolean drawDragBox() { return false; } /** * Called when a {@code JTextReporter} dialog used by this tool is closed. */ @Override public void onReporterClosed() { textReporterConnection = null; } /** * Called when text is updated in a {@linkplain JTextReporter} dialog being used * by this tool. This is an empty method but may be useful to override. * */ @Override public void onReporterUpdated() { } }