/*
* 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:
* Stefan Steiniger
* perriger@gmx.de
*
*/
/*****************************************************
* created: 11.12.2005
* last modified: 28.01.2006 cursor change on mouse over corner
*
* author: sstein
*
* description:
* - scales selected items (using the bounding box)
*
*****************************************************/
package org.openjump.core.ui.plugin.edittoolbox.cursortools;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.geom.NoninvertibleTransformException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import org.openjump.core.geomutils.GeoUtils;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.CoordinateFilter;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
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.EnableCheckFactory;
import com.vividsolutions.jump.workbench.ui.EditTransaction;
import com.vividsolutions.jump.workbench.ui.LayerViewPanel;
import com.vividsolutions.jump.workbench.ui.cursortool.DragTool;
import com.vividsolutions.jump.workbench.ui.images.IconLoader;
import com.vividsolutions.jump.workbench.ui.snap.SnapManager;
public class ScaleSelectedItemsTool extends DragTool {
private String scaleSelectedItems = "Scale Selected Items";
private String sScaleFactor = "scale factor";
private EnableCheckFactory checkFactory;
private Shape selectionBBoxShape;
private Geometry originalItemsAsLines=null;
private Geometry outlineItems=null;
//private Shape selectedItemsShape=null;
private Shape outlineItemsShape=null;
private GeometryFactory geometryFactory = new GeometryFactory();
private List verticesToSnap = null;
private Coordinate centerCoord;
private Geometry originalBBox = null;
private double xscale = 0.0;
private double yscale = 0.0;
private Coordinate mousePos = null;
private Coordinate center = null;
DecimalFormat df2 = new DecimalFormat("##0.0#");
boolean startScaling = false;
double toleranceFactor = 2.0;
private BasicStroke originalStroke = null;
boolean somethingChanged = false;
int style = 1;
Cursor cursor2 = createCursor(IconLoader.icon("MoveVertexCursor.gif").getImage());
Cursor cursor1 = null;
public ScaleSelectedItemsTool(EnableCheckFactory checkFactory) {
this.scaleSelectedItems =I18N.get("org.openjump.core.ui.plugin.edittoolbox.cursortools.ScaleSelectedItemsTool.Scale-Selected-Items");
this.sScaleFactor =I18N.get("org.openjump.core.ui.plugin.edittoolbox.cursortools.ScaleSelectedItemsTool.scale-factor");
this.checkFactory = checkFactory;
this.originalStroke = new BasicStroke(
1,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_BEVEL,
0,
new float[] { 3, 3 },
0);
setStroke(this.originalStroke);
this.style=1;
allowSnapping();
this.cursor1 = this.getCursor();
}
protected void gestureFinished() throws java.lang.Exception {
//System.out.println("gesture finished");
if (this.startScaling == true){
reportNothingToUndoYet();
ArrayList transactions = new ArrayList();
for (Iterator i = getPanel().getSelectionManager().getLayersWithSelectedItems().iterator();
i.hasNext();
) {
Layer layerWithSelectedItems = (Layer) i.next();
transactions.add(createTransaction(layerWithSelectedItems));
}
EditTransaction.commit(transactions);
this.startScaling=false;
//-- calc new box
this.originalBBox = createSelectedItemsBoundingBox();
this.selectionBBoxShape = getPanel().getJava2DConverter().toShape(this.originalBBox);
this.outlineItemsShape = this.selectionBBoxShape; //-- do this only to show inital box
/*
//-- visualize box
Polygon box = (Polygon)this.originalBBox.clone(); //cloned!
PolygonScale.scalePolygon(box,xscale, yscale,center.x,center.y);
ArrayList geoms = new ArrayList();
geoms.add(box);
FeatureCollection myFC = FeatureDatasetFactory.createFromGeometry(geoms);
getPanel().getLayerManager().addLayer(StandardCategoryNames.WORKING, "fbox", myFC);
*/
this.startScaling = false;
somethingChanged = true;
}
}
/**
private EditTransaction createTransactionPolygon(Layer layer) {
EditTransaction transaction =
EditTransaction.createTransactionOnSelection(new EditTransaction.SelectionEditor() {
public Geometry edit(Geometry geometryWithSelectedItems, Collection selectedItems) {
for (Iterator j = selectedItems.iterator(); j.hasNext();) {
Geometry item = (Geometry) j.next();
if(item instanceof Polygon){
PolygonScale.scalePolygon((Polygon)item,xscale, yscale, center.x,center.y);
}
else{
getPanel().getContext().warnUser("no polygon!");
}
}
return geometryWithSelectedItems;
}
}, getPanel(), getPanel().getContext(), getName(), layer, isRollingBackInvalidEdits(), false);
return transaction;
}
**/
private EditTransaction createTransaction(Layer layer) {
EditTransaction transaction =
EditTransaction.createTransactionOnSelection(new EditTransaction.SelectionEditor() {
public Geometry edit(Geometry geometryWithSelectedItems, Collection selectedItems) {
for (Iterator j = selectedItems.iterator(); j.hasNext();) {
Geometry item = (Geometry) j.next();
scale(item);
}
return geometryWithSelectedItems;
}
}, getPanel(), getPanel().getContext(), getName(), layer, isRollingBackInvalidEdits(), false);
return transaction;
}
private void scale(Geometry geometry) {
geometry.apply(new CoordinateFilter() {
public void filter(Coordinate coordinate) {
coordinate.x=center.x+xscale*(coordinate.x-center.x);
coordinate.y=center.y+yscale*(coordinate.y-center.y);
}
});
}
public Icon getIcon() {
return new ImageIcon(getClass().getResource("ScalePolygon.gif"));
}
public String getName() {
return scaleSelectedItems;
}
public void activate(LayerViewPanel panel){
super.activate(panel);
try{
this.originalBBox = createSelectedItemsBoundingBox();
this.selectionBBoxShape = getPanel().getJava2DConverter().toShape(this.originalBBox);
this.outlineItemsShape = this.selectionBBoxShape; //-- do this only to show inital box
// later we will replace it by the item geometries
this.setStroke(this.originalStroke);
this.setColor(Color.RED);
this.style=1;
somethingChanged = true;
}
catch (Throwable t) {
getPanel().getContext().handleThrowable(t);
}
}
public void mousePressed(MouseEvent e) {
//System.out.println("mouse pressed");
this.setStroke(this.originalStroke);
this.setColor(Color.RED);
this.style=1;
//---------------
try{
this.setMousePos(getPanel().getViewport().toModelCoordinate(e.getPoint())); //-- includes snap
}
catch (Throwable t) {
getPanel().getContext().handleThrowable(t);
}
//-- check if mouse is withing a certain distance of the boundingBox
double tolerance = SnapManager.getToleranceInPixels(this.getWorkbench().getBlackboard()) / this.getPanel().getViewport().getScale();
//-- calc a buffer around the corner points.. so that only near to corner points scaling is activated
LineString ls = (LineString)this.originalBBox.getBoundary();
MultiPoint mps = new GeometryFactory().createMultiPoint(ls.getCoordinates());
Geometry buffergeom = mps.buffer(tolerance*this.toleranceFactor);
Point mousep = new GeometryFactory().createPoint(this.mousePos);
/*
//-- visualize
ArrayList geoms = new ArrayList();
geoms.add(buffergeom);
FeatureCollection myFC = FeatureDatasetFactory.createFromGeometry(geoms);
getPanel().getLayerManager().addLayer(StandardCategoryNames.WORKING, "buffer", myFC);
*/
if (buffergeom.contains(mousep)){
try {
if (!check(checkFactory.createAtLeastNFeaturesMustBeSelectedCheck(1))) {
return;
}
if (!check(checkFactory.createSelectedItemsLayersMustBeEditableCheck())) {
return;
}
verticesToSnap = null;
super.mousePressed(e);
this.setSelectedItemsOutlines(); //creates a geometry of all polygon items
this.originalBBox = createSelectedItemsBoundingBox();
//-- union should be done with the transformed polygon to linestring!
// boundary delivers the next lower dimension (OGC Simple Features spec)
LineString lsBBox = (LineString)this.originalBBox.getBoundary();
this.outlineItems = this.originalItemsAsLines.union(lsBBox); //clone
//-- test: use this if drawing the bounding box only (=> change therfore #getShape() as well)
//this.selectionBBoxShape = getPanel().getJava2DConverter().toShape(this.originalBBox);
this.outlineItemsShape = getPanel().getJava2DConverter().toShape(this.outlineItems);
//-- set centroid on first press
this.center = this.getFarestPoint(this.originalBBox, this.mousePos); //clone since it should not change!
/*
//-- visualize
ArrayList geoms = new ArrayList();
geoms.add(new GeometryFactory().createPoint(this.center));
FeatureCollection myFC = FeatureDatasetFactory.createFromGeometry(geoms);
getPanel().getLayerManager().addLayer(StandardCategoryNames.WORKING, "CenterSet", myFC);
*/
this.startScaling = true;
somethingChanged = true;
} catch (Throwable t) {
getPanel().getContext().handleThrowable(t);
}
}
}
public void mouseDragged(MouseEvent e){
if (startScaling == true){
super.mouseDragged(e);
try {
this.setMousePos(getPanel().getViewport().toModelCoordinate(e.getPoint())); //-- includes snap
double dxMouse=Math.abs(this.center.x- this.mousePos.x);
double dyMouse=Math.abs(this.center.y- this.mousePos.y);
this.xscale = dxMouse / this.originalBBox.getEnvelopeInternal().getWidth();
this.yscale = dyMouse / this.originalBBox.getEnvelopeInternal().getHeight();
//-- attention: key must be pressed before mouse button is pressed
// otherwise it wont be recognized
if (e.isShiftDown()){
//System.out.println("key pressed");
this.yscale=this.xscale;
}
getPanel().getContext().setStatusMessage(sScaleFactor+ " x: " + df2.format(xscale) + " " + sScaleFactor + " y: " + df2.format(yscale));
/*
//-- reset shape of selectedFeatureShape = bbox
Polygon box = (Polygon)this.originalBBox.clone(); //cloned!
PolygonScale.scalePolygon(box,xscale, yscale,center.x,center.y);
this.selectionBBoxShape = getPanel().getJava2DConverter().toShape(box);
*/
Geometry geoms = (Geometry)this.outlineItems.clone();
this.scale(geoms);
this.outlineItemsShape = getPanel().getJava2DConverter().toShape(geoms);
somethingChanged = true;
}
catch (Throwable t) {
getPanel().getContext().handleThrowable(t);
}
}
}
/* //overwrite method
public Cursor getCursor() {
return Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
}
*/
public void mouseMoved(MouseEvent e){
try{
this.setMousePos(getPanel().getViewport().toModelCoordinate(e.getPoint())); //-- includes snap
double tolerance = SnapManager.getToleranceInPixels(this.getWorkbench().getBlackboard()) / this.getPanel().getViewport().getScale();
//-- getBoundary().getBoundary schould return the corner points
//Geometry buffergeom = this.originalBBox.getBoundary().buffer(tolerance*this.toleranceFactor);
//-- calc a buffer around the corner points.. so that only near to corner points scaling is activated
LineString ls = (LineString)this.originalBBox.getBoundary();
MultiPoint mps = new GeometryFactory().createMultiPoint(ls.getCoordinates());
Geometry buffergeom = mps.buffer(tolerance*this.toleranceFactor);
Point mousep = new GeometryFactory().createPoint(this.mousePos);
if (buffergeom.contains(mousep)){
//-- this does not work
if (this.style == 1){
//this.setStroke(new BasicStroke(2));
this.getPanel().setCursor(this.cursor2);
this.style = 2;
this.somethingChanged = true;
}
}
else{
if ((this.style == 2) /*|| (this.isShapeOnScreen()== true)*/){
//this.setStroke(this.originalStroke);
this.getPanel().setCursor(this.cursor1);
this.style = 1;
this.somethingChanged = true;
}
}
if(somethingChanged == true){
this.redrawShape();
somethingChanged = false;
}
}
catch (Throwable t) {
getPanel().getContext().handleThrowable(t);
}
}
private Geometry createSelectedItemsBoundingBox(){
//private Shape createSelectedItemsBoundingBox() throws NoninvertibleTransformException {
Collection selectedGeos = (getPanel().getSelectionManager().getSelectedItems());
double xmin=0, xmax=0, ymin=0, ymax=0;
int count = 0;
for (Iterator iter = selectedGeos.iterator(); iter.hasNext();) {
Geometry element = (Geometry) iter.next();
if (count == 0){
xmin=element.getEnvelopeInternal().getMinX();
xmax=element.getEnvelopeInternal().getMaxX();
ymin=element.getEnvelopeInternal().getMinY();
ymax=element.getEnvelopeInternal().getMaxY();
}
else{
if(element.getEnvelopeInternal().getMinX() < xmin){
xmin=element.getEnvelopeInternal().getMinX();
}
if(element.getEnvelopeInternal().getMaxX() > xmax){
xmax=element.getEnvelopeInternal().getMaxX();
}
if(element.getEnvelopeInternal().getMinY() < ymin){
ymin=element.getEnvelopeInternal().getMinY();
}
if(element.getEnvelopeInternal().getMaxY() > ymax){
ymax=element.getEnvelopeInternal().getMaxY();
}
}
count++;
}
Coordinate[] coords = new Coordinate[]{new Coordinate(xmin,ymin),
new Coordinate(xmin,ymax), new Coordinate(xmax, ymax),
new Coordinate(xmax,ymin), new Coordinate(xmin,ymin)};
LinearRing ring = new GeometryFactory().createLinearRing(coords);
Geometry geo = new GeometryFactory().createPolygon(ring, null);
this.centerCoord = geo.getCentroid().getCoordinate();
return geo;
}
private void setSelectedItemsOutlines() throws NoninvertibleTransformException {
Collection selectedGeos = (getPanel().getSelectionManager().getSelectedItems());
Geometry geo = null;
for (Iterator iter = selectedGeos.iterator(); iter.hasNext();) {
Geometry element = (Geometry) iter.next();
if (geo==null){
if (element instanceof Polygon){
geo=element.getBoundary();
}
else{
geo=(Geometry)element.clone();
}
}
else{
//-- boundary delivers the next lower dimension geometry (polys=> lines)
// specified by OGC simple features
if (element instanceof Polygon){
geo = geo.union(element.getBoundary());
}
else{
geo = geo.union((Geometry)element.clone());
}
}
}
this.originalItemsAsLines = geo;
}
/**
* changed to show bounding box or geometries
*/
protected Shape getShape(){
return this.outlineItemsShape;
//return this.selectionBBoxShape;
}
public void deactivate(MouseEvent e){
super.deactivate();
this.cleanup((Graphics2D)getPanel().getGraphics());
}
protected void setMousePos(Coordinate destination) {
this.mousePos = snap(destination);
}
private Coordinate getFarestPoint(Geometry box, Coordinate point){
Coordinate farestp = null;
double maxDist=0, dist = 0;
Coordinate[] coords = box.getCoordinates();
for(int i=0; i < coords.length; i++){
dist = GeoUtils.distance(coords[i], point);
if (dist > maxDist){
maxDist = dist;
farestp = (Coordinate)coords[i].clone();
}
}
return farestp;
}
}