Skip to content

Instantly share code, notes, and snippets.

@hastebrot
Created January 3, 2012 12:13
Show Gist options
  • Save hastebrot/1554708 to your computer and use it in GitHub Desktop.
Save hastebrot/1554708 to your computer and use it in GitHub Desktop.
/*
* 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 org.openjump.core.ui.plugin.tools;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.geom.util.*;
import com.vividsolutions.jts.operation.linemerge.LineMerger;
import com.vividsolutions.jts.operation.union.UnaryUnionOp;
import com.vividsolutions.jump.I18N;
import com.vividsolutions.jump.feature.AttributeType;
import com.vividsolutions.jump.feature.BasicFeature;
import com.vividsolutions.jump.feature.Feature;
import com.vividsolutions.jump.feature.FeatureCollection;
import com.vividsolutions.jump.feature.FeatureDataset;
import com.vividsolutions.jump.feature.FeatureDatasetFactory;
import com.vividsolutions.jump.feature.FeatureSchema;
import com.vividsolutions.jump.task.TaskMonitor;
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.MenuNames;
import com.vividsolutions.jump.workbench.ui.MultiInputDialog;
import com.vividsolutions.jump.workbench.ui.images.IconLoader;
/**
* UnionByAttribute plugin is used to perform selective unions based on attribute values.
* All the features having a same attribute value are unioned together.<br>
* An option can be used to eliminate null or empty attribute values. Useful to merge,
* for example, all named rivers, but not unnamed one, even if they are in the same layer.<br>
* An other option makes it possible to total numeric fields of unioned features.<br>
*/
public class UnionByAttributePlugIn extends AbstractPlugIn implements ThreadedPlugIn {
private final static String LAYER = I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.layer");
private final static String ATTRIBUTE = I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.attribute");
private final static String IGNORE_EMPTY = I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.ignore-empty");
private final static String MERGE_LINES = I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.merge-lines");
private final static String TOTAL_NUMERIC_FIELDS = I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.total-numeric-fields");
private MultiInputDialog dialog;
private GeometryFactory fact;
public UnionByAttributePlugIn() {}
public String getName() {return I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.union-by-attribute");}
public void initialize(PlugInContext context) throws Exception {
context.getFeatureInstaller().addMainMenuItem(
this,
new String[] { MenuNames.TOOLS, MenuNames.TOOLS_ANALYSIS, MenuNames.ONELAYER },
this.getName(),
false,
null,
new MultiEnableCheck()
.add(new EnableCheckFactory(context.getWorkbenchContext())
.createTaskWindowMustBeActiveCheck())
.add(new EnableCheckFactory(context.getWorkbenchContext())
.createAtLeastNLayersMustExistCheck(1)));
}
public boolean execute(PlugInContext context) throws Exception {
initDialog(context);
dialog.setVisible(true);
if (!dialog.wasOKPressed()) {
return false;
}
return true;
}
private void initDialog(PlugInContext context) {
dialog = new MultiInputDialog(context.getWorkbenchFrame(),
I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.union-by-attribute"), true);
dialog.setSideBarImage(IconLoader.icon("UnionByAttribute.gif"));
dialog.setSideBarDescription(
I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.creates-a-new-layer-containing-the-unions-of-features-having-a-common-attribute-value"));
dialog.addLayerComboBox(LAYER, context.getCandidateLayer(0), context.getLayerManager());
List list = getFieldsFromLayerWithoutGeometry(context.getCandidateLayer(0));
Object val = list.size()>0?list.iterator().next():null;
final JComboBox jcb_attribute = dialog.addComboBox(ATTRIBUTE, val, list,
I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.select-attribute"));
if (list.size() == 0) jcb_attribute.setEnabled(false);
final JCheckBox jcb_ignore_empty = dialog.addCheckBox(IGNORE_EMPTY, true);
if (list.size() == 0) jcb_ignore_empty.setEnabled(false);
dialog.addSeparator();
final JCheckBox jcb_merge_lines = dialog.addCheckBox(MERGE_LINES, true);
final JCheckBox jcb_total_numeric_fields = dialog.addCheckBox(TOTAL_NUMERIC_FIELDS, true);
dialog.getComboBox(LAYER).addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
List list = getFieldsFromLayerWithoutGeometry();
if (list.size() == 0) {
jcb_attribute.setModel(new DefaultComboBoxModel(new String[0]));
jcb_attribute.setEnabled(false);
jcb_ignore_empty.setEnabled(false);
}
jcb_attribute.setModel(new DefaultComboBoxModel(list.toArray(new String[0])));
}
});
GUIUtil.centreOnWindow(dialog);
}
public void run(TaskMonitor monitor, PlugInContext context) throws Exception {
monitor.allowCancellationRequests();
// Get options from the dialog
Layer layer = dialog.getLayer(LAYER);
FeatureCollection fc = layer.getFeatureCollectionWrapper();
FeatureSchema schema = fc.getFeatureSchema();
String att = dialog.getText(ATTRIBUTE);
boolean ignore_empty = dialog.getBoolean(IGNORE_EMPTY);
boolean merge_lines = dialog.getBoolean(MERGE_LINES);
boolean total_numeric_fields = dialog.getBoolean(TOTAL_NUMERIC_FIELDS);
if (fc.getFeatures().size() > 0 &&
((Feature)fc.getFeatures().get(0)).getGeometry()!=null) {
fact = ((Feature)fc.getFeatures().get(0)).getGeometry().getFactory();
}
else {
context.getWorkbenchFrame().warnUser(I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.no-data-to-be-unioned"));
return;
}
// Create the schema for the output dataset
FeatureSchema newSchema = new FeatureSchema();
//fix bug on 2007-09-17 : must take the geometry name of the source layer
//newSchema.addAttribute("GEOMETRY", AttributeType.GEOMETRY);
newSchema.addAttribute(schema.getAttributeName(schema.getGeometryIndex()), AttributeType.GEOMETRY);
newSchema.addAttribute(att, schema.getAttributeType(att));
if (total_numeric_fields) {
for (int i = 0, max = schema.getAttributeCount() ; i < max ; i++) {
if (schema.getAttributeType(i) == AttributeType.INTEGER ||
schema.getAttributeType(i) == AttributeType.DOUBLE)
newSchema.addAttribute(schema.getAttributeName(i),
schema.getAttributeType(i));
}
}
// Order features by attribute value in a map
Map map = new HashMap();
monitor.report(I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.union-by-attribute"));
//monitor.report(-1, -1, I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.sorting"));
for (Iterator i = fc.iterator() ; i.hasNext() ; ) {
Feature f = (Feature)i.next();
Object key = f.getAttribute(att);
if (ignore_empty && (key == null || key.toString().trim().length() == 0)) {
continue;
}
else if (!map.containsKey(key)) {
FeatureCollection fd = new FeatureDataset(fc.getFeatureSchema());
fd.add(f);
map.put(key, fd);
}
else {
((FeatureCollection)map.get(key)).add(f);
}
}
// Computing the result
int count = 1;
FeatureCollection resultfc = new FeatureDataset(newSchema);
for (Iterator i = map.keySet().iterator() ; i.hasNext() ; ) {
monitor.report(I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.union-by-attribute") + " (" + count++ + "/" + map.size() + ")");
Object key = i.next();
FeatureCollection fca = (FeatureCollection)map.get(key);
if (fca.size() > 0) {
Feature feature = union(context, monitor, fca, merge_lines, total_numeric_fields);
feature.setAttribute(att, key);
Feature newFeature = new BasicFeature(newSchema);
// Copy feature attributes in newFeature
for (int j = 0, max = newSchema.getAttributeCount() ; j < max ; j++) {
newFeature.setAttribute(j, feature.getAttribute(newSchema.getAttributeName(j)));
}
resultfc.add(newFeature);
}
}
context.getLayerManager().addCategory(StandardCategoryNames.RESULT);
context.addLayer(StandardCategoryNames.RESULT, layer.getName() + "-" + att + " (union)", resultfc);
}
/**
* New method for union. Instead of the naive algorithm looping over the features and
* unioning each time, this one union small groups of features which are closed to each
* other, then iterates over the result.
* The difference is not so important for small datasets, but for large datasets, the
* difference may of 5 minutes versus 5 hours.
*/
private Feature union(PlugInContext context, TaskMonitor monitor,
FeatureCollection fc, boolean merge_lines, boolean total) {
Collection points = new ArrayList();
Collection lineStrings = new ArrayList();
Collection polygons = new ArrayList();
Collection geoms = new ArrayList();
for (Iterator it = fc.iterator() ; it.hasNext() ; ) {
Geometry g = (Geometry)((Feature) it.next()).getGeometry();
if (g instanceof Point) points.add(g);
else if (g instanceof LineString) lineStrings.add(g);
else if (g instanceof Polygon) polygons.add(g);
else if (g instanceof GeometryCollection) {
Geometry gc = (GeometryCollection)g;
for (int j = 0 ; j < gc.getNumGeometries() ; j++) {
Geometry gp = gc.getGeometryN(j);
if (gp instanceof Point) points.add(gp);
else if (gp instanceof LineString) lineStrings.add(gp);
else if (gp instanceof Polygon) polygons.add(gp);
else;
}
}
}
Geometry gp;
if (points.size()>0 && null != (gp = UnaryUnionOp.union(points))) {
geoms.add(gp);
}
if (merge_lines && lineStrings.size()>0) {
LineMerger merger = new LineMerger();
merger.add(lineStrings);
geoms.addAll(merger.getMergedLineStrings());
}
else if (lineStrings.size()>0) {
context.getOutputFrame().addText("merge_lines = true");
gp = UnaryUnionOp.union(lineStrings);
if (gp != null) geoms.add(gp);
}
if (polygons.size()>0 && null != (gp = UnaryUnionOp.union(polygons))) {
geoms.add(gp);
}
FeatureSchema schema = fc.getFeatureSchema();
Feature feature = new BasicFeature(schema);
if (geoms.size()==0) {
feature.setGeometry(fact.createGeometryCollection(new Geometry[]{}));
}
else {
feature.setGeometry(UnaryUnionOp.union(geoms));
}
if (total) {
feature = totalNumericValues(fc, feature);
}
return feature;
}
// Set the sum of fc collection numeric attribute values into feature numeric attributes.
private Feature totalNumericValues(FeatureCollection fc, Feature feature) {
FeatureSchema schema = fc.getFeatureSchema();
for (Iterator it = fc.iterator() ; it.hasNext() ; ) {
Feature f = (Feature)it.next();
for (int i = 0, max = schema.getAttributeCount() ; i < max ; i++) {
if (schema.getAttributeType(i) == AttributeType.INTEGER) {
Object val = feature.getAttribute(i);
int val1 = (val == null)? 0 : ((Integer)val).intValue();
val = f.getAttribute(i);
val1 = (val == null)? val1 : val1 + ((Integer)val).intValue();
feature.setAttribute(i, new Integer(val1));
}
else if (schema.getAttributeType(i) == AttributeType.DOUBLE) {
Object val = feature.getAttribute(i);
double val1 = (val == null)? 0 : ((Double)val).doubleValue();
val = f.getAttribute(i);
val1 = (val == null)? val1 : val1 + ((Double)val).doubleValue();
feature.setAttribute(i, new Double(val1));
}
}
}
return feature;
}
private Geometry mergeLines(Geometry g) {
List linesList = new ArrayList();
LinearComponentExtracter lineFilter = new LinearComponentExtracter(linesList);
g.apply(lineFilter);
LineMerger merger = new LineMerger();
merger.add(linesList);
return fact.buildGeometry(merger.getMergedLineStrings());
}
private List getFieldsFromLayerWithoutGeometry(Layer lyr) {
List fields = new ArrayList();
FeatureSchema schema = lyr.getFeatureCollectionWrapper().getFeatureSchema();
for (int i = 0 ; i < schema.getAttributeCount() ; i++) {
if (schema.getAttributeType(i) != AttributeType.GEOMETRY) {
fields.add(schema.getAttributeName(i));
}
}
return fields;
}
private List getFieldsFromLayerWithoutGeometry() {
return getFieldsFromLayerWithoutGeometry(dialog.getLayer(LAYER));
}
}
/*
* 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 org.openjump.core.ui.plugin.tools;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.operation.linemerge.LineMerger;
import com.vividsolutions.jts.operation.union.UnaryUnionOp;
import com.vividsolutions.jump.feature.AttributeType;
import com.vividsolutions.jump.feature.BasicFeature;
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.I18N;
import com.vividsolutions.jump.task.TaskMonitor;
import com.vividsolutions.jump.workbench.model.Layer;
import com.vividsolutions.jump.workbench.model.StandardCategoryNames;
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.ui.AttributeTypeFilter;
import com.vividsolutions.jump.workbench.ui.GUIUtil;
import com.vividsolutions.jump.workbench.ui.images.IconLoader;
import com.vividsolutions.jump.workbench.ui.MenuNames;
import com.vividsolutions.jump.workbench.ui.MultiInputDialog;
import com.vividsolutions.jump.workbench.ui.plugin.clipboard.PasteItemsPlugIn;
import com.vividsolutions.jump.workbench.WorkbenchContext;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import javax.swing.ImageIcon;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import org.openjump.core.ui.plugin.AbstractThreadedUiPlugIn;
/**
* UnionByAttribute plugin is used to union features in a Layer or to Dissolve
* features using an attribute. It can optionnaly merge unioned LineStrings
* (union just create MultiLineStrings by default).
* <br>
* There are three options available :
* <ul>
* <li>Features with empty values can be discarded</li>
* <li>Attribute values can be added up (numeric) or concatened (strings)</li>
* </ul>
*/
public class UnionByAttributePlugIn extends AbstractThreadedUiPlugIn {
private final static String LAYER = I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.layer");
private final static String SELECTION = I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.selection");
private final static String SELECTION_HELP = I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.selection-help");
private final static String USE_ATTRIBUTE = I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.use-attribute");
private final static String ATTRIBUTE = I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.attribute");
private final static String IGNORE_EMPTY = I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.ignore-empty");
private final static String MERGE_LINESTRINGS = I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.merge-linestrings");
private final static String AGG_UNUSED_FIELDS = I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.aggregate-unused-fields");
private Layer layer;
private boolean use_selection = false;
private boolean use_attribute = false;
private String attribute;
private boolean ignore_empty = false;
private boolean merge_linestrings = true;
private boolean aggregate_unused_fields = false;
private GeometryFactory factory;
public UnionByAttributePlugIn() {
//super(
// //I18N.get("ui.plugin.analysis.UnionByAttributePlugIn") + "...",
// getName() + "...",
// IconLoader.icon("union_layer_icon.gif")
//);
}
public String getName() {
return I18N.get("ui.plugin.analysis.UnionByAttributePlugIn");
}
@Override
public void initialize(PlugInContext context) throws Exception {
context.getFeatureInstaller().addMainMenuItem(
new String[] { MenuNames.TOOLS, MenuNames.TOOLS_ANALYSIS},
this,
new JMenuItem(getName()+"...", IconLoader.icon("union_layer_icon.gif")),
createEnableCheck(context.getWorkbenchContext()), -1);
}
public static MultiEnableCheck createEnableCheck(WorkbenchContext workbenchContext) {
EnableCheckFactory checkFactory = new EnableCheckFactory(workbenchContext);
return new MultiEnableCheck()
.add(checkFactory.createTaskWindowMustBeActiveCheck())
.add(checkFactory.createAtLeastNLayersMustExistCheck(1));
}
@Override
public boolean execute(PlugInContext context) throws Exception {
MultiInputDialog dialog = new MultiInputDialog(
context.getWorkbenchFrame(), getName(), true);
int n = context.getLayerViewPanel().getSelectionManager().getFeaturesWithSelectedItems().size();
use_selection = (n > 0);
initDialog(dialog, context);
dialog.setVisible(true);
if (!dialog.wasOKPressed()) {
return false;
}
getDialogValues(dialog);
return true;
}
private void initDialog(final MultiInputDialog dialog, PlugInContext context) {
//dialog.setSideBarImage(IconLoader.icon("union_layer.png"));
//dialog.setSideBarDescription("");
final JLabel processedDataLabel = dialog.addSubTitle(
I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.processed-data"));
final JLabel selectionLabel = dialog.addLabel(SELECTION);
final JLabel selectionHelpLabel = dialog.addLabel(SELECTION_HELP);
final JComboBox layerComboBox = dialog.addLayerComboBox(LAYER, context.getCandidateLayer(0), context.getLayerManager());
final JCheckBox useAttributeCheckBox = dialog.addCheckBox(USE_ATTRIBUTE, false, "");
final JComboBox attributeComboBox = dialog.addAttributeComboBox(ATTRIBUTE, LAYER, AttributeTypeFilter.NO_GEOMETRY_FILTER, null);
final JCheckBox ignoreEmptyCheckBox = dialog.addCheckBox(IGNORE_EMPTY, true);
dialog.addSeparator();
final JCheckBox mergeLineStringsCheckBox = dialog.addCheckBox(MERGE_LINESTRINGS, merge_linestrings,
I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.merge-linestrings-tooltip"));
final JCheckBox aggUnusedFieldsCheckBox = dialog.addCheckBox(AGG_UNUSED_FIELDS, aggregate_unused_fields,
I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.aggregation-tooltip"));
updateControls(dialog);
useAttributeCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
updateControls(dialog);
}
});
layerComboBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (ActionListener listener : layerComboBox.getActionListeners()) {
// execute other ActionListener methods before this one
if (listener != this) listener.actionPerformed(e);
}
updateControls(dialog);
}
});
mergeLineStringsCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
updateControls(dialog);
}
});
// Give the dialog box a minimum height of 250, so that the description
// of the infoPanel is always visible.
dialog.setPreferredSize(new java.awt.Dimension(
(int)dialog.getPreferredSize().getWidth(), 280));
GUIUtil.centreOnWindow(dialog);
}
private void getDialogValues(MultiInputDialog dialog) {
layer = dialog.getLayer(LAYER);
use_attribute = dialog.getBoolean(USE_ATTRIBUTE);
attribute = dialog.getText(ATTRIBUTE);
ignore_empty = dialog.getBoolean(IGNORE_EMPTY) && use_attribute;
merge_linestrings = dialog.getBoolean(MERGE_LINESTRINGS);
aggregate_unused_fields = dialog.getBoolean(AGG_UNUSED_FIELDS);
}
private void updateControls(MultiInputDialog dialog) {
getDialogValues(dialog);
FeatureSchema schema = layer.getFeatureCollectionWrapper().getFeatureSchema();
boolean has_attributes =
!AttributeTypeFilter.NO_GEOMETRY_FILTER.filter(schema).isEmpty();
int other_fields =
AttributeTypeFilter.NUMSTRING_FILTER.filter(schema).size();
if (use_attribute) other_fields--;
dialog.setFieldVisible(SELECTION, use_selection);
dialog.setFieldVisible(SELECTION_HELP, use_selection);
dialog.setFieldVisible(LAYER, !use_selection);
dialog.setFieldEnabled(USE_ATTRIBUTE, has_attributes && !use_selection);
dialog.setFieldEnabled(ATTRIBUTE, has_attributes && !use_selection);
dialog.setFieldEnabled(IGNORE_EMPTY, has_attributes && !use_selection);
dialog.setFieldEnabled(AGG_UNUSED_FIELDS, has_attributes && !use_selection);
dialog.setFieldEnabled(USE_ATTRIBUTE, has_attributes);
dialog.setFieldEnabled(ATTRIBUTE, has_attributes && use_attribute);
dialog.setFieldEnabled(IGNORE_EMPTY, has_attributes && use_attribute);
dialog.setFieldEnabled(AGG_UNUSED_FIELDS, other_fields>0);
if (use_selection) {
dialog.setSideBarDescription(
I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.union-selection-description"));
if (merge_linestrings) dialog.setSideBarImage(IconLoader.icon("union_selection_merge.png"));
else dialog.setSideBarImage(IconLoader.icon("union_selection_no_merge.png"));
}
else if (!use_attribute) {
dialog.setSideBarDescription(I18N.getMessage(
"ui.plugin.analysis.UnionByAttributePlugIn.union-layer-description",
new Object[]{layer.getName()}
));
if (merge_linestrings) dialog.setSideBarImage(IconLoader.icon("union_layer_merge.png"));
else dialog.setSideBarImage(IconLoader.icon("union_layer_no_merge.png"));
}
else {
dialog.setSideBarDescription(I18N.getMessage(
"ui.plugin.analysis.UnionByAttributePlugIn.union-layer-by-attribute-description",
new Object[]{layer.getName(), attribute}
));
if (merge_linestrings) dialog.setSideBarImage(IconLoader.icon("dissolve_layer_merge.png"));
else dialog.setSideBarImage(IconLoader.icon("dissolve_layer_no_merge.png"));
}
}
public void run(TaskMonitor monitor, PlugInContext context) throws Exception {
monitor.allowCancellationRequests();
Collection inputC = null;
FeatureSchema schema = null;
if (use_selection) {
inputC = context.getLayerViewPanel().getSelectionManager().getFeaturesWithSelectedItems();
Feature feature = (Feature) inputC.iterator().next();
schema = feature.getSchema();
inputC = PasteItemsPlugIn.conform(inputC, schema);
} else {
inputC = layer.getFeatureCollectionWrapper().getFeatures();
schema = layer.getFeatureCollectionWrapper().getFeatureSchema();
}
FeatureDataset inputFC = new FeatureDataset(inputC, schema);
if (inputFC.getFeatures().size() > 1 &&
((Feature)inputFC.getFeatures().get(0)).getGeometry() != null) {
factory = ((Feature)inputFC.getFeatures().get(0)).getGeometry().getFactory();
writeReport(context);
}
else {
context.getWorkbenchFrame().warnUser(
I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.needs-two-features-or-more"));
return;
}
// Create the schema for the output dataset
FeatureSchema newSchema = new FeatureSchema();
//fix bug on 2007-09-17 : must take the geometry name of the source layer
//newSchema.addAttribute("GEOMETRY", AttributeType.GEOMETRY);
newSchema.addAttribute(schema.getAttributeName(schema.getGeometryIndex()), AttributeType.GEOMETRY);
if (use_attribute && !attribute.equals(MultiInputDialog.NO_VALID_ATTRIBUTE)) {
newSchema.addAttribute(attribute, schema.getAttributeType(attribute));
}
// if total_numeric_fields is true, add numeric fields to the result layer
if (aggregate_unused_fields) {
for (int i = 0, max = schema.getAttributeCount() ; i < max ; i++) {
if (schema.getAttributeType(i) == AttributeType.INTEGER ||
schema.getAttributeType(i) == AttributeType.DOUBLE ||
schema.getAttributeType(i) == AttributeType.STRING)
newSchema.addAttribute(schema.getAttributeName(i),
schema.getAttributeType(i));
}
}
// Order features by attribute value in a map
Map map = new HashMap();
monitor.report(I18N.get("ui.plugin.analysis.UnionByAttributePlugIn"));
for (Iterator i = inputFC.iterator() ; i.hasNext() ; ) {
Feature f = (Feature)i.next();
Object key = use_attribute ? f.getAttribute(attribute) : null;
if (ignore_empty && (key == null || key.toString().trim().length() == 0)) {
continue;
}
else if (!map.containsKey(key)) {
FeatureCollection fd = new FeatureDataset(inputFC.getFeatureSchema());
fd.add(f);
map.put(key, fd);
}
else {
((FeatureCollection)map.get(key)).add(f);
}
}
// Computing the result
int count = 1;
FeatureCollection resultfc = new FeatureDataset(newSchema);
for (Iterator i = map.keySet().iterator() ; i.hasNext() ; ) {
monitor.report(I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.computing-union") + " (" + count++ + "/" + map.size() + ")");
Object key = i.next();
FeatureCollection fca = (FeatureCollection)map.get(key);
if (fca.size() > 0) {
Feature feature = union(context, monitor, fca);
if (use_attribute) feature.setAttribute(attribute, key);
Feature newFeature = new BasicFeature(newSchema);
// Copy feature attributes in newFeature
for (int j = 0, max = newSchema.getAttributeCount() ; j < max ; j++) {
newFeature.setAttribute(j, feature.getAttribute(newSchema.getAttributeName(j)));
}
resultfc.add(newFeature);
}
}
context.getLayerManager().addCategory(StandardCategoryNames.RESULT);
String newLayerName = layer.getName() +
(use_attribute ? ("-" + attribute + " (dissolve)") : " (union)");
context.addLayer(StandardCategoryNames.RESULT, newLayerName, resultfc);
context.getOutputFrame().append("<h3>"+
I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.end-of-process") + " " +
I18N.get("ui.plugin.analysis.UnionByAttributePlugIn") + "</h3>");
}
/**
* New method for union. Uses new UnaryUnionOp which is much more
* efficient for large datasets.
*/
private Feature union(PlugInContext context, TaskMonitor monitor, FeatureCollection fc) {
Collection points = new ArrayList();
Collection lineStrings = new ArrayList();
Collection polygons = new ArrayList();
Collection geoms = new ArrayList();
for (Iterator it = fc.iterator() ; it.hasNext() ; ) {
Feature f = (Feature) it.next();
Geometry g = f.getGeometry();
if (!g.isValid()) {
context.getWorkbenchFrame().warnUser(
I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.invalid-geometry-excluded"));
context.getOutputFrame().addText(
I18N.getMessage("ui.plugin.analysis.UnionByAttributePlugIn.exclusion", new Object[]{f.getID()}));
continue;
}
else if (g.isEmpty()) continue;
else if (g instanceof Point) points.add(g);
else if (g instanceof LineString) lineStrings.add(g);
else if (g instanceof Polygon) polygons.add(g);
else if (g instanceof GeometryCollection) {
Geometry gc = (GeometryCollection)g;
for (int j = 0 ; j < gc.getNumGeometries() ; j++) {
Geometry gp = gc.getGeometryN(j);
if (gp instanceof Point) points.add(gp);
else if (gp instanceof LineString) lineStrings.add(gp);
else if (gp instanceof Polygon) polygons.add(gp);
else;
}
}
}
Geometry gp;
if (points.size()>0 && null != (gp = UnaryUnionOp.union(points))) {
geoms.add(gp);
}
if (merge_linestrings && lineStrings.size()>0) {
LineMerger merger = new LineMerger();
merger.add(lineStrings);
geoms.addAll(merger.getMergedLineStrings());
}
else if (lineStrings.size()>0) {
gp = UnaryUnionOp.union(lineStrings);
if (gp != null) geoms.add(gp);
}
if (polygons.size()>0 && null != (gp = UnaryUnionOp.union(polygons))) {
geoms.add(gp);
}
FeatureSchema schema = fc.getFeatureSchema();
Feature feature = new BasicFeature(schema);
if (geoms.size()==0) {
feature.setGeometry(factory.createGeometryCollection(new Geometry[]{}));
}
else {
feature.setGeometry(UnaryUnionOp.union(geoms));
}
if (aggregate_unused_fields) {
feature = aggregateValues(context, fc, feature);
}
return feature;
}
private Feature aggregateValues(PlugInContext context, FeatureCollection fc, Feature feature) {
FeatureSchema schema = fc.getFeatureSchema();
for (int i = 0, max = schema.getAttributeCount() ; i < max ; i++) {
if (schema.getAttributeType(i) == AttributeType.INTEGER) {
int total = 0;
for (Iterator it = fc.iterator() ; it.hasNext() ; ) {
Object val = ((Feature)it.next()).getAttribute(i);
if (val != null) total += ((Integer)val).intValue();
}
feature.setAttribute(i, new Integer(total));
}
else if (schema.getAttributeType(i) == AttributeType.DOUBLE) {
double total = 0;
for (Iterator it = fc.iterator() ; it.hasNext() ; ) {
Object val = ((Feature)it.next()).getAttribute(i);
if (val != null) total += ((Double)val).doubleValue();
}
feature.setAttribute(i, new Double(total));
}
else if (schema.getAttributeType(i) == AttributeType.STRING) {
java.util.Set set = new java.util.TreeSet();
for (Iterator it = fc.iterator() ; it.hasNext() ; ) {
Object val = ((Feature)it.next()).getAttribute(i);
if (val != null) set.add(val);
}
feature.setAttribute(i, java.util.Arrays.toString(set.toArray()));
}
else if (schema.getAttributeType(i) != AttributeType.GEOMETRY) {
context.getOutputFrame().addText(
I18N.getMessage("ui.plugin.analysis.UnionByAttributePlugIn.cannot-be-aggregated",
new Object[]{schema.getAttributeName(i), schema.getAttributeType(i)}));
}
}
return feature;
}
private void writeReport(PlugInContext context) {
context.getOutputFrame().createNewDocument();
context.getOutputFrame().append(
"<h1>" + I18N.get("ui.plugin.analysis.UnionByAttributePlugIn") + "</h1>");
context.getOutputFrame().addText(
I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.processed-data") + " : " +
(use_selection ? I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.selection") : layer.getName()));
context.getOutputFrame().addText(
I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.use-attribute") + " : " +
(use_attribute ? attribute : I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.no-attribute-used")));
context.getOutputFrame().addText(
I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.empty-values") + " : " +
(ignore_empty ? I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.ignore") :
I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.process")));
context.getOutputFrame().addText(
I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.merge-linestrings") + " : " +
(merge_linestrings ? I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.yes") :
I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.no")));
context.getOutputFrame().addText(
I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.aggregate-unused-fields") + " : " +
(aggregate_unused_fields ? I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.yes") :
I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.no")));
context.getOutputFrame().append("<h3>" +
I18N.get("ui.plugin.analysis.UnionByAttributePlugIn.warnings") + "</h3>");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment