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: * Copyright (C) 2003 Vivid Solutions
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: *
021: * For more information, contact:
022: *
023: * Vivid Solutions
024: * Suite #1A
025: * 2328 Government Street
026: * Victoria BC V8T 5G5
027: * Canada
028: *
029: * (250)385-6040
030: * www.vividsolutions.com
031: */
032:
033: package com.vividsolutions.jump.plugin.edit;
034:
035: import java.util.*;
036: import java.awt.*;
037: import java.awt.event.*;
038:
039: import javax.swing.*;
040: import com.vividsolutions.jump.I18N;
041:
042: import com.vividsolutions.jts.geom.*;
043: import com.vividsolutions.jump.geom.*;
044: import com.vividsolutions.jump.util.ColorUtil;
045: import com.vividsolutions.jump.feature.*;
046: import com.vividsolutions.jump.task.*;
047: import com.vividsolutions.jump.workbench.WorkbenchContext;
048: import com.vividsolutions.jump.workbench.model.*;
049: import com.vividsolutions.jump.workbench.plugin.*;
050: import com.vividsolutions.jump.workbench.ui.*;
051:
052: /**
053: * Applies an {@link AffineTransformation} to a layer.
054: *
055: * @author Martin Davis
056: */
057: public class AffineTransformationPlugIn extends ThreadedBasePlugIn {
058:
059: private MultiInputDialog dialog;
060: private Layer layer;
061: private double originX = 0.0;
062: private double originY = 0.0;
063: private double transX = 0.0;
064: private double transY = 0.0;
065: private double scaleX = 1.0;
066: private double scaleY = 1.0;
067: private double shearX = 0.0;
068: private double shearY = 0.0;
069: private double rotationAngle = 0.0;
070:
071: public AffineTransformationPlugIn() {
072: }
073:
074: public String getName() {
075: return I18N
076: .get("jump.plugin.edit.AffineTransformationPlugIn.Affine-Transformation");
077: }
078:
079: public EnableCheck createEnableCheck(
080: WorkbenchContext workbenchContext) {
081: EnableCheckFactory checkFactory = new EnableCheckFactory(
082: workbenchContext);
083: return new MultiEnableCheck()
084: .add(
085: checkFactory
086: .createWindowWithLayerManagerMustBeActiveCheck())
087: .add(checkFactory.createAtLeastNLayersMustExistCheck(1));
088: }
089:
090: public boolean execute(PlugInContext context) throws Exception {
091: dialog = new MultiInputDialog(context.getWorkbenchFrame(),
092: getName(), true);
093: setDialogValues(dialog, context);
094: GUIUtil.centreOnWindow(dialog);
095: dialog.setVisible(true);
096: if (!dialog.wasOKPressed()) {
097: return false;
098: }
099: getDialogValues(dialog);
100: //perform(dialog, context);
101: return true;
102: }
103:
104: public void run(TaskMonitor monitor, PlugInContext context)
105: throws Exception {
106: AffineTransformation trans = new AffineTransformation();
107:
108: AffineTransformation toOriginTrans = AffineTransformation
109: .translationInstance(-originX, -originY);
110: trans.compose(toOriginTrans);
111:
112: if (scaleX != 1.0 || scaleY != 1.0) {
113: AffineTransformation scaleTrans = AffineTransformation
114: .scaleInstance(scaleX, scaleY);
115: //trans.compose(scaleTrans);
116: trans.scale(scaleX, scaleY);
117: }
118: if (shearX != 0.0 || shearY != 0.0) {
119: trans.shear(shearX, shearY);
120: }
121: if (rotationAngle != 0.0) {
122: AffineTransformation rotTrans = AffineTransformation
123: .rotationInstance(Math.toRadians(rotationAngle));
124: // trans.compose(rotTrans);
125: trans.rotate(Math.toRadians(rotationAngle));
126: }
127:
128: AffineTransformation fromOriginTrans = AffineTransformation
129: .translationInstance(originX, originY);
130: trans.compose(fromOriginTrans);
131:
132: if (transX != 0.0 || transY != 0.0) {
133: AffineTransformation translateTrans = AffineTransformation
134: .translationInstance(transX, transY);
135: trans.compose(translateTrans);
136: }
137:
138: FeatureCollection fc = layer.getFeatureCollectionWrapper();
139:
140: FeatureCollection resultFC = new FeatureDataset(fc
141: .getFeatureSchema());
142:
143: for (Iterator i = fc.iterator(); i.hasNext();) {
144: Feature f = (Feature) i.next();
145: Feature f2 = f.clone(true);
146: f2.getGeometry().apply(trans);
147: f2.getGeometry().geometryChanged();
148: resultFC.add(f2);
149: }
150:
151: createLayers(context, resultFC);
152: }
153:
154: private void createLayers(PlugInContext context,
155: FeatureCollection transFC) {
156: Layer lyr = context
157: .addLayer(
158: StandardCategoryNames.RESULT,
159: I18N
160: .get("jump.plugin.edit.AffineTransformationPlugIn.Affine")
161: + layer.getName(), transFC);
162: lyr.fireAppearanceChanged();
163: }
164:
165: private static String LAYER = GenericNames.LAYER;
166: private static String ORIGIN = I18N
167: .get("jump.plugin.edit.AffineTransformationPlugIn.Anchor-Point");
168: private static String ORIGIN_FROM_LL = I18N
169: .get("jump.plugin.edit.AffineTransformationPlugIn.Set-to-Lower-Left");
170: private static String ORIGIN_FROM_MIDPOINT = I18N
171: .get("jump.plugin.edit.AffineTransformationPlugIn.Set-to-Midpoint");
172: private final static String ORIGIN_X = "X";
173: private final static String ORIGIN_Y = "Y";
174: private final static String TRANS_DX = "DX";
175: private final static String TRANS_DY = "DY";
176: private static String TRANS_DX_DY = I18N
177: .get("jump.plugin.edit.AffineTransformationPlugIn.Translate-by")
178: + " (X,Y)";
179: private static String SCALE_X = I18N
180: .get("jump.plugin.edit.AffineTransformationPlugIn.X-Factor");
181: private static String SCALE_Y = I18N
182: .get("jump.plugin.edit.AffineTransformationPlugIn.Y-Factor");
183: private static String ROTATE_ANGLE = GenericNames.ANGLE;
184: private static String SHEAR_X = I18N
185: .get("jump.plugin.edit.AffineTransformationPlugIn.X-Shear");
186: private static String SHEAR_Y = I18N
187: .get("jump.plugin.edit.AffineTransformationPlugIn.Y-Shear");
188: private static String SRC_BASE_LAYER = GenericNames.SOURCE_LAYER;
189: private static String DEST_BASE_LAYER = GenericNames.TARGET_LAYER;
190: private static String BASELINE_BUTTON = I18N
191: .get("jump.plugin.edit.AffineTransformationPlugIn.Compute-Parameters");
192:
193: // private JRadioButton matchSegmentsRB;
194: private JTextField originXField;
195: private JTextField originYField;
196: private JTextField transXField;
197: private JTextField transYField;
198: private JTextField scaleXField;
199: private JTextField scaleYField;
200: private JTextField shearXField;
201: private JTextField shearYField;
202: private JTextField rotateAngleField;
203:
204: private void setDialogValues(MultiInputDialog dialog,
205: PlugInContext context) {
206:
207: String LAYER = GenericNames.LAYER;
208: ORIGIN = I18N
209: .get("jump.plugin.edit.AffineTransformationPlugIn.Anchor-Point");
210: ORIGIN_FROM_LL = I18N
211: .get("jump.plugin.edit.AffineTransformationPlugIn.Set-to-Lower-Left");
212: ORIGIN_FROM_MIDPOINT = I18N
213: .get("jump.plugin.edit.AffineTransformationPlugIn.Set-to-Midpoint");
214: TRANS_DX_DY = I18N
215: .get("jump.plugin.edit.AffineTransformationPlugIn.Translate-by")
216: + " (X,Y)";
217: SCALE_X = I18N
218: .get("jump.plugin.edit.AffineTransformationPlugIn.X-Factor");
219: SCALE_Y = I18N
220: .get("jump.plugin.edit.AffineTransformationPlugIn.Y-Factor");
221: ROTATE_ANGLE = GenericNames.ANGLE;
222: SHEAR_X = I18N
223: .get("jump.plugin.edit.AffineTransformationPlugIn.X-Shear");
224: SHEAR_Y = I18N
225: .get("jump.plugin.edit.AffineTransformationPlugIn.Y-Shear");
226: SRC_BASE_LAYER = GenericNames.SOURCE_LAYER;
227: DEST_BASE_LAYER = GenericNames.TARGET_LAYER;
228: BASELINE_BUTTON = I18N
229: .get("jump.plugin.edit.AffineTransformationPlugIn.Compute-Parameters");
230:
231: dialog.setSideBarImage(new ImageIcon(getClass().getResource(
232: "AffineTransformation.png")));
233: dialog
234: .setSideBarDescription(I18N
235: .get("jump.plugin.edit.AffineTransformationPlugIn.Applies-an-Affine-Transformation-to-all-features-in-a-layer")
236: + " "
237: + I18N
238: .get("jump.plugin.edit.AffineTransformationPlugIn.The-transformation-is-specified-by-a-combination-of-scaling-rotation-shearing-and-translation")
239: + " "
240: + I18N
241: .get("jump.plugin.edit.AffineTransformationPlugIn.Transformation-parameters-may-be-computed-from-two-layers-containing-baseline-vectors"));
242:
243: dialog.addLayerComboBox(LAYER, context.getCandidateLayer(0),
244: context.getLayerManager());
245:
246: dialog
247: .addLabel("<HTML><B>"
248: + I18N
249: .get("jump.plugin.edit.AffineTransformationPlugIn.Anchor-Point")
250: + "</B></HTML>");
251:
252: originXField = dialog
253: .addDoubleField(
254: ORIGIN_X,
255: originX,
256: 20,
257: I18N
258: .get("jump.plugin.edit.AffineTransformationPlugIn.Anchor-Point-X-value"));
259: originYField = dialog
260: .addDoubleField(
261: ORIGIN_Y,
262: originY,
263: 20,
264: I18N
265: .get("jump.plugin.edit.AffineTransformationPlugIn.Anchor-Point-Y-value"));
266:
267: JButton buttonOriginLL = dialog.addButton(ORIGIN_FROM_LL);
268: buttonOriginLL.addActionListener(new OriginLLListener(true));
269:
270: JButton buttonOriginMid = dialog
271: .addButton(ORIGIN_FROM_MIDPOINT);
272: buttonOriginMid.addActionListener(new OriginLLListener(false));
273:
274: dialog
275: .addLabel("<HTML><B>"
276: + I18N
277: .get("jump.plugin.edit.AffineTransformationPlugIn.Scaling")
278: + "</B></HTML>");
279: scaleXField = dialog
280: .addDoubleField(
281: SCALE_X,
282: scaleX,
283: 20,
284: I18N
285: .get("jump.plugin.edit.AffineTransformationPlugIn.Scale-X-Factor"));
286: scaleYField = dialog
287: .addDoubleField(
288: SCALE_Y,
289: scaleY,
290: 20,
291: I18N
292: .get("jump.plugin.edit.AffineTransformationPlugIn.Scale-Y-Factor"));
293:
294: dialog
295: .addLabel("<HTML><B>"
296: + I18N
297: .get("jump.plugin.edit.AffineTransformationPlugIn.Rotation")
298: + "</B></HTML>");
299: rotateAngleField = dialog
300: .addDoubleField(
301: ROTATE_ANGLE,
302: rotationAngle,
303: 20,
304: I18N
305: .get("jump.plugin.edit.AffineTransformationPlugIn.Rotation-Angle-in-degrees"));
306:
307: dialog
308: .addLabel("<HTML><B>"
309: + I18N
310: .get("jump.plugin.edit.AffineTransformationPlugIn.Shearing")
311: + "</B></HTML>");
312: shearXField = dialog
313: .addDoubleField(
314: SHEAR_X,
315: shearX,
316: 20,
317: I18N
318: .get("jump.plugin.edit.AffineTransformationPlugIn.Shear-X-Factor"));
319: shearYField = dialog
320: .addDoubleField(
321: SHEAR_Y,
322: shearY,
323: 20,
324: I18N
325: .get("jump.plugin.edit.AffineTransformationPlugIn.Shear-Y-Factor"));
326:
327: dialog
328: .addLabel("<HTML><B>"
329: + I18N
330: .get("jump.plugin.edit.AffineTransformationPlugIn.Translation")
331: + "</B></HTML>");
332: transXField = dialog
333: .addDoubleField(
334: TRANS_DX,
335: transX,
336: 20,
337: I18N
338: .get("jump.plugin.edit.AffineTransformationPlugIn.Translation-X-value"));
339: transYField = dialog
340: .addDoubleField(
341: TRANS_DY,
342: transY,
343: 20,
344: I18N
345: .get("jump.plugin.edit.AffineTransformationPlugIn.Translation-Y-value"));
346:
347: dialog.startNewColumn();
348: JButton setIdentityButton = dialog
349: .addButton(I18N
350: .get("jump.plugin.edit.AffineTransformationPlugIn.Set-to-Identity"));
351: setIdentityButton.addActionListener(new SetIdentityListener());
352: dialog.addSeparator();
353:
354: dialog
355: .addLabel("<HTML><B>"
356: + I18N
357: .get("jump.plugin.edit.AffineTransformationPlugIn.Baseline-Vectors")
358: + "</B></HTML>");
359: dialog.addLayerComboBox(SRC_BASE_LAYER, context
360: .getLayerManager().getLayer(0), context
361: .getLayerManager());
362: dialog.addLayerComboBox(DEST_BASE_LAYER, context
363: .getLayerManager().getLayer(0), context
364: .getLayerManager());
365: JButton buttonParam = dialog.addButton(BASELINE_BUTTON);
366: buttonParam.addActionListener(new UpdateParamListener());
367:
368: }
369:
370: private void getDialogValues(MultiInputDialog dialog) {
371: layer = dialog.getLayer(LAYER);
372: originX = dialog.getDouble(ORIGIN_X);
373: originY = dialog.getDouble(ORIGIN_Y);
374: transX = dialog.getDouble(TRANS_DX);
375: transY = dialog.getDouble(TRANS_DY);
376: scaleX = dialog.getDouble(SCALE_X);
377: scaleY = dialog.getDouble(SCALE_Y);
378: shearX = dialog.getDouble(SHEAR_X);
379: shearY = dialog.getDouble(SHEAR_Y);
380: rotationAngle = dialog.getDouble(ROTATE_ANGLE);
381: }
382:
383: private void updateOriginLL(boolean isLowerLeft) {
384: Layer lyr = dialog.getLayer(LAYER);
385: FeatureCollection fc = lyr.getFeatureCollectionWrapper();
386: Envelope env = fc.getEnvelope();
387:
388: double x = env.getMinX();
389: double y = env.getMinY();
390: // if not LowerLeft, set to midpoint
391: if (!isLowerLeft) {
392: x = (env.getMinX() + env.getMaxX()) / 2;
393: y = (env.getMinY() + env.getMaxY()) / 2;
394: }
395: originXField.setText(x + "");
396: originYField.setText(y + "");
397: }
398:
399: private String updateParams() {
400: Layer layerSrc = dialog.getLayer(SRC_BASE_LAYER);
401: Layer layerDest = dialog.getLayer(DEST_BASE_LAYER);
402:
403: FeatureCollection fcSrc = layerSrc
404: .getFeatureCollectionWrapper();
405: FeatureCollection fcDest = layerDest
406: .getFeatureCollectionWrapper();
407:
408: AffineTransControlPointExtracter controlPtExtracter = new AffineTransControlPointExtracter(
409: fcSrc, fcDest);
410: String parseErrMsg = null;
411: if (controlPtExtracter.getInputType() == AffineTransControlPointExtracter.TYPE_UNKNOWN) {
412: parseErrMsg = controlPtExtracter.getParseErrorMessage();
413: return parseErrMsg;
414: }
415:
416: Coordinate[] srcPts = controlPtExtracter.getSrcControlPoints();
417: Coordinate[] destPts = controlPtExtracter
418: .getDestControlPoints();
419:
420: TransRotScaleBuilder trsBuilder = null;
421: switch (srcPts.length) {
422: case 2:
423: trsBuilder = new TwoPointTransRotScaleBuilder(srcPts,
424: destPts);
425: break;
426: case 3:
427: trsBuilder = new TriPointTransRotScaleBuilder(srcPts,
428: destPts);
429: break;
430: }
431:
432: if (trsBuilder != null)
433: updateParams(trsBuilder);
434: return null;
435: }
436:
437: private void updateParams(TransRotScaleBuilder trsBuilder) {
438: originXField.setText(trsBuilder.getOriginX() + "");
439: originYField.setText(trsBuilder.getOriginY() + "");
440:
441: scaleXField.setText(trsBuilder.getScaleX() + "");
442: scaleYField.setText(trsBuilder.getScaleY() + "");
443:
444: transXField.setText(trsBuilder.getTranslateX() + "");
445: transYField.setText(trsBuilder.getTranslateY() + "");
446:
447: rotateAngleField.setText(trsBuilder.getRotationAngle() + "");
448: }
449:
450: private void setToIdentity() {
451: scaleXField.setText("1.0");
452: scaleYField.setText("1.0");
453:
454: shearXField.setText("0.0");
455: shearYField.setText("0.0");
456:
457: transXField.setText("0.0");
458: transYField.setText("0.0");
459:
460: rotateAngleField.setText("0.0");
461: }
462:
463: private class OriginLLListener implements ActionListener {
464: private boolean isLowerLeft;
465:
466: OriginLLListener(boolean isLowerLeft) {
467: this .isLowerLeft = isLowerLeft;
468: }
469:
470: public void actionPerformed(ActionEvent e) {
471: updateOriginLL(isLowerLeft);
472: }
473: }
474:
475: private class UpdateParamListener implements ActionListener {
476: public void actionPerformed(ActionEvent e) {
477: String errMsg = updateParams();
478: if (errMsg != null) {
479: JOptionPane
480: .showMessageDialog(
481: null,
482: errMsg,
483: I18N
484: .get("jump.plugin.edit.AffineTransformationPlugIn.Control-Point-Error"),
485: JOptionPane.ERROR_MESSAGE);
486: }
487: }
488: }
489:
490: private class SetIdentityListener implements ActionListener {
491: public void actionPerformed(ActionEvent e) {
492: setToIdentity();
493: }
494: }
495: }
|