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:
033: package com.vividsolutions.jump.workbench.ui.plugin.analysis;
034:
035: import java.awt.event.ItemEvent;
036: import java.awt.event.ItemListener;
037: import java.util.*;
038:
039: import javax.swing.DefaultComboBoxModel;
040: import javax.swing.JComboBox;
041: import javax.swing.JRadioButton;
042:
043: import com.vividsolutions.jts.geom.Geometry;
044: import com.vividsolutions.jts.geom.LineString;
045: import com.vividsolutions.jts.geom.MultiLineString;
046: import com.vividsolutions.jump.I18N;
047: import com.vividsolutions.jump.feature.*;
048: import com.vividsolutions.jump.task.TaskMonitor;
049: import com.vividsolutions.jump.util.StringUtil;
050: import com.vividsolutions.jump.workbench.model.Layer;
051: import com.vividsolutions.jump.workbench.model.StandardCategoryNames;
052: import com.vividsolutions.jump.workbench.plugin.*;
053: import com.vividsolutions.jump.workbench.plugin.util.*;
054: import com.vividsolutions.jump.workbench.ui.GUIUtil;
055: import com.vividsolutions.jump.workbench.ui.GenericNames;
056: import com.vividsolutions.jump.workbench.ui.MultiInputDialog;
057: import com.vividsolutions.jump.workbench.ui.SelectionManager;
058:
059: /**
060: * Queries a layer by a spatial predicate.
061: */
062: public class AttributeQueryPlugIn extends AbstractPlugIn implements
063: ThreadedPlugIn {
064: private static String ATTR_GEOMETRY_AREA = I18N
065: .get("ui.plugin.analysis.AttributeQueryPlugIn.Geometry.Area");
066: private static String ATTR_GEOMETRY_LENGTH = I18N
067: .get("ui.plugin.analysis.AttributeQueryPlugIn.Geometry.Length");
068: private static String ATTR_GEOMETRY_NUMPOINTS = I18N
069: .get("ui.plugin.analysis.AttributeQueryPlugIn.Geometry.NumPoints");
070: private static String ATTR_GEOMETRY_NUMCOMPONENTS = I18N
071: .get("ui.plugin.analysis.AttributeQueryPlugIn.Geometry.NumComponents");
072: private static String ATTR_GEOMETRY_ISCLOSED = I18N
073: .get("ui.plugin.analysis.AttributeQueryPlugIn.Geometry.IsClosed");
074: private static String ATTR_GEOMETRY_ISSIMPLE = I18N
075: .get("ui.plugin.analysis.AttributeQueryPlugIn.Geometry.IsSimple");
076: private static String ATTR_GEOMETRY_ISVALID = I18N
077: .get("ui.plugin.analysis.AttributeQueryPlugIn.Geometry.IsValid");
078: private static String ATTR_GEOMETRY_TYPE = I18N
079: .get("ui.plugin.analysis.AttributeQueryPlugIn.Geometry.Type");
080:
081: // MD - could easily add this later
082: //private final static String DIALOG_COMPLEMENT = "Complement Result";
083:
084: private Collection functionNames;
085: private MultiInputDialog dialog;
086: private Layer srcLayer;
087: private String attrName;
088: private String funcNameToRun;
089: private String value = "";
090: private boolean complementResult = false;
091: private boolean exceptionThrown = false;
092: private JRadioButton updateSourceRB;
093: private JRadioButton createNewLayerRB;
094: private boolean createLayer = true;
095:
096: public AttributeQueryPlugIn() {
097: functionNames = AttributePredicate.getNames();
098: }
099:
100: private String categoryName = StandardCategoryNames.RESULT;
101:
102: public void setCategoryName(String value) {
103: categoryName = value;
104: }
105:
106: public String getName() {
107: return I18N
108: .get("ui.plugin.analysis.AttributeQueryPlugIn.Attribute-Query");
109: }
110:
111: public boolean execute(PlugInContext context) throws Exception {
112: //[sstein] reset for correct language
113: ATTR_GEOMETRY_AREA = I18N
114: .get("ui.plugin.analysis.AttributeQueryPlugIn.Geometry.Area");
115: ATTR_GEOMETRY_LENGTH = I18N
116: .get("ui.plugin.analysis.AttributeQueryPlugIn.Geometry.Length");
117: ATTR_GEOMETRY_NUMPOINTS = I18N
118: .get("ui.plugin.analysis.AttributeQueryPlugIn.Geometry.NumPoints");
119: ATTR_GEOMETRY_NUMCOMPONENTS = I18N
120: .get("ui.plugin.analysis.AttributeQueryPlugIn.Geometry.NumComponents");
121: ATTR_GEOMETRY_ISCLOSED = I18N
122: .get("ui.plugin.analysis.AttributeQueryPlugIn.Geometry.IsClosed");
123: ATTR_GEOMETRY_ISSIMPLE = I18N
124: .get("ui.plugin.analysis.AttributeQueryPlugIn.Geometry.IsSimple");
125: ATTR_GEOMETRY_ISVALID = I18N
126: .get("ui.plugin.analysis.AttributeQueryPlugIn.Geometry.IsValid");
127: ATTR_GEOMETRY_TYPE = I18N
128: .get("ui.plugin.analysis.AttributeQueryPlugIn.Geometry.Type");
129:
130: dialog = new MultiInputDialog(context.getWorkbenchFrame(),
131: getName(), true);
132: setDialogValues(dialog, context);
133: GUIUtil.centreOnWindow(dialog);
134: dialog.setVisible(true);
135:
136: if (!dialog.wasOKPressed()) {
137: return false;
138: }
139:
140: getDialogValues(dialog);
141:
142: // input-proofing
143: if (srcLayer == null)
144: return false;
145: if (StringUtil.isEmpty(value))
146: return false;
147: if (StringUtil.isEmpty(attrName))
148: return false;
149: if (StringUtil.isEmpty(funcNameToRun))
150: return false;
151:
152: return true;
153: }
154:
155: public void run(TaskMonitor monitor, PlugInContext context)
156: throws Exception {
157: monitor.allowCancellationRequests();
158:
159: monitor
160: .report(I18N
161: .get("ui.plugin.analysis.SpatialQueryPlugIn.Executing-query")
162: + "...");
163:
164: FeatureCollection sourceFC = srcLayer
165: .getFeatureCollectionWrapper();
166:
167: if (monitor.isCancelRequested())
168: return;
169:
170: FeatureCollection resultFC = executeQuery(sourceFC, attrName,
171: value);
172:
173: if (createLayer) {
174: String outputLayerName = LayerNameGenerator
175: .generateOperationOnLayerName(funcNameToRun,
176: srcLayer.getName());
177: context.getLayerManager().addCategory(categoryName);
178: context.addLayer(categoryName, outputLayerName, resultFC);
179: } else {
180: SelectionManager selectionManager = context
181: .getLayerViewPanel().getSelectionManager();
182: selectionManager.clear();
183: selectionManager.getFeatureSelection().selectItems(
184: srcLayer, resultFC.getFeatures());
185: }
186:
187: if (exceptionThrown) {
188: context
189: .getWorkbenchFrame()
190: .warnUser(
191: I18N
192: .get("ui.plugin.analysis.SpatialQueryPlugIn.Errors-found-while-executing-query"));
193: }
194: }
195:
196: private FeatureCollection executeQuery(FeatureCollection sourceFC,
197: String attrName, String value) {
198: AttributePredicate pred = AttributePredicate
199: .getPredicate(funcNameToRun);
200: FeatureCollection resultFC = new FeatureDataset(sourceFC
201: .getFeatureSchema());
202:
203: for (Iterator i = sourceFC.iterator(); i.hasNext();) {
204: Feature f = (Feature) i.next();
205: Object fVal = getValue(f, attrName);
206: boolean predResult = pred.isTrue(fVal, value);
207:
208: if (complementResult)
209: predResult = !predResult;
210:
211: if (predResult) {
212: if (createLayer) {
213: resultFC.add(f.clone(true));
214: } else {
215: resultFC.add(f);
216: }
217: }
218: }
219:
220: return resultFC;
221: }
222:
223: private Object getValue(Feature f, String attrName) {
224: if (attrName == ATTR_GEOMETRY_AREA) {
225: Geometry g = f.getGeometry();
226: double area = (g == null) ? 0.0 : g.getArea();
227: return new Double(area);
228: }
229: if (attrName == ATTR_GEOMETRY_LENGTH) {
230: Geometry g = f.getGeometry();
231: double len = (g == null) ? 0.0 : g.getLength();
232: return new Double(len);
233: }
234: if (attrName == ATTR_GEOMETRY_NUMPOINTS) {
235: Geometry g = f.getGeometry();
236: double len = (g == null) ? 0.0 : g.getNumPoints();
237: return new Double(len);
238: }
239: if (attrName == ATTR_GEOMETRY_NUMCOMPONENTS) {
240: Geometry g = f.getGeometry();
241: double len = (g == null) ? 0.0 : g.getNumGeometries();
242: return new Double(len);
243: }
244: if (attrName == ATTR_GEOMETRY_ISCLOSED) {
245: Geometry g = f.getGeometry();
246: if (g instanceof LineString)
247: return new Boolean(((LineString) g).isClosed());
248: if (g instanceof MultiLineString)
249: return new Boolean(((MultiLineString) g).isClosed());
250: return new Boolean(false);
251: }
252: if (attrName == ATTR_GEOMETRY_ISSIMPLE) {
253: Geometry g = f.getGeometry();
254: boolean bool = g.isSimple();
255: return new Boolean(bool);
256: }
257: if (attrName == ATTR_GEOMETRY_ISVALID) {
258: Geometry g = f.getGeometry();
259: boolean bool = g.isValid();
260: return new Boolean(bool);
261: }
262: if (attrName == ATTR_GEOMETRY_TYPE) {
263: Geometry g = f.getGeometry();
264: return StringUtil.classNameWithoutQualifiers(g.getClass()
265: .getName());
266: }
267: return f.getAttribute(attrName);
268: }
269:
270: private static String LAYER = GenericNames.SOURCE_LAYER;
271: private static String ATTRIBUTE = I18N
272: .get("ui.plugin.analysis.AttributeQueryPlugIn.Attribute");
273: private static String PREDICATE = I18N
274: .get("ui.plugin.analysis.SpatialQueryPlugIn.Relation");
275: private static String VALUE = I18N
276: .get("ui.plugin.analysis.AttributeQueryPlugIn.Value");
277: private static String DIALOG_COMPLEMENT = I18N
278: .get("ui.plugin.analysis.SpatialQueryPlugIn.Complement-Result");
279:
280: private static String UPDATE_SRC = I18N
281: .get("ui.plugin.analysis.SpatialQueryPlugIn.Select-features-in-the-source-layer");
282: private static String CREATE_LYR = I18N
283: .get("ui.plugin.analysis.SpatialQueryPlugIn.Create-a-new-layer-for-the-results");
284:
285: private JComboBox attrComboBox;
286:
287: private void setDialogValues(MultiInputDialog dialog,
288: PlugInContext context) {
289: //[sstein] reset for language
290: LAYER = GenericNames.SOURCE_LAYER;
291: ATTRIBUTE = I18N
292: .get("ui.plugin.analysis.AttributeQueryPlugIn.Attribute");
293: PREDICATE = I18N
294: .get("ui.plugin.analysis.SpatialQueryPlugIn.Relation");
295: VALUE = I18N
296: .get("ui.plugin.analysis.AttributeQueryPlugIn.Value");
297: DIALOG_COMPLEMENT = I18N
298: .get("ui.plugin.analysis.SpatialQueryPlugIn.Complement-Result");
299: UPDATE_SRC = I18N
300: .get("ui.plugin.analysis.SpatialQueryPlugIn.Select-features-in-the-source-layer");
301: CREATE_LYR = I18N
302: .get("ui.plugin.analysis.SpatialQueryPlugIn.Create-a-new-layer-for-the-results");
303:
304: dialog
305: .setSideBarDescription(I18N
306: .get("ui.plugin.analysis.AttributeQueryPlugIn.Finds-the-Source-features-which-have-attribute-values-satisfying-a-given-condition"));
307:
308: //Set initial layer values to the first and second layers in the layer list.
309: //In #initialize we've already checked that the number of layers >= 1. [Jon Aquino]
310: Layer initLayer = (srcLayer == null) ? context
311: .getCandidateLayer(0) : srcLayer;
312:
313: JComboBox lyrCombo = dialog.addLayerComboBox(LAYER, initLayer,
314: context.getLayerManager());
315: lyrCombo.addItemListener(new LayerItemListener());
316: attrComboBox = dialog.addComboBox(ATTRIBUTE, attrName,
317: functionNames, null);
318: dialog.addComboBox(PREDICATE, funcNameToRun, functionNames,
319: null);
320: dialog.addTextField(VALUE, value, 20, null, null);
321: dialog.addCheckBox(DIALOG_COMPLEMENT, complementResult);
322:
323: final String OUTPUT_GROUP = "OUTPUT_GROUP";
324: createNewLayerRB = dialog.addRadioButton(CREATE_LYR,
325: OUTPUT_GROUP, createLayer, CREATE_LYR);
326: updateSourceRB = dialog.addRadioButton(UPDATE_SRC,
327: OUTPUT_GROUP, !createLayer, UPDATE_SRC);
328:
329: updateUI(initLayer);
330: }
331:
332: private void getDialogValues(MultiInputDialog dialog) {
333: srcLayer = dialog.getLayer(LAYER);
334: attrName = dialog.getText(ATTRIBUTE);
335: funcNameToRun = dialog.getText(PREDICATE);
336: value = dialog.getText(VALUE);
337: complementResult = dialog.getBoolean(DIALOG_COMPLEMENT);
338: createLayer = dialog.getBoolean(CREATE_LYR);
339: }
340:
341: private void updateUI(Layer lyr) {
342: List attrNames = null;
343: if (lyr != null) {
344: FeatureCollection fc = lyr.getFeatureCollectionWrapper();
345: FeatureSchema fs = fc.getFeatureSchema();
346:
347: attrNames = getAttributeNames(fs);
348: } else {
349: attrNames = new ArrayList();
350: }
351: attrComboBox.setModel(new DefaultComboBoxModel(new Vector(
352: attrNames)));
353: attrComboBox.setSelectedItem(attrName);
354: }
355:
356: private static List getAttributeNames(FeatureSchema fs) {
357: List names = new ArrayList();
358: for (int i = 0; i < fs.getAttributeCount(); i++) {
359: if (fs.getAttributeType(i) != AttributeType.GEOMETRY)
360: names.add(fs.getAttributeName(i));
361: }
362: names.add(ATTR_GEOMETRY_AREA);
363: names.add(ATTR_GEOMETRY_LENGTH);
364: names.add(ATTR_GEOMETRY_NUMPOINTS);
365: names.add(ATTR_GEOMETRY_NUMCOMPONENTS);
366: names.add(ATTR_GEOMETRY_ISCLOSED);
367: names.add(ATTR_GEOMETRY_ISSIMPLE);
368: names.add(ATTR_GEOMETRY_ISVALID);
369: names.add(ATTR_GEOMETRY_TYPE);
370:
371: return names;
372: }
373:
374: private class LayerItemListener implements ItemListener {
375: public void itemStateChanged(ItemEvent e) {
376: updateUI((Layer) e.getItem());
377: }
378: }
379:
380: }
|