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.plugin.edit;
033:
034: import java.awt.Color;
035: import javax.swing.ImageIcon;
036: import javax.swing.JComboBox;
037:
038: import java.util.*;
039:
040: import com.vividsolutions.jump.I18N;
041: import com.vividsolutions.jump.workbench.*;
042: import com.vividsolutions.jump.workbench.model.*;
043: import com.vividsolutions.jump.workbench.plugin.*;
044: import com.vividsolutions.jump.workbench.ui.GUIUtil;
045: import com.vividsolutions.jump.workbench.ui.MultiInputDialog;
046: import com.vividsolutions.jump.workbench.ui.plugin.*;
047: import com.vividsolutions.jump.feature.*;
048: import com.vividsolutions.jts.util.*;
049: import com.vividsolutions.jts.geom.*;
050: import com.vividsolutions.jts.operation.polygonize.*;
051: import com.vividsolutions.jts.geom.util.LinearComponentExtracter;
052: import com.vividsolutions.jump.task.*;
053: import com.vividsolutions.jump.workbench.ui.*;
054:
055: public class PolygonizerPlugIn extends ThreadedBasePlugIn {
056: private boolean useSelected = false;
057:
058: private MultiInputDialog dialog;
059: private String layerName;
060: private boolean splitLineStrings = false;
061: private boolean nodeInputLines = false;
062: private int inputEdgeCount = 0;
063: private int dangleCount = 0;
064: private int cutCount = 0;
065: private int invalidRingCount = 0;
066:
067: private GeometryFactory fact = new GeometryFactory();
068:
069: public PolygonizerPlugIn() {
070: }
071:
072: /**
073: * Returns a very brief description of this task.
074: * @return the name of this task
075: */
076: public String getName() {
077: return I18N
078: .get("jump.plugin.edit.PolygonizerPlugIn.Polygonize");
079: }
080:
081: public EnableCheck createEnableCheck(
082: WorkbenchContext workbenchContext) {
083: EnableCheckFactory checkFactory = new EnableCheckFactory(
084: workbenchContext);
085: return new MultiEnableCheck()
086: .add(
087: checkFactory
088: .createWindowWithLayerManagerMustBeActiveCheck())
089: .add(checkFactory.createAtLeastNLayersMustExistCheck(1));
090: }
091:
092: public boolean execute(PlugInContext context) throws Exception {
093: dialog = new MultiInputDialog(context.getWorkbenchFrame(), I18N
094: .get("jump.plugin.edit.PolygonizerPlugIn.Polygonize"),
095: true);
096: setDialogValues(dialog, context);
097: GUIUtil.centreOnWindow(dialog);
098: dialog.setVisible(true);
099: if (!dialog.wasOKPressed()) {
100: return false;
101: }
102: getDialogValues(dialog);
103: return true;
104: }
105:
106: public void run(TaskMonitor monitor, PlugInContext context)
107: throws Exception {
108: monitor.allowCancellationRequests();
109:
110: Polygonizer polygonizer = new Polygonizer();
111: // polygonizer.setSplitLineStrings(splitLineStrings);
112:
113: monitor
114: .report(I18N
115: .get("jump.plugin.edit.PolygonizerPlugIn.Polygonizing"));
116:
117: Layer layer = dialog.getLayer(SRC_LAYER);
118:
119: Collection inputFeatures = getFeaturesToProcess(layer, context);
120: inputEdgeCount = inputFeatures.size();
121:
122: Collection lines = getLines(inputFeatures);
123:
124: Collection nodedLines = lines;
125: if (nodeInputLines) {
126: monitor
127: .report(I18N
128: .get("jump.plugin.edit.PolygonizerPlugIn.Noding-input-lines"));
129: nodedLines = nodeLines((List) lines);
130: }
131:
132: for (Iterator i = nodedLines.iterator(); i.hasNext();) {
133: Geometry g = (Geometry) i.next();
134: polygonizer.add(g);
135: }
136: if (monitor.isCancelRequested())
137: return;
138: createLayers(context, polygonizer);
139: }
140:
141: private Collection getFeaturesToProcess(Layer lyr,
142: PlugInContext context) {
143: if (useSelected)
144: return context.getLayerViewPanel().getSelectionManager()
145: .getFeaturesWithSelectedItems(lyr);
146: return lyr.getFeatureCollectionWrapper().getFeatures();
147: }
148:
149: private Collection getLines(Collection inputFeatures) {
150: List linesList = new ArrayList();
151: LinearComponentExtracter lineFilter = new LinearComponentExtracter(
152: linesList);
153: for (Iterator i = inputFeatures.iterator(); i.hasNext();) {
154: Feature f = (Feature) i.next();
155: Geometry g = f.getGeometry();
156: g.apply(lineFilter);
157: }
158: return linesList;
159: }
160:
161: /**
162: * Nodes a collection of linestrings.
163: * Noding is done via JTS union, which is reasonably effective but
164: * may exhibit robustness failures.
165: *
166: * @param lines the linear geometries to node
167: * @return a collection of linear geometries, noded together
168: */
169: private Collection nodeLines(Collection lines) {
170: Geometry linesGeom = fact.createMultiLineString(fact
171: .toLineStringArray(lines));
172:
173: Geometry unionInput = fact.createMultiLineString(null);
174: // force the unionInput to be non-empty if possible, to ensure union is not optimized away
175: Geometry point = extractPoint(lines);
176: if (point != null)
177: unionInput = point;
178:
179: Geometry noded = linesGeom.union(unionInput);
180: List nodedList = new ArrayList();
181: nodedList.add(noded);
182: return nodedList;
183: }
184:
185: private Geometry extractPoint(Collection lines) {
186: int minPts = Integer.MAX_VALUE;
187: Geometry point = null;
188: // extract first point from first non-empty geometry
189: for (Iterator i = lines.iterator(); i.hasNext();) {
190: Geometry g = (Geometry) i.next();
191: if (!g.isEmpty()) {
192: Coordinate p = g.getCoordinate();
193: point = g.getFactory().createPoint(p);
194: }
195: }
196: return point;
197: }
198:
199: private void createLayers(PlugInContext context,
200: Polygonizer polygonizer) throws Exception {
201: FeatureCollection dangleFC = FeatureDatasetFactory
202: .createFromGeometry(polygonizer.getDangles());
203: dangleCount = dangleFC.size();
204: if (dangleFC.size() > 0) {
205: Layer lyr4 = context
206: .addLayer(
207: StandardCategoryNames.QA,
208: I18N
209: .get("jump.plugin.edit.PolygonizerPlugIn.Dangles"),
210: dangleFC);
211: LayerStyleUtil.setLinearStyle(lyr4, Color.red, 2, 0);
212: lyr4
213: .setDescription(I18N
214: .get("jump.plugin.edit.PolygonizerPlugIn.Dangling-edges"));
215: }
216:
217: FeatureCollection cutFC = FeatureDatasetFactory
218: .createFromGeometry(polygonizer.getCutEdges());
219: cutCount = cutFC.size();
220:
221: if (cutFC.size() > 0) {
222: Layer lyr = context.addLayer(StandardCategoryNames.QA, I18N
223: .get("jump.plugin.edit.PolygonizerPlugIn.Cuts"),
224: cutFC);
225: LayerStyleUtil.setLinearStyle(lyr, Color.blue, 2, 0);
226: lyr
227: .setDescription(I18N
228: .get("jump.plugin.edit.PolygonizerPlugIn.Cut-edges"));
229: }
230:
231: FeatureCollection invalidRingFC = FeatureDatasetFactory
232: .createFromGeometry(polygonizer.getInvalidRingLines());
233: invalidRingCount = invalidRingFC.size();
234:
235: if (invalidRingFC.size() > 0) {
236: Layer lyr = context
237: .addLayer(
238: StandardCategoryNames.QA,
239: I18N
240: .get("jump.plugin.edit.PolygonizerPlugIn.Invalid-Rings"),
241: invalidRingFC);
242: LayerStyleUtil.setLinearStyle(lyr, Color.blue, 2, 0);
243: lyr
244: .setDescription(I18N
245: .get("jump.plugin.edit.PolygonizerPlugIn.Invalid-Rings"));
246: }
247:
248: FeatureCollection polyFC = FeatureDatasetFactory
249: .createFromGeometry(polygonizer.getPolygons());
250: context
251: .addLayer(
252: StandardCategoryNames.RESULT,
253: layerName
254: + " "
255: + I18N
256: .get("jump.plugin.edit.PolygonizerPlugIn.Polygons"),
257: polyFC);
258:
259: createOutput(context, polyFC);
260:
261: }
262:
263: private void createOutput(PlugInContext context,
264: FeatureCollection polyFC) {
265: context.getOutputFrame().createNewDocument();
266: context
267: .getOutputFrame()
268: .addHeader(
269: 1,
270: I18N
271: .get("jump.plugin.edit.PolygonizerPlugIn.Polygonization"));
272: context.getOutputFrame().addField("Layer: ", layerName);
273:
274: context.getOutputFrame().addText(" ");
275: context
276: .getOutputFrame()
277: .addField(
278: I18N
279: .get("jump.plugin.edit.PolygonizerPlugIn.Number-of-input-edges"),
280: "" + inputEdgeCount);
281: context
282: .getOutputFrame()
283: .addField(
284: I18N
285: .get("jump.plugin.edit.PolygonizerPlugIn.Number-of-polygons-created"),
286: "" + polyFC.size());
287: context
288: .getOutputFrame()
289: .addField(
290: I18N
291: .get("jump.plugin.edit.PolygonizerPlugIn.Number-of-dangling-edges-found"),
292: "" + dangleCount);
293: context
294: .getOutputFrame()
295: .addField(
296: I18N
297: .get("jump.plugin.edit.PolygonizerPlugIn.Number-of-cut-edges-found"),
298: "" + cutCount);
299: context
300: .getOutputFrame()
301: .addField(
302: I18N
303: .get("jump.plugin.edit.PolygonizerPlugIn.Number-of-invalid-rings-found"),
304: "" + invalidRingCount);
305: }
306:
307: private final static String SRC_LAYER = I18N
308: .get("jump.plugin.edit.PolygonizerPlugIn.Line-Layer");
309: private final static String NODE_INPUT = I18N
310: .get("jump.plugin.edit.PolygonizerPlugIn.Node-input-before-polygonizing");
311: // private final static String SPLIT_LINESTRINGS = "Split linestrings into segments";
312: private final static String SELECTED_ONLY = GenericNames.USE_SELECTED_FEATURES_ONLY;
313:
314: private void setDialogValues(MultiInputDialog dialog,
315: PlugInContext context) {
316: dialog.setSideBarImage(new ImageIcon(getClass().getResource(
317: "Polygonize.png")));
318: dialog
319: .setSideBarDescription(I18N
320: .get("jump.plugin.edit.PolygonizerPlugIn.Polygonizes-the-line-segments-in-a-layer")
321: + " "
322: + I18N
323: .get("jump.plugin.edit.PolygonizerPlugIn.Polygonization-requires-correctly-noded-data")
324: + " "
325: + I18N
326: .get("jump.plugin.edit.PolygonizerPlugIn.If-desired-the-input-data-may-be-noded-before-polygonizing-is-performed")
327: + " "
328: + I18N
329: .get("jump.plugin.edit.PolygonizerPlugIn.Dangles-Cutlines-and-Invalid-Rings-are-identified"));
330: String fieldName = SRC_LAYER;
331: JComboBox addLayerComboBox = dialog.addLayerComboBox(fieldName,
332: context.getCandidateLayer(0), null, context
333: .getLayerManager());
334: dialog.addCheckBox(SELECTED_ONLY, useSelected);
335: dialog.addCheckBox(NODE_INPUT, nodeInputLines, NODE_INPUT);
336: // dialog.addCheckBox(SPLIT_LINESTRINGS, splitLineStrings, "If lines are noded at vertices rather than endpoints "
337: // + "this options allows input linestrings to be split into separate line segments.");
338: }
339:
340: private void getDialogValues(MultiInputDialog dialog) {
341: Layer layer = dialog.getLayer(SRC_LAYER);
342: layerName = layer.getName();
343: useSelected = dialog.getBoolean(SELECTED_ONLY);
344: nodeInputLines = dialog.getBoolean(NODE_INPUT);
345: // splitLineStrings = dialog.getBoolean(SPLIT_LINESTRINGS);
346: }
347: }
|