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.edittoolbox.cursortools;
035:
036: import java.awt.BasicStroke;
037: import java.awt.Color;
038: import java.awt.Cursor;
039: import java.awt.Shape;
040: import java.awt.event.MouseEvent;
041: import java.awt.geom.Ellipse2D;
042: import java.awt.geom.GeneralPath;
043: import java.awt.geom.NoninvertibleTransformException;
044: import java.awt.geom.Point2D;
045: import java.util.ArrayList;
046: import java.util.Collection;
047: import java.util.Iterator;
048:
049: import javax.swing.Icon;
050: import javax.swing.ImageIcon;
051: import javax.swing.JComponent;
052:
053: import com.vividsolutions.jts.geom.Coordinate;
054: import com.vividsolutions.jts.geom.CoordinateFilter;
055: import com.vividsolutions.jts.geom.Envelope;
056: import com.vividsolutions.jts.geom.Geometry;
057: import com.vividsolutions.jts.geom.LineString;
058: import com.vividsolutions.jts.geom.LinearRing;
059: import com.vividsolutions.jts.geom.MultiPoint;
060: import com.vividsolutions.jts.geom.Point;
061: import com.vividsolutions.jts.geom.Polygon;
062: import com.vividsolutions.jump.I18N;
063: import com.vividsolutions.jump.workbench.model.Layer;
064: import com.vividsolutions.jump.workbench.plugin.EnableCheck;
065: import com.vividsolutions.jump.workbench.plugin.EnableCheckFactory;
066: import com.vividsolutions.jump.workbench.ui.EditTransaction;
067: import com.vividsolutions.jump.workbench.ui.images.IconLoader;
068:
069: public class ConstrainedMoveVertexTool extends ConstrainedDragTool {
070:
071: final static String constrainedMoveVertex = I18N
072: .get("org.openjump.core.ui.plugin.edittoolbox.ConstrainedMoveVertexTool.Constrained-Move-Vertex");
073: final static String noEditableSelectionHandlesHere = I18N
074: .get("org.openjump.core.ui.plugin.edittoolbox.ConstrainedMoveVertexTool.No-editable-selection-handles-here");
075:
076: public final static int TOLERANCE = 5;
077: private EnableCheckFactory checkFactory;
078: //private int vertexIndex; //used in getShape
079: private Coordinate prevPoint = null; //used in getShape
080: private Coordinate nextPoint = null; //used in getShape
081:
082: public ConstrainedMoveVertexTool(EnableCheckFactory checkFactory) {
083: this .checkFactory = checkFactory;
084: setColor(new Color(194, 179, 205));
085: setStroke(new BasicStroke(5));
086: allowSnapping();
087: }
088:
089: public Cursor getCursor() {
090:
091: return createCursor(IconLoader.icon("MoveVertexCursor3.gif")
092: .getImage());
093: }
094:
095: public Icon getIcon() {
096: return new ImageIcon(getClass().getResource(
097: "MoveVertexConstrained.gif"));
098: }
099:
100: public String getName() {
101: return constrainedMoveVertex;
102: }
103:
104: protected void gestureFinished() throws Exception {
105: reportNothingToUndoYet();
106:
107: //#execute(UndoableCommand) will be called. [Jon Aquino]
108: moveVertices(getModelSource(), getModelDestination());
109: }
110:
111: public void mousePressed(final MouseEvent e) {
112: try {
113: if (!check(checkFactory
114: .createAtLeastNLayersMustBeEditableCheck(1))) {
115: return;
116: }
117: if (!check(checkFactory
118: .createExactlyNItemsMustBeSelectedCheck(1))) {
119: return;
120: }
121: if (!check(new EnableCheck() {
122: public String check(JComponent component) {
123: try {
124: return !nearSelectionHandle(e.getPoint()) ? noEditableSelectionHandlesHere
125: : null;
126: } catch (Exception e) {
127: return e.toString();
128: }
129: }
130:
131: })) {
132: return;
133: }
134: super .mousePressed(e);
135: } catch (Throwable t) {
136: getPanel().getContext().handleThrowable(t);
137: }
138: }
139:
140: private boolean nearSelectionHandle(Point2D p)
141: throws NoninvertibleTransformException {
142: final Envelope buffer = vertexBuffer(getPanel().getViewport()
143: .toModelCoordinate(p));
144: final boolean[] result = new boolean[] { false };
145: for (Iterator i = getPanel().getSelectionManager()
146: .getLayersWithSelectedItems().iterator(); i.hasNext();) {
147: Layer layer = (Layer) i.next();
148: if (!layer.isEditable()) {
149: continue;
150: }
151: for (Iterator j = getPanel().getSelectionManager()
152: .getSelectedItems(layer).iterator(); j.hasNext();) {
153: Geometry item = (Geometry) j.next();
154: item.apply(new CoordinateFilter() {
155: public void filter(Coordinate coord) {
156: if (buffer.contains(coord)) {
157: result[0] = true;
158: }
159: }
160: });
161:
162: if (result[0]) {
163: getCoordinates(item, buffer);
164: }
165: }
166: }
167: return result[0];
168: }
169:
170: private int getVertexIndex(LineString poly, Envelope buffer) {
171: int numPts = poly.getNumPoints();
172: for (int i = 0; i < numPts; i++)
173: if (buffer.contains(poly.getCoordinateN(i))) {
174: return i;
175: }
176: return 0;
177: }
178:
179: private void loadLine(LineString line, Envelope buffer) {
180: if (line != null) {
181: int numPts = line.getNumPoints();
182: int vertexIndex = getVertexIndex(line, buffer);
183:
184: for (int i = 0; i < vertexIndex; i++) {
185: coordinates.add(line.getCoordinateN(i));
186: }
187:
188: if (vertexIndex == 0)
189: prevPoint = null;
190: else
191: prevPoint = line.getCoordinateN(vertexIndex - 1);
192:
193: if (vertexIndex == line.getNumPoints() - 1)
194: nextPoint = null;
195: else
196: nextPoint = line.getCoordinateN(vertexIndex + 1);
197: }
198: }
199:
200: private void loadPoly(LineString poly, Envelope buffer) {
201: if (poly != null) {
202: int numPts = poly.getNumPoints();
203: int vertexIndex = getVertexIndex(poly, buffer);
204: int startPt = vertexIndex + 2 - numPts;
205: int endPt = vertexIndex - 1;
206: if (vertexIndex == 0) {
207: endPt = numPts - 2;
208: }
209:
210: for (int i = startPt; i <= endPt; i++) {
211: int index = i;
212: if (index < 0) {
213: index += (numPts - 1);
214: }
215: coordinates.add(poly.getCoordinateN(index));
216: }
217:
218: prevPoint = (Coordinate) coordinates.get(0);
219: nextPoint = (Coordinate) coordinates
220: .get(coordinates.size() - 1);
221: }
222: }
223:
224: private void getCoordinates(Geometry geometry, Envelope buffer) {
225: coordinates.clear();
226:
227: if (geometry instanceof LineString) //open poly
228: {
229: //java.awt.Toolkit.getDefaultToolkit().beep();
230: loadLine((LineString) geometry, buffer);
231: return;
232: }
233:
234: if (geometry instanceof LinearRing) //closed poly (no holes)
235: {
236: loadPoly((LinearRing) geometry, buffer);
237: return;
238: }
239:
240: if (geometry instanceof Polygon) //poly with 0 or more holes
241: {
242: loadPoly(((Polygon) geometry).getExteriorRing(), buffer);
243: return;
244: }
245:
246: if (geometry instanceof MultiPoint) {
247: coordinates.add(((Point) geometry).getCoordinate());
248: prevPoint = null;
249: nextPoint = null;
250: return;
251: }
252:
253: // else if (geometry instanceof MultiLineString)
254: // {
255: // writeMultiPoly((MultiLineString) geometry, idStr, writer);
256: // }
257: // else if (geometry instanceof MultiPolygon)
258: // {
259: // writeMultiPolygon((MultiPolygon) geometry, idStr, writer);
260: // }
261: // else if (geometry instanceof GeometryCollection)
262: // {
263: // writeGroup((GeometryCollection) geometry, idStr, writer);
264: // }
265: // else
266: // {
267: // Assert.shouldNeverReachHere("Unsupported Geometry implementation:" + geometry.getClass());
268: // }
269: return;
270: }
271:
272: private Envelope vertexBuffer(Coordinate c)
273: throws NoninvertibleTransformException {
274: double tolerance = TOLERANCE
275: / getPanel().getViewport().getScale();
276: return vertexBuffer(c, tolerance);
277: }
278:
279: public void moveVertices(Coordinate initialLocation,
280: Coordinate finalLocation) throws Exception {
281: final Envelope oldVertexBuffer = vertexBuffer(initialLocation);
282: final Coordinate newVertex = finalLocation;
283: ArrayList transactions = new ArrayList();
284: for (Iterator i = getPanel().getSelectionManager()
285: .getLayersWithSelectedItems().iterator(); i.hasNext();) {
286: Layer layerWithSelectedItems = (Layer) i.next();
287: if (!layerWithSelectedItems.isEditable()) {
288: continue;
289: }
290: transactions.add(createTransaction(layerWithSelectedItems,
291: oldVertexBuffer, newVertex));
292: }
293: EditTransaction.commit(transactions);
294: }
295:
296: private EditTransaction createTransaction(Layer layer,
297: final Envelope oldVertexBuffer, final Coordinate newVertex) {
298: return EditTransaction.createTransactionOnSelection(
299: new EditTransaction.SelectionEditor() {
300: public Geometry edit(
301: Geometry geometryWithSelectedItems,
302: Collection selectedItems) {
303: for (Iterator j = selectedItems.iterator(); j
304: .hasNext();) {
305: Geometry item = (Geometry) j.next();
306: edit(item);
307: }
308: return geometryWithSelectedItems;
309: }
310:
311: private void edit(Geometry selectedItem) {
312: selectedItem.apply(new CoordinateFilter() {
313: public void filter(Coordinate coordinate) {
314: if (oldVertexBuffer
315: .contains(coordinate)) {
316: coordinate.x = newVertex.x;
317: coordinate.y = newVertex.y;
318: }
319: }
320: });
321: }
322: }, getPanel(), getPanel().getContext(), getName(),
323: layer, isRollingBackInvalidEdits(), false);
324: }
325:
326: protected Shape getShape(Point2D source, Point2D destination)
327: throws Exception {
328: if ((prevPoint == null) && (nextPoint == null)) {
329: double radius = 20;
330: return new Ellipse2D.Double(destination.getX()
331: - (radius / 2), destination.getY() - (radius / 2),
332: radius, radius);
333: } else {
334: GeneralPath path = new GeneralPath();
335:
336: if (prevPoint == null) {
337: path.moveTo((int) destination.getX(), (int) destination
338: .getY());
339: } else {
340: Point2D firstPoint = getPanel().getViewport()
341: .toViewPoint(prevPoint);
342: path.moveTo((float) firstPoint.getX(),
343: (float) firstPoint.getY());
344: path.lineTo((int) destination.getX(), (int) destination
345: .getY());
346: }
347:
348: if (nextPoint != null) {
349: Point2D lastPoint = getPanel().getViewport()
350: .toViewPoint(nextPoint);
351: path.lineTo((int) lastPoint.getX(), (int) lastPoint
352: .getY());
353: }
354:
355: return path;
356: }
357: }
358:
359: private Envelope vertexBuffer(Coordinate vertex, double tolerance) {
360: return new Envelope(vertex.x - tolerance, vertex.x + tolerance,
361: vertex.y - tolerance, vertex.y + tolerance);
362: }
363: }
|