001: /* uDig - User Friendly Desktop Internet GIS client
002: * http://udig.refractions.net
003: * (C) 2004, Refractions Research Inc.
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation;
008: * version 2.1 of the License.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: */
015: package net.refractions.udig.tools.edit.commands;
016:
017: import java.awt.Rectangle;
018: import java.awt.geom.NoninvertibleTransformException;
019: import java.io.IOException;
020: import java.util.ArrayList;
021: import java.util.HashSet;
022: import java.util.List;
023: import java.util.Set;
024:
025: import net.refractions.udig.core.internal.GeometryBuilder;
026: import net.refractions.udig.project.ILayer;
027: import net.refractions.udig.project.command.AbstractCommand;
028: import net.refractions.udig.project.command.UndoableComposite;
029: import net.refractions.udig.project.command.UndoableMapCommand;
030: import net.refractions.udig.project.ui.AnimationUpdater;
031: import net.refractions.udig.tool.edit.internal.Messages;
032: import net.refractions.udig.tools.edit.EditState;
033: import net.refractions.udig.tools.edit.EditToolHandler;
034: import net.refractions.udig.tools.edit.animation.GeometryOperationAnimation;
035: import net.refractions.udig.tools.edit.support.EditBlackboard;
036: import net.refractions.udig.tools.edit.support.EditGeom;
037: import net.refractions.udig.tools.edit.support.IsBusyStateProvider;
038: import net.refractions.udig.tools.edit.support.Point;
039: import net.refractions.udig.tools.edit.support.PrimitiveShape;
040: import net.refractions.udig.tools.edit.support.PrimitiveShapeIterator;
041:
042: import org.eclipse.core.runtime.IProgressMonitor;
043: import org.eclipse.core.runtime.SubProgressMonitor;
044: import org.geotools.data.DefaultQuery;
045: import org.geotools.data.FeatureSource;
046: import org.geotools.data.Query;
047: import org.geotools.feature.Feature;
048: import org.geotools.feature.FeatureCollection;
049: import org.geotools.feature.FeatureIterator;
050: import org.geotools.feature.FeatureType;
051: import org.geotools.feature.IllegalAttributeException;
052: import org.geotools.filter.BBoxExpression;
053: import org.geotools.filter.FilterFactory;
054: import org.geotools.filter.FilterFactoryFinder;
055: import org.geotools.filter.FilterType;
056: import org.geotools.filter.GeometryFilter;
057: import org.geotools.filter.IllegalFilterException;
058: import org.geotools.geometry.jts.JTS;
059: import org.opengis.referencing.operation.MathTransform;
060:
061: import com.vividsolutions.jts.geom.Coordinate;
062: import com.vividsolutions.jts.geom.Envelope;
063: import com.vividsolutions.jts.geom.Geometry;
064: import com.vividsolutions.jts.geom.GeometryCollection;
065: import com.vividsolutions.jts.geom.GeometryFactory;
066: import com.vividsolutions.jts.geom.LinearRing;
067: import com.vividsolutions.jts.geom.MultiPolygon;
068: import com.vividsolutions.jts.geom.Polygon;
069:
070: /**
071: * Splits a feature based on the current shape in the handler. After the command the current shape
072: * will be set to null and the edit blackboard will be cleared.
073: *
074: * @author jones
075: * @since 1.1.0
076: */
077: public class DifferenceFeatureCommand extends AbstractCommand implements
078: UndoableMapCommand {
079:
080: private EditToolHandler handler;
081: private PrimitiveShape shape;
082: private EditState state;
083: private ILayer layer;
084: private ArrayList<EditGeom> geoms;
085: private EditState endState;
086: private UndoableComposite writeCommand;
087: private boolean addedEndVertex = false;
088:
089: /**
090: * @param handler
091: */
092: public DifferenceFeatureCommand(EditToolHandler handler,
093: EditState endState) {
094: this .handler = handler;
095: this .layer = handler.getEditLayer();
096: this .endState = endState;
097: }
098:
099: @SuppressWarnings("unchecked")
100: public void run(IProgressMonitor monitor) throws Exception {
101: monitor.beginTask(
102: Messages.DifferenceFeatureCommand_runTaskMessage, 10);
103: monitor.worked(1);
104: this .state = handler.getCurrentState();
105: this .shape = handler.getCurrentShape();
106: handler.setCurrentShape(null);
107: List<UndoableMapCommand> commands = new ArrayList<UndoableMapCommand>();
108:
109: Point startPoint = shape.getPoint(0);
110: if (!startPoint
111: .equals(shape.getPoint(shape.getNumPoints() - 1))) {
112: addedEndVertex = true;
113: shape.getEditBlackboard().addPoint(startPoint.getX(),
114: startPoint.getY(), shape);
115: }
116: GeometryOperationAnimation indicator = new GeometryOperationAnimation(
117: PrimitiveShapeIterator.getPathIterator(shape).toShape(),
118: new IsBusyStateProvider(handler));
119: try {
120: AnimationUpdater.runTimer(handler.getContext()
121: .getMapDisplay(), indicator);
122: handler.setCurrentState(EditState.BUSY);
123:
124: if (writeCommand == null) {
125: EditBlackboard editBlackboard = handler
126: .getEditBlackboard(layer);
127: this .geoms = new ArrayList<EditGeom>(editBlackboard
128: .getGeoms());
129: geoms.remove(shape.getEditGeom());
130:
131: editBlackboard.clear();
132:
133: FeatureCollection features = getFeatures(monitor);
134:
135: try {
136: List<Geometry> geoms = new ArrayList<Geometry>();
137: geoms.add(createReferenceGeom());
138:
139: Feature first = runDifferenceOp(
140: features.features(), geoms);
141:
142: if (first == null)
143: return;
144:
145: createAddFeatureCommands(commands, geoms, first);
146: } finally {
147: monitor.worked(2);
148: }
149: this .writeCommand = new UndoableComposite(commands);
150: }
151: writeCommand.setMap(getMap());
152: handler.setCurrentState(EditState.COMMITTING);
153: writeCommand.execute(new SubProgressMonitor(monitor, 5));
154: } finally {
155: indicator.setValid(false);
156: handler.setCurrentState(endState);
157: monitor.done();
158: }
159: }
160:
161: @SuppressWarnings("unchecked")
162: private void createAddFeatureCommands(
163: List<UndoableMapCommand> commands, List<Geometry> geoms,
164: Feature first) throws IllegalAttributeException {
165: FeatureType featureType = first.getFeatureType();
166: if ((geoms.size() > 1 && !featureType.getDefaultGeometry()
167: .getType().isAssignableFrom(MultiPolygon.class))
168: || !featureType.getDefaultGeometry().getType()
169: .isAssignableFrom(MultiPolygon.class)) {
170: for (Geometry geom : geoms) {
171: Feature newFeature = featureType.duplicate(first);
172: newFeature.setDefaultGeometry(geom);
173: commands.add(handler.getContext().getEditFactory()
174: .createAddFeatureCommand(newFeature, layer));
175: }
176: } else {
177: Feature newFeature = featureType.duplicate(first);
178: GeometryFactory factory = new GeometryFactory();
179:
180: newFeature.setDefaultGeometry(factory
181: .createMultiPolygon(geoms.toArray(new Polygon[geoms
182: .size()])));
183: commands.add(handler.getContext().getEditFactory()
184: .createAddFeatureCommand(newFeature, layer));
185: }
186: }
187:
188: /**
189: * @param iter
190: * @param geoms the geometry to remove the features in iter from. IE the geometries that will be diffed. Is
191: * also the list of resulting geometries.
192: * @return
193: */
194: private Feature runDifferenceOp(FeatureIterator iter,
195: List<Geometry> geoms) {
196: try {
197: Feature first = null;
198: while (iter.hasNext()) {
199: Feature f = iter.next();
200: if (first == null)
201: first = f;
202: Set<Geometry> featureGeoms = new HashSet<Geometry>();
203: if (f.getDefaultGeometry() instanceof GeometryCollection) {
204: for (int i = 0; i < f.getDefaultGeometry()
205: .getNumGeometries(); i++) {
206: featureGeoms.add(f.getDefaultGeometry()
207: .getGeometryN(i));
208: }
209: } else {
210: featureGeoms.add(f.getDefaultGeometry());
211: }
212: // featureGeom is the geometry to subtract from the geometries in geoms.
213: for (Geometry featureGeom : featureGeoms) {
214: List<Geometry> toAdd = new ArrayList<Geometry>();
215:
216: for (Geometry geometry : geoms) {
217: Geometry difference = geometry
218: .difference(featureGeom);
219:
220: if (difference instanceof GeometryCollection) {
221: for (int i = 0; i < difference
222: .getNumGeometries(); i++) {
223: toAdd.add(difference.getGeometryN(i));
224: }
225: } else
226: toAdd.add(difference);
227: }
228:
229: if (!toAdd.isEmpty()) {
230: geoms.clear();
231: geoms.addAll(toAdd);
232: }
233: }
234: }
235: return first;
236: } finally {
237: if (iter != null)
238: iter.close();
239: }
240: }
241:
242: private FeatureCollection getFeatures(IProgressMonitor monitor)
243: throws IOException, NoninvertibleTransformException,
244: IllegalFilterException {
245: FeatureSource source = layer.getResource(FeatureSource.class,
246: new SubProgressMonitor(monitor, 2));
247: FeatureType schema = layer.getSchema();
248: Rectangle bounds = shape.getBounds();
249: double[] toTransform = new double[] { bounds.getMinX(),
250: bounds.getMinY(), bounds.getMaxX(), bounds.getMaxY() };
251: handler.getContext().worldToScreenTransform().inverseTransform(
252: toTransform, 0, toTransform, 0, 2);
253: Envelope transformedBounds = new Envelope(toTransform[0],
254: toTransform[2], toTransform[1], toTransform[3]);
255:
256: FilterFactory filterFactory = FilterFactoryFinder
257: .createFilterFactory();
258: Envelope layerBounds;
259: try {
260: MathTransform transform = layer.mapToLayerTransform();
261: layerBounds = JTS.transform(transformedBounds, transform);
262: } catch (Exception e) {
263: layerBounds = transformedBounds;
264: }
265: BBoxExpression bb = filterFactory
266: .createBBoxExpression(layerBounds);
267: GeometryFilter filter = filterFactory
268: .createGeometryFilter(FilterType.GEOMETRY_BBOX);
269: filter.addRightGeometry(bb);
270:
271: String geomAttributeName = layer.getSchema()
272: .getDefaultGeometry().getName();
273:
274: filter.addLeftGeometry(filterFactory
275: .createAttributeExpression(geomAttributeName));
276: Query query = new DefaultQuery(schema.getTypeName(), filter);
277:
278: return source.getFeatures(query);
279: }
280:
281: /**
282: * @return
283: */
284: private Geometry createReferenceGeom() {
285: LinearRing ring = GeometryBuilder.create().safeCreateGeometry(
286: LinearRing.class, shape.coordArray());
287: GeometryFactory fac = new GeometryFactory();
288: return fac.createPolygon(ring, new LinearRing[0]);
289:
290: }
291:
292: public void rollback(IProgressMonitor monitor) throws Exception {
293: GeometryOperationAnimation indicator = new GeometryOperationAnimation(
294: PrimitiveShapeIterator.getPathIterator(shape).toShape(),
295: new IsBusyStateProvider(handler));
296: try {
297: monitor.beginTask(
298: Messages.DifferenceFeatureCommand_undoTaskMessage,
299: 10);
300: monitor.worked(1);
301:
302: AnimationUpdater.runTimer(handler.getContext()
303: .getMapDisplay(), indicator);
304:
305: handler.setCurrentState(EditState.BUSY);
306:
307: SubProgressMonitor submonitor = new SubProgressMonitor(
308: monitor, 5);
309: writeCommand.rollback(submonitor);
310: submonitor.done();
311:
312: EditBlackboard bb = handler.getEditBlackboard(layer);
313: bb.clear();
314: for (EditGeom geom : geoms) {
315: addGeom(bb, geom);
316: }
317: PrimitiveShape shell = addGeom(bb, shape.getEditGeom())
318: .getShell();
319: handler.setCurrentShape(shell);
320: if (addedEndVertex) {
321: bb.removeCoordinate(shape.getNumCoords() - 1, shape
322: .getCoord(shape.getNumCoords() - 1), shell);
323: }
324: handler.setCurrentState(state);
325: } catch (Exception e) {
326: handler.setCurrentState(EditState.NONE);
327: throw e;
328: } finally {
329: indicator.setValid(false);
330: monitor.done();
331: }
332: }
333:
334: /**
335: * @param bb
336: * @param geom
337: * @return
338: */
339: private EditGeom addGeom(EditBlackboard bb, EditGeom geom) {
340: EditGeom newGeom = bb.newGeom(geom.getFeatureIDRef().get(),
341: geom.getShapeType());
342: newGeom.setChanged(geom.isChanged());
343: for (PrimitiveShape shape : geom) {
344: PrimitiveShape newShape = newGeom.getShell();
345: if (shape != geom.getShell())
346: newShape = newGeom.newHole();
347: Coordinate[] coords = shape.coordArray();
348: for (int i = 0; i < coords.length; i++) {
349: bb.addCoordinate(coords[i], newShape);
350: }
351: }
352: return newGeom;
353: }
354:
355: public String getName() {
356: return Messages.DifferenceFeatureCommand_name;
357: }
358: }
|