/*
* 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.dialog;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import javax.swing.ButtonGroup;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import net.miginfocom.swing.MigLayout;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.swing.control.JIntegerField;
import org.geotools.swing.control.ValueChangedEvent;
import org.geotools.swing.control.ValueChangedListener;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.cs.AxisDirection;
import org.opengis.referencing.cs.CoordinateSystem;
/**
* A dialog to prompt for the size of an image into which a map
* will be drawn and the name and format of the file to save this
* image to. It allows the image size to be set indirectly,
* by setting the map pixel scale, or directly.
*
* @todo Add ability to work with DPI setting
*
* @author Michael Bedward
* @since 2.6.1
*
*
* @source $URL$
* @version $Id$
*/
public class JMapImageDialog extends JDialog {
public static final int DEFAULT_IMAGE_WIDTH = 1000;
private static final int WIDTH_FIELD = 0;
private static final int HEIGHT_FIELD = 1;
private JTextField fileField;
private JComboBox scaleCombo;
private JIntegerField widthField;
private JIntegerField heightField;
private boolean completed;
private Rectangle imageSize;
private boolean setByScale;
private File selectedFile;
private double heightToWidth;
private ReferencedEnvelope mapBounds;
private final int XAXIS;
private final int YAXIS;
/**
* Creates a new dialog
*
* @param mapBounds the bounds (world coordinates) of the map area to
* draw into the image
*/
public JMapImageDialog(ReferencedEnvelope mapBounds) {
if (mapBounds == null) {
throw new IllegalArgumentException("The mapBounds argument cannot be null");
}
setTitle("Output image size");
setModal(true);
setResizable(false);
this.mapBounds = new ReferencedEnvelope(mapBounds);
CoordinateReferenceSystem crs = mapBounds.getCoordinateReferenceSystem();
boolean swapAxes = false;
if (crs != null) {
CoordinateSystem cs = crs.getCoordinateSystem();
if (cs != null) {
AxisDirection dir = cs.getAxis(0).getDirection().absolute();
if (dir.equals(AxisDirection.NORTH) || dir.equals(AxisDirection.DISPLAY_UP)) {
swapAxes = true;
}
}
}
XAXIS = swapAxes ? 1 : 0;
YAXIS = swapAxes ? 0 : 1;
initComponents();
initValues();
}
/**
* Test if the dialog was completed
*
* @return true if the dialog was completed; false otherwise
*/
public boolean isCompleted() {
return completed;
}
/**
* Get the requested image bounds
*
* @return image bounds
*/
public Rectangle getImageSize() {
return imageSize;
}
/**
* Get the file to save the map image to
*
* @return the selected file
*/
public File getSelectedFile() {
return selectedFile;
}
/**
* Get the image file format as a file suffix understood
* by {@code ImageIO}
*
* @return file suffix representing the image file format
*/
public String getImageFormat() {
String format = null;
if (selectedFile != null) {
int dot = selectedFile.getName().lastIndexOf('.');
if (dot > 0) {
format = selectedFile.getName().substring(dot + 1);
}
}
return format;
}
/**
* Create and lay out the dialog components
*/
private void initComponents() {
MigLayout layout = new MigLayout(
"",
"[][]30px[][]", // col constraints
"[][][][]30px[]"); // row constraints
JPanel panel = new JPanel(layout);
JLabel fileLabel = new JLabel("File");
panel.add(fileLabel, "cell 0 0");
fileField = new JTextField(20);
panel.add(fileField, "cell 1 0 3 1");
JButton browseBtn = new JButton("Browse...");
browseBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
brosweFile();
}
});
panel.add(browseBtn, "cell 1 0");
int[] baseScales = {10, 25, 50};
int[] multipliers = {1, 10, 100, 1000, 10000, 100000};
Object[] scales = new Object[baseScales.length * multipliers.length];
for (int i = 0, k=0; i < multipliers.length; i++) {
for (int j = 0; j < baseScales.length; j++, k++) {
scales[k] = new Integer(multipliers[i] * baseScales[j]);
}
}
final JCheckBox scaleCheck = new JCheckBox("Set by pixel scale", false);
panel.add(scaleCheck, "cell 1 1");
JLabel scaleLabel = new JLabel("Scale 1:");
panel.add(scaleLabel, "cell 1 2, gapbefore indent");
DefaultComboBoxModel scaleModel = new DefaultComboBoxModel(scales);
scaleCombo = new JComboBox(scaleModel);
scaleCombo.setEditable(true);
scaleCombo.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
onScaleChange();
}
});
panel.add(scaleCombo, "cell 1 2");
final JCheckBox sizeCheck = new JCheckBox("Set by image size", true);
panel.add(sizeCheck, "cell 2 1 2 1");
JLabel widthLabel = new JLabel("width");
panel.add(widthLabel, "cell 2 2, gapbefore indent");
widthField = new JIntegerField();
widthField.addValueChangedListener( new ValueChangedListener() {
public void onValueChanged(ValueChangedEvent ev) {
onImageSizeChange(WIDTH_FIELD);
}
} );
final int fieldWidth = widthField.getFontMetrics(widthField.getFont()).stringWidth("00000000");
panel.add(widthField, String.format("cell 3 2, w %d!", fieldWidth));
JLabel heightLabel = new JLabel("height");
panel.add(heightLabel, "cell 2 3, gapbefore indent");
heightField = new JIntegerField();
heightField.addValueChangedListener( new ValueChangedListener() {
public void onValueChanged(ValueChangedEvent ev) {
onImageSizeChange(HEIGHT_FIELD);
}
} );
panel.add(heightField, String.format("cell 3 3, w %d!", fieldWidth));
JButton okBtn = new JButton("OK");
okBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
completed = true;
setVisible(false);
}
});
panel.add(okBtn, "cell 1 4 3 1");
JButton cancelBtn = new JButton("Cancel");
cancelBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
completed = false;
setVisible(false);
}
});
panel.add(cancelBtn, "cell 1 4");
ButtonGroup grp = new ButtonGroup();
grp.add(scaleCheck);
grp.add(sizeCheck);
scaleCheck.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setByScale = scaleCheck.isSelected();
enableItems();
}
});
sizeCheck.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setByScale = scaleCheck.isSelected();
enableItems();
}
});
setByScale = scaleCheck.isSelected();
enableItems();
getContentPane().add(panel);
pack();
}
/**
* Enable dialog controls for the current dialog mode:
* setting map scale or setting image size directly.
*/
private void enableItems() {
scaleCombo.setEnabled(setByScale);
widthField.setEnabled(!setByScale);
heightField.setEnabled(!setByScale);
}
/**
* Set initial default values for image size
*/
private void initValues() {
heightToWidth = mapBounds.getSpan(YAXIS) / mapBounds.getSpan(XAXIS);
imageSize = new Rectangle();
// this will cause the height and scale controls to be updated
widthField.setValue(DEFAULT_IMAGE_WIDTH);
}
/**
* Update controls when one of the image size fields has been edited
*
* @param controlModified constant indicating which field was edited
*/
private void onImageSizeChange(int controlModified) {
switch (controlModified) {
case WIDTH_FIELD:
imageSize.width = widthField.getValue();
imageSize.height = (int) Math.round(imageSize.width * heightToWidth);
heightField.setValue(imageSize.height, false);
break;
case HEIGHT_FIELD:
imageSize.height = heightField.getValue();
imageSize.width = (int) Math.round(imageSize.height / heightToWidth);
widthField.setValue(imageSize.width, false);
break;
}
int scaleDenominator = 0;
if (imageSize.width > 0) {
scaleDenominator = (int) Math.round(mapBounds.getSpan(XAXIS) / imageSize.width);
}
scaleCombo.getEditor().setItem(Integer.valueOf(scaleDenominator));
}
/**
* Update the controls when the scale has been changed
*/
private void onScaleChange() {
int scaleDenominator = ((Number) scaleCombo.getEditor().getItem()).intValue();
if (scaleDenominator > 0) {
imageSize.width = (int) (mapBounds.getSpan(XAXIS) / scaleDenominator);
imageSize.height = (int) (mapBounds.getSpan(YAXIS) / scaleDenominator);
} else {
imageSize.width = imageSize.height = 0;
}
widthField.setValue( imageSize.width, false );
heightField.setValue( imageSize.height, false );
}
/**
* Display a {@code JFileImageChooser} to browse for the path and file
* to save the image to
*/
private void brosweFile() {
File file = JFileImageChooser.showSaveFile(this);
if (file != null) {
selectedFile = file;
fileField.setText(file.getAbsolutePath());
}
}
/**
* Demonstrate the dialog
* @param args ignored
*/
public static void main(String[] args) {
ReferencedEnvelope bounds = new ReferencedEnvelope(0, 10000, 0, 20000, null);
JMapImageDialog dialog = new JMapImageDialog(bounds);
dialog.setVisible(true);
dialog.dispose();
}
}