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.Color;
018: import java.awt.Rectangle;
019: import java.awt.geom.Ellipse2D;
020: import java.util.ArrayList;
021: import java.util.List;
022:
023: import net.refractions.udig.project.ILayer;
024: import net.refractions.udig.project.command.AbstractCommand;
025: import net.refractions.udig.project.command.UndoableComposite;
026: import net.refractions.udig.project.command.UndoableMapCommand;
027: import net.refractions.udig.project.internal.ProjectPlugin;
028: import net.refractions.udig.project.ui.AnimationUpdater;
029: import net.refractions.udig.project.ui.IAnimation;
030: import net.refractions.udig.project.ui.commands.AbstractDrawCommand;
031: import net.refractions.udig.project.ui.render.displayAdapter.MapMouseEvent;
032: import net.refractions.udig.project.ui.tool.IToolContext;
033: import net.refractions.udig.tool.edit.internal.Messages;
034: import net.refractions.udig.tools.edit.EditPlugin;
035: import net.refractions.udig.tools.edit.EditState;
036: import net.refractions.udig.tools.edit.EditToolHandler;
037: import net.refractions.udig.tools.edit.support.EditBlackboard;
038: import net.refractions.udig.tools.edit.support.EditGeom;
039: import net.refractions.udig.tools.edit.support.Point;
040: import net.refractions.udig.tools.edit.support.ShapeType;
041:
042: import org.eclipse.core.runtime.IProgressMonitor;
043: import org.geotools.data.FeatureSource;
044: import org.geotools.data.FeatureStore;
045: import org.geotools.feature.Feature;
046: import org.geotools.feature.FeatureCollection;
047: import org.geotools.feature.FeatureIterator;
048: import org.geotools.filter.BBoxExpression;
049: import org.geotools.filter.Filter;
050: import org.geotools.filter.FilterFactory;
051: import org.geotools.filter.FilterFactoryFinder;
052: import org.geotools.filter.FilterType;
053: import org.geotools.filter.GeometryFilter;
054: import org.geotools.geometry.jts.JTS;
055: import org.geotools.geometry.jts.ReferencedEnvelope;
056: import org.opengis.referencing.operation.MathTransform;
057:
058: import com.vividsolutions.jts.geom.Envelope;
059: import com.vividsolutions.jts.geom.Geometry;
060: import com.vividsolutions.jts.geom.GeometryFactory;
061:
062: /**
063: * Searches a layer for an intersection between a mouse click and a feature and selects the features
064: * if found.
065: *
066: * @author Jesse
067: * @since 1.1.0
068: */
069: public class SelectGeometryCommand extends AbstractCommand implements
070: UndoableMapCommand {
071:
072: private EditToolHandler handler;
073: private Class[] acceptableClasses;
074: private boolean onlyAdd;
075: private boolean permitClear;
076: private MapMouseEvent event;
077: private short filterType;
078:
079: private UndoableMapCommand command;
080: private ShapeType toCreate;
081:
082: public SelectGeometryCommand(EditToolHandler handler,
083: MapMouseEvent e, Class[] acceptableClasses,
084: short filterType, boolean permitClear, boolean onlyAdd) {
085: this .handler = handler;
086: this .event = e;
087: Class[] c = new Class[0];
088: if (acceptableClasses != null) {
089: c = new Class[acceptableClasses.length];
090: System.arraycopy(acceptableClasses, 0, c, 0, c.length);
091: }
092: this .acceptableClasses = c;
093:
094: this .filterType = filterType;
095: this .onlyAdd = onlyAdd;
096: this .permitClear = permitClear;
097: }
098:
099: public void run(IProgressMonitor monitor) throws Exception {
100: if (command == null) {
101: IToolContext context = handler.getContext();
102: ILayer editLayer = handler.getEditLayer();
103:
104: EditBlackboard editBlackboard = handler
105: .getEditBlackboard(editLayer);
106: editBlackboard.startBatchingEvents();
107: BlockingSelectionAnim animation = new BlockingSelectionAnim(
108: event.x, event.y);
109: try {
110: AnimationUpdater.runTimer(context.getMapDisplay(),
111: animation);
112: try {
113: FeatureStore store = editLayer.getResource(
114: FeatureStore.class, null);
115: ReferencedEnvelope bbox = handler.getContext()
116: .getBoundingBox(event.getPoint(), 7);
117: Filter createBBoxFilter = createBBoxFilter(bbox,
118: editLayer, filterType);
119: FeatureCollection collection = store
120: .getFeatures(createBBoxFilter);
121: FeatureIterator reader = collection.features();
122: try {
123: boolean hasFeature = false;
124: try {
125: hasFeature = reader.hasNext();
126: } catch (Exception e) {
127: reader.close();
128: createBBoxFilter = createBBoxFilter(bbox,
129: editLayer, FilterType.GEOMETRY_BBOX);
130: collection = store
131: .getFeatures(createBBoxFilter);
132: reader = collection.features();
133: try {
134: hasFeature = reader.hasNext();
135: } catch (Exception e2) {
136: EditPlugin.log("", e2); //$NON-NLS-1$
137: }
138: }
139: if (hasFeature) {
140: selectAllIntersectedFeatures(monitor,
141: editLayer, reader);
142: } else {
143: if (!event
144: .isModifierDown(MapMouseEvent.MOD1_DOWN_MASK)
145: && !event.isShiftDown()
146: && permitClear) {
147: writeModifiedFeaturesAndStartEditing(
148: monitor, editBlackboard);
149: }
150: }
151:
152: if (command == null && toCreate != null) {
153: List<UndoableMapCommand> commands = new ArrayList<UndoableMapCommand>();
154: commands.add(handler.getContext()
155: .getEditFactory()
156: .createNullEditFeatureCommand());
157: commands.add(new StartEditingCommand(
158: handler, this .event, toCreate));
159: }
160: } finally {
161: collection.close(reader);
162: }
163: } catch (Exception e2) {
164: EditPlugin.log("", e2); //$NON-NLS-1$
165: }
166: } finally {
167: if (animation != null) {
168: animation.setValid(false);
169: animation = null;
170: }
171: editBlackboard.fireBatchedEvents();
172: }
173: } else {
174: command.run(monitor);
175: }
176: }
177:
178: /**
179: *
180: * @param monitor
181: * @param editBlackboard
182: * @throws Exception
183: */
184: private void writeModifiedFeaturesAndStartEditing(
185: IProgressMonitor monitor, EditBlackboard editBlackboard)
186: throws Exception {
187: List<UndoableMapCommand> commands = new ArrayList<UndoableMapCommand>();
188: if (hasDirtyGeom())
189: commands.add(handler.getCommand(handler
190: .getAcceptBehaviours()));
191: commands.add(new RemoveEditGeomCommand(handler, editBlackboard
192: .getGeoms()));
193: if (toCreate != null) {
194: commands.add(handler.getContext().getEditFactory()
195: .createNullEditFeatureCommand());
196: if (toCreate == ShapeType.POINT) {
197: commands
198: .add(new StartEditingCommand(handler,
199: this .event, toCreate, handler
200: .getCurrentState()));
201: commands.add(handler.getCommand(handler
202: .getAcceptBehaviours()));
203: } else {
204: commands.add(new StartEditingCommand(handler,
205: this .event, toCreate, EditState.CREATING));
206: }
207: }
208: this .command = new UndoableComposite(commands);
209: setAndRun(monitor, command);
210: }
211:
212: /**
213: *
214: * @param monitor
215: * @param selectedLayer
216: * @param reader
217: * @throws Exception
218: */
219: private void selectAllIntersectedFeatures(IProgressMonitor monitor,
220: ILayer selectedLayer, FeatureIterator reader)
221: throws Exception {
222: List<UndoableMapCommand> commands = new ArrayList<UndoableMapCommand>();
223: boolean firstPass = true;
224: GeometryFactory factory = new GeometryFactory();
225: Envelope temp = handler.getContext().getPixelBoundingBox(
226: event.getPoint());
227: temp = JTS.transform(temp, selectedLayer.mapToLayerTransform());
228: Geometry pixelBoundingBox = factory.toGeometry(temp);
229: List<Feature> allFeatures = new ArrayList<Feature>();
230: Feature intersectedFeature = processReader(reader, allFeatures,
231: pixelBoundingBox);
232:
233: for (Feature feature : allFeatures) {
234: if (intersectedFeature != null
235: && !feature.getDefaultGeometry().intersects(
236: pixelBoundingBox))
237: continue;
238: for (Class<Geometry> clazz : acceptableClasses) {
239: if (onlyAdd
240: || handler.getCurrentGeom() != null
241: && (event.isShiftDown() || event
242: .isModifierDown(MapMouseEvent.MOD1_DOWN_MASK))) {
243: setAndRun(monitor, new AddGeomCommand(handler,
244: feature, Point.valueOf(event.x, event.y)));
245: } else {
246: if (clazz.isAssignableFrom(feature
247: .getDefaultGeometry().getClass())) {
248:
249: if (firstPass) {
250: commands.add(handler.getCommand(handler
251: .getAcceptBehaviours()));
252: commands
253: .add(new SetGeomCommand(handler,
254: feature, selectedLayer,
255: Point.valueOf(event.x,
256: event.y)));
257: firstPass = false;
258: } else {
259: commands.add(new AddGeomCommand(handler,
260: feature, null));
261: }
262: EditPlugin
263: .trace(
264: EditPlugin.SELECTION,
265: "Feature is one of the acceptable classes " + feature.getID(), null); //$NON-NLS-1$
266: } else {
267: EditPlugin
268: .trace(
269: EditPlugin.SELECTION,
270: "Feature is not one of the acceptable classes " + feature.getID(), null); //$NON-NLS-1$
271: }
272: }
273: }
274: }
275: UndoableComposite undoableComposite = new UndoableComposite(
276: commands);
277: undoableComposite.setName(Messages.SelectGeometryCommand_name);
278: setAndRun(monitor, undoableComposite);
279: }
280:
281: private Feature processReader(FeatureIterator reader,
282: List<Feature> allFeatures, Geometry pixelBoundingBox) {
283:
284: Feature intersectingFeature = null;
285:
286: while (reader.hasNext()) {
287: Feature feature = reader.next();
288: if (intersectingFeature == null
289: && feature.getDefaultGeometry().intersects(
290: pixelBoundingBox)) {
291: intersectingFeature = feature;
292: }
293: allFeatures.add(feature);
294: }
295: return intersectingFeature;
296: }
297:
298: private boolean hasDirtyGeom() {
299: if (handler.getCurrentGeom() != null
300: && handler.getCurrentGeom().isChanged())
301: return true;
302: List<EditGeom> geoms = handler.getEditBlackboard(
303: handler.getEditLayer()).getGeoms();
304: for (EditGeom geom : geoms) {
305: if (geom.isChanged())
306: return true;
307: }
308: return false;
309: }
310:
311: /**
312: * Creates A geometry filter for the given layer.
313: *
314: * @param boundingBox in the same crs as the viewport model.
315: * @return a Geometry filter in the correct CRS or null if an exception occurs.
316: */
317: public Filter createBBoxFilter(Envelope boundingBox, ILayer layer,
318: short filterType) {
319: FilterFactory factory = FilterFactoryFinder
320: .createFilterFactory();
321: GeometryFilter bboxFilter = null;
322: if (!layer.hasResource(FeatureSource.class))
323: return Filter.ALL;
324: try {
325:
326: Envelope bbox;
327: try {
328: MathTransform transform = layer.mapToLayerTransform();
329: bbox = JTS.transform(boundingBox, transform);
330: } catch (Exception e) {
331: bbox = boundingBox;
332: }
333: BBoxExpression bb = factory.createBBoxExpression(bbox);
334: bboxFilter = factory.createGeometryFilter(filterType);
335: bboxFilter.addRightGeometry(bb);
336:
337: String geom = layer.getSchema().getDefaultGeometry()
338: .getName();
339:
340: bboxFilter.addLeftGeometry(factory
341: .createAttributeExpression(geom));
342: } catch (Exception e) {
343: ProjectPlugin.getPlugin().log(e);
344: }
345: return bboxFilter;
346: }
347:
348: private void setAndRun(IProgressMonitor monitor,
349: UndoableMapCommand undoableComposite) throws Exception {
350: this .command = undoableComposite;
351: command.setMap(getMap());
352: command.run(monitor);
353: }
354:
355: public String getName() {
356: return Messages.SelectGeometryCommand_name;
357: }
358:
359: public void rollback(IProgressMonitor monitor) throws Exception {
360: command.rollback(monitor);
361: }
362:
363: private static class BlockingSelectionAnim extends
364: AbstractDrawCommand implements IAnimation {
365: private int x;
366: private int y;
367:
368: BlockingSelectionAnim(int x, int y) {
369: this .x = x;
370: this .y = y;
371: }
372:
373: int frame = 2;
374:
375: public short getFrameInterval() {
376: return 250;
377: }
378:
379: public void nextFrame() {
380: frame -= 2;
381: if (frame < 0)
382: frame = 6;
383: }
384:
385: public boolean hasNext() {
386: return isValid();
387: }
388:
389: public void run(IProgressMonitor monitor) throws Exception {
390: graphics.setColor(Color.RED);
391: int rad = 3 + frame;
392: graphics.draw(new Ellipse2D.Float(x - rad, y - rad,
393: rad * 2, rad * 2));
394: }
395:
396: public Rectangle getValidArea() {
397: int rad = 3 + frame;
398: return new Rectangle(x - rad, y - rad, rad * 2, rad * 2);
399: }
400:
401: }
402:
403: public void setCreateOnNoSelect(ShapeType toCreate) {
404: this.toCreate = toCreate;
405: }
406:
407: }
|