/* * 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 class implements extensions to JUMP and is * Copyright (C) Stefan Steiniger. * * 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 on 21.Nov.2007 * last modified: 08.May.2008 * * author: sstein * * description: * * ***********************************************/ package org.openjump.core.ui.plugin.tools.statistics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; import org.openjump.core.apitools.FeatureSchemaTools; import com.vividsolutions.jump.I18N; import com.vividsolutions.jump.feature.AttributeType; import com.vividsolutions.jump.feature.Feature; import com.vividsolutions.jump.feature.FeatureCollection; import com.vividsolutions.jump.feature.FeatureDataset; import com.vividsolutions.jump.feature.FeatureSchema; 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.plugin.AbstractPlugIn; import com.vividsolutions.jump.workbench.plugin.EnableCheckFactory; import com.vividsolutions.jump.workbench.plugin.MultiEnableCheck; import com.vividsolutions.jump.workbench.plugin.PlugInContext; import com.vividsolutions.jump.workbench.plugin.ThreadedPlugIn; import com.vividsolutions.jump.workbench.ui.GUIUtil; import com.vividsolutions.jump.workbench.ui.GenericNames; import com.vividsolutions.jump.workbench.ui.MenuNames; import com.vividsolutions.jump.workbench.ui.MultiInputDialog; import com.vividsolutions.jump.workbench.ui.plugin.FeatureInstaller; public class CalcVarianceAndMeanPerClassPlugIn extends AbstractPlugIn implements ThreadedPlugIn{ private MultiInputDialog dialog; private String sidetext = "Calculates mean and variance for a specified attribute " + "in accordance with the classes, which need to be given"; private String OLAYER = "select origin layer"; private String ATTRIBUTEA = "Select attribute to calculate ratio from"; private String ATTRIBUTEB = "Select attribute with classification"; private Layer selLayerO = null; private String selAttributeA = null; private String selAttributeB = null; private String sName = "Calculate Mean and Variance Per Class"; private String sWrongDataType = "Wrong datatype of chosen attribute"; private String sSearch = "reading data and search for complement"; private String sCalcRatios = "calculating ratios"; /** * this method is called on the startup by JUMP/OpenJUMP. * We set here the menu entry for calling the function. */ public void initialize(PlugInContext context) throws Exception { sidetext = I18N.get("org.openjump.core.ui.plugin.tools.statistics.CalcVarianceAndMeanPerClassPlugIn.descriptiontext"); OLAYER = GenericNames.SELECT_LAYER; ATTRIBUTEA = I18N.get("org.openjump.core.ui.plugin.tools.statistics.CalcVarianceAndMeanPerClassPlugIn.Select-attribute-to-calculate-statistics-from"); ATTRIBUTEB = I18N.get("org.openjump.core.ui.plugin.tools.statistics.CalcVarianceAndMeanPerClassPlugIn.Select-attribute-with-classification"); sName = I18N.get("org.openjump.core.ui.plugin.tools.statistics.CalcVarianceAndMeanPerClassPlugIn.Calculate-Mean-and-Variance-Per-Class"); sWrongDataType = I18N.get("org.openjump.core.ui.plugin.tools.statistics.CreateBarPlotPlugIn.Wrong-datatype-of-chosen-attribute"); sSearch = I18N.get("org.openjump.core.ui.plugin.tools.statistics.CalcVarianceAndMeanPerClassPlugIn.reading-data-and-searching-for-complement"); sCalcRatios = I18N.get("org.openjump.core.ui.plugin.tools.statistics.CalcVarianceAndMeanPerClassPlugIn.calculating-statistics"); FeatureInstaller featureInstaller = new FeatureInstaller(context.getWorkbenchContext()); featureInstaller.addMainMenuItem( this, //exe //new String[] {MenuNames.TOOLS, MenuNames.TOOLS_ANALYSIS}, //menu path new String[] {MenuNames.TOOLS, MenuNames.STATISTICS}, this.sName + "...", //name methode .getName recieved by AbstractPlugIn false, //checkbox null, //icon createEnableCheck(context.getWorkbenchContext())); //enable check } /** * This method is used to define when the menu entry is activated or * disabled. In this example we allow the menu entry to be usable only * if one layer exists. */ public static MultiEnableCheck createEnableCheck(WorkbenchContext workbenchContext) { EnableCheckFactory checkFactory = new EnableCheckFactory(workbenchContext); return new MultiEnableCheck() .add(checkFactory.createAtLeastNLayersMustExistCheck(1)) .add(checkFactory.createTaskWindowMustBeActiveCheck()); } /** * this function is called by JUMP/OpenJUMP if one clicks on the menu entry. * It is called before the "run" method and useful to do all the GUI /user-input things * In this example we call two additional methods {@link #setDialogValues(MultiInputDialog, PlugInContext)} * and {@link #getDialogValues(MultiInputDialog)} to obtain the Layer and the buffer radius by the user. */ public boolean execute(PlugInContext context) throws Exception{ this.reportNothingToUndoYet(context); dialog = new MultiInputDialog( context.getWorkbenchFrame(), sName, true); this.setDialogValues(dialog, context); GUIUtil.centreOnWindow(dialog); dialog.setVisible(true); if (! dialog.wasOKPressed()) { return false; } this.getDialogValues(dialog); return true; } public void run(TaskMonitor monitor, PlugInContext context) throws Exception { calcClass(context, monitor, this.selLayerO); } private void setDialogValues(MultiInputDialog dialog, PlugInContext context) { dialog.setSideBarDescription(this.sidetext); dialog.addLayerComboBox(OLAYER, context.getCandidateLayer(0), context.getLayerManager()); List listO = FeatureSchemaTools.getFieldsFromLayerWithoutGeometry(context.getCandidateLayer(0)); Object valA = listO.size()>0?listO.iterator().next():null; Object valB = listO.size()>0?listO.iterator().next():null; final JComboBox jcb_attributeA = dialog.addComboBox(ATTRIBUTEA, valA, listO,ATTRIBUTEA); if (listO.size() == 0) jcb_attributeA.setEnabled(false); final JComboBox jcb_attributeB = dialog.addComboBox(ATTRIBUTEB, valB, listO,ATTRIBUTEB); if (listO.size() == 0) jcb_attributeB.setEnabled(false); dialog.getComboBox(OLAYER).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { List list = getFieldsFromLayerWithoutGeometryO(); if (list.size() == 0) { jcb_attributeA.setModel(new DefaultComboBoxModel(new String[0])); jcb_attributeA.setEnabled(false); } jcb_attributeA.setModel(new DefaultComboBoxModel(list.toArray(new String[0]))); } }); dialog.getComboBox(OLAYER).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { List list = getFieldsFromLayerWithoutGeometryO(); if (list.size() == 0) { jcb_attributeB.setModel(new DefaultComboBoxModel(new String[0])); jcb_attributeB.setEnabled(false); } jcb_attributeB.setModel(new DefaultComboBoxModel(list.toArray(new String[0]))); } }); } private void getDialogValues(MultiInputDialog dialog) { //this.itemlayer = dialog.getLayer(this.CLAYER); this.selLayerO = dialog.getLayer(OLAYER); this.selAttributeA = dialog.getText(ATTRIBUTEA); this.selAttributeB = dialog.getText(ATTRIBUTEB); } private boolean calcClass(final PlugInContext context, TaskMonitor monitor,Layer orgLayer) throws Exception { // ====================================================== // collect attribute values for every object // ====================================================== FeatureCollection fcO = orgLayer.getFeatureCollectionWrapper(); FeatureSchema fsO = fcO.getFeatureSchema(); //assuming that all classes are stored with same type AttributeType typeA = null; AttributeType typeB = null; if ((fsO.getAttributeType(this.selAttributeA) == AttributeType.DOUBLE) || (fsO.getAttributeType(this.selAttributeA) == AttributeType.INTEGER)) { //move on typeA = fsO.getAttributeType(this.selAttributeA); typeB = fsO.getAttributeType(this.selAttributeB); } else{ //System.out.println("ClassifyAttributesPlugIn: wrong datatype of chosen attribute"); context.getWorkbenchFrame().warnUser(sWrongDataType); return false; } double[] data = new double[fcO.size()]; int[] classes = new int[fcO.size()]; //store class id int[] exists = new int[fcO.size()]; //to store if value should be included in calculation: 1 = yes, 0 = no //can be used to define a value that needs to be excluded from calculations int i=0; for (Iterator iter = fcO.iterator(); iter.hasNext();) { monitor.report(i, fcO.size(), sSearch); Feature f = (Feature) iter.next(); Object valA = f.getAttribute(this.selAttributeA); // get ratio attribute Object valB = f.getAttribute(this.selAttributeB); // get class nr if (typeA == AttributeType.DOUBLE){ data[i] = ((Double)valA).doubleValue(); } else if (typeA == AttributeType.INTEGER){ data[i] = ((Integer)valA).intValue(); } //-- class nr if (typeB == AttributeType.DOUBLE){ classes[i] = ((Double)valB).intValue(); } else if (typeB == AttributeType.INTEGER){ classes[i] = ((Integer)valB).intValue(); } //-- set global for now (maybe refine it later to exclude specific values) exists[i] = 1; i++; } // ====================================================== // calculate ratio for every class // ====================================================== monitor.report(sCalcRatios); double[] var = org.math.array.StatisticSample.fill(fcO.size(), -9999.0); double[] mean = org.math.array.StatisticSample.fill(fcO.size(), -9999.0); int[] diffClasses = this.getDifferentClassValues(classes); for (int j = 0; j < diffClasses.length; j++) { int actualClass = diffClasses[j]; int[] idxValsOfSameClass = getIndexValuesOfSameClass(classes, actualClass); int[] existingIdxVals = reduceToExisting(idxValsOfSameClass, exists); double[] vals = getValuesToIndex(data,existingIdxVals); double meanC = org.math.array.StatisticSample.mean(vals); double varC = org.math.array.StatisticSample.variance(vals); for (int k = 0; k < existingIdxVals.length; k++) { var[existingIdxVals[k]]= varC; mean[existingIdxVals[k]]= meanC; } } // ====================================================== // create layer with new field for results // ====================================================== FeatureDataset fd = null; ArrayList outData = new ArrayList(); FeatureSchema targetFSnew = null; int count=0; Iterator iterp = fcO.iterator(); String attnameB = this.selAttributeA + "_mean"; String attname = this.selAttributeA + "_var"; while(iterp.hasNext()){ count=count+1; // if(monitor != null){ // monitor.report("item: " + count + " of " + size); // } Feature p = (Feature)iterp.next(); if (count == 1){ FeatureSchema targetFs = p.getSchema(); targetFSnew = FeatureSchemaTools.copyFeatureSchema(targetFs); if (targetFSnew.hasAttribute(attnameB)){ //attribute will be overwriten } else{ //add attribute targetFSnew.addAttribute(attnameB, AttributeType.DOUBLE); } if (targetFSnew.hasAttribute(attname)){ //attribute will be overwriten } else{ //add attribute targetFSnew.addAttribute(attname, AttributeType.DOUBLE); } } //-- evaluate value for every polygon Feature fcopy = FeatureSchemaTools.copyFeature(p, targetFSnew); //fcopy.setAttribute(this.selClassifier, new Integer(classes[count-1])); fcopy.setAttribute(attnameB, new Double(mean[count-1])); fcopy.setAttribute(attname, new Double(var[count-1])); outData.add(fcopy); } fd = new FeatureDataset(targetFSnew); fd.addAll(outData); String name = this.selAttributeA + "_meanvar"; context.addLayer(StandardCategoryNames.WORKING, name, fd); return true; } public double[] getValuesToIndex(double[] data, int[] existingIdxVals) { double[] vals = new double[existingIdxVals.length]; for (int i = 0; i < existingIdxVals.length; i++) { vals[i] = data[existingIdxVals[i]]; } return vals; } /** * * @param idxValsOfSameClass * @param exists containing values of 0 = not existing, 1= existing * @return */ private int[] reduceToExisting(int[] idxValsOfSameClass, int[] exists) { int[] idxVals = null; ArrayList<Integer> vals = new ArrayList<Integer>(); for (int i = 0; i < idxValsOfSameClass.length; i++) { if (exists[i] == 1){ vals.add(new Integer(idxValsOfSameClass[i])); } } idxVals = new int[vals.size()]; int i = 0; for (Iterator iter = vals.iterator(); iter.hasNext();) { Integer idx = (Integer) iter.next(); idxVals[i] = idx; i++; } return idxVals; } /** * * @param classes * @param actualClass the reference class value to search for * @return all position index values for items that belong to the "actualClass" */ public int[] getIndexValuesOfSameClass(int[] classes, int actualClass) { int[] idxVals = null; ArrayList<Integer> vals = new ArrayList<Integer>(); for (int i = 0; i < classes.length; i++) { if (classes[i] == actualClass){ vals.add(new Integer(i)); } } idxVals = new int[vals.size()]; int i = 0; for (Iterator iter = vals.iterator(); iter.hasNext();) { Integer idx = (Integer) iter.next(); idxVals[i] = idx; i++; } return idxVals; } /** * * @param classes * @return all classes that are found in the array */ public int[] getDifferentClassValues(int[] classes) { int[] classVals = null; ArrayList<Integer> vals = new ArrayList<Integer>(); for (int i = 0; i < classes.length; i++) { if (i==0){ //-- add first class directly vals.add(new Integer(classes[i])); } else{ //-- search if already added // TODO: make while loop to avoid parsing always the full list boolean found = false; for (Iterator iter = vals.iterator(); iter.hasNext();) { Integer existingClass = (Integer) iter.next(); if(classes[i] == existingClass.intValue()){ found = true; } } if (found == false){ vals.add(new Integer(classes[i])); } } } classVals = new int[vals.size()]; int i = 0; for (Iterator iter = vals.iterator(); iter.hasNext();) { Integer idx = (Integer) iter.next(); classVals[i] = idx; i++; } return classVals; } private List getFieldsFromLayerWithoutGeometryO() { return FeatureSchemaTools.getFieldsFromLayerWithoutGeometry(dialog.getLayer(OLAYER)); } }