/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2008, 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;
import java.awt.Color;
import java.awt.Dimension;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import net.miginfocom.swing.MigLayout;
import org.geotools.renderer.GTRenderer;
import org.geotools.swing.action.PanAction;
import org.geotools.swing.action.ResetAction;
import org.geotools.swing.action.ZoomInAction;
import org.geotools.swing.action.ZoomOutAction;
import org.geotools.map.MapContext;
import org.geotools.renderer.lite.StreamingRenderer;
import org.geotools.swing.action.InfoAction;
/**
* A Swing frame containing a map display pane and (optionally) a toolbar,
* status bar and map layer table.
* <p>
* Simplest use is with the static {@linkplain #showMap(MapContext)} method:
* <pre>{@code \u0000
* MapContext context = new DefaultMapContext();
* context.setTitle("Maps R Us");
*
* // add some layers to the MapContext...
*
* JMapFrame.showMap(context);
* }</pre>
*
* @see MapLayerTable
* @see StatusBar
*
* @author Michael Bedward
* @since 2.6
*
* @source $URL$
*/
public class JMapFrame extends JFrame {
/**
* Constants for available toolbar buttons used with the
* {@linkplain #enableTool} method.
*/
public enum Tool {
/**
* Used to request that an empty toolbar be created
*/
NONE,
/**
* Requests the feature info cursor tool
*/
INFO,
/**
* Requests the pan cursor tool
*/
PAN,
/**
* Requests the reset map extent cursor tool
*/
RESET,
/**
* Requests the zoom in and out cursor tools
*/
ZOOM;
}
private Set<Tool> toolSet;
/*
* UI elements
*/
private JMapPane mapPane;
private MapLayerTable mapLayerTable;
private JToolBar toolBar;
private StatusBar statusBar;
private boolean showStatusBar;
private boolean showLayerTable;
private boolean uiSet;
/**
* Creates a new {@code JMapFrame} object with a toolbar, map pane and status
* bar; sets the supplied {@code MapContext}; and displays the frame on the
* AWT event dispatching thread. The context's title is used as the frame's
* title.
*
* @param context the map context containing the layers to display
*/
public static void showMap(MapContext context) {
final JMapFrame frame = new JMapFrame(context);
frame.enableStatusBar(true);
frame.enableToolBar(true);
frame.initComponents();
frame.setSize(500, 500);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
frame.setVisible(true);
}
});
}
/**
* Default constructor. Creates a {@code JMapFrame} with
* no context or renderer set
*/
public JMapFrame() {
this(null);
}
/**
* Constructs a new {@code JMapFrame} object with specified context
* and a default renderer (an instance of {@link StreamingRenderer}).
*
* @param context the map context with layers to be displayed
*/
public JMapFrame(MapContext context) {
this(context, new StreamingRenderer());
}
/**
* Constructs a new {@code JMapFrame} object with specified context and renderer
*
* @param context the map context with layers to be displayed
* @param renderer the renderer to be used
*/
public JMapFrame(MapContext context, GTRenderer renderer) {
super(context == null ? "" : context.getTitle());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
showLayerTable = false;
showStatusBar = false;
toolSet = new HashSet<Tool>();
// the map pane is the one element that is always displayed
mapPane = new JMapPane();
mapPane.setBackground(Color.WHITE);
mapPane.setMapContext(context);
mapPane.setRenderer(renderer);
}
/**
* Set whether a toolbar, with a basic set of map tools, will be displayed
* (default is false). Calling this with state == true is equivalent to
* calling {@linkplain #enableTool} with all {@linkplain JMapFrame.Tool}
* constants.
*
* @param state whether the toolbar is required
*/
public void enableToolBar(boolean state) {
if (state) {
toolSet.addAll(EnumSet.allOf(Tool.class));
} else {
toolSet.clear();
}
}
/**
* This method is an alternative to {@linkplain #enableToolBar(boolean)}.
* It requests that a tool bar be created with specific tools, identified
* by {@linkplain JMapFrame.Tool} constants.
* <code><pre>
* myMapFrame.enableTool(Tool.PAN, Tool.ZOOM);
* </pre></code>
*
* @param tool one or more {@linkplain JMapFrame.Tool} constants
*/
public void enableTool(Tool ...tool) {
for (Tool t : tool) {
toolSet.add(t);
}
}
/**
* Set whether a status bar will be displayed to display cursor position
* and map bounds.
*
* @param state whether the status bar is required.
*/
public void enableStatusBar(boolean state) {
showStatusBar = state;
}
/**
* Set whether a map layer table will be displayed to show the list
* of layers in the map context and set their order, visibility and
* selected status.
*
* @param state whether the map layer table is required.
*/
public void enableLayerTable(boolean state) {
showLayerTable = state;
}
/**
* Calls {@linkplain #initComponents()} if it has not already been called explicitly
* to construct the frame's components before showing the frame.
*
* @param state true to show the frame; false to hide.
*/
@Override
public void setVisible(boolean state) {
if (state && !uiSet) {
initComponents();
}
super.setVisible(state);
}
/**
* Creates and lays out the frame's components that have been
* specified with the enable methods (e.g. {@linkplain #enableToolBar(boolean)} ).
* If not called explicitly by the client this method will be invoked by
* {@linkplain #setVisible(boolean) } when the frame is first shown.
*/
public void initComponents() {
if (uiSet) {
// @todo log a warning ?
return;
}
/*
* We use the MigLayout manager to make it easy to manually code
* our UI design
*/
StringBuilder sb = new StringBuilder();
if (!toolSet.isEmpty()) {
sb.append("[]"); // fixed size
}
sb.append("[grow]"); // map pane and optionally layer table fill space
if (showStatusBar) {
sb.append("[30px::]"); // status bar height
}
JPanel panel = new JPanel(new MigLayout(
"wrap 1, insets 0", // layout constrains: 1 component per row, no insets
"[grow]", // column constraints: col grows when frame is resized
sb.toString() ));
/*
* A toolbar with buttons for zooming in, zooming out,
* panning, and resetting the map to its full extent.
* The cursor tool buttons (zooming and panning) are put
* in a ButtonGroup.
*
* Note the use of the XXXAction objects which makes constructing
* the tool bar buttons very simple.
*/
if (!toolSet.isEmpty()) {
toolBar = new JToolBar();
toolBar.setOrientation(JToolBar.HORIZONTAL);
toolBar.setFloatable(false);
ButtonGroup cursorToolGrp = new ButtonGroup();
if (toolSet.contains(Tool.ZOOM)) {
JButton zoomInBtn = new JButton(new ZoomInAction(mapPane));
toolBar.add(zoomInBtn);
cursorToolGrp.add(zoomInBtn);
JButton zoomOutBtn = new JButton(new ZoomOutAction(mapPane));
toolBar.add(zoomOutBtn);
cursorToolGrp.add(zoomOutBtn);
toolBar.addSeparator();
}
if (toolSet.contains(Tool.PAN)) {
JButton panBtn = new JButton(new PanAction(mapPane));
toolBar.add(panBtn);
cursorToolGrp.add(panBtn);
toolBar.addSeparator();
}
if (toolSet.contains(Tool.INFO)) {
JButton infoBtn = new JButton(new InfoAction(mapPane));
toolBar.add(infoBtn);
toolBar.addSeparator();
}
if (toolSet.contains(Tool.RESET)) {
JButton resetBtn = new JButton(new ResetAction(mapPane));
toolBar.add(resetBtn);
}
panel.add(toolBar, "grow");
}
if (showLayerTable) {
mapLayerTable = new MapLayerTable(mapPane);
/*
* We put the map layer panel and the map pane into a JSplitPane
* so that the user can adjust their relative sizes as needed
* during a session. The call to setPreferredSize for the layer
* panel has the effect of setting the initial position of the
* JSplitPane divider
*/
mapLayerTable.setPreferredSize(new Dimension(200, -1));
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, false, mapLayerTable, mapPane);
panel.add(splitPane, "grow");
} else {
/*
* No layer table, just the map pane
*/
panel.add(mapPane, "grow");
}
if (showStatusBar) {
statusBar = new StatusBar(mapPane);
panel.add(statusBar, "grow");
}
this.getContentPane().add(panel);
uiSet = true;
}
/**
* Get the map context associated with this frame.
* Returns {@code null} if no map context has been set explicitly with the
* constructor or {@linkplain #setMapContext}.
*
* @return the current {@code MapContext} object
*/
public MapContext getMapContext() {
return mapPane.getMapContext();
}
/**
* Set the MapContext object used by this frame.
*
* @param context a MapContext instance
* @throws IllegalArgumentException if context is null
*/
public void setMapContext(MapContext context) {
if (context == null) {
throw new IllegalArgumentException("context must not be null");
}
mapPane.setMapContext(context);
}
/**
* Get the renderer being used by this frame.
* Returns {@code null} if no renderer was set via the constructor
* or {@linkplain #setRenderer}.
*
* @return the current {@code GTRenderer} object
*/
public GTRenderer getRenderer() {
return mapPane.getRenderer();
}
/**
* Set the renderer to be used by this frame.
*
* @param renderer a GTRenderer instance
* @throws IllegalArgumentException if renderer is null
*/
public void setRenderer(GTRenderer renderer) {
if (renderer == null) {
throw new IllegalArgumentException("renderer must not be null");
}
mapPane.setRenderer(renderer);
}
/**
* Provides access to the instance of {@code JMapPane} being used
* by this frame.
*
* @return the {@code JMapPane} object
*/
public JMapPane getMapPane() {
return mapPane;
}
/**
* Provides access to the toolbar being used by this frame.
* If {@linkplain #initComponents} has not been called yet
* this method will invoke it.
*
* @return the toolbar or null if the toolbar was not enabled
*/
public JToolBar getToolBar() {
if (!uiSet) initComponents();
return toolBar;
}
}