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.util.ArrayList;
036: import java.util.Collection;
037: import java.util.Comparator;
038: import java.util.Iterator;
039: import java.util.List;
040: import java.util.Set;
041: import java.util.TreeSet;
042:
043: import javax.swing.JComboBox;
044:
045: import com.vividsolutions.jts.geom.Envelope;
046: import com.vividsolutions.jts.geom.Geometry;
047: import com.vividsolutions.jump.I18N;
048: import com.vividsolutions.jump.feature.AttributeType;
049: import com.vividsolutions.jump.feature.Feature;
050: import com.vividsolutions.jump.feature.FeatureCollection;
051: import com.vividsolutions.jump.feature.FeatureDataset;
052: import com.vividsolutions.jump.feature.FeatureDatasetFactory;
053: import com.vividsolutions.jump.feature.FeatureSchema;
054: import com.vividsolutions.jump.task.TaskMonitor;
055: import com.vividsolutions.jump.workbench.model.StandardCategoryNames;
056: import com.vividsolutions.jump.workbench.plugin.AbstractPlugIn;
057: import com.vividsolutions.jump.workbench.plugin.PlugInContext;
058: import com.vividsolutions.jump.workbench.plugin.ThreadedPlugIn;
059: import com.vividsolutions.jump.workbench.ui.GUIUtil;
060: import com.vividsolutions.jump.workbench.ui.MultiInputDialog;
061:
062: public class UnionPlugIn extends AbstractPlugIn implements
063: ThreadedPlugIn {
064: private String LAYER = I18N
065: .get("ui.plugin.analysis.UnionPlugIn.layer");
066: private String SELECTED_ONLY = I18N
067: .get("ui.plugin.analysis.UnionPlugIn.selected-features-only");
068: private boolean useSelected = false;
069: private MultiInputDialog dialog;
070: private JComboBox addLayerComboBox;
071:
072: public UnionPlugIn() {
073: }
074:
075: /*
076: public void initialize(PlugInContext context) throws Exception {
077: context.getFeatureInstaller().addMainMenuItem(
078: this, "Tools", "Find Unaligned Segments...", null, new MultiEnableCheck()
079: .add(context.getCheckFactory().createWindowWithLayerNamePanelMustBeActiveCheck())
080: .add(context.getCheckFactory().createAtLeastNLayersMustExistCheck(1)));
081: }
082: */
083: public boolean execute(PlugInContext context) throws Exception {
084: //[sstein, 16.07.2006] put here again to load correct language
085: //[mmichaud 2007-05-20] move to UnionPlugIn constructor to load the string only once
086: //LAYER = I18N.get("ui.plugin.analysis.UnionPlugIn.layer");
087: //Unlike ValidatePlugIn, here we always call #initDialog because we want
088: //to update the layer comboboxes. [Jon Aquino]
089: int n = context.getLayerViewPanel().getSelectionManager()
090: .getFeaturesWithSelectedItems().size();
091: useSelected = (n > 0);
092: initDialog(context);
093: dialog.setVisible(true);
094:
095: if (!dialog.wasOKPressed()) {
096: return false;
097: }
098:
099: return true;
100: }
101:
102: private void initDialog(PlugInContext context) {
103: dialog = new MultiInputDialog(context.getWorkbenchFrame(), I18N
104: .get("ui.plugin.analysis.UnionPlugIn.union"), true);
105:
106: //dialog.setSideBarImage(IconLoader.icon("Overlay.gif"));
107: if (useSelected) {
108: dialog
109: .setSideBarDescription(I18N
110: .get("ui.plugin.analysis.UnionPlugIn.creates-a-new-layer-containing-the-union-of-selected-features-in-the-input-layer"));
111: } else {
112: dialog
113: .setSideBarDescription(I18N
114: .get("ui.plugin.analysis.UnionPlugIn.creates-a-new-layer-containing-the-union-of-all-the-features-in-the-input-layer"));
115: }
116: String fieldName = LAYER;
117: if (useSelected) {
118: dialog.addLabel(SELECTED_ONLY);
119: } else {
120: addLayerComboBox = dialog.addLayerComboBox(fieldName,
121: context.getCandidateLayer(0), null, context
122: .getLayerManager());
123: }
124: GUIUtil.centreOnWindow(dialog);
125: }
126:
127: public void run(TaskMonitor monitor, PlugInContext context)
128: throws Exception {
129: FeatureCollection a;
130: Collection inputC;
131: if (useSelected) {
132: inputC = context.getLayerViewPanel().getSelectionManager()
133: .getFeaturesWithSelectedItems();
134: FeatureSchema featureSchema = new FeatureSchema();
135: featureSchema.addAttribute("GEOMETRY",
136: AttributeType.GEOMETRY);
137: a = new FeatureDataset(inputC, featureSchema);
138: } else {
139: a = dialog.getLayer(LAYER).getFeatureCollectionWrapper();
140: }
141:
142: FeatureCollection union = progressiveUnion(monitor, a);
143:
144: context.getLayerManager().addCategory(
145: StandardCategoryNames.RESULT);
146: context.addLayer(StandardCategoryNames.RESULT, I18N
147: .get("ui.plugin.analysis.UnionPlugIn.union"), union);
148:
149: }
150:
151: // The naive algorithm is not efficient for dataset containing more than one thousand
152: // features. See a replacement in progressiveUnion [mmichaud 2007-06-10]
153: private FeatureCollection union(TaskMonitor monitor,
154: FeatureCollection fc) {
155: monitor.allowCancellationRequests();
156: monitor.report(I18N
157: .get("ui.plugin.analysis.UnionPlugIn.computing-union"));
158:
159: List unionGeometryList = new ArrayList();
160:
161: Geometry currUnion = null;
162: int size = fc.size();
163: int count = 1;
164:
165: for (Iterator i = fc.iterator(); i.hasNext();) {
166: Feature f = (Feature) i.next();
167: Geometry geom = f.getGeometry();
168:
169: if (currUnion == null) {
170: currUnion = geom;
171: } else {
172: currUnion = currUnion.union(geom);
173: }
174:
175: monitor.report(count++, size, "features");
176: }
177:
178: unionGeometryList.add(currUnion);
179:
180: return FeatureDatasetFactory
181: .createFromGeometry(unionGeometryList);
182: }
183:
184: /**
185: * New method for union. Instead of the naive algorithm looping over the features and
186: * unioning each time, this one union small groups of features which are closed to each
187: * other, then iterates over the result.
188: * The difference is not so important for small datasets, but for large datasets, the
189: * difference may of 5 minutes versus 5 hours.
190: */
191: private FeatureCollection progressiveUnion(TaskMonitor monitor,
192: FeatureCollection fc) {
193: monitor.allowCancellationRequests();
194: //monitor.report(I18N.get("ui.plugin.analysis.UnionPlugIn.computing-union"));
195:
196: List unionGeometryList = new ArrayList();
197: for (Iterator i = fc.iterator(); i.hasNext();) {
198: unionGeometryList.add(((Feature) i.next()).getGeometry());
199: }
200: int iteration = 1;
201: int nbIteration = 1 + (int) (Math.log(unionGeometryList.size()) / Math
202: .log(4));
203: while (unionGeometryList.size() > 1) {
204: monitor
205: .report(I18N
206: .get("ui.plugin.analysis.UnionPlugIn.computing-union")
207: + " ("
208: + iteration++
209: + "/"
210: + nbIteration
211: + ")");
212: final int cellSize = 1 + (int) Math.sqrt(unionGeometryList
213: .size());
214: java.util.Comparator comparator = new java.util.Comparator() {
215: public int compare(Object o1, Object o2) {
216: if (o1 == null || o2 == null)
217: return 0;
218: Envelope env1 = ((Geometry) o1)
219: .getEnvelopeInternal();
220: Envelope env2 = ((Geometry) o2)
221: .getEnvelopeInternal();
222: double indice1 = env1.getMinX() / cellSize
223: + cellSize
224: * ((int) env1.getMinY() / cellSize);
225: double indice2 = env2.getMinX() / cellSize
226: + cellSize
227: * ((int) env2.getMinY() / cellSize);
228: return indice1 >= indice2 ? 1
229: : indice1 < indice2 ? -1 : 0;
230: }
231:
232: public boolean equals(Object obj) {
233: return this .equals(obj);
234: }
235: };
236: java.util.TreeSet treeSet = new java.util.TreeSet(
237: comparator);
238: treeSet.addAll(unionGeometryList);
239: // Testes with groups of 4, 8 and 16 (4 is better than 8 which is better than 16
240: // for large datasets).
241: unionGeometryList = union(monitor, treeSet, 4);
242: }
243:
244: return FeatureDatasetFactory
245: .createFromGeometry(unionGeometryList);
246: }
247:
248: /**
249: * Method unioning an ordered set of geometries by small groups.
250: *
251: */
252: private List union(TaskMonitor monitor, Set set, int groupSize) {
253: List unionGeometryList = new ArrayList();
254: Geometry currUnion = null;
255: int size = set.size();
256: int count = 0;
257: for (Iterator i = set.iterator(); i.hasNext();) {
258: Geometry geom = (Geometry) i.next();
259: if (count % groupSize == 0)
260: currUnion = geom;
261: else {
262: currUnion = currUnion.union(geom);
263: if (groupSize - count % groupSize == 1)
264: unionGeometryList.add(currUnion);
265: }
266: monitor.report(++count, size, "features");
267: }
268: if (groupSize - count % groupSize != 0) {
269: unionGeometryList.add(currUnion);
270: }
271: return unionGeometryList;
272: }
273:
274: }
|