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: * Stefan Steiniger
026: * perriger@gmx.de
027: *
028: */
029: /*****************************************************
030: * created: 11.12.2005
031: * last modified: 28.01.2006 cursor change on mouse over corner
032: *
033: * author: sstein
034: *
035: * description:
036: * - scales selected items (using the bounding box)
037: *
038: *****************************************************/package org.openjump.core.ui.plugin.edittoolbox.cursortools;
039:
040: import java.awt.BasicStroke;
041: import java.awt.Color;
042: import java.awt.Cursor;
043: import java.awt.Graphics2D;
044: import java.awt.Shape;
045: import java.awt.event.MouseEvent;
046: import java.awt.geom.NoninvertibleTransformException;
047: import java.text.DecimalFormat;
048: import java.util.ArrayList;
049: import java.util.Collection;
050: import java.util.Iterator;
051: import java.util.List;
052:
053: import javax.swing.Icon;
054: import javax.swing.ImageIcon;
055:
056: import org.openjump.core.geomutils.GeoUtils;
057:
058: import com.vividsolutions.jts.geom.Coordinate;
059: import com.vividsolutions.jts.geom.CoordinateFilter;
060: import com.vividsolutions.jts.geom.Geometry;
061: import com.vividsolutions.jts.geom.GeometryFactory;
062: import com.vividsolutions.jts.geom.LineString;
063: import com.vividsolutions.jts.geom.LinearRing;
064: import com.vividsolutions.jts.geom.MultiPoint;
065: import com.vividsolutions.jts.geom.Point;
066: import com.vividsolutions.jts.geom.Polygon;
067: import com.vividsolutions.jump.I18N;
068: import com.vividsolutions.jump.workbench.model.Layer;
069: import com.vividsolutions.jump.workbench.plugin.EnableCheckFactory;
070: import com.vividsolutions.jump.workbench.ui.EditTransaction;
071: import com.vividsolutions.jump.workbench.ui.LayerViewPanel;
072: import com.vividsolutions.jump.workbench.ui.cursortool.DragTool;
073: import com.vividsolutions.jump.workbench.ui.images.IconLoader;
074: import com.vividsolutions.jump.workbench.ui.snap.SnapManager;
075:
076: public class ScaleSelectedItemsTool extends DragTool {
077:
078: private String scaleSelectedItems = "Scale Selected Items";
079: private String sScaleFactor = "scale factor";
080:
081: private EnableCheckFactory checkFactory;
082: private Shape selectionBBoxShape;
083: private Geometry originalItemsAsLines = null;
084: private Geometry outlineItems = null;
085: //private Shape selectedItemsShape=null;
086: private Shape outlineItemsShape = null;
087: private GeometryFactory geometryFactory = new GeometryFactory();
088: private List verticesToSnap = null;
089: private Coordinate centerCoord;
090: private Geometry originalBBox = null;
091: private double xscale = 0.0;
092: private double yscale = 0.0;
093: private Coordinate mousePos = null;
094: private Coordinate center = null;
095: DecimalFormat df2 = new DecimalFormat("##0.0#");
096: boolean startScaling = false;
097: double toleranceFactor = 2.0;
098: private BasicStroke originalStroke = null;
099: boolean somethingChanged = false;
100: int style = 1;
101: Cursor cursor2 = createCursor(IconLoader.icon(
102: "MoveVertexCursor.gif").getImage());
103: Cursor cursor1 = null;
104:
105: public ScaleSelectedItemsTool(EnableCheckFactory checkFactory) {
106:
107: this .scaleSelectedItems = I18N
108: .get("org.openjump.core.ui.plugin.edittoolbox.cursortools.ScaleSelectedItemsTool.Scale-Selected-Items");
109: this .sScaleFactor = I18N
110: .get("org.openjump.core.ui.plugin.edittoolbox.cursortools.ScaleSelectedItemsTool.scale-factor");
111:
112: this .checkFactory = checkFactory;
113:
114: this .originalStroke = new BasicStroke(1, BasicStroke.CAP_BUTT,
115: BasicStroke.JOIN_BEVEL, 0, new float[] { 3, 3 }, 0);
116: setStroke(this .originalStroke);
117: this .style = 1;
118: allowSnapping();
119: this .cursor1 = this .getCursor();
120: }
121:
122: protected void gestureFinished() throws java.lang.Exception {
123: //System.out.println("gesture finished");
124: if (this .startScaling == true) {
125: reportNothingToUndoYet();
126: ArrayList transactions = new ArrayList();
127: for (Iterator i = getPanel().getSelectionManager()
128: .getLayersWithSelectedItems().iterator(); i
129: .hasNext();) {
130: Layer layerWithSelectedItems = (Layer) i.next();
131: transactions
132: .add(createTransaction(layerWithSelectedItems));
133: }
134: EditTransaction.commit(transactions);
135: this .startScaling = false;
136: //-- calc new box
137: this .originalBBox = createSelectedItemsBoundingBox();
138: this .selectionBBoxShape = getPanel().getJava2DConverter()
139: .toShape(this .originalBBox);
140: this .outlineItemsShape = this .selectionBBoxShape; //-- do this only to show inital box
141:
142: /*
143: //-- visualize box
144: Polygon box = (Polygon)this.originalBBox.clone(); //cloned!
145: PolygonScale.scalePolygon(box,xscale, yscale,center.x,center.y);
146: ArrayList geoms = new ArrayList();
147: geoms.add(box);
148: FeatureCollection myFC = FeatureDatasetFactory.createFromGeometry(geoms);
149: getPanel().getLayerManager().addLayer(StandardCategoryNames.WORKING, "fbox", myFC);
150: */
151: this .startScaling = false;
152: somethingChanged = true;
153: }
154: }
155:
156: /**
157: private EditTransaction createTransactionPolygon(Layer layer) {
158: EditTransaction transaction =
159: EditTransaction.createTransactionOnSelection(new EditTransaction.SelectionEditor() {
160: public Geometry edit(Geometry geometryWithSelectedItems, Collection selectedItems) {
161: for (Iterator j = selectedItems.iterator(); j.hasNext();) {
162: Geometry item = (Geometry) j.next();
163: if(item instanceof Polygon){
164: PolygonScale.scalePolygon((Polygon)item,xscale, yscale, center.x,center.y);
165: }
166: else{
167: getPanel().getContext().warnUser("no polygon!");
168: }
169: }
170: return geometryWithSelectedItems;
171: }
172: }, getPanel(), getPanel().getContext(), getName(), layer, isRollingBackInvalidEdits(), false);
173: return transaction;
174: }
175: **/
176:
177: private EditTransaction createTransaction(Layer layer) {
178: EditTransaction transaction = EditTransaction
179: .createTransactionOnSelection(
180: new EditTransaction.SelectionEditor() {
181: public Geometry edit(
182: Geometry geometryWithSelectedItems,
183: Collection selectedItems) {
184: for (Iterator j = selectedItems
185: .iterator(); j.hasNext();) {
186: Geometry item = (Geometry) j.next();
187: scale(item);
188: }
189: return geometryWithSelectedItems;
190: }
191: }, getPanel(), getPanel().getContext(),
192: getName(), layer, isRollingBackInvalidEdits(),
193: false);
194: return transaction;
195: }
196:
197: private void scale(Geometry geometry) {
198: geometry.apply(new CoordinateFilter() {
199: public void filter(Coordinate coordinate) {
200: coordinate.x = center.x + xscale
201: * (coordinate.x - center.x);
202: coordinate.y = center.y + yscale
203: * (coordinate.y - center.y);
204: }
205: });
206: }
207:
208: public Icon getIcon() {
209: return new ImageIcon(getClass().getResource("ScalePolygon.gif"));
210: }
211:
212: public String getName() {
213: return scaleSelectedItems;
214: }
215:
216: public void activate(LayerViewPanel panel) {
217: super .activate(panel);
218: try {
219: this .originalBBox = createSelectedItemsBoundingBox();
220: this .selectionBBoxShape = getPanel().getJava2DConverter()
221: .toShape(this .originalBBox);
222: this .outlineItemsShape = this .selectionBBoxShape; //-- do this only to show inital box
223: // later we will replace it by the item geometries
224: this .setStroke(this .originalStroke);
225: this .setColor(Color.RED);
226: this .style = 1;
227: somethingChanged = true;
228: } catch (Throwable t) {
229: getPanel().getContext().handleThrowable(t);
230: }
231: }
232:
233: public void mousePressed(MouseEvent e) {
234: //System.out.println("mouse pressed");
235: this .setStroke(this .originalStroke);
236: this .setColor(Color.RED);
237: this .style = 1;
238: //---------------
239: try {
240: this .setMousePos(getPanel().getViewport()
241: .toModelCoordinate(e.getPoint())); //-- includes snap
242: } catch (Throwable t) {
243: getPanel().getContext().handleThrowable(t);
244: }
245: //-- check if mouse is withing a certain distance of the boundingBox
246: double tolerance = SnapManager.getToleranceInPixels(this
247: .getWorkbench().getBlackboard())
248: / this .getPanel().getViewport().getScale();
249: //-- calc a buffer around the corner points.. so that only near to corner points scaling is activated
250: LineString ls = (LineString) this .originalBBox.getBoundary();
251: MultiPoint mps = new GeometryFactory().createMultiPoint(ls
252: .getCoordinates());
253: Geometry buffergeom = mps.buffer(tolerance
254: * this .toleranceFactor);
255: Point mousep = new GeometryFactory().createPoint(this .mousePos);
256: /*
257: //-- visualize
258: ArrayList geoms = new ArrayList();
259: geoms.add(buffergeom);
260: FeatureCollection myFC = FeatureDatasetFactory.createFromGeometry(geoms);
261: getPanel().getLayerManager().addLayer(StandardCategoryNames.WORKING, "buffer", myFC);
262: */
263: if (buffergeom.contains(mousep)) {
264: try {
265: if (!check(checkFactory
266: .createAtLeastNFeaturesMustBeSelectedCheck(1))) {
267: return;
268: }
269:
270: if (!check(checkFactory
271: .createSelectedItemsLayersMustBeEditableCheck())) {
272: return;
273: }
274: verticesToSnap = null;
275: super .mousePressed(e);
276: this .setSelectedItemsOutlines(); //creates a geometry of all polygon items
277: this .originalBBox = createSelectedItemsBoundingBox();
278: //-- union should be done with the transformed polygon to linestring!
279: // boundary delivers the next lower dimension (OGC Simple Features spec)
280: LineString lsBBox = (LineString) this .originalBBox
281: .getBoundary();
282: this .outlineItems = this .originalItemsAsLines
283: .union(lsBBox); //clone
284: //-- test: use this if drawing the bounding box only (=> change therfore #getShape() as well)
285: //this.selectionBBoxShape = getPanel().getJava2DConverter().toShape(this.originalBBox);
286: this .outlineItemsShape = getPanel()
287: .getJava2DConverter()
288: .toShape(this .outlineItems);
289: //-- set centroid on first press
290: this .center = this .getFarestPoint(this .originalBBox,
291: this .mousePos); //clone since it should not change!
292: /*
293: //-- visualize
294: ArrayList geoms = new ArrayList();
295: geoms.add(new GeometryFactory().createPoint(this.center));
296: FeatureCollection myFC = FeatureDatasetFactory.createFromGeometry(geoms);
297: getPanel().getLayerManager().addLayer(StandardCategoryNames.WORKING, "CenterSet", myFC);
298: */
299:
300: this .startScaling = true;
301: somethingChanged = true;
302: } catch (Throwable t) {
303: getPanel().getContext().handleThrowable(t);
304: }
305: }
306: }
307:
308: public void mouseDragged(MouseEvent e) {
309: if (startScaling == true) {
310: super .mouseDragged(e);
311: try {
312: this .setMousePos(getPanel().getViewport()
313: .toModelCoordinate(e.getPoint())); //-- includes snap
314: double dxMouse = Math.abs(this .center.x
315: - this .mousePos.x);
316: double dyMouse = Math.abs(this .center.y
317: - this .mousePos.y);
318: this .xscale = dxMouse
319: / this .originalBBox.getEnvelopeInternal()
320: .getWidth();
321: this .yscale = dyMouse
322: / this .originalBBox.getEnvelopeInternal()
323: .getHeight();
324: //-- attention: key must be pressed before mouse button is pressed
325: // otherwise it wont be recognized
326: if (e.isShiftDown()) {
327: //System.out.println("key pressed");
328: this .yscale = this .xscale;
329: }
330: getPanel().getContext().setStatusMessage(
331: sScaleFactor + " x: " + df2.format(xscale)
332: + " " + sScaleFactor + " y: "
333: + df2.format(yscale));
334: /*
335: //-- reset shape of selectedFeatureShape = bbox
336: Polygon box = (Polygon)this.originalBBox.clone(); //cloned!
337: PolygonScale.scalePolygon(box,xscale, yscale,center.x,center.y);
338: this.selectionBBoxShape = getPanel().getJava2DConverter().toShape(box);
339: */
340: Geometry geoms = (Geometry) this .outlineItems.clone();
341: this .scale(geoms);
342: this .outlineItemsShape = getPanel()
343: .getJava2DConverter().toShape(geoms);
344: somethingChanged = true;
345: } catch (Throwable t) {
346: getPanel().getContext().handleThrowable(t);
347: }
348: }
349: }
350:
351: /* //overwrite method
352: public Cursor getCursor() {
353: return Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
354: }
355: */
356:
357: public void mouseMoved(MouseEvent e) {
358: try {
359: this .setMousePos(getPanel().getViewport()
360: .toModelCoordinate(e.getPoint())); //-- includes snap
361: double tolerance = SnapManager.getToleranceInPixels(this
362: .getWorkbench().getBlackboard())
363: / this .getPanel().getViewport().getScale();
364: //-- getBoundary().getBoundary schould return the corner points
365: //Geometry buffergeom = this.originalBBox.getBoundary().buffer(tolerance*this.toleranceFactor);
366: //-- calc a buffer around the corner points.. so that only near to corner points scaling is activated
367: LineString ls = (LineString) this .originalBBox
368: .getBoundary();
369: MultiPoint mps = new GeometryFactory().createMultiPoint(ls
370: .getCoordinates());
371: Geometry buffergeom = mps.buffer(tolerance
372: * this .toleranceFactor);
373: Point mousep = new GeometryFactory()
374: .createPoint(this .mousePos);
375: if (buffergeom.contains(mousep)) {
376: //-- this does not work
377: if (this .style == 1) {
378: //this.setStroke(new BasicStroke(2));
379: this .getPanel().setCursor(this .cursor2);
380: this .style = 2;
381: this .somethingChanged = true;
382: }
383: } else {
384: if ((this .style == 2) /*|| (this.isShapeOnScreen()== true)*/) {
385: //this.setStroke(this.originalStroke);
386: this .getPanel().setCursor(this .cursor1);
387: this .style = 1;
388: this .somethingChanged = true;
389: }
390: }
391: if (somethingChanged == true) {
392: this .redrawShape();
393: somethingChanged = false;
394: }
395: } catch (Throwable t) {
396: getPanel().getContext().handleThrowable(t);
397: }
398: }
399:
400: private Geometry createSelectedItemsBoundingBox() {
401: //private Shape createSelectedItemsBoundingBox() throws NoninvertibleTransformException {
402: Collection selectedGeos = (getPanel().getSelectionManager()
403: .getSelectedItems());
404: double xmin = 0, xmax = 0, ymin = 0, ymax = 0;
405: int count = 0;
406: for (Iterator iter = selectedGeos.iterator(); iter.hasNext();) {
407: Geometry element = (Geometry) iter.next();
408: if (count == 0) {
409: xmin = element.getEnvelopeInternal().getMinX();
410: xmax = element.getEnvelopeInternal().getMaxX();
411: ymin = element.getEnvelopeInternal().getMinY();
412: ymax = element.getEnvelopeInternal().getMaxY();
413: } else {
414: if (element.getEnvelopeInternal().getMinX() < xmin) {
415: xmin = element.getEnvelopeInternal().getMinX();
416: }
417: if (element.getEnvelopeInternal().getMaxX() > xmax) {
418: xmax = element.getEnvelopeInternal().getMaxX();
419: }
420: if (element.getEnvelopeInternal().getMinY() < ymin) {
421: ymin = element.getEnvelopeInternal().getMinY();
422: }
423: if (element.getEnvelopeInternal().getMaxY() > ymax) {
424: ymax = element.getEnvelopeInternal().getMaxY();
425: }
426: }
427: count++;
428: }
429:
430: Coordinate[] coords = new Coordinate[] {
431: new Coordinate(xmin, ymin), new Coordinate(xmin, ymax),
432: new Coordinate(xmax, ymax), new Coordinate(xmax, ymin),
433: new Coordinate(xmin, ymin) };
434: LinearRing ring = new GeometryFactory()
435: .createLinearRing(coords);
436: Geometry geo = new GeometryFactory().createPolygon(ring, null);
437: this .centerCoord = geo.getCentroid().getCoordinate();
438: return geo;
439: }
440:
441: private void setSelectedItemsOutlines()
442: throws NoninvertibleTransformException {
443: Collection selectedGeos = (getPanel().getSelectionManager()
444: .getSelectedItems());
445: Geometry geo = null;
446: for (Iterator iter = selectedGeos.iterator(); iter.hasNext();) {
447: Geometry element = (Geometry) iter.next();
448: if (geo == null) {
449: if (element instanceof Polygon) {
450: geo = element.getBoundary();
451: } else {
452: geo = (Geometry) element.clone();
453: }
454: } else {
455: //-- boundary delivers the next lower dimension geometry (polys=> lines)
456: // specified by OGC simple features
457: if (element instanceof Polygon) {
458: geo = geo.union(element.getBoundary());
459: } else {
460: geo = geo.union((Geometry) element.clone());
461: }
462: }
463: }
464: this .originalItemsAsLines = geo;
465: }
466:
467: /**
468: * changed to show bounding box or geometries
469: */
470: protected Shape getShape() {
471: return this .outlineItemsShape;
472: //return this.selectionBBoxShape;
473: }
474:
475: public void deactivate(MouseEvent e) {
476: super .deactivate();
477: this .cleanup((Graphics2D) getPanel().getGraphics());
478: }
479:
480: protected void setMousePos(Coordinate destination) {
481: this .mousePos = snap(destination);
482: }
483:
484: private Coordinate getFarestPoint(Geometry box, Coordinate point) {
485: Coordinate farestp = null;
486: double maxDist = 0, dist = 0;
487: Coordinate[] coords = box.getCoordinates();
488: for (int i = 0; i < coords.length; i++) {
489: dist = GeoUtils.distance(coords[i], point);
490: if (dist > maxDist) {
491: maxDist = dist;
492: farestp = (Coordinate) coords[i].clone();
493: }
494: }
495: return farestp;
496: }
497: }
|