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: * JUMP is Copyright (C) 2003 Vivid Solutions
006: *
007: * This program implements extensions to JUMP and is
008: * Copyright (C) 2004 Integrated Systems Analysts, Inc.
009: *
010: * This program is free software; you can redistribute it and/or
011: * modify it under the terms of the GNU General Public License
012: * as published by the Free Software Foundation; either version 2
013: * of the License, or (at your option) any later version.
014: *
015: * This program is distributed in the hope that it will be useful,
016: * but WITHOUT ANY WARRANTY; without even the implied warranty of
017: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
018: * GNU General Public License for more details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with this program; if not, write to the Free Software
022: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
023: *
024: * For more information, contact:
025: *
026: * Integrated Systems Analysts, Inc.
027: * 630C Anchors St., Suite 101
028: * Fort Walton Beach, Florida
029: * USA
030: *
031: * (850)862-7321
032: */
033:
034: package org.openjump.core.ui.plugin.tools;
035:
036: import java.util.ArrayList;
037: import java.util.Collection;
038: import java.util.Iterator;
039: import javax.swing.JComponent;
040:
041: import org.openjump.core.geomutils.GeoUtils;
042:
043: import com.vividsolutions.jts.geom.*;
044: import com.vividsolutions.jump.I18N;
045: import com.vividsolutions.jump.feature.Feature;
046: import com.vividsolutions.jump.feature.FeatureCollection;
047: import com.vividsolutions.jump.feature.FeatureDataset;
048: import com.vividsolutions.jump.task.TaskMonitor;
049: import com.vividsolutions.jump.workbench.WorkbenchContext;
050: import com.vividsolutions.jump.workbench.model.Layer;
051: import com.vividsolutions.jump.workbench.model.StandardCategoryNames;
052: import com.vividsolutions.jump.workbench.model.UndoableCommand;
053: import com.vividsolutions.jump.workbench.plugin.*;
054: import com.vividsolutions.jump.workbench.plugin.util.*;
055: import com.vividsolutions.jump.workbench.ui.GUIUtil;
056: import com.vividsolutions.jump.workbench.ui.MenuNames;
057: import com.vividsolutions.jump.workbench.ui.MultiInputDialog;
058: import com.vividsolutions.jump.workbench.ui.SelectionManagerProxy;
059: import com.vividsolutions.jump.workbench.ui.plugin.analysis.GeometryFunction;
060:
061: public class CutPolygonPlugIn extends AbstractPlugIn implements
062: ThreadedPlugIn {
063: private static String UPDATE_SRC = I18N
064: .get("org.openjump.core.ui.plugin.tools.CutPolygonPlugIn.Update-the-polygon-with-result");
065: private static String ADD_TO_SRC = I18N
066: .get("org.openjump.core.ui.plugin.tools.CutPolygonPlugIn.Add-result-to-the-polygon-layer");
067: private static String CREATE_LYR = I18N
068: .get("org.openjump.core.ui.plugin.tools.CutPolygonPlugIn.Create-new-layer-for-result");
069: private static String sCutPolygon = I18N
070: .get("org.openjump.core.ui.plugin.tools.CutPolygonPlugIn.Cut-Polygon");
071: private static String sError = I18N
072: .get("org.openjump.core.ui.plugin.tools.CutPolygonPlugIn.Errors-found-while-executing-Cut-Polygon");
073: private static String sExecuting = I18N
074: .get("org.openjump.core.ui.plugin.tools.CutPolygonPlugIn.Executing-Difference-function");
075: private static String sMustSelect = I18N
076: .get("org.openjump.core.ui.plugin.tools.CutPolygonPlugIn.Must-select-one-polygon-and-one-linestring");
077: private static String sDescription = I18N
078: .get("org.openjump.core.ui.plugin.tools.CutPolygonPlugIn.Uses-the-selected-linestring-to-cut-the-selected-polygon-into-separate-sections");
079:
080: private MultiInputDialog dialog;
081: private Layer srcLayer;
082: private GeometryFunction differenceFunction = GeometryFunction
083: .getFunction("Difference (Source-Mask)");
084: private GeometryFunction intersectionFunction = GeometryFunction
085: .getFunction("Intersection");
086: private boolean createLayer = true;
087: private boolean updateSource = false;
088: private boolean addToSource = false;
089:
090: public CutPolygonPlugIn() {
091: }
092:
093: public void initialize(PlugInContext context) throws Exception {
094: //-- load again in the correct language
095: UPDATE_SRC = I18N
096: .get("org.openjump.core.ui.plugin.tools.CutPolygonPlugIn.Update-the-polygon-with-result");
097: ADD_TO_SRC = I18N
098: .get("org.openjump.core.ui.plugin.tools.CutPolygonPlugIn.Add-result-to-the-polygon-layer");
099: CREATE_LYR = I18N
100: .get("org.openjump.core.ui.plugin.tools.CutPolygonPlugIn.Create-new-layer-for-result");
101: sCutPolygon = I18N
102: .get("org.openjump.core.ui.plugin.tools.CutPolygonPlugIn.Cut-Polygon");
103: sError = I18N
104: .get("org.openjump.core.ui.plugin.tools.CutPolygonPlugIn.Errors-found-while-executing-Cut-Polygon");
105: sExecuting = I18N
106: .get("org.openjump.core.ui.plugin.tools.CutPolygonPlugIn.Executing-Difference-function");
107: sMustSelect = I18N
108: .get("org.openjump.core.ui.plugin.tools.CutPolygonPlugIn.Must-select-one-polygon-and-one-linestring");
109: sDescription = I18N
110: .get("org.openjump.core.ui.plugin.tools.CutPolygonPlugIn.Uses-the-selected-linestring-to-cut-the-selected-polygon-into-separate-sections");
111:
112: //-- [sstein 11 March 2007] it is a bit circumstantially to access a geometry function
113: // using i18n strings - we should introduce an unique ID
114: differenceFunction = GeometryFunction
115: .getFunction(I18N
116: .get("ui.plugin.analysis.GeometryFunction.difference-a-b"));
117: intersectionFunction = GeometryFunction
118: .getFunction(I18N
119: .get("ui.plugin.analysis.GeometryFunction.intersection"));
120:
121: WorkbenchContext workbenchContext = context
122: .getWorkbenchContext();
123: context.getFeatureInstaller().addMainMenuItem(
124: this ,
125: new String[] { MenuNames.TOOLS,
126: MenuNames.TOOLS_EDIT_GEOMETRY },
127: getName() + "...", false, null,
128: this .createEnableCheck(workbenchContext));
129: }
130:
131: public String getName() {
132: return this .sCutPolygon;
133: }
134:
135: public boolean execute(PlugInContext context) throws Exception {
136: dialog = new MultiInputDialog(context.getWorkbenchFrame(),
137: getName(), true);
138: setDialogValues(dialog, context);
139: GUIUtil.centreOnWindow(dialog);
140: dialog.setVisible(true);
141: if (!dialog.wasOKPressed()) {
142: return false;
143: }
144: getDialogValues(dialog);
145: return true;
146: }
147:
148: public void run(TaskMonitor monitor, PlugInContext context)
149: throws Exception {
150: monitor.allowCancellationRequests();
151: Collection selectedFeatures = context.getLayerViewPanel()
152: .getSelectionManager().getFeaturesWithSelectedItems();
153: Iterator i = selectedFeatures.iterator();
154: Feature featureOne = (Feature) i.next();
155: Feature featureTwo = (Feature) i.next();
156:
157: //this works because enable check ensures that one is polygon and the other is linestring
158: Feature polyFeature = featureOne;
159: Geometry linestring = featureTwo.getGeometry();
160:
161: if (linestring instanceof Polygon) {
162: polyFeature = featureTwo;
163: linestring = featureOne.getGeometry();
164: }
165:
166: double bufferWidth = 0.01;
167: Geometry buffer = linestring.buffer(bufferWidth);
168:
169: //find the poly layer since linestring can be on different layer
170: for (Iterator lyr = context.getWorkbenchContext()
171: .getLayerViewPanel().getSelectionManager()
172: .getLayersWithSelectedItems().iterator(); lyr.hasNext();) {
173: Layer layer = (Layer) lyr.next();
174:
175: for (Iterator ftr = layer.getFeatureCollectionWrapper()
176: .getFeatures().iterator(); ftr.hasNext();) {
177: if (polyFeature == ftr.next()) {
178: srcLayer = layer;
179: break;
180: }
181: }
182:
183: if (srcLayer == layer)
184: break;
185: }
186: monitor.report(sExecuting + "...");
187:
188: Collection resultFeatures = new ArrayList();
189: Geometry result = null;
190: Geometry intersection = null;
191:
192: try {
193: Geometry geoms[] = new Geometry[2];
194: geoms[0] = polyFeature.getGeometry();
195: geoms[1] = buffer;
196: result = differenceFunction.execute(geoms, new double[2]);
197:
198: geoms[1] = linestring;
199: intersection = intersectionFunction.execute(geoms,
200: new double[2]);
201: } catch (RuntimeException ex) {
202: context.getWorkbenchFrame().warnUser(sError);
203: }
204:
205: Coordinate[] intersectionPts = intersection.getCoordinates();
206:
207: if (result == null || result.isEmpty())
208: return;
209:
210: int lsNumPts = linestring.getNumPoints();
211: Coordinate[] lsCoords = linestring.getCoordinates();
212:
213: for (int j = 0; j < result.getNumGeometries(); j++) {
214: Feature fNew = polyFeature.clone(true);
215:
216: //snap the resulting geos to the cut line
217: Geometry geo = (Geometry) result.getGeometryN(j).clone();
218: int numPts = geo.getNumPoints();
219: Coordinate[] coords = geo.getCoordinates();
220:
221: for (int m = 0; m < numPts; m++) {
222: for (int n = 0; n < lsNumPts - 1; n++) {
223: Coordinate p0 = lsCoords[n];
224: Coordinate p1 = lsCoords[n + 1];
225: double distToLine = GeoUtils.getDistance(coords[m],
226: p0, p1);
227: if (Math.abs((distToLine) - (bufferWidth)) < 0.001) {
228: //is close to buffer boundary
229: Coordinate snapPt = getNearestSnapPoint(
230: coords[m], intersectionPts);
231: coords[m].x = snapPt.x;
232: coords[m].y = snapPt.y;
233: }
234: }
235: }
236: CoordinateList coordList = new CoordinateList(coords, false); //gets rid of duplicates
237: Polygon poly = new GeometryFactory().createPolygon(
238: new GeometryFactory().createLinearRing(coordList
239: .toCoordinateArray()), null);
240: fNew.setGeometry(poly);
241: resultFeatures.add(fNew);
242: }
243:
244: if (createLayer) {
245: String outputLayerName = LayerNameGenerator
246: .generateOperationOnLayerName(sCutPolygon, srcLayer
247: .getName());
248: FeatureCollection resultFC = new FeatureDataset(srcLayer
249: .getFeatureCollectionWrapper().getFeatureSchema());
250: resultFC.addAll(resultFeatures);
251: String categoryName = StandardCategoryNames.RESULT;
252: context.getLayerManager().addCategory(categoryName);
253: Layer newLayer = context.addLayer(categoryName,
254: outputLayerName, resultFC);
255: newLayer.setFeatureCollectionModified(true);
256: }
257:
258: else if (updateSource) {
259: final Collection undoableNewFeatures = resultFeatures;
260: final Feature undoablePolyFeatures = polyFeature;
261:
262: UndoableCommand cmd = new UndoableCommand(getName()) {
263: public void execute() {
264: srcLayer.getFeatureCollectionWrapper().remove(
265: undoablePolyFeatures);
266: srcLayer.getFeatureCollectionWrapper().addAll(
267: undoableNewFeatures);
268: }
269:
270: public void unexecute() {
271: srcLayer.getFeatureCollectionWrapper().removeAll(
272: undoableNewFeatures);
273: srcLayer.getFeatureCollectionWrapper().add(
274: undoablePolyFeatures);
275: }
276: };
277: execute(cmd, context);
278: }
279:
280: else if (addToSource) {
281: final Collection undoableFeatures = resultFeatures;
282:
283: UndoableCommand cmd = new UndoableCommand(getName()) {
284: public void execute() {
285: srcLayer.getFeatureCollectionWrapper().addAll(
286: undoableFeatures);
287: }
288:
289: public void unexecute() {
290: srcLayer.getFeatureCollectionWrapper().removeAll(
291: undoableFeatures);
292: }
293: };
294: execute(cmd, context);
295: }
296: }
297:
298: private Coordinate getNearestSnapPoint(Coordinate coord,
299: Coordinate[] intersectionPts) {
300: Coordinate closestPt = (Coordinate) ((Coordinate) intersectionPts[0])
301: .clone();
302: double shortestDist = coord.distance(intersectionPts[0]);
303:
304: for (int i = 1; i < intersectionPts.length; i++) {
305: if (coord.distance(intersectionPts[i]) < shortestDist) {
306: closestPt = (Coordinate) ((Coordinate) intersectionPts[i])
307: .clone();
308: shortestDist = coord.distance(intersectionPts[i]);
309: }
310: }
311: return closestPt;
312: }
313:
314: private void setDialogValues(MultiInputDialog dialog,
315: PlugInContext context) {
316: dialog.setSideBarDescription(sDescription);
317:
318: final String OUTPUT_GROUP = "Match Type";
319: dialog.addRadioButton(CREATE_LYR, OUTPUT_GROUP, createLayer,
320: CREATE_LYR);
321:
322: dialog.addRadioButton(UPDATE_SRC, OUTPUT_GROUP, updateSource,
323: UPDATE_SRC);
324:
325: dialog.addRadioButton(ADD_TO_SRC, OUTPUT_GROUP, addToSource,
326: ADD_TO_SRC);
327: }
328:
329: private void getDialogValues(MultiInputDialog dialog) {
330: createLayer = dialog.getBoolean(CREATE_LYR);
331: updateSource = dialog.getBoolean(UPDATE_SRC);
332: addToSource = dialog.getBoolean(ADD_TO_SRC);
333: }
334:
335: public EnableCheck onlyPolyAndLinestringMayBeSelected(
336: final WorkbenchContext workbenchContext) {
337: return new EnableCheck() {
338: public String check(JComponent component) {
339: Collection selectedItems = ((SelectionManagerProxy) workbenchContext
340: .getWorkbench().getFrame()
341: .getActiveInternalFrame())
342: .getSelectionManager().getSelectedItems();
343: int polyCount = 0;
344: int lsCount = 0;
345:
346: for (Iterator i = selectedItems.iterator(); i.hasNext();) {
347: Geometry geo = (Geometry) i.next();
348: if (geo instanceof Polygon)
349: polyCount++;
350: if (geo instanceof LineString)
351: lsCount++;
352: }
353:
354: if (polyCount == 1 && lsCount == 1)
355: return null;
356: return sMustSelect;
357: }
358: };
359: }
360:
361: public MultiEnableCheck createEnableCheck(
362: final WorkbenchContext workbenchContext) {
363: EnableCheckFactory checkFactory = new EnableCheckFactory(
364: workbenchContext);
365: return new MultiEnableCheck()
366: .add(
367: checkFactory
368: .createWindowWithLayerViewPanelMustBeActiveCheck())
369: .add(
370: checkFactory
371: .createExactlyNFeaturesMustBeSelectedCheck(2))
372: .add(
373: onlyPolyAndLinestringMayBeSelected(workbenchContext));
374: }
375: }
|