/* * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI * for visualizing and manipulating spatial features with geometry and attributes. * * Copyright (C) 2003 Vivid Solutions * * 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: * * Vivid Solutions * Suite #1A * 2328 Government Street * Victoria BC V8T 5G5 * Canada * * (250)385-6040 * www.vividsolutions.com */ package com.vividsolutions.jump.plugin.edit; import java.util.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import com.vividsolutions.jump.I18N; import com.vividsolutions.jts.geom.*; import com.vividsolutions.jts.geom.util.AffineTransformation; import com.vividsolutions.jump.util.ColorUtil; import com.vividsolutions.jump.feature.*; import com.vividsolutions.jump.task.*; import com.vividsolutions.jump.workbench.WorkbenchContext; import com.vividsolutions.jump.workbench.model.*; import com.vividsolutions.jump.workbench.plugin.*; import com.vividsolutions.jump.workbench.ui.*; import com.vividsolutions.jump.workbench.ui.plugin.FeatureInstaller; /** * Applies an {@link AffineTransformation} to a layer. * * @author Martin Davis */ public class AffineTransformationPlugIn extends ThreadedBasePlugIn { private DualPaneInputDialog dialog; private Layer layer; private double originX = 0.0; private double originY = 0.0; private double transX = 0.0; private double transY = 0.0; private double scaleX = 1.0; private double scaleY = 1.0; private double shearX = 0.0; private double shearY = 0.0; private double rotationAngle = 0.0; public AffineTransformationPlugIn() { } public String getName() { return I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Affine-Transformation"); } public void initialize(PlugInContext context) throws Exception { FeatureInstaller featureInstaller = new FeatureInstaller(context.getWorkbenchContext()); featureInstaller.addMainMenuItem( this, //exe new String[] {MenuNames.TOOLS, MenuNames.TOOLS_WARP}, //menu path this.getName() + "...", //name methode .getName recieved by AbstractPlugIn false, //checkbox null, //icon createEnableCheck(context.getWorkbenchContext())); //enable check } public EnableCheck createEnableCheck(WorkbenchContext workbenchContext) { EnableCheckFactory checkFactory = new EnableCheckFactory(workbenchContext); return new MultiEnableCheck() .add(checkFactory.createWindowWithLayerManagerMustBeActiveCheck()) .add(checkFactory.createAtLeastNLayersMustExistCheck(1)); } public boolean execute(PlugInContext context) throws Exception { dialog = new DualPaneInputDialog( context.getWorkbenchFrame(), getName(), true); setDialogValues(dialog, context); GUIUtil.centreOnWindow(dialog); dialog.setVisible(true); if (! dialog.wasOKPressed()) { return false; } getDialogValues(dialog); return true; } public void run(TaskMonitor monitor, PlugInContext context) throws Exception { AffineTransformation trans = new AffineTransformation(); AffineTransformation toOriginTrans = AffineTransformation.translationInstance(-originX, -originY); trans.compose(toOriginTrans); if (scaleX != 1.0 || scaleY != 1.0) { AffineTransformation scaleTrans = AffineTransformation.scaleInstance(scaleX, scaleY); trans.scale(scaleX, scaleY); } if (shearX != 0.0 || shearY != 0.0) { trans.shear(shearX, shearY); } if (rotationAngle != 0.0) { AffineTransformation rotTrans = AffineTransformation.rotationInstance(Math.toRadians(rotationAngle)); trans.rotate(Math.toRadians(rotationAngle)); } AffineTransformation fromOriginTrans = AffineTransformation.translationInstance(originX, originY); trans.compose(fromOriginTrans); if (transX != 0.0 || transY != 0.0) { AffineTransformation translateTrans = AffineTransformation.translationInstance(transX, transY); trans.compose(translateTrans); } FeatureCollection fc = layer.getFeatureCollectionWrapper(); FeatureCollection resultFC = new FeatureDataset(fc.getFeatureSchema()); for (Iterator i = fc.iterator(); i.hasNext();) { Feature f = (Feature) i.next(); Feature f2 = f.clone(true); f2.getGeometry().apply(trans); f2.getGeometry().geometryChanged(); resultFC.add(f2); } createLayers(context, resultFC); } private void createLayers(PlugInContext context, FeatureCollection transFC) { Layer lyr = context.addLayer(StandardCategoryNames.RESULT, I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Affine") + layer.getName(), transFC); lyr.fireAppearanceChanged(); } private static String LAYER = GenericNames.LAYER; private static String ORIGIN = I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Anchor-Point"); private static String ORIGIN_FROM_LL = I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Set-to-Lower-Left"); private static String ORIGIN_FROM_MIDPOINT = I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Set-to-Midpoint"); private final static String ORIGIN_X = "X"; private final static String ORIGIN_Y = "Y"; private final static String TRANS_DX = "DX"; private final static String TRANS_DY = "DY"; private static String TRANS_DX_DY = I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Translate-by") +" (X,Y)"; private static String SCALE_X = I18N.get("jump.plugin.edit.AffineTransformationPlugIn.X-Factor"); private static String SCALE_Y = I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Y-Factor"); private static String ROTATE_ANGLE = GenericNames.ANGLE; private static String SHEAR_X = I18N.get("jump.plugin.edit.AffineTransformationPlugIn.X-Shear"); private static String SHEAR_Y = I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Y-Shear"); private static String SRC_BASE_LAYER = GenericNames.SOURCE_LAYER; private static String DEST_BASE_LAYER = GenericNames.TARGET_LAYER; private static String BASELINE_BUTTON = I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Compute-Parameters"); // private JRadioButton matchSegmentsRB; private JTextField originXField; private JTextField originYField; private JTextField transXField; private JTextField transYField; private JTextField scaleXField; private JTextField scaleYField; private JTextField shearXField; private JTextField shearYField; private JTextField rotateAngleField; private void setDialogValues(DualPaneInputDialog dialog, PlugInContext context) { String LAYER = GenericNames.LAYER; ORIGIN = I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Anchor-Point"); ORIGIN_FROM_LL = I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Set-to-Lower-Left"); ORIGIN_FROM_MIDPOINT = I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Set-to-Midpoint"); TRANS_DX_DY = I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Translate-by") +" (X,Y)"; SCALE_X = I18N.get("jump.plugin.edit.AffineTransformationPlugIn.X-Factor"); SCALE_Y = I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Y-Factor"); ROTATE_ANGLE = GenericNames.ANGLE; SHEAR_X = I18N.get("jump.plugin.edit.AffineTransformationPlugIn.X-Shear"); SHEAR_Y = I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Y-Shear"); SRC_BASE_LAYER = GenericNames.SOURCE_LAYER; DEST_BASE_LAYER = GenericNames.TARGET_LAYER; BASELINE_BUTTON = I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Compute-Parameters"); dialog.setSideBarImage(new ImageIcon(getClass().getResource("AffineTransformation.png"))); dialog.setSideBarDescription( I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Applies-an-Affine-Transformation-to-all-features-in-a-layer") + " " + I18N.get("jump.plugin.edit.AffineTransformationPlugIn.The-transformation-is-specified-by-a-combination-of-scaling-rotation-shearing-and-translation") + " " + I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Transformation-parameters-may-be-computed-from-two-layers-containing-baseline-vectors")); dialog.addLayerComboBox(LAYER, context.getCandidateLayer(0), context.getLayerManager()); dialog.addLabel("<HTML><B>"+I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Anchor-Point")+"</B></HTML>"); originXField = dialog.addDoubleField(ORIGIN_X, originX, 20, I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Anchor-Point-X-value")); originYField = dialog.addDoubleField(ORIGIN_Y, originY, 20, I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Anchor-Point-Y-value")); JButton buttonOriginLL = dialog.addButton(ORIGIN_FROM_LL); buttonOriginLL.addActionListener(new OriginLLListener(true)); JButton buttonOriginMid = dialog.addButton(ORIGIN_FROM_MIDPOINT); buttonOriginMid.addActionListener(new OriginLLListener(false)); dialog.addLabel("<HTML><B>"+I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Scaling")+"</B></HTML>"); scaleXField = dialog.addDoubleField(SCALE_X, scaleX, 20, I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Scale-X-Factor")); scaleYField = dialog.addDoubleField(SCALE_Y, scaleY, 20, I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Scale-Y-Factor")); dialog.addLabel("<HTML><B>"+I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Rotation")+"</B></HTML>"); rotateAngleField = dialog.addDoubleField(ROTATE_ANGLE, rotationAngle, 20, I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Rotation-Angle-in-degrees")); dialog.addLabel("<HTML><B>"+I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Shearing")+"</B></HTML>"); shearXField = dialog.addDoubleField(SHEAR_X, shearX, 20, I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Shear-X-Factor")); shearYField = dialog.addDoubleField(SHEAR_Y, shearY, 20, I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Shear-Y-Factor")); dialog.addLabel("<HTML><B>"+I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Translation")+"</B></HTML>"); transXField = dialog.addDoubleField(TRANS_DX, transX, 20, I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Translation-X-value")); transYField = dialog.addDoubleField(TRANS_DY, transY, 20, I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Translation-Y-value")); //dialog.startNewColumn(); dialog.setRightPane(); JButton setIdentityButton = dialog.addButton(I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Set-to-Identity")); setIdentityButton.addActionListener(new SetIdentityListener()); dialog.addSeparator(); dialog.addLabel("<HTML><B>"+I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Baseline-Vectors")+"</B></HTML>"); dialog.addLayerComboBox(SRC_BASE_LAYER, context.getLayerManager().getLayer(0), context.getLayerManager()); dialog.addLayerComboBox(DEST_BASE_LAYER, context.getLayerManager().getLayer(0), context.getLayerManager()); JButton buttonParam = dialog.addButton(BASELINE_BUTTON); buttonParam.addActionListener(new UpdateParamListener()); } private void getDialogValues(MultiInputDialog dialog) { layer = dialog.getLayer(LAYER); originX = dialog.getDouble(ORIGIN_X); originY = dialog.getDouble(ORIGIN_Y); transX = dialog.getDouble(TRANS_DX); transY = dialog.getDouble(TRANS_DY); scaleX = dialog.getDouble(SCALE_X); scaleY = dialog.getDouble(SCALE_Y); shearX = dialog.getDouble(SHEAR_X); shearY = dialog.getDouble(SHEAR_Y); rotationAngle = dialog.getDouble(ROTATE_ANGLE); } private void updateOriginLL(boolean isLowerLeft) { Layer lyr = dialog.getLayer(LAYER); FeatureCollection fc = lyr.getFeatureCollectionWrapper(); Envelope env = fc.getEnvelope(); double x = env.getMinX(); double y = env.getMinY(); // if not LowerLeft, set to midpoint if (! isLowerLeft) { x = (env.getMinX() + env.getMaxX()) / 2; y = (env.getMinY() + env.getMaxY()) / 2; } originXField.setText(x + ""); originYField.setText(y + ""); } private String updateParams() { Layer layerSrc = dialog.getLayer(SRC_BASE_LAYER); Layer layerDest = dialog.getLayer(DEST_BASE_LAYER); FeatureCollection fcSrc = layerSrc.getFeatureCollectionWrapper(); FeatureCollection fcDest = layerDest.getFeatureCollectionWrapper(); AffineTransControlPointExtracter controlPtExtracter = new AffineTransControlPointExtracter(fcSrc, fcDest); String parseErrMsg = null; if (controlPtExtracter.getInputType() == AffineTransControlPointExtracter.TYPE_UNKNOWN) { parseErrMsg = controlPtExtracter.getParseErrorMessage(); return parseErrMsg; } Coordinate[] srcPts = controlPtExtracter.getSrcControlPoints(); Coordinate[] destPts = controlPtExtracter.getDestControlPoints(); TransRotScaleBuilder trsBuilder = null; switch (srcPts.length) { case 2: trsBuilder = new TwoPointTransRotScaleBuilder(srcPts, destPts); break; case 3: trsBuilder = new TriPointTransRotScaleBuilder(srcPts, destPts); break; } if (trsBuilder != null) updateParams(trsBuilder); return null; } private void updateParams(TransRotScaleBuilder trsBuilder) { originXField.setText(trsBuilder.getOriginX() + ""); originYField.setText(trsBuilder.getOriginY() + ""); scaleXField.setText(trsBuilder.getScaleX() + ""); scaleYField.setText(trsBuilder.getScaleY() + ""); transXField.setText(trsBuilder.getTranslateX() + ""); transYField.setText(trsBuilder.getTranslateY() + ""); rotateAngleField.setText(trsBuilder.getRotationAngle() + ""); } private void setToIdentity() { scaleXField.setText("1.0"); scaleYField.setText("1.0"); shearXField.setText("0.0"); shearYField.setText("0.0"); transXField.setText("0.0"); transYField.setText("0.0"); rotateAngleField.setText("0.0"); } private class OriginLLListener implements ActionListener { private boolean isLowerLeft; OriginLLListener(boolean isLowerLeft) { this.isLowerLeft = isLowerLeft; } public void actionPerformed(ActionEvent e) { updateOriginLL(isLowerLeft); } } private class UpdateParamListener implements ActionListener { public void actionPerformed(ActionEvent e) { String errMsg = updateParams(); if (errMsg != null) { JOptionPane.showMessageDialog(null, errMsg, I18N.get("jump.plugin.edit.AffineTransformationPlugIn.Control-Point-Error"), JOptionPane.ERROR_MESSAGE); } } } private class SetIdentityListener implements ActionListener { public void actionPerformed(ActionEvent e) { setToIdentity(); } } }