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.behaviour;
016:
017: import java.awt.geom.GeneralPath;
018: import java.util.ArrayList;
019: import java.util.List;
020:
021: import net.refractions.udig.core.IBlockingProvider;
022: import net.refractions.udig.project.ILayer;
023: import net.refractions.udig.project.command.MapCommand;
024: import net.refractions.udig.project.command.UndoRedoCommand;
025: import net.refractions.udig.project.command.UndoableComposite;
026: import net.refractions.udig.project.command.UndoableMapCommand;
027: import net.refractions.udig.project.ui.internal.commands.draw.DrawPathCommand;
028: import net.refractions.udig.project.ui.render.displayAdapter.MapMouseEvent;
029: import net.refractions.udig.tools.edit.EditPlugin;
030: import net.refractions.udig.tools.edit.EditState;
031: import net.refractions.udig.tools.edit.EditToolHandler;
032: import net.refractions.udig.tools.edit.EventType;
033: import net.refractions.udig.tools.edit.LockingBehaviour;
034: import net.refractions.udig.tools.edit.commands.AddVertexCommand;
035: import net.refractions.udig.tools.edit.commands.CreateAndSelectHoleCommand;
036: import net.refractions.udig.tools.edit.commands.CreateEditGeomCommand;
037: import net.refractions.udig.tools.edit.commands.SetCurrentGeomCommand;
038: import net.refractions.udig.tools.edit.commands.SetEditStateCommand;
039: import net.refractions.udig.tools.edit.preferences.PreferenceConstants;
040: import net.refractions.udig.tools.edit.preferences.PreferenceUtil;
041: import net.refractions.udig.tools.edit.support.EditBlackboard;
042: import net.refractions.udig.tools.edit.support.EditUtils;
043: import net.refractions.udig.tools.edit.support.PathAdapter;
044: import net.refractions.udig.tools.edit.support.Point;
045: import net.refractions.udig.tools.edit.support.PrimitiveShape;
046: import net.refractions.udig.tools.edit.support.ShapeType;
047:
048: import org.eclipse.core.runtime.IProgressMonitor;
049: import org.eclipse.core.runtime.NullProgressMonitor;
050: import org.eclipse.core.runtime.Platform;
051: import org.eclipse.swt.graphics.Device;
052: import org.eclipse.swt.graphics.Path;
053: import org.eclipse.swt.widgets.Display;
054: import org.eclipse.ui.PlatformUI;
055: import org.geotools.feature.FeatureType;
056:
057: import com.vividsolutions.jts.geom.LineString;
058: import com.vividsolutions.jts.geom.LinearRing;
059: import com.vividsolutions.jts.geom.MultiLineString;
060: import com.vividsolutions.jts.geom.MultiPolygon;
061: import com.vividsolutions.jts.geom.Polygon;
062:
063: /**
064: * <p>
065: * Requirements:
066: * <ul>
067: * <li>EventType==DRAGGED</li>
068: * <li>button1 is down</li>
069: * </ul>
070: * </p>
071: * <p>
072: * Action:
073: * <ul>
074: * <li>if currentshape is null then a new polygon is created by free hand drawing.</li>
075: * <li>if currentshape is not null then a hole is created by free hand drawing.</li>
076: * <li>handler is locked until mouse is released</li>
077: * </ul>
078: * </p>
079: *
080: * @author jones
081: * @since 1.1.0
082: */
083: public class FreeHandPolygonDrawBehaviour implements LockingBehaviour {
084:
085: private Creator creator;
086: private DrawPathCommand drawShapeCommand;
087: private ShapeType type = ShapeType.POLYGON;
088: private PathAdapter generalPath;
089: private DrawPathCommand drawEndPoints;
090:
091: public boolean isValid(EditToolHandler handler, MapMouseEvent e,
092: EventType eventType) {
093: boolean draggedEvent = eventType == EventType.DRAGGED;
094: boolean legalButtons = e.buttons == MapMouseEvent.BUTTON1;
095: return draggedEvent && legalButtons;
096:
097: }
098:
099: public UndoableMapCommand getCommand(EditToolHandler handler,
100: MapMouseEvent e, EventType eventType) {
101: UndoableMapCommand command = null;
102: if (creator == null || generalPath == null
103: || drawEndPoints == null || drawShapeCommand == null)
104: command = init(handler, e, eventType);
105: generalPath.lineTo(e.x, e.y);
106:
107: updateShapes(handler, e);
108:
109: SetEditStateCommand setEditStateCommand = new SetEditStateCommand(
110: handler, EditState.CREATING);
111:
112: return executeCommand(handler, command, setEditStateCommand);
113: }
114:
115: private UndoableMapCommand executeCommand(EditToolHandler handler,
116: UndoableMapCommand command2,
117: SetEditStateCommand setEditStateCommand) {
118: UndoableMapCommand command = command2;
119: if (command != null) {
120: List<UndoableMapCommand> commands = new ArrayList<UndoableMapCommand>();
121: commands.add(command);
122: commands.add(setEditStateCommand);
123: UndoableComposite undoableComposite = new UndoableComposite(
124: commands);
125: command = undoableComposite;
126: } else {
127: command = setEditStateCommand;
128: }
129: command.setMap(handler.getContext().getMap());
130: try {
131: command.run(new NullProgressMonitor());
132: } catch (Exception e1) {
133: throw (RuntimeException) new RuntimeException(e1);
134: }
135: return new UndoRedoCommand(command);
136: }
137:
138: private void updateShapes(EditToolHandler handler, MapMouseEvent e) {
139: Point dragStarted = handler.getMouseTracker().getDragStarted();
140: if (isOverEndPoint(e, PreferenceUtil.instance()
141: .getVertexRadius(), dragStarted)) {
142: drawEndPoints.setFill(PreferenceUtil.instance()
143: .getDrawVertexFillColor());
144: } else {
145: drawEndPoints.setFill(null);
146: }
147:
148: if (type == ShapeType.POLYGON)
149: drawShapeCommand.line(e.x, e.y, dragStarted.getX(),
150: dragStarted.getY());
151:
152: handler.repaint();
153: }
154:
155: private UndoableMapCommand init(final EditToolHandler handler,
156: MapMouseEvent e, EventType eventType) {
157: if (!handler.isLockOwner(this ))
158: handler.lock(this );
159: Point dragStarted = handler.getMouseTracker().getDragStarted();
160: if (creator != null)
161: handler.getBehaviours().remove(creator);
162: creator = new Creator(dragStarted);
163: initDrawCommands(handler, dragStarted);
164: handler.getBehaviours().add(creator);
165:
166: ILayer selectedLayer = handler.getEditLayer();
167: EditBlackboard editBlackboard = handler
168: .getEditBlackboard(selectedLayer);
169: int vertexRadius = PreferenceUtil.instance().getVertexRadius();
170: Point nearestPoint = editBlackboard.overVertex(dragStarted,
171: vertexRadius);
172:
173: if (nearestPoint == null)
174: nearestPoint = dragStarted;
175:
176: initShapePath(nearestPoint);
177:
178: FeatureType schema = selectedLayer.getSchema();
179: determineShapeType(schema);
180:
181: if (type == ShapeType.POLYGON
182: && EditPlugin
183: .getDefault()
184: .getPreferenceStore()
185: .getBoolean(PreferenceConstants.P_FILL_POLYGONS)) {
186: drawShapeCommand.setFill(PreferenceUtil.instance()
187: .getDrawGeomsFill());
188: }
189:
190: EditBlackboard blackboard = editBlackboard;
191: PrimitiveShape currentShape = handler.getCurrentShape();
192: if (currentShape == null) {
193: UndoableComposite undoableComposite = startNewShape(
194: handler, blackboard);
195: return undoableComposite;
196: }
197:
198: currentShape = currentShape.getEditGeom().getShell();
199:
200: //if current shape is a line and point matches up with one of the end points then
201: // continue line.
202: if (currentShape.getEditGeom().getShapeType() == ShapeType.LINE) {
203: if (currentShape.getNumPoints() > 0
204: && nearestPoint.equals(currentShape.getPoint(0))) {
205: // over the first end point so reverse order and return;
206: EditUtils.instance.reverseOrder(currentShape);
207: return null;
208: }
209:
210: if (currentShape.getNumPoints() > 1
211: && !nearestPoint.equals(currentShape
212: .getPoint(currentShape.getNumPoints() - 1))) {
213: // not over one of the end points
214: return createNewGeom(handler);
215: }
216: return null;
217: }
218:
219: if (currentShape.getEditGeom().getShapeType() == ShapeType.POINT) {
220: // it's a point create a new point.
221: return createNewGeom(handler);
222: } else {
223: if (currentShape.contains(nearestPoint, true)) {
224: if (handler.getCurrentGeom().getShell() == currentShape) {
225: for (PrimitiveShape hole : handler.getCurrentGeom()
226: .getHoles()) {
227: if (hole.contains(nearestPoint, true)) {
228: return createNewGeom(handler);
229: }
230: }
231: type = ShapeType.POLYGON;
232: handler.getContext().sendSyncCommand(
233: new CreateAndSelectHoleCommand(handler));
234: } else {
235: return createNewGeom(handler);
236: }
237: } else {
238: return createNewGeom(handler);
239: }
240: }
241: return null;
242: }
243:
244: private UndoableComposite startNewShape(
245: final EditToolHandler handler, EditBlackboard blackboard) {
246: List<UndoableMapCommand> commands = new ArrayList<UndoableMapCommand>();
247: final CreateEditGeomCommand createEditGeomCommand = new CreateEditGeomCommand(
248: blackboard, "freeHandDraw", ShapeType.LINE); //$NON-NLS-1$
249: commands.add(createEditGeomCommand);
250: class PrimitiveProvider implements
251: IBlockingProvider<PrimitiveShape> {
252:
253: public PrimitiveShape get(IProgressMonitor monitor) {
254: return createEditGeomCommand.get(monitor).getShell();
255: }
256:
257: }
258:
259: commands.add(new SetCurrentGeomCommand(handler,
260: new PrimitiveProvider()));
261:
262: if (handler.getContext().getEditManager().getEditFeature() != null) {
263: commands.add(handler.getContext().getEditFactory()
264: .createNullEditFeatureCommand());
265: }
266: UndoableComposite undoableComposite = new UndoableComposite(
267: commands);
268: return undoableComposite;
269: }
270:
271: private void determineShapeType(FeatureType schema) {
272: if (Polygon.class.isAssignableFrom(schema.getDefaultGeometry()
273: .getType())
274: || MultiPolygon.class.isAssignableFrom(schema
275: .getDefaultGeometry().getType()))
276: type = ShapeType.POLYGON;
277: else if (LineString.class.isAssignableFrom(schema
278: .getDefaultGeometry().getType())
279: || LinearRing.class.isAssignableFrom(schema
280: .getDefaultGeometry().getType())
281: || MultiLineString.class.isAssignableFrom(schema
282: .getDefaultGeometry().getType()))
283: type = ShapeType.LINE;
284: else {
285: type = ShapeType.UNKNOWN;
286: }
287: }
288:
289: private void initShapePath(Point nearestPoint) {
290: generalPath = new PathAdapter();
291: if (Platform.getOS().equals(Platform.OS_LINUX)) {
292: generalPath.setPath(new GeneralPath());
293: } else {
294: generalPath.setPath(new Path(getDisplay()));
295: }
296: generalPath.moveTo(nearestPoint.getX(), nearestPoint.getY());
297: if (generalPath.isPath())
298: drawShapeCommand.setPath(generalPath.getPath());
299: else
300: drawShapeCommand.setPath(Display.getCurrent(), generalPath
301: .getPathIterator());
302: }
303:
304: private void initDrawCommands(final EditToolHandler handler,
305: Point dragStarted) {
306: if (drawShapeCommand == null) {
307: drawShapeCommand = new DrawPathCommand();
308: drawShapeCommand.setPaint(PreferenceUtil.instance()
309: .getDrawGeomsLine());
310:
311: handler.getContext().sendASyncCommand(drawShapeCommand);
312: }
313: if (drawEndPoints == null) {
314: drawEndPoints = new DrawPathCommand();
315: int vertexRadius = PreferenceUtil.instance()
316: .getVertexRadius();
317: Path path = new Path(getDisplay());
318: int i = vertexRadius + vertexRadius;
319: path.addRectangle(dragStarted.getX() - vertexRadius,
320: dragStarted.getY() - vertexRadius, i, i);
321: if (EditPlugin.getDefault().getPreferenceStore()
322: .getBoolean(PreferenceConstants.P_FILL_VERTICES)) {
323: drawEndPoints.setFill(PreferenceUtil.instance()
324: .getDrawVertexFillColor());
325: }
326: drawEndPoints.setPath(path);
327: handler.getContext().sendASyncCommand(drawEndPoints);
328:
329: }
330: }
331:
332: private Device getDisplay() {
333: Display display = PlatformUI.getWorkbench().getDisplay();
334: if (display != null)
335: return display;
336: return Display.getDefault();
337: }
338:
339: /**
340: * Runs the accept behaviours, creates a new EditGeom and sets it to be the Current Geom
341: *
342: * @param handler
343: */
344: private UndoableMapCommand createNewGeom(EditToolHandler handler) {
345: List<UndoableMapCommand> commands = new ArrayList<UndoableMapCommand>();
346:
347: EditBlackboard editBlackboard = handler.getCurrentShape()
348: .getEditBlackboard();
349: final CreateEditGeomCommand createEditGeomCommand = new CreateEditGeomCommand(
350: editBlackboard,
351: "freeHandDraw" + System.currentTimeMillis(), ShapeType.LINE); //$NON-NLS-1$
352: commands.add(createEditGeomCommand);
353: class PrimitiveProvider implements
354: IBlockingProvider<PrimitiveShape> {
355: public PrimitiveShape get(IProgressMonitor monitor) {
356: return createEditGeomCommand.get(monitor).getShell();
357: }
358:
359: }
360:
361: commands.add(new SetCurrentGeomCommand(handler,
362: new PrimitiveProvider()));
363:
364: return new UndoableComposite(commands);
365: }
366:
367: public void handleError(EditToolHandler handler, Throwable error,
368: UndoableMapCommand command) {
369: EditPlugin.log("", error); //$NON-NLS-1$
370: }
371:
372: public Object getKey(EditToolHandler handler) {
373: return this ;
374: }
375:
376: boolean isOverEndPoint(MapMouseEvent e, int vertexRadius,
377: Point start) {
378: EditUtils.MinFinder finder = new EditUtils.MinFinder(start);
379: double dist = finder.dist(Point.valueOf(e.x, e.y));
380: boolean b = dist < vertexRadius;
381: return b;
382: }
383:
384: private class Creator implements LockingBehaviour {
385:
386: private Point start;
387:
388: /**
389: * @param dragStarted
390: */
391: public Creator(Point dragStarted) {
392: start = dragStarted;
393: }
394:
395: public Object getKey(EditToolHandler handler) {
396: return FreeHandPolygonDrawBehaviour.this ;
397: }
398:
399: public boolean isValid(EditToolHandler handler,
400: MapMouseEvent e, EventType eventType) {
401:
402: return handler.isLockOwner(this )
403: && eventType == EventType.RELEASED;
404: }
405:
406: public UndoableMapCommand getCommand(EditToolHandler handler,
407: MapMouseEvent e, EventType eventType) {
408:
409: try {
410:
411: PrimitiveShape shape = handler.getCurrentShape();
412: int vertexRadius = PreferenceUtil.instance()
413: .getVertexRadius();
414: if (type == ShapeType.UNKNOWN) {
415: if (isOverEndPoint(e, vertexRadius, start))
416: shape.getEditGeom().setShapeType(
417: ShapeType.POLYGON);
418: else {
419: shape.getEditGeom()
420: .setShapeType(ShapeType.LINE);
421: }
422: } else {
423: shape.getEditGeom().setShapeType(type);
424: }
425:
426: if (shape.getEditGeom().getShapeType() == ShapeType.POLYGON)
427: generalPath.close();
428:
429: UndoableComposite appendPathToShape;
430: appendPathToShape = EditUtils.instance
431: .appendPathToShape(handler, generalPath
432: .getPathIterator(), shape);
433: // Now we need to collapse the last two vertices if they are
434: // within snapping distance.
435: if (shape.getEditGeom().getShapeType() == ShapeType.POLYGON) {
436:
437: List<? extends MapCommand> commands = appendPathToShape
438: .getCommands();
439: AddVertexCommand lastVertexCommand = (AddVertexCommand) commands
440: .get(commands.size() - 1);
441: AddVertexCommand previousVertexCommand = (AddVertexCommand) commands
442: .get(commands.size() - 2);
443: EditUtils.MinFinder finder = new EditUtils.MinFinder(
444: lastVertexCommand.getPointToAdd());
445: double dist = finder.dist(previousVertexCommand
446: .getPointToAdd());
447: if (dist < vertexRadius) {
448: commands.remove(previousVertexCommand);
449: }
450: }
451: if (handler.getCurrentShape() != null)
452: appendPathToShape.getFinalizerCommands().add(
453: new SetEditStateCommand(handler,
454: EditState.MODIFYING));
455: else {
456: appendPathToShape.getFinalizerCommands().add(
457: new SetEditStateCommand(handler,
458: EditState.CREATING));
459: }
460:
461: appendPathToShape.setMap(handler.getContext().getMap());
462: appendPathToShape.run(new NullProgressMonitor());
463:
464: return new UndoRedoCommand(appendPathToShape);
465: } catch (Exception exception) {
466: throw (RuntimeException) new RuntimeException(exception);
467: } finally {
468: creator = null;
469: handler.getBehaviours().remove(this );
470: handler.unlock(this );
471: drawShapeCommand.dispose();
472: drawShapeCommand = null;
473: drawEndPoints.dispose();
474: drawEndPoints = null;
475: generalPath = null;
476: }
477: }
478:
479: public void handleError(EditToolHandler handler,
480: Throwable error, UndoableMapCommand command) {
481: EditPlugin.log("", error); //$NON-NLS-1$
482: }
483:
484: }
485:
486: }
|