/* * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI * for visualizing and manipulating spatial features with geometry and attributes. * * JUMP is Copyright (C) 2003 Vivid Solutions * * This program implements extensions to JUMP and is * Copyright (C) 2004 Integrated Systems Analysts, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For more information, contact: * * Integrated Systems Analysts, Inc. * 630C Anchors St., Suite 101 * Fort Walton Beach, Florida * USA * * (850)862-7321 */ package org.openjump.core.ui.plugin.edittoolbox.cursortools; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Cursor; import java.awt.Shape; import java.awt.event.MouseEvent; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JComponent; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.CoordinateFilter; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.LinearRing; import com.vividsolutions.jts.geom.MultiPoint; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jump.I18N; import com.vividsolutions.jump.workbench.model.Layer; import com.vividsolutions.jump.workbench.plugin.EnableCheck; import com.vividsolutions.jump.workbench.plugin.EnableCheckFactory; import com.vividsolutions.jump.workbench.ui.EditTransaction; import com.vividsolutions.jump.workbench.ui.images.IconLoader; public class ConstrainedMoveVertexTool extends ConstrainedDragTool { final static String constrainedMoveVertex =I18N.get("org.openjump.core.ui.plugin.edittoolbox.ConstrainedMoveVertexTool.Constrained-Move-Vertex"); final static String noEditableSelectionHandlesHere =I18N.get("org.openjump.core.ui.plugin.edittoolbox.ConstrainedMoveVertexTool.No-editable-selection-handles-here"); public final static int TOLERANCE = 5; private EnableCheckFactory checkFactory; //private int vertexIndex; //used in getShape private Coordinate prevPoint = null; //used in getShape private Coordinate nextPoint = null; //used in getShape public ConstrainedMoveVertexTool(EnableCheckFactory checkFactory) { this.checkFactory = checkFactory; setColor(new Color(194, 179, 205)); setStroke(new BasicStroke(5)); allowSnapping(); } public Cursor getCursor() { return createCursor(IconLoader.icon("MoveVertexCursor3.gif").getImage()); } public Icon getIcon() { return new ImageIcon(getClass().getResource("MoveVertexConstrained.gif")); } public String getName(){ return constrainedMoveVertex; } protected void gestureFinished() throws Exception { reportNothingToUndoYet(); //#execute(UndoableCommand) will be called. [Jon Aquino] moveVertices(getModelSource(), getModelDestination()); } public void mousePressed(final MouseEvent e) { try { if (!check(checkFactory.createAtLeastNLayersMustBeEditableCheck(1))) { return; } if (e.isControlDown() || e.isShiftDown()) { if (!check(checkFactory.createExactlyNItemsMustBeSelectedCheck(1))) { return; } } else { if (!check(checkFactory.createAtLeastNItemsMustBeSelectedCheck(1))) { return; } } if (!check(new EnableCheck() { public String check(JComponent component) { try { return !nearSelectionHandle(e.getPoint()) ? noEditableSelectionHandlesHere : null; } catch (Exception e) { return e.toString(); } } })) { return; } super.mousePressed(e); } catch (Throwable t) { getPanel().getContext().handleThrowable(t); } } private boolean nearSelectionHandle(Point2D p) throws NoninvertibleTransformException { final Envelope buffer = vertexBuffer(getPanel().getViewport().toModelCoordinate(p)); final boolean[] result = new boolean[] { false }; for (Iterator i = getPanel().getSelectionManager().getLayersWithSelectedItems().iterator(); i.hasNext(); ) { Layer layer = (Layer) i.next(); if (!layer.isEditable()) { continue; } for (Iterator j = getPanel().getSelectionManager().getSelectedItems(layer).iterator(); j.hasNext(); ) { Geometry item = (Geometry) j.next(); item.apply(new CoordinateFilter() { public void filter(Coordinate coord) { if (buffer.contains(coord)) { result[0] = true; } } }); if (result[0]) { getCoordinates(item, buffer); } } } return result[0]; } private int getVertexIndex(LineString poly, Envelope buffer) { int numPts = poly.getNumPoints(); for (int i = 0; i < numPts; i++) if (buffer.contains(poly.getCoordinateN(i))) {return i;} return 0; } private void loadLine(LineString line, Envelope buffer) { if (line != null) { int numPts = line.getNumPoints(); int vertexIndex = getVertexIndex(line, buffer); for (int i = 0; i < vertexIndex; i++) {coordinates.add(line.getCoordinateN(i));} if (vertexIndex == 0) prevPoint = null; else prevPoint = line.getCoordinateN(vertexIndex - 1); if (vertexIndex == line.getNumPoints() - 1) nextPoint = null; else nextPoint = line.getCoordinateN(vertexIndex + 1); } } private void loadPoly(LineString poly, Envelope buffer) { if (poly != null) { int numPts = poly.getNumPoints(); int vertexIndex = getVertexIndex(poly, buffer); int startPt = vertexIndex + 2 - numPts; int endPt = vertexIndex - 1; if (vertexIndex == 0) {endPt = numPts - 2;} for (int i = startPt; i <= endPt; i++) { int index = i; if (index < 0) {index += (numPts - 1);} coordinates.add(poly.getCoordinateN(index)); } prevPoint = (Coordinate)coordinates.get(0); nextPoint = (Coordinate)coordinates.get(coordinates.size() - 1); } } private void getCoordinates(Geometry geometry, Envelope buffer) { coordinates.clear(); if (geometry instanceof LineString) //open poly { //java.awt.Toolkit.getDefaultToolkit().beep(); loadLine((LineString)geometry, buffer); return; } if (geometry instanceof LinearRing) //closed poly (no holes) { loadPoly((LinearRing)geometry, buffer); return; } if (geometry instanceof Polygon) //poly with 0 or more holes { loadPoly(((Polygon)geometry).getExteriorRing(), buffer); return; } if (geometry instanceof MultiPoint) { coordinates.add(((Point) geometry).getCoordinate()); prevPoint = null; nextPoint = null; return; } // else if (geometry instanceof MultiLineString) // { // writeMultiPoly((MultiLineString) geometry, idStr, writer); // } // else if (geometry instanceof MultiPolygon) // { // writeMultiPolygon((MultiPolygon) geometry, idStr, writer); // } // else if (geometry instanceof GeometryCollection) // { // writeGroup((GeometryCollection) geometry, idStr, writer); // } // else // { // Assert.shouldNeverReachHere("Unsupported Geometry implementation:" + geometry.getClass()); // } return; } private Envelope vertexBuffer(Coordinate c) throws NoninvertibleTransformException { double tolerance = TOLERANCE / getPanel().getViewport().getScale(); return vertexBuffer(c, tolerance); } public void moveVertices(Coordinate initialLocation, Coordinate finalLocation) throws Exception { final Envelope oldVertexBuffer = vertexBuffer(initialLocation); final Coordinate newVertex = finalLocation; ArrayList transactions = new ArrayList(); for (Iterator i = getPanel().getSelectionManager().getLayersWithSelectedItems().iterator(); i.hasNext(); ) { Layer layerWithSelectedItems = (Layer) i.next(); if (!layerWithSelectedItems.isEditable()) { continue; } transactions.add(createTransaction(layerWithSelectedItems, oldVertexBuffer, newVertex)); } EditTransaction.commit(transactions); } private EditTransaction createTransaction( Layer layer, final Envelope oldVertexBuffer, final Coordinate newVertex) { return EditTransaction.createTransactionOnSelection(new EditTransaction.SelectionEditor() { public Geometry edit(Geometry geometryWithSelectedItems, Collection selectedItems) { for (Iterator j = selectedItems.iterator(); j.hasNext();) { Geometry item = (Geometry) j.next(); edit(item); } return geometryWithSelectedItems; } private void edit(Geometry selectedItem) { selectedItem.apply(new CoordinateFilter() { public void filter(Coordinate coordinate) { if (oldVertexBuffer.contains(coordinate)) { coordinate.x = newVertex.x; coordinate.y = newVertex.y; } } }); } }, getPanel(), getPanel().getContext(), getName(), layer, isRollingBackInvalidEdits(), false); } protected Shape getShape(Point2D source, Point2D destination) throws Exception { if ((prevPoint == null) && (nextPoint == null)) { double radius = 20; return new Ellipse2D.Double( destination.getX() - (radius / 2), destination.getY() - (radius / 2), radius, radius); } else { GeneralPath path = new GeneralPath(); if (prevPoint == null) { path.moveTo((int) destination.getX(), (int) destination.getY()); } else { Point2D firstPoint = getPanel().getViewport().toViewPoint(prevPoint); path.moveTo((float) firstPoint.getX(), (float) firstPoint.getY()); path.lineTo((int) destination.getX(), (int) destination.getY()); } if (nextPoint != null) { Point2D lastPoint = getPanel().getViewport().toViewPoint(nextPoint); path.lineTo((int) lastPoint.getX(), (int) lastPoint.getY()); } return path; } } private Envelope vertexBuffer(Coordinate vertex, double tolerance) { return new Envelope( vertex.x - tolerance, vertex.x + tolerance, vertex.y - tolerance, vertex.y + tolerance); } }