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.qa;
034:
035: import java.awt.*;
036: import java.awt.event.ItemEvent;
037: import java.awt.event.ItemListener;
038:
039: import javax.swing.*;
040:
041: import com.vividsolutions.jump.I18N;
042: import com.vividsolutions.jump.qa.diff.*;
043: import com.vividsolutions.jump.util.ColorUtil;
044: import com.vividsolutions.jump.feature.*;
045: import com.vividsolutions.jump.task.*;
046: import com.vividsolutions.jump.workbench.model.*;
047: import com.vividsolutions.jump.workbench.plugin.*;
048: import com.vividsolutions.jump.workbench.ui.*;
049:
050: /**
051: * Computes both raw segment diffs and geometry diffs
052: * for two input layers.
053: *
054: * @author Martin Davis
055: */
056: public class DiffGeometryPlugIn extends ThreadedBasePlugIn {
057:
058: private String sLayer = I18N
059: .get("jump.plugin.qa.DiffGeometryPlugIn.Layer");
060: private String LAYER1 = sLayer + " 1";
061: private String LAYER2 = sLayer + " 2";
062: private String MATCH_SEGMENTS = I18N
063: .get("jump.plugin.qa.DiffGeometryPlugIn.Match-Segments");
064: private String MATCH_GEOMETRY = I18N
065: .get("jump.plugin.qa.DiffGeometryPlugIn.Match-entire-Geometry");
066: private String EXACT_COORD_ORDER = I18N
067: .get("jump.plugin.qa.DiffGeometryPlugIn.Test-for-identical-Start-point-and-Orientation");
068: private String USE_TOLERANCE = I18N
069: .get("jump.plugin.qa.DiffGeometryPlugIn.Match-using-Distance-Tolerance");
070: private String DISTANCE_TOL = I18N
071: .get("jump.plugin.qa.DiffGeometryPlugIn.Distance-Tolerance");
072: private String SPLIT_COMPONENTS = I18N
073: .get("jump.plugin.qa.DiffGeometryPlugIn.Match-components-of-MultiGeometries");
074:
075: private String sSegmentDiffs = I18N
076: .get("jump.plugin.qa.DiffGeometryPlugIn.Segment-Diffs");
077: private String sUnmGeoms = I18N
078: .get("jump.plugin.qa.DiffGeometryPlugIn.Unmatched-Geometries-in-Layer");
079: private String sUnmSegms = I18N
080: .get("jump.plugin.qa.DiffGeometryPlugIn.Unmatched-Segments-in-Geometry-Diffs-in-Layer");
081: //note: further strings below
082:
083: private MultiInputDialog dialog;
084: private Layer layer1, layer2;
085: private boolean matchGeometry = false;
086: private boolean useTolerance = false;
087: private double distanceTolerance = 1.0;
088: private boolean testExactCoordinateOrder = false;
089: private boolean splitIntoComponents = false;
090:
091: public DiffGeometryPlugIn() {
092: }
093:
094: /*
095: public void initialize(PlugInContext context) throws Exception {
096: context.getFeatureInstaller().addMainMenuItem(this, new String[] {"QA"},
097: getName() + "...", false, null, new MultiEnableCheck()
098: .add(context.getCheckFactory().createWindowWithLayerViewPanelMustBeActiveCheck())
099: .add(context.getCheckFactory().createAtLeastNLayersMustExistCheck(2)));
100: }
101: */
102:
103: public String getName() {
104: return I18N
105: .get("jump.plugin.qa.DiffGeometryPlugIn.Calculate-Geometry-Differences");
106: }
107:
108: public boolean execute(PlugInContext context) throws Exception {
109:
110: //[sstein, 16.07.2006] set again to obtain correct language
111: sLayer = I18N.get("jump.plugin.qa.DiffGeometryPlugIn.Layer");
112: LAYER1 = sLayer + " 1";
113: LAYER2 = sLayer + " 2";
114: MATCH_SEGMENTS = I18N
115: .get("jump.plugin.qa.DiffGeometryPlugIn.Match-Segments");
116: MATCH_GEOMETRY = I18N
117: .get("jump.plugin.qa.DiffGeometryPlugIn.Match-entire-Geometry");
118: EXACT_COORD_ORDER = I18N
119: .get("jump.plugin.qa.DiffGeometryPlugIn.Test-for-identical-Start-point-and-Orientation");
120: USE_TOLERANCE = I18N
121: .get("jump.plugin.qa.DiffGeometryPlugIn.Match-using-Distance-Tolerance");
122: DISTANCE_TOL = I18N
123: .get("jump.plugin.qa.DiffGeometryPlugIn.Distance-Tolerance");
124: SPLIT_COMPONENTS = I18N
125: .get("jump.plugin.qa.DiffGeometryPlugIn.Match-components-of-MultiGeometries");
126:
127: sSegmentDiffs = I18N
128: .get("jump.plugin.qa.DiffGeometryPlugIn.Segment-Diffs");
129: sUnmGeoms = I18N
130: .get("jump.plugin.qa.DiffGeometryPlugIn.Unmatched-Geometries-in-Layer");
131: sUnmSegms = I18N
132: .get("jump.plugin.qa.DiffGeometryPlugIn.Unmatched-Segments-in-Geometry-Diffs-in-Layer");
133:
134: dialog = new MultiInputDialog(context.getWorkbenchFrame(),
135: getName(), true);
136: setDialogValues(dialog, context);
137: GUIUtil.centreOnWindow(dialog);
138: dialog.setVisible(true);
139: if (!dialog.wasOKPressed()) {
140: return false;
141: }
142: getDialogValues(dialog);
143: //perform(dialog, context);
144: return true;
145: }
146:
147: public void run(TaskMonitor monitor, PlugInContext context)
148: throws Exception {
149: FeatureCollection[] diffFC = new FeatureCollection[] {
150: layer1.getFeatureCollectionWrapper(),
151: layer2.getFeatureCollectionWrapper() };
152:
153: if (matchGeometry) {
154: DiffGeometryComponents diff = new DiffGeometryComponents(
155: layer1.getFeatureCollectionWrapper(), layer2
156: .getFeatureCollectionWrapper(), monitor);
157: diff.setNormalize(!testExactCoordinateOrder);
158: diff.setSplitIntoComponents(splitIntoComponents);
159: if (useTolerance) {
160: diff.setMatcher(new BufferGeometryMatcher(
161: distanceTolerance));
162: }
163: diffFC = diff.diff();
164: }
165: monitor
166: .report(I18N
167: .get("jump.plugin.qa.DiffGeometryPlugIn.Computing-Segment-Diffs"));
168: FeatureCollection[] diffSegFC = diffSegments(diffFC, monitor);
169:
170: createLayers(context, diffFC, diffSegFC);
171: createOutput(context, diffFC, diffSegFC);
172: }
173:
174: public FeatureCollection[] diffSegments(FeatureCollection[] diffFC,
175: TaskMonitor monitor) throws Exception {
176: FeatureCollection[] diffSegFC;
177: if (!useTolerance) {
178: DiffSegments diff = new DiffSegments(monitor);
179: diff.setSegments(0, diffFC[0]);
180: diff.setSegments(1, diffFC[1]);
181:
182: diffSegFC = new FeatureCollection[2];
183: diffSegFC[0] = diff.computeDiffEdges(0);
184: diffSegFC[1] = diff.computeDiffEdges(1);
185: } else {
186: DiffSegmentsWithTolerance diff = new DiffSegmentsWithTolerance(
187: diffFC[0], diffFC[1], distanceTolerance);
188:
189: diffSegFC = diff.diff();
190: }
191: return diffSegFC;
192: }
193:
194: /**
195: * Sets the style for a diff geometry layer.
196: * @param lyr
197: * @param fillColor
198: * @param lineColor
199: */
200: public static void setDiffGeometryStyle(Layer lyr, Color fillColor,
201: Color lineColor) {
202: lyr.getBasicStyle().setRenderingFill(true);
203: lyr.getBasicStyle().setFillColor(fillColor);
204: lyr.setSynchronizingLineColor(false);
205: lyr.getBasicStyle().setAlpha(200);
206: lyr.getBasicStyle().setLineWidth(1);
207: lyr.getBasicStyle().setLineColor(lineColor);
208: lyr.getVertexStyle().setEnabled(false);
209: }
210:
211: private void createLayers(PlugInContext context,
212: FeatureCollection[] diffFC, FeatureCollection[] diffSegFC) {
213: // segment diffs
214: Layer segLyr = context.addLayer(StandardCategoryNames.QA,
215: sSegmentDiffs + " - " + layer1.getName(), diffSegFC[0]);
216: LayerStyleUtil.setLinearStyle(segLyr, Color.red, 2, 4);
217: segLyr.fireAppearanceChanged();
218:
219: Layer segLyr2 = context.addLayer(StandardCategoryNames.QA,
220: sSegmentDiffs + " - " + layer2.getName(), diffSegFC[1]);
221: LayerStyleUtil.setLinearStyle(segLyr2, Color.blue, 2, 4);
222: segLyr2.fireAppearanceChanged();
223:
224: if (matchGeometry) {
225: // Geometry diffs
226: Layer lyr = context
227: .addLayer(StandardCategoryNames.QA, sSegmentDiffs
228: + " - " + layer1.getName(), diffFC[0]);
229: setDiffGeometryStyle(lyr, ColorUtil.PALE_RED, Color.red);
230: lyr.fireAppearanceChanged();
231:
232: Layer lyr2 = context
233: .addLayer(StandardCategoryNames.QA, sSegmentDiffs
234: + " - " + layer2.getName(), diffFC[1]);
235: setDiffGeometryStyle(lyr2, ColorUtil.PALE_BLUE, Color.blue);
236: lyr2.fireAppearanceChanged();
237: }
238: }
239:
240: private void createOutput(PlugInContext context,
241: FeatureCollection[] diffFC, FeatureCollection[] diffSegFC) {
242: context.getOutputFrame().createNewDocument();
243: context.getOutputFrame().addHeader(1, getName());
244: context.getOutputFrame().addField(sLayer + " 1: ",
245: layer1.getName());
246: context.getOutputFrame().addField(sLayer + " 2: ",
247: layer2.getName());
248: if (useTolerance) {
249: context.getOutputFrame().addField(DISTANCE_TOL + ":",
250: "" + distanceTolerance);
251: }
252: if (testExactCoordinateOrder) {
253: context.getOutputFrame().addField(EXACT_COORD_ORDER + ":",
254: (new Boolean(testExactCoordinateOrder)).toString());
255: }
256:
257: if (matchGeometry) {
258: context.getOutputFrame().addText(" ");
259: context.getOutputFrame().addField(
260: "# " + sUnmGeoms + " 1: ", "" + diffFC[0].size());
261: context.getOutputFrame().addField(
262: "# " + sUnmGeoms + " 2: ", "" + diffFC[1].size());
263: }
264:
265: context.getOutputFrame().addText(" ");
266: context.getOutputFrame().addField("# " + sUnmSegms + " 1: ",
267: "" + diffSegFC[0].size());
268: context.getOutputFrame().addField("# " + sUnmSegms + " 2: ",
269: "" + diffSegFC[1].size());
270: }
271:
272: private JRadioButton matchSegmentsRB;
273: private JRadioButton matchGeometryRB;
274: private JCheckBox matchGeometryCheckbox;
275: private JCheckBox splitComponentsCheckbox;
276: private JCheckBox exactOrderCheckbox;
277: private JCheckBox useToleranceCheckbox;
278: private JTextField distanceTextField;
279:
280: private void setDialogValues(MultiInputDialog dialog,
281: PlugInContext context) {
282: dialog.setSideBarImage(new ImageIcon(getClass().getResource(
283: "DiffSegments.png")));
284: dialog
285: .setSideBarDescription(I18N
286: .get("jump.plugin.qa.DiffGeometryPlugIn.Finds-differences-between-the-Segments-or-Geometries-in-two-layers")
287: + " "
288: + I18N
289: .get("jump.plugin.qa.DiffGeometryPlugIn.Matching-can-be-either-exact-or-within-a-Distance-Tolerance"));
290:
291: // Set initial layer values to the first and second layers in the layer list.
292: // In #initialize we've already checked that the number of layers >= 2.
293: dialog.addLayerComboBox(LAYER1, context.getLayerManager()
294: .getLayer(0), context.getLayerManager());
295: dialog.addLayerComboBox(LAYER2, context.getLayerManager()
296: .getLayer(1), context.getLayerManager());
297:
298: final String MATCH_TYPE_GROUP = I18N
299: .get("jump.plugin.qa.DiffGeometryPlugIn.Match-Type");
300: matchSegmentsRB = dialog
301: .addRadioButton(
302: MATCH_SEGMENTS,
303: MATCH_TYPE_GROUP,
304: !matchGeometry,
305: I18N
306: .get("jump.plugin.qa.DiffGeometryPlugIn.Matches-using-segments-only"));
307: matchGeometryRB = dialog
308: .addRadioButton(
309: MATCH_GEOMETRY,
310: MATCH_TYPE_GROUP,
311: matchGeometry,
312: I18N
313: .get("jump.plugin.qa.DiffGeometryPlugIn.Matches-using-full-geometry-topology,-not-just-segments"));
314: matchSegmentsRB.addItemListener(new StateItemListener());
315: matchGeometryRB.addItemListener(new StateItemListener());
316:
317: splitComponentsCheckbox = dialog
318: .addCheckBox(
319: SPLIT_COMPONENTS,
320: splitIntoComponents,
321: I18N
322: .get("jump.plugin.qa.DiffGeometryPlugIn.Matches-individual-components-of-MultiGeometries"));
323: exactOrderCheckbox = dialog
324: .addCheckBox(
325: EXACT_COORD_ORDER,
326: false,
327: I18N
328: .get("jump.plugin.qa.DiffGeometryPlugIn.Requires-coordinate-lists-in-matching-geometries-to-have-identical-start-points-and-ring-orientation"));
329: dialog.addLabel("");
330: useToleranceCheckbox = dialog
331: .addCheckBox(
332: USE_TOLERANCE,
333: useTolerance,
334: I18N
335: .get("jump.plugin.qa.DiffGeometryPlugIn.Matches-geometries-if-all-points-are-within-the-Distance-Tolerance-of-the-other-Geometry"));
336: distanceTextField = dialog
337: .addDoubleField(
338: DISTANCE_TOL,
339: distanceTolerance,
340: 8,
341: I18N
342: .get("jump.plugin.qa.DiffGeometryPlugIn.Specifies-how-close-geometries-must-be-to-match"));
343: useToleranceCheckbox.addItemListener(new StateItemListener());
344:
345: updateUI();
346: }
347:
348: private void getDialogValues(MultiInputDialog dialog) {
349: layer1 = dialog.getLayer(LAYER1);
350: layer2 = dialog.getLayer(LAYER2);
351: matchGeometry = dialog.getBoolean(MATCH_GEOMETRY);
352: useTolerance = dialog.getBoolean(USE_TOLERANCE);
353: distanceTolerance = dialog.getDouble(DISTANCE_TOL);
354: splitIntoComponents = dialog.getBoolean(SPLIT_COMPONENTS);
355: testExactCoordinateOrder = dialog.getBoolean(EXACT_COORD_ORDER);
356: }
357:
358: private void updateUI() {
359: boolean matchGeometry = dialog.getBoolean(MATCH_GEOMETRY);
360: splitComponentsCheckbox.setEnabled(matchGeometry);
361: exactOrderCheckbox.setEnabled(matchGeometry);
362:
363: boolean useDistance = dialog.getBoolean(USE_TOLERANCE);
364: distanceTextField.setEnabled(useDistance);
365: // this has the effect of making the background gray (disabled)
366: distanceTextField.setOpaque(useDistance);
367: }
368:
369: private class StateItemListener implements ItemListener {
370: public void itemStateChanged(ItemEvent e) {
371: updateUI();
372: }
373: }
374: }
|