/* * 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.workbench.ui.plugin.analysis; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JRadioButton; import javax.swing.JTextField; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jump.feature.Feature; import com.vividsolutions.jump.feature.FeatureCollection; import com.vividsolutions.jump.feature.FeatureDataset; import com.vividsolutions.jump.task.TaskMonitor; import com.vividsolutions.jump.workbench.WorkbenchContext; import com.vividsolutions.jump.workbench.model.Layer; import com.vividsolutions.jump.workbench.model.StandardCategoryNames; import com.vividsolutions.jump.workbench.model.UndoableCommand; import com.vividsolutions.jump.workbench.plugin.*; import com.vividsolutions.jump.workbench.plugin.util.*; import com.vividsolutions.jump.workbench.ui.GUIUtil; import com.vividsolutions.jump.workbench.ui.MenuNames; import com.vividsolutions.jump.workbench.ui.MultiInputDialog; import com.vividsolutions.jump.workbench.ui.plugin.FeatureInstaller; import com.vividsolutions.jump.I18N; /** * Provides basic functions for computation with {@link Geometry} objects. * <p> * Uses {@link GeometryFunction} objects obtained from the Registry * by the key GEOMETRY_FUNCTION_REG_KEY. * Other plug-ins can add further Geometry functions to the Registry. * * @see GeometryFunction */ public class GeometryFunctionPlugIn extends AbstractPlugIn implements ThreadedPlugIn { public static final String GEOMETRY_FUNCTION_REG_KEY = "Geometry Function Registry Key"; //-- [sstein 15.02.2006] private String sErrorsFound = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.errors-found-while-executing-function"); private String sFunction = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.function"); private String sFeatures = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.features"); private String SRC_LAYER = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Source"); private String MASK_LAYER = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Mask"); private String METHODS = sFunction; private String PARAM = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Parameter"); private String SELECTED_ONLY = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Use-selected-features-only"); private String UPDATE_SRC = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Update-Source-features-with-result"); private String ADD_TO_SRC = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Add-result-to-Source-layer"); private String CREATE_LYR = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Create-new-layer-for-result"); private Collection functions; private MultiInputDialog dialog; private Layer srcLayer, maskLayer; //private String funcNameToRun; private GeometryFunction functionToRun = null; private boolean exceptionThrown = false; private boolean createLayer = false; private boolean updateSource = false; private boolean addToSource = false; private boolean useSelected = false; private Geometry geoms[] = new Geometry[2]; private double[] params = new double[2]; public GeometryFunctionPlugIn() { } private String categoryName = StandardCategoryNames.RESULT; public void setCategoryName(String value) { categoryName = value; } private boolean addToSourceAllowed = true; public void setAddToSourceAllowed(boolean value) { addToSourceAllowed = value; } public void initialize(PlugInContext context) throws Exception { FeatureInstaller featureInstaller = new FeatureInstaller(context.getWorkbenchContext()); featureInstaller.addMainMenuItem( this, new String[] {MenuNames.TOOLS, MenuNames.TOOLS_ANALYSIS}, this.getName() + "...", false, //checkbox null, //icon createEnableCheck(context.getWorkbenchContext())); registerFunctions(context); } private void registerFunctions(PlugInContext context){ // register standard functions GeometryFunction[] functions = GeometryFunction.getFunctions(); context.getWorkbenchContext().getRegistry().createClassification(GEOMETRY_FUNCTION_REG_KEY,GeometryFunction.class); for (int i = 0; i < functions.length; i++) { context.getWorkbenchContext().getRegistry() .createEntry(GEOMETRY_FUNCTION_REG_KEY, functions[i]); } } public static MultiEnableCheck createEnableCheck(WorkbenchContext workbenchContext) { EnableCheckFactory checkFactory = new EnableCheckFactory(workbenchContext); return new MultiEnableCheck() .add(checkFactory.createWindowWithLayerNamePanelMustBeActiveCheck()) .add(checkFactory.createAtLeastNLayersMustExistCheck(1)); } public boolean execute(PlugInContext context) throws Exception { //-- [sstein 16.07.2006] put here again for langugae settings sErrorsFound = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.errors-found-while-executing-function"); sFunction = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.function"); sFeatures = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.features"); SRC_LAYER = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Source"); MASK_LAYER = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Mask"); METHODS = sFunction; PARAM = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Parameter"); SELECTED_ONLY = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Use-selected-features-only"); UPDATE_SRC = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Update-Source-features-with-result"); ADD_TO_SRC = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Add-result-to-Source-layer"); CREATE_LYR = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Create-new-layer-for-result"); functions = context.getWorkbenchContext().getRegistry() .getEntries(GEOMETRY_FUNCTION_REG_KEY); dialog = new MultiInputDialog( 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 { monitor.allowCancellationRequests(); // input-proofing if (functionToRun == null) return; if (srcLayer == null) return; if ( (updateSource || addToSource) && ! srcLayer.isEditable()) { context.getWorkbenchFrame().warnUser(srcLayer.getName() + " " + I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.is-not-editable")); return; } monitor.report(I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Executing-function") + " " + functionToRun.getName() + "..."); ArrayList modifiedFeatures = new ArrayList(); Collection resultFeatures = null; int nArgs = functionToRun.getGeometryArgumentCount(); if (nArgs == 2) { if (maskLayer == null) return; Collection fc1 = getFeaturesToProcess(srcLayer, context); Collection fc2 = getFeaturesToProcess(maskLayer, context); // check for valid size of input if (fc2.size() != 1) { context.getWorkbenchFrame().warnUser(I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Mask-must-contain-exactly-one-geometry")); return; } Geometry geomMask = ((Feature) fc2.iterator().next()).getGeometry(); resultFeatures = runGeometryMethodWithMask(monitor, fc1, geomMask, functionToRun, modifiedFeatures); } else { Collection fc1 = getFeaturesToProcess(srcLayer, context); resultFeatures = runGeometryMethod(monitor, fc1, functionToRun, modifiedFeatures); } // this will happen if plugin was cancelled if (resultFeatures == null) return; if (modifiedFeatures.size() == 0) { context.getWorkbenchFrame().warnUser(I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.No-geometries-were-processed")); return; } if (createLayer) { String outputLayerName = LayerNameGenerator.generateOperationOnLayerName( functionToRun.toString(), srcLayer.getName()); FeatureCollection resultFC = new FeatureDataset(srcLayer.getFeatureCollectionWrapper().getFeatureSchema()); resultFC.addAll(resultFeatures); context.getLayerManager().addCategory(categoryName); context.addLayer(categoryName, outputLayerName, resultFC); } else if (updateSource) { final Collection undoableNewFeatures = resultFeatures; final Collection undoableModifiedFeatures = modifiedFeatures; UndoableCommand cmd = new UndoableCommand( getName() ) { public void execute() { srcLayer.getFeatureCollectionWrapper().removeAll( undoableModifiedFeatures ); srcLayer.getFeatureCollectionWrapper().addAll( undoableNewFeatures ); } public void unexecute() { srcLayer.getFeatureCollectionWrapper().removeAll( undoableNewFeatures ); srcLayer.getFeatureCollectionWrapper().addAll( undoableModifiedFeatures ); } }; execute( cmd, context ); } else if (addToSource) { final Collection undoableFeatures = resultFeatures; UndoableCommand cmd = new UndoableCommand( getName() ) { public void execute() { srcLayer.getFeatureCollectionWrapper().addAll( undoableFeatures ); } public void unexecute() { srcLayer.getFeatureCollectionWrapper().removeAll( undoableFeatures ); } }; execute( cmd, context ); } if (exceptionThrown) { context.getWorkbenchFrame().warnUser(sErrorsFound); } } private Feature getSelectedFeature(Layer lyr, PlugInContext context){ Collection selected = context .getLayerViewPanel() .getSelectionManager().getFeaturesWithSelectedItems(lyr); if (selected.size() != 1) return null; return (Feature) selected.iterator().next(); } private Collection getSelectedFeatures(Layer lyr, PlugInContext context){ Collection selected = context .getLayerViewPanel() .getSelectionManager().getFeaturesWithSelectedItems(lyr); return selected; } private Collection getFeaturesToProcess(Layer lyr, PlugInContext context){ if (useSelected) return context.getLayerViewPanel() .getSelectionManager().getFeaturesWithSelectedItems(lyr); return lyr.getFeatureCollectionWrapper().getFeatures(); } private Collection runGeometryMethodWithMask(TaskMonitor monitor, Collection fcA, Geometry geomB, GeometryFunction func, Collection modifiedFeatures ) { exceptionThrown = false; Collection resultColl = new ArrayList(); int total = fcA.size(); int count = 0; for (Iterator ia = fcA.iterator(); ia.hasNext(); ) { monitor.report(count++, total, sFeatures); if (monitor.isCancelRequested()) return null; Feature fa = (Feature) ia.next(); Geometry ga = fa.getGeometry(); geoms[0] = ga; geoms[1] = geomB; Geometry result = execute(func, geoms, params); saveResult(fa, result, resultColl, modifiedFeatures); } return resultColl; } private Collection runGeometryMethod(TaskMonitor monitor, Collection fc, GeometryFunction func, Collection modifiedFeatures ){ exceptionThrown = false; Collection resultColl = new ArrayList(); int total = fc.size(); int count = 0; for (Iterator iSrc = fc.iterator(); iSrc.hasNext(); ) { monitor.report(count++, total, sFeatures); if (monitor.isCancelRequested()) return null; Feature fSrc = (Feature) iSrc.next(); Geometry gSrc = fSrc.getGeometry(); if (gSrc == null) continue; geoms[0] = gSrc; Geometry result = execute(func, geoms, params); saveResult(fSrc, result, resultColl, modifiedFeatures); } return resultColl; } private void saveResult(Feature srcFeat, Geometry resultGeom, Collection resultColl, Collection modifiedFeatures) { if (resultGeom == null || resultGeom.isEmpty()) { // do nothing } else { Feature fNew = srcFeat.clone(true); fNew.setGeometry(resultGeom); resultColl.add(fNew); modifiedFeatures.add(srcFeat); } } private Geometry execute(GeometryFunction func, Geometry[] geoms, double[] params){ try { return func.execute(geoms, params); } catch (RuntimeException ex) { // simply eat exceptions and report them by returning null exceptionThrown = true; } return null; } private JComboBox layer2ComboBox; private JTextField paramField; private JLabel labelField; // private JCheckBox replaceSrcChkBox; private JRadioButton updateSourceRB; private JRadioButton createNewLayerRB; private JRadioButton addToSourceRB; private void setDialogValues(MultiInputDialog dialog, PlugInContext context) { //dialog.setSideBarImage(new ImageIcon(getClass().getResource("DiffSegments.png"))); dialog.setSideBarDescription( I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Computes-a-geometric-function-on-features-in-the-Source-layer") + " " + I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Geometry-can-be-saved-to-a-new-layer,-updated-in-place,-or-added-to-the-Source-layer")+ " " + I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Binary-geometric-functions-take-a-mask-feature-as-their-second-operand")); //Set initial layer values to the first and second layers in the layer list. //In #initialize we've already checked that the number of layers >= 1. [Jon Aquino] if (srcLayer == null) srcLayer = context.getCandidateLayer(0); dialog.addLayerComboBox(SRC_LAYER, srcLayer, I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.The-Source-layer-features-provide-the-first-operand-for-the-chosen-function"), context.getLayerManager()); JComboBox functionComboBox = dialog.addComboBox(METHODS, functionToRun, functions, null); functionComboBox.addItemListener(new MethodItemListener()); paramField = dialog.addDoubleField(PARAM, params[0], 10); layer2ComboBox = dialog.addLayerComboBox(MASK_LAYER, maskLayer, I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.The-Mask-layer-must-contain-a-single-feature,-which-is-used-as-the-second-operand-for-binary-functions"), context.getLayerManager() ); dialog.addCheckBox(SELECTED_ONLY, useSelected); final String OUTPUT_GROUP = I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Match-Type"); createNewLayerRB = dialog.addRadioButton(CREATE_LYR, OUTPUT_GROUP, true, I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Create-a-new-layer-for-the-results")); updateSourceRB = dialog.addRadioButton(UPDATE_SRC, OUTPUT_GROUP, false, I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Replace-the-geometry-of-Source-features-with-the-result-geometry") + " "); if ( addToSourceAllowed ) { addToSourceRB = dialog.addRadioButton(ADD_TO_SRC, OUTPUT_GROUP, false, I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.Add-the-result-geometry-to-the-Source-layer")+" "); } updateUIForMethod(functionToRun); } private void getDialogValues(MultiInputDialog dialog) { srcLayer = dialog.getLayer(SRC_LAYER); maskLayer = dialog.getLayer(MASK_LAYER); functionToRun = (GeometryFunction) dialog.getComboBox(METHODS).getSelectedItem(); params[0] = dialog.getDouble(PARAM); useSelected = dialog.getBoolean(SELECTED_ONLY); createLayer = dialog.getBoolean(CREATE_LYR); updateSource = dialog.getBoolean(UPDATE_SRC); if ( addToSourceAllowed ) { addToSource = dialog.getBoolean(ADD_TO_SRC); } } private void updateUIForMethod(GeometryFunction func){ boolean layer2Used = false; boolean paramUsed = false; if (func != null) { layer2Used = func.getGeometryArgumentCount() > 1; paramUsed = func.getParameterCount() > 0; } layer2ComboBox.setEnabled(layer2Used); paramField.setEnabled(paramUsed); // this has the effect of making the background gray (disabled) paramField.setOpaque(paramUsed); dialog.validate(); } private class MethodItemListener implements ItemListener { public void itemStateChanged(ItemEvent e) { updateUIForMethod((GeometryFunction) e.getItem()); } } }