001: /*
002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
003: * for visualizing and manipulating spatial features with geometry and attributes.
004: *
005: * Copyright (C) 2003 Vivid Solutions
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: *
021: * For more information, contact:
022: *
023: * Vivid Solutions
024: * Suite #1A
025: * 2328 Government Street
026: * Victoria BC V8T 5G5
027: * Canada
028: *
029: * (250)385-6040
030: * www.vividsolutions.com
031: */
032: package com.vividsolutions.jump.workbench.ui.plugin.analysis;
033:
034: import java.awt.event.ActionEvent;
035: import java.awt.event.ActionListener;
036: import java.awt.image.BufferedImage;
037: import java.lang.reflect.InvocationTargetException;
038: import java.util.ArrayList;
039: import java.util.Arrays;
040: import java.util.Collection;
041: import java.util.HashMap;
042: import java.util.Iterator;
043: import java.util.List;
044: import java.util.Map;
045: import java.util.Vector;
046: import javax.swing.DefaultComboBoxModel;
047: import javax.swing.Icon;
048: import javax.swing.ImageIcon;
049: import javax.swing.JCheckBox;
050: import javax.swing.JComboBox;
051: import javax.swing.JComponent;
052: import javax.swing.JLabel;
053: import com.vividsolutions.jts.geom.Geometry;
054: import com.vividsolutions.jump.I18N;
055: import com.vividsolutions.jump.feature.AttributeType;
056: import com.vividsolutions.jump.feature.Feature;
057: import com.vividsolutions.jump.feature.FeatureSchema;
058: import com.vividsolutions.jump.util.Blackboard;
059: import com.vividsolutions.jump.util.CollectionUtil;
060: import com.vividsolutions.jump.util.StringUtil;
061: import com.vividsolutions.jump.workbench.WorkbenchContext;
062: import com.vividsolutions.jump.workbench.model.FeatureEventType;
063: import com.vividsolutions.jump.workbench.model.Layer;
064: import com.vividsolutions.jump.workbench.plugin.AbstractPlugIn;
065: import com.vividsolutions.jump.workbench.plugin.EnableCheck;
066: import com.vividsolutions.jump.workbench.plugin.EnableCheckFactory;
067: import com.vividsolutions.jump.workbench.plugin.MultiEnableCheck;
068: import com.vividsolutions.jump.workbench.plugin.PlugInContext;
069: import com.vividsolutions.jump.workbench.ui.GUIUtil;
070: import com.vividsolutions.jump.workbench.ui.LayerNamePanelProxy;
071: import com.vividsolutions.jump.workbench.ui.MultiInputDialog;
072: import com.vividsolutions.jump.workbench.ui.WorkbenchFrame;
073: import com.vividsolutions.jump.workbench.ui.images.IconLoader;
074:
075: /**
076: * Calculates areas and lengths from information obtained
077: * from the user via a {@link MultiInputDialog}.
078: *
079: */
080: public class CalculateAreasAndLengthsPlugIn extends AbstractPlugIn {
081:
082: private String LAYER_COMBO_BOX = I18N
083: .get("ui.plugin.analysis.CalculateAreasAndLengthsPlugIn.layer");
084: private String AREA_COMBO_BOX = I18N
085: .get("ui.plugin.analysis.CalculateAreasAndLengthsPlugIn.area-attribute-name");
086: private String LENGTH_COMBO_BOX = I18N
087: .get("ui.plugin.analysis.CalculateAreasAndLengthsPlugIn.length-attribute-name");
088: private String LENGTH_CHECK_BOX = I18N
089: .get("ui.plugin.analysis.CalculateAreasAndLengthsPlugIn.calculate-length");
090: private String AREA_CHECK_BOX = I18N
091: .get("ui.plugin.analysis.CalculateAreasAndLengthsPlugIn.calculate-area");
092:
093: public boolean execute(PlugInContext context) throws Exception {
094: //[sstein, 16.07.2006] set again to obtain correct language
095: LAYER_COMBO_BOX = I18N
096: .get("ui.plugin.analysis.CalculateAreasAndLengthsPlugIn.layer");
097: AREA_COMBO_BOX = I18N
098: .get("ui.plugin.analysis.CalculateAreasAndLengthsPlugIn.area-attribute-name");
099: LENGTH_COMBO_BOX = I18N
100: .get("ui.plugin.analysis.CalculateAreasAndLengthsPlugIn.length-attribute-name");
101: LENGTH_CHECK_BOX = I18N
102: .get("ui.plugin.analysis.CalculateAreasAndLengthsPlugIn.calculate-length");
103: AREA_CHECK_BOX = I18N
104: .get("ui.plugin.analysis.CalculateAreasAndLengthsPlugIn.calculate-area");
105:
106: //<<TODO>> Undo? [Jon Aquino]
107: //<<TODO>> Two-phase commit? [Jon Aquino]
108: MultiInputDialog dialog = prompt(context);
109: if (!dialog.wasOKPressed()) {
110: return false;
111: }
112: if (dialog.getBoolean(AREA_CHECK_BOX)) {
113: updateAreas(dialog.getLayer(LAYER_COMBO_BOX), dialog
114: .getText(AREA_COMBO_BOX));
115: }
116: if (dialog.getBoolean(LENGTH_CHECK_BOX)) {
117: updateLengths(dialog.getLayer(LAYER_COMBO_BOX), dialog
118: .getText(LENGTH_COMBO_BOX));
119: }
120: context.getLayerManager().fireFeaturesChanged(
121: dialog.getLayer(LAYER_COMBO_BOX)
122: .getFeatureCollectionWrapper().getFeatures(),
123: FeatureEventType.ATTRIBUTES_MODIFIED,
124: dialog.getLayer(LAYER_COMBO_BOX));
125: return true;
126: }
127:
128: private MultiInputDialog prompt(PlugInContext context) {
129: final MultiInputDialog dialog = new MultiInputDialog(context
130: .getWorkbenchFrame(), getName(), true);
131: dialog.addEditableLayerComboBox(LAYER_COMBO_BOX, null, null,
132: context.getLayerManager());
133: initFields(dialog, AREA_CHECK_BOX, AREA_COMBO_BOX, 0);
134: initFields(dialog, LENGTH_CHECK_BOX, LENGTH_COMBO_BOX, 1);
135: initEnableChecks(dialog);
136: loadValues(dialog, context);
137: dialog.setVisible(true);
138: if (dialog.wasOKPressed()) {
139: saveValues(dialog, context);
140: }
141: return dialog;
142: }
143:
144: private void saveValues(MultiInputDialog dialog,
145: PlugInContext context) {
146: Blackboard blackboard = context.getLayerManager()
147: .getBlackboard();
148: blackboard.put(namespace() + LAYER_COMBO_BOX, dialog
149: .getLayer(LAYER_COMBO_BOX));
150: blackboard.put(namespace() + AREA_CHECK_BOX, dialog
151: .getCheckBox(AREA_CHECK_BOX).isSelected());
152: blackboard.put(namespace() + LENGTH_CHECK_BOX, dialog
153: .getCheckBox(LENGTH_CHECK_BOX).isSelected());
154: blackboard.put(namespace() + AREA_COMBO_BOX, dialog
155: .getComboBox(AREA_COMBO_BOX).getSelectedItem());
156: blackboard.put(namespace() + LENGTH_COMBO_BOX, dialog
157: .getComboBox(LENGTH_COMBO_BOX).getSelectedItem());
158: }
159:
160: private void loadValues(MultiInputDialog dialog,
161: PlugInContext context) {
162: Blackboard blackboard = context.getLayerManager()
163: .getBlackboard();
164: dialog.getComboBox(LAYER_COMBO_BOX).setSelectedItem(
165: CollectionUtil.ifNotIn(blackboard.get(namespace()
166: + LAYER_COMBO_BOX), GUIUtil.items(dialog
167: .getComboBox(LAYER_COMBO_BOX)),
168: candidateLayer(context)));
169: GUIUtil.setSelectedWithClick(
170: dialog.getCheckBox(AREA_CHECK_BOX), blackboard.get(
171: namespace() + AREA_CHECK_BOX, true));
172: GUIUtil.setSelectedWithClick(dialog
173: .getCheckBox(LENGTH_CHECK_BOX), blackboard.get(
174: namespace() + LENGTH_CHECK_BOX, true));
175: dialog.getComboBox(AREA_COMBO_BOX)
176: .setSelectedItem(
177: CollectionUtil.ifNotIn(blackboard
178: .get(namespace() + AREA_COMBO_BOX),
179: GUIUtil.items(dialog
180: .getComboBox(AREA_COMBO_BOX)),
181: dialog.getComboBox(AREA_COMBO_BOX)
182: .getSelectedItem()));
183: dialog.getComboBox(LENGTH_COMBO_BOX).setSelectedItem(
184: CollectionUtil.ifNotIn(blackboard.get(namespace()
185: + LENGTH_COMBO_BOX), GUIUtil.items(dialog
186: .getComboBox(LENGTH_COMBO_BOX)), dialog
187: .getComboBox(LENGTH_COMBO_BOX)
188: .getSelectedItem()));
189: }
190:
191: private String namespace() {
192: return getClass().getName() + " - ";
193: }
194:
195: private void initEnableChecks(final MultiInputDialog dialog) {
196: dialog.addEnableChecks(LENGTH_COMBO_BOX, Arrays
197: .asList(new Object[] { new EnableCheck() {
198: public String check(JComponent component) {
199: return dialog.getBoolean(AREA_CHECK_BOX)
200: && dialog.getBoolean(LENGTH_CHECK_BOX)
201: && dialog
202: .getText(AREA_COMBO_BOX)
203: .equals(
204: dialog
205: .getText(LENGTH_COMBO_BOX)) ? I18N
206: .get("ui.plugin.analysis.CalculateAreasAndLengthsPlugIn.area-and-length-attribute-names-must-be-different")
207: : null;
208: }
209: } }));
210: }
211:
212: private String attributeName(List attributeNames, int preferredIndex) {
213: return (String) attributeNames
214: .get(attributeNames.size() > preferredIndex ? preferredIndex
215: : 0);
216: }
217:
218: private void initFields(final MultiInputDialog dialog,
219: final String checkBoxFieldName,
220: final String comboBoxFieldName,
221: final int preferredCandidateAttributeIndex) {
222: dialog.addCheckBox(checkBoxFieldName, true);
223: dialog.addComboBox(comboBoxFieldName, null, new ArrayList(),
224: null);
225: dialog.getComboBox(LAYER_COMBO_BOX).addActionListener(
226: new ActionListener() {
227: private Layer lastLayer = null;
228:
229: public void actionPerformed(ActionEvent e) {
230: Layer newLayer = (Layer) dialog.getComboBox(
231: LAYER_COMBO_BOX).getSelectedItem();
232: if (lastLayer == newLayer) {
233: return;
234: }
235: lastLayer = newLayer;
236: dialog
237: .getComboBox(comboBoxFieldName)
238: .setModel(
239: new DefaultComboBoxModel(
240: new Vector(
241: candidateAttributeNames(newLayer))));
242: if (!candidateAttributeNames(newLayer)
243: .isEmpty()) {
244: dialog
245: .getComboBox(comboBoxFieldName)
246: .setSelectedItem(
247: attributeName(
248: candidateAttributeNames(newLayer),
249: preferredCandidateAttributeIndex));
250: }
251: }
252: });
253: dialog.getCheckBox(checkBoxFieldName).addActionListener(
254: new ActionListener() {
255: public void actionPerformed(ActionEvent e) {
256: dialog.getComboBox(comboBoxFieldName)
257: .setEnabled(
258: dialog.getCheckBox(
259: checkBoxFieldName)
260: .isSelected());
261: dialog.getLabel(comboBoxFieldName).setEnabled(
262: dialog.getCheckBox(checkBoxFieldName)
263: .isSelected());
264: }
265: });
266: dialog.addEnableChecks(comboBoxFieldName, Arrays
267: .asList(new Object[] { new EnableCheck() {
268: public String check(JComponent component) {
269: return dialog.getBoolean(checkBoxFieldName)
270: && dialog
271: .getComboBox(comboBoxFieldName)
272: .getItemCount() == 0 ? "Layer has no string, integer, or double attributes"
273: : null;
274: }
275: } }));
276: dialog.indentLabel(comboBoxFieldName);
277: }
278:
279: private Layer candidateLayer(PlugInContext context) {
280: if (context.getActiveInternalFrame() instanceof LayerNamePanelProxy) {
281: Layer[] selectedLayers = context.getSelectedLayers();
282: for (int i = 0; i < selectedLayers.length; i++) {
283: if (selectedLayers[i].isEditable()) {
284: return selectedLayers[i];
285: }
286: }
287: }
288: return (Layer) context.getLayerManager().getEditableLayers()
289: .iterator().next();
290: }
291:
292: private static interface Converter {
293: public Object convert(double d);
294: }
295:
296: private Map typeToConverterMap = new HashMap() {
297: {
298: put(AttributeType.STRING, new Converter() {
299: public Object convert(double d) {
300: return "" + d;
301: }
302: });
303: put(AttributeType.INTEGER, new Converter() {
304: public Object convert(double d) {
305: return new Integer((int) d);
306: }
307: });
308: put(AttributeType.DOUBLE, new Converter() {
309: public Object convert(double d) {
310: return new Double(d);
311: }
312: });
313: }
314: };
315:
316: private List candidateAttributeNames(Layer layer) {
317: ArrayList candidateAttributeNames = new ArrayList();
318: FeatureSchema schema = layer.getFeatureCollectionWrapper()
319: .getFeatureSchema();
320: for (int i = 0; i < schema.getAttributeCount(); i++) {
321: if (typeToConverterMap.keySet().contains(
322: schema.getAttributeType(i))) {
323: candidateAttributeNames.add(schema.getAttributeName(i));
324: }
325: }
326: return candidateAttributeNames;
327: }
328:
329: private static interface Op {
330: public double compute(Geometry g);
331: }
332:
333: private void updateLengths(Layer layer, String attributeName) {
334: update(layer, attributeName, new Op() {
335: public double compute(Geometry g) {
336: return g.getLength();
337: }
338: });
339: }
340:
341: private void update(Layer layer, String attributeName, Op op) {
342: int attributeIndex = layer.getFeatureCollectionWrapper()
343: .getFeatureSchema().getAttributeIndex(attributeName);
344: AttributeType attributeType = layer
345: .getFeatureCollectionWrapper().getFeatureSchema()
346: .getAttributeType(attributeIndex);
347: for (Iterator i = layer.getFeatureCollectionWrapper()
348: .getFeatures().iterator(); i.hasNext();) {
349: Feature feature = (Feature) i.next();
350: feature.setAttribute(attributeIndex, convert(op
351: .compute(feature.getGeometry()), attributeType));
352: }
353: }
354:
355: private Object convert(double d, AttributeType attributeType) {
356: return ((Converter) typeToConverterMap.get(attributeType))
357: .convert(d);
358: }
359:
360: private void updateAreas(Layer layer, String attributeName) {
361: update(layer, attributeName, new Op() {
362: public double compute(Geometry g) {
363: return g.getArea();
364: }
365: });
366: }
367:
368: public MultiEnableCheck createEnableCheck(
369: WorkbenchContext workbenchContext) {
370: EnableCheckFactory checkFactory = new EnableCheckFactory(
371: workbenchContext);
372: return new MultiEnableCheck()
373: .add(
374: checkFactory
375: .createWindowWithLayerManagerMustBeActiveCheck())
376: .add(checkFactory.createAtLeastNLayersMustExistCheck(1))
377: .add(
378: checkFactory
379: .createAtLeastNLayersMustBeEditableCheck(1));
380: }
381: }
|