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.Shape;
037: import java.awt.geom.GeneralPath;
038: import java.awt.geom.NoninvertibleTransformException;
039: import java.awt.geom.Point2D;
040: import java.util.ArrayList;
041:
042: import javax.swing.Icon;
043: import javax.swing.ImageIcon;
044:
045: import org.openjump.core.geomutils.Circle;
046:
047: import com.vividsolutions.jts.geom.Coordinate;
048: import com.vividsolutions.jts.geom.Polygon;
049: import com.vividsolutions.jts.operation.valid.IsValidOp;
050: import com.vividsolutions.jump.I18N;
051: import com.vividsolutions.jump.workbench.ui.EditTransaction;
052: import com.vividsolutions.jump.workbench.ui.LayerNamePanelProxy;
053: import com.vividsolutions.jump.workbench.ui.cursortool.CursorTool;
054: import com.vividsolutions.jump.workbench.ui.cursortool.editing.FeatureDrawingUtil;
055:
056: public class DrawConstrainedCircleTool extends
057: ConstrainedMultiClickTool {
058: private FeatureDrawingUtil featureDrawingUtil;
059: final static String drawConstrainedCircle = I18N
060: .get("org.openjump.core.ui.plugin.edittoolbox.cursortools.DrawConstrainedCircleTool.Draw-Constrained-Circle");
061: final static String theCircleMustHaveAtLeast2Points = I18N
062: .get("org.openjump.core.ui.plugin.edittoolbox.cursortools.DrawConstrainedCircleTool.The-circle-must-have-at-least-2-points");
063:
064: private DrawConstrainedCircleTool(
065: FeatureDrawingUtil featureDrawingUtil) {
066: drawClosed = true;
067: this .featureDrawingUtil = featureDrawingUtil;
068: }
069:
070: public static CursorTool create(
071: LayerNamePanelProxy layerNamePanelProxy) {
072: FeatureDrawingUtil featureDrawingUtil = new FeatureDrawingUtil(
073: layerNamePanelProxy);
074:
075: return featureDrawingUtil
076: .prepare(new DrawConstrainedCircleTool(
077: featureDrawingUtil), true);
078: }
079:
080: public String getName() {
081: return drawConstrainedCircle;
082: }
083:
084: public Icon getIcon() {
085: return new ImageIcon(getClass().getResource(
086: "DrawCircleConstrained.gif"));
087: }
088:
089: protected void gestureFinished() throws Exception {
090: reportNothingToUndoYet();
091:
092: if (!checkCircle()) {
093: return;
094: }
095:
096: execute(featureDrawingUtil.createAddCommand(getCircle(),
097: isRollingBackInvalidEdits(), getPanel(), this ));
098: }
099:
100: protected Polygon getCircle()
101: throws NoninvertibleTransformException {
102: // Subject 1.04: How do I generate a circle through three points?
103: //
104: // Let the three given points be a, b, c. Use _0 and _1 to represent
105: // x and y coordinates. The coordinates of the center p=(p_0,p_1) of
106: // the circle determined by a, b, and c are:
107: //
108: // A = b_0 - a_0;
109: // B = b_1 - a_1;
110: // C = c_0 - a_0;
111: // D = c_1 - a_1;
112: //
113: // E = A*(a_0 + b_0) + B*(a_1 + b_1);
114: // F = C*(a_0 + c_0) + D*(a_1 + c_1);
115: //
116: // G = 2.0*(A*(c_1 - b_1)-B*(c_0 - b_0));
117: //
118: // p_0 = (D*E - B*F) / G;
119: // p_1 = (A*F - C*E) / G;
120: //
121: // If G is zero then the three points are collinear and no finite-radius
122: // circle through them exists. Otherwise, the radius of the circle is:
123: //
124: // r^2 = (a_0 - p_0)^2 + (a_1 - p_1)^2
125: //
126: // Reference:
127: // [O' Rourke (C)] p. 201. Simplified by Jim Ward.
128:
129: double angle = 360.0;
130: ArrayList points = new ArrayList(getCoordinates());
131:
132: if (getCoordinates().size() == 2) {
133: Circle circle = new Circle((Coordinate) points.get(0),
134: ((Coordinate) points.get(0))
135: .distance((Coordinate) points.get(1)));
136: return circle.getPoly();
137: } else {
138: Coordinate a = (Coordinate) points.get(0);
139: Coordinate b = (Coordinate) points.get(1);
140: Coordinate c = (Coordinate) points.get(2);
141: return getCircle3points(a, b, c);
142: }
143: }
144:
145: private Polygon getCircle3points(Coordinate a, Coordinate b,
146: Coordinate c) {
147:
148: double A = b.x - a.x;
149: double B = b.y - a.y;
150: double C = c.x - a.x;
151: double D = c.y - a.y;
152: double E = A * (a.x + b.x) + B * (a.y + b.y);
153: double F = C * (a.x + c.x) + D * (a.y + c.y);
154: double G = 2.0 * (A * (c.y - b.y) - B * (c.x - b.x));
155: if (G != 0.0) {
156: double px = (D * E - B * F) / G;
157: double py = (A * F - C * E) / G;
158: Coordinate center = new Coordinate(px, py);
159: double radius = Math.sqrt((a.x - px) * (a.x - px)
160: + (a.y - py) * (a.y - py));
161: Circle circle = new Circle(center, radius);
162: return circle.getPoly();
163: } else //points are colinear; use second for center; third for the radius
164: {
165: Circle circle = new Circle(b, (b).distance(c));
166: return circle.getPoly();
167: }
168: }
169:
170: protected boolean checkCircle()
171: throws NoninvertibleTransformException {
172: if (getCoordinates().size() < 2) {
173: getPanel().getContext().warnUser(
174: theCircleMustHaveAtLeast2Points);
175:
176: return false;
177: }
178:
179: IsValidOp isValidOp = new IsValidOp(getCircle());
180:
181: if (!isValidOp.isValid()) {
182: getPanel().getContext().warnUser(
183: isValidOp.getValidationError().getMessage());
184:
185: if (getWorkbench().getBlackboard().get(
186: EditTransaction.ROLLING_BACK_INVALID_EDITS_KEY,
187: false)) {
188: return false;
189: }
190: }
191:
192: return true;
193: }
194:
195: /*
196: * (non-Javadoc)
197: * @see com.vividsolutions.jump.workbench.ui.cursortool.AbstractCursorTool#getShape()
198: */
199: protected Shape getShape() throws NoninvertibleTransformException {
200: if (coordinates.size() > 1) {
201:
202: GeneralPath shape = new GeneralPath();
203:
204: Coordinate a = (Coordinate) coordinates.get(0);
205: Coordinate b = (Coordinate) coordinates.get(1);
206: Coordinate c = tentativeCoordinate;
207:
208: Polygon polygon = getCircle3points(a, b, c);
209: Coordinate[] polygonCoordinates = polygon.getCoordinates();
210:
211: Coordinate firstCoordinate = (Coordinate) polygonCoordinates[0];
212: Point2D firstPoint = getPanel().getViewport().toViewPoint(
213: firstCoordinate);
214: shape.moveTo((float) firstPoint.getX(), (float) firstPoint
215: .getY());
216:
217: for (int i = 1, n = polygonCoordinates.length; i < n; i++) {
218: Point2D nextPoint = getPanel().getViewport()
219: .toViewPoint(polygonCoordinates[i]);
220: shape.lineTo((int) nextPoint.getX(), (int) nextPoint
221: .getY());
222: }
223: return shape;
224: }
225: return super.getShape();
226: }
227:
228: }
|