/*
* The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
* for visualizing and manipulating spatial features with geometry and attributes.
*
* JUMP is Copyright (C) 2003 Vivid Solutions
*
* This program implements extensions to JUMP and is
* Copyright (C) 2004 Integrated Systems Analysts, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* For more information, contact:
*
* Integrated Systems Analysts, Inc.
* 630C Anchors St., Suite 101
* Fort Walton Beach, Florida
* USA
*
* (850)862-7321
* www.ashs.isa.com
*/
package org.openjump.core.ui.plugin.tools;
import java.util.*;
import java.awt.Color;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.JSpinner;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jump.I18N;
import com.vividsolutions.jump.feature.*;
import com.vividsolutions.jump.task.*;
import com.vividsolutions.jump.util.Range;
import com.vividsolutions.jump.workbench.WorkbenchContext;
import com.vividsolutions.jump.workbench.model.*;
import com.vividsolutions.jump.workbench.plugin.*;
import com.vividsolutions.jump.workbench.ui.*;
import com.vividsolutions.jump.workbench.ui.renderer.style.LabelStyle;
import com.vividsolutions.jump.workbench.ui.renderer.style.ColorThemingStyle;
import com.vividsolutions.jump.workbench.ui.renderer.style.BasicStyle;
import com.vividsolutions.jump.workbench.ui.renderer.style.ColorScheme;
import com.vividsolutions.jts.util.Assert;
public class MultiRingBufferSelectedPlugIn extends AbstractPlugIn
implements ThreadedPlugIn {
private static String BUFFER;
private static String LAYER;
private static String NEWLAYERNAME;
private static String LABEL;
private static String THEMING;
private static String LAYERNAME;
private static String ATTRIBUTENAME;
private static String ATTRIBUTEVALUE;
private static String BUFFERDISTANCE;
private static String BUFFERNUMBER;
private static String DISTANCEATTRIBUTE;
private static String LAYEROPTIONS;
private static String BUFFEROPTIONS;
private static String SELECTED_ONLY;
private static String MULTIPLE_RING_BUFFER;
private static String sRESET;
private static String layerName;
private static String attributeName = "Label";
private static boolean enableTheming = true;
private static boolean enableRanging = false; //if this is set true need to add sorting routine to attributes
private static boolean enableLabeling = true;
private JSpinner bufferSpinner;
private boolean exceptionThrown = false;
private static String[] bufferDistances = {"10","50","100","250","0","0","0","0","0","0"};
private static String[] bufferAttributeValues = {"A","B","C","D","","","","","",""};
private int currBufferNumber = 1;
private JTextField currBufferDistance;
private JTextField currAttributeValue;
public MultiRingBufferSelectedPlugIn() {
}
public void initialize(PlugInContext context) throws Exception {
MULTIPLE_RING_BUFFER =
I18N.get("org.openjump.core.ui.plugin.tools.MultiRingBufferSelectedPlugIn.Multiple-Ring-Buffer");
context.getFeatureInstaller().addMainMenuItem(this,
new String[] {MenuNames.TOOLS , MenuNames.TOOLS_ANALYSIS},
MULTIPLE_RING_BUFFER + "...",
false, null, this.createEnableCheck(context.getWorkbenchContext()));
}
public boolean execute(PlugInContext context) throws Exception {
MULTIPLE_RING_BUFFER = I18N.get("org.openjump.core.ui.plugin.tools.MultiRingBufferSelectedPlugIn.Multiple-Ring-Buffer");
sRESET = I18N.get("org.openjump.core.ui.plugin.tools.MultiRingBufferSelectedPlugIn.Reset-all-buffer-options");
SELECTED_ONLY = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Use-selected-features-only");
BUFFERDISTANCE = I18N.get("ui.plugin.analysis.BufferPlugIn.buffer-distance");
BUFFER = I18N.get("com.vividsolutions.jump.workbench.ui.plugin.analysis.BufferPlugIn");
NEWLAYERNAME = BUFFER + "-" + I18N.get("ui.MenuNames.SELECTION");
LAYER = MenuNames.LAYER;
layerName = NEWLAYERNAME;
String options = I18N.get("com.vividsolutions.jump.workbench.ui.plugin.OptionsPlugIn");
LAYEROPTIONS = LAYER+" "+options+":";
BUFFEROPTIONS = BUFFER + " "+options+":";
LABEL = I18N.get("ui.style.LabelStylePanel.labels");
THEMING = I18N.get("ui.renderer.style.ColorThemingPanel.colour-theming");
String name = I18N.get("jump.workbench.ui.plugin.datastore.ConnectionDescriptorPanel.Name");
LAYERNAME = LAYER + " " + name;
String attribute = I18N.get("org.openjump.core.ui.plugin.queries.SimpleQuery.attribute");
ATTRIBUTENAME = attribute+ " "+ name;
ATTRIBUTEVALUE =BUFFER +" "+ LABEL;
BUFFERNUMBER = BUFFER +" Number";
DISTANCEATTRIBUTE = I18N.get("org.openjump.core.ui.plugin.tools.MeasureM_FTool.Distance");
MultiInputDialog dialog = new MultiInputDialog(
context.getWorkbenchFrame(), getName(), true);
setDialogValues(dialog, context);
GUIUtil.centreOnWindow(dialog);
boolean goodEntry = false;
while (!goodEntry) {
dialog.setVisible(true);
if (! dialog.wasOKPressed()) {
return false;
}
goodEntry = getDialogValues(dialog);
}
return true;
}
public void run(TaskMonitor monitor, PlugInContext context) throws Exception {
FeatureSchema featureSchema = new FeatureSchema();
featureSchema.addAttribute("GEOMETRY", AttributeType.GEOMETRY);
if (! DISTANCEATTRIBUTE.equalsIgnoreCase(attributeName))
featureSchema.addAttribute(DISTANCEATTRIBUTE, AttributeType.DOUBLE);
featureSchema.addAttribute(attributeName, AttributeType.STRING);
FeatureDataset featureDataset = new FeatureDataset(featureSchema);
Collection selectedCategories =
context.getLayerNamePanel().getSelectedCategories();
Layer layer = context.addLayer(selectedCategories.isEmpty() ?
StandardCategoryNames.WORKING
:selectedCategories.iterator().next().toString(),
layerName, featureDataset);
layer.setFeatureCollectionModified(true).setEditable(true);
Collection selectedFeatures =
context.getLayerViewPanel().getSelectionManager().getFeaturesWithSelectedItems();
Collection bufferCollection = getBuffers(featureSchema, selectedFeatures);
for (Iterator i = bufferCollection.iterator(); i.hasNext();) {
layer.getFeatureCollectionWrapper().add((Feature)i.next());
}
if (enableLabeling) {
LabelStyle labelStyle = layer.getLabelStyle();
labelStyle.setAttribute(attributeName);
labelStyle.setVerticalAlignment(LabelStyle.ON_LINE);
labelStyle.setEnabled(true);
}
//ColorThemingStyle.get(layer).setEnabled(enableTheming);
ColorScheme colorScheme = ColorScheme.create("spectral (ColorBrewer)");
Map attributeToStyleMap = new HashMap();
if (enableTheming) {
for (Iterator i = bufferCollection.iterator(); i.hasNext();) {
Feature feature = (Feature) i.next();
attributeToStyleMap.put((String)feature.getAttribute(attributeName), new BasicStyle(colorScheme.next()));
}
}
else if (enableRanging) {
colorScheme = ColorScheme.create("Reds (ColorBrewer)");
attributeToStyleMap = new Range.RangeTreeMap();
Object previousValue = null;
for (Iterator i = bufferCollection.iterator(); i.hasNext();) {
Object value = ((Feature) i.next()).getAttribute(attributeName);
try {
if (previousValue == null) continue;
attributeToStyleMap.put(new Range(previousValue, true, value, false), new BasicStyle(colorScheme.next()));
}
finally {
previousValue = value;
}
}
attributeToStyleMap.put(new Range(previousValue, true, new Range.PositiveInfinity(), false), new BasicStyle(colorScheme.next()));
}
if (enableTheming) {
layer.getBasicStyle().setEnabled(false);
ColorThemingStyle themeStyle = new ColorThemingStyle(attributeName, attributeToStyleMap, new BasicStyle(Color.gray));
themeStyle.setEnabled(true);
layer.addStyle(themeStyle);
ColorThemingStyle.get(layer).setEnabled(enableTheming);
layer.removeStyle(ColorThemingStyle.get(layer));
ColorThemingStyle.get(layer).setEnabled(true);
layer.getBasicStyle().setEnabled(false);
}
if (exceptionThrown) {
context.getWorkbenchFrame().warnUser("Errors found while executing buffer");
}
}
private Collection getBuffers(FeatureSchema featureSchema, Collection selectedFeatures){
exceptionThrown = false;
Collection bufferFeatureCollection = new ArrayList();
Geometry prevGeo = null;
Iterator ia = selectedFeatures.iterator();
prevGeo = ((Feature) selectedFeatures.iterator().next()).getGeometry();
while (ia.hasNext()) {
prevGeo = prevGeo.union(((Feature) ia.next()).getGeometry());
}
for (int bufferNum = 0; bufferNum < bufferDistances.length; bufferNum++) {
Geometry bufferGeo = null;
//no need for error checking for valid doubles
//it was done in getDialogValues
double bufferDistance = Double.parseDouble(bufferDistances[bufferNum]);
if (bufferDistance > 0) {
ia = selectedFeatures.iterator();
Geometry featureGeo = ((Feature) selectedFeatures.iterator().next()).getGeometry();
bufferGeo = getBuffer(featureGeo, bufferDistance);
while (ia.hasNext()) {
featureGeo = ((Feature) ia.next()).getGeometry();
Geometry result = getBuffer(featureGeo, bufferDistance);
if (result != null) {
bufferGeo = bufferGeo.union(result);
}
}
Feature bufferFeature = new BasicFeature(featureSchema);
// if (constructDonuts)
bufferFeature.setGeometry(bufferGeo.difference(prevGeo));
// else
// bufferFeature.setGeometry(bufferGeo);
bufferFeature.setAttribute(DISTANCEATTRIBUTE, new Double(bufferDistances[bufferNum]));
bufferFeature.setAttribute(attributeName, bufferAttributeValues[bufferNum]);
bufferFeatureCollection.add(bufferFeature);
prevGeo = bufferGeo;
}
}
return bufferFeatureCollection;
}
private Geometry getBuffer(Geometry a, double distance) {
Geometry result = null;
try {
result = a.buffer(distance);// * conversionFactor);
return result;
}
catch (RuntimeException ex) {
// simply eat exceptions and report them by returning null
exceptionThrown = true;
}
return null;
}
private void setDialogValues(final MultiInputDialog dialog, PlugInContext context) {
dialog.setSideBarDescription( SELECTED_ONLY);
dialog.addSeparator();
dialog.addLabel(LAYEROPTIONS);
dialog.addSeparator();
dialog.addTextField(LAYERNAME, layerName, 30, null, "");
dialog.addTextField(ATTRIBUTENAME, attributeName, 30, null, "");
dialog.addCheckBox(LABEL, enableLabeling, "");
dialog.addCheckBox(THEMING, enableTheming, "");
dialog.addSeparator();
dialog.addLabel(BUFFEROPTIONS);
dialog.addSeparator();
int maxVal = bufferDistances.length;
SpinnerModel bufferSpinnerModel = new SpinnerNumberModel(1, 1, maxVal, 1);
bufferSpinner = new JSpinner(bufferSpinnerModel);
bufferSpinner.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
JSpinner spinner = (JSpinner)e.getSource();
int spinVal = ((Integer)spinner.getValue()).intValue();
bufferDistances[currBufferNumber-1] = currBufferDistance.getText().trim();
bufferAttributeValues[currBufferNumber-1] = currAttributeValue.getText().trim();
currBufferNumber = spinVal;
currBufferDistance.setText("" + bufferDistances[spinVal-1]);
currAttributeValue.setText(bufferAttributeValues[spinVal-1]);
}
});
dialog.addRow(BUFFERNUMBER, new JLabel(BUFFERNUMBER), bufferSpinner, null, "");
currBufferDistance = dialog.addTextField(BUFFERDISTANCE, bufferDistances[0], 10, null, "");
currAttributeValue = dialog.addTextField(ATTRIBUTEVALUE, bufferAttributeValues[0], 30, null, "");
JButton resetButton = dialog.addButton(sRESET);
resetButton.addActionListener(new ResetButtonListener());
currBufferNumber = 1;
currBufferDistance.setText("" + bufferDistances[currBufferNumber - 1]);
currAttributeValue.setText(bufferAttributeValues[currBufferNumber - 1]);
}
private boolean getDialogValues(MultiInputDialog dialog) {
int spinVal = ((Integer)bufferSpinner.getValue()).intValue();
bufferDistances[spinVal-1] = currBufferDistance.getText().trim();
bufferAttributeValues[spinVal-1] = currAttributeValue.getText().trim();
layerName = dialog.getText(LAYERNAME);
attributeName = dialog.getText(ATTRIBUTENAME);
enableLabeling = dialog.getCheckBox(LABEL).isSelected();
enableTheming = dialog.getCheckBox(THEMING).isSelected();
int bufNum = 0;
try {
//check out the values before trying to use them
for (bufNum = 0; bufNum < bufferDistances.length; bufNum++) {
double bufDist = Double.parseDouble(bufferDistances[bufNum]);
if (bufDist < 0) {
reportValidationError(dialog, BUFFER+" #" + (bufNum+1) + " < 0.");
return false;
}
}
}
catch (NumberFormatException e) {
reportValidationError(dialog, "\"" + bufferDistances[bufNum]
+ "\" is an invalid double for buffer distance #" + (bufNum+1) + ".");
return false;
}
return true;
}
public MultiEnableCheck createEnableCheck(final WorkbenchContext workbenchContext) {
EnableCheckFactory checkFactory = new EnableCheckFactory(workbenchContext);
return new MultiEnableCheck()
.add(checkFactory.createWindowWithLayerViewPanelMustBeActiveCheck())
.add(checkFactory.createAtLeastNFeaturesMustHaveSelectedItemsCheck(1));
}
private class ResetButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
currBufferNumber = 1;
bufferSpinner.setValue(new Integer(currBufferNumber));
for (int i = 0; i < bufferDistances.length; i++) {
bufferDistances[i] = "0";
bufferAttributeValues[i] = "";
}
currBufferDistance.setText("" + bufferDistances[currBufferNumber - 1]);
currAttributeValue.setText(bufferAttributeValues[currBufferNumber - 1]);
}
}
private void reportValidationError(MultiInputDialog dialog, String errorMessage) {
JOptionPane.showMessageDialog(
dialog,
errorMessage,
"JUMP",
JOptionPane.ERROR_MESSAGE);
}
}