package org.openjump.core.ui.plugin.tools.analysis.onelayer; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.openjump.core.geomutils.algorithm.GeometryConverter; import org.openjump.core.geomutils.algorithm.IntersectGeometries; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.MultiPolygon; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.index.SpatialIndex; import com.vividsolutions.jts.index.strtree.STRtree; import com.vividsolutions.jts.operation.polygonize.Polygonizer; 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.FeatureSchema; import com.vividsolutions.jump.task.TaskMonitor; import com.vividsolutions.jump.tools.AttributeMapping; 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.plugin.ThreadedBasePlugIn; 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; /** * @author sstein * * url www.openjump.org * curentdate 27 May 2010 * license GPL2 * * Intersects polygons in one layer. It therefore calculates * all geometric intersections between the polygons. Afterwards the attributes * are transferred. The later step assumes that a new created intersection * polygon has at max only one correspondent polygon per layer. */ public class IntersectPolygonsOneLayerPlugIn extends ThreadedBasePlugIn { private final static String LAYER1 = GenericNames.LAYER_A; private final static String sTRANSFER = I18N.get("org.openjump.plugin.tools.IntersectPolygonLayersPlugIn.Transfer-attributes"); private String sDescription = "Intersects all polygon geometries in a layer. Regions that can be " + "mapped to two source polygons will not contain attributes. Note: The Planar Graph function provides similar functionality."; private PlugInContext context; private Layer layer1 = null; private boolean exceptionThrown = false; private boolean transferAtt = true; public void initialize(PlugInContext context) throws Exception { context.getFeatureInstaller().addMainMenuItem( this, new String[] { MenuNames.TOOLS, MenuNames.TOOLS_ANALYSIS}, this.getName() + "...", false, null, new MultiEnableCheck().add( new EnableCheckFactory(context.getWorkbenchContext()) .createTaskWindowMustBeActiveCheck()).add( new EnableCheckFactory(context.getWorkbenchContext()) .createAtLeastNLayersMustExistCheck(1))); // -- reset in execute to correct language this.sDescription = I18N.get("org.openjump.plugin.tools.analysis.onelayer.IntersectPolygonsOneLayerPlugIn.sDescription"); } public boolean execute(PlugInContext context) throws Exception { MultiInputDialog dialog = new MultiInputDialog(context .getWorkbenchFrame(), getName(), true); if(layer1 == null){ layer1 = context.getCandidateLayer(0); } setDialogValues(dialog, context); GUIUtil.centreOnWindow(dialog); dialog.setVisible(true); if (!dialog.wasOKPressed()) { return false; } getDialogValues(dialog); return true; } public String getName() { //return "Intersect-Polygons"; return I18N.get("org.openjump.plugin.tools.analysis.onelayer.IntersectPolygonsOneLayerPlugIn.Intersect-Polygons"); } public void run(TaskMonitor monitor, PlugInContext context) throws Exception { this.context = context; monitor.allowCancellationRequests(); FeatureSchema featureSchema = new FeatureSchema(); FeatureCollection resultColl = runIntersectionNew(layer1 .getFeatureCollectionWrapper(), this.transferAtt, monitor, context); if ((resultColl != null) && (resultColl.size() > 0)) { context.addLayer(StandardCategoryNames.RESULT, I18N.get("ui.plugin.analysis.GeometryFunctionPlugIn.intersection") + "-" + layer1.getName(), resultColl); } if (exceptionThrown) context.getWorkbenchFrame() .warnUser(I18N.get("org.openjump.sigle.plugin.SpatialJoinPlugIn.Error-while-executing-spatial-function")); } /** * Merges/Intersects polygons in one layer. It therefore * extracts all lines of the polygons and creates new polygons * with the Polygonizer class. Afterwards it is checked which polygons have a * correspondent in the input layers. If the polygon does not have it is removed * otherwise the attributes are transferred. The later step assumes that a new created * intersection polygon has at max only one correspondent polygon per layer. * * @param fcA * @param transfer Attributes should attributes be transfered? * @param monitor can be null * @param context can be null * @return a FeatureCollection that contains the result of the intersection * (i.e. the new created features) */ private FeatureCollection runIntersectionNew(FeatureCollection fcA, boolean transferAttributes, TaskMonitor monitor, PlugInContext context) { FeatureCollection fd = null; // -- put all geoms in one list and calculate their intersections // i.e. iterate until pure ArrayList<Geometry> geomsToCheck = new ArrayList<Geometry>(); for (Iterator iterator = fcA.iterator(); iterator.hasNext();) { Feature f = (Feature) iterator.next(); geomsToCheck.add(f.getGeometry()); } // -- sort out the different geometry types and receive Lines ArrayList lines = new ArrayList(); for (Iterator iterator = geomsToCheck.iterator(); iterator.hasNext();) { Geometry geom = (Geometry) iterator.next(); if ((geom instanceof Polygon) || (geom instanceof MultiPolygon)) { // everything is fine // -- get Lines lines.addAll(GeometryConverter.transformPolygonToLineStrings(geom)); } else { // -- if (context != null) { context.getWorkbenchFrame().warnUser( I18N.get("org.openjump.plugin.tools.IntersectPolygonLayersPlugIn.Geometry-no-Polygon-or-Multi-Polygon")); } // -- return null; } } //-- calculate the intersections and use the Polygonizer Collection nodedLines = IntersectGeometries.nodeLines((List) lines); Polygonizer polygonizer = new Polygonizer(); for (Iterator i = nodedLines.iterator(); i.hasNext(); ) { Geometry g = (Geometry) i.next(); polygonizer.add(g); } //-- get the Polygons Collection withoutIntersection = polygonizer.getPolygons(); //-- check if the polygon has a correspondent // if yes, transfer the attributes - if no: remove the polygon //-- build a tree for the existing layers first. SpatialIndex treeA = new STRtree(); for (Iterator iterator = fcA.iterator(); iterator.hasNext();) { Feature f = (Feature) iterator.next(); treeA.insert(f.getGeometry().getEnvelopeInternal(), f); } // -- get all intersecting features (usually there should be only one // corresponding feature per layer) // to avoid problems with spatial predicates we do the query for an // internal point of the result polygons // and apply an point in polygon test AttributeMapping mapping = new AttributeMapping(fcA.getFeatureSchema(), new FeatureSchema()); // -- create the empty dataset with the final FeatureSchema fd = new FeatureDataset(mapping.createSchema("Geometry")); // -- add the features and do the attribute mapping for (Iterator iterator = withoutIntersection.iterator(); iterator .hasNext();) { boolean errorInA = false; Geometry geom = (Geometry) iterator.next(); Point pt = geom.getInteriorPoint(); Feature f = new BasicFeature(fd.getFeatureSchema()); Feature featureA = null; // -- query Layer A --- List candidatesA = treeA.query(pt.getEnvelopeInternal()); int foundCountA = 0; for (Iterator iterator2 = candidatesA.iterator(); iterator2.hasNext();){ Feature ftemp = (Feature) iterator2.next(); if (ftemp.getGeometry().contains(pt)) { foundCountA++; featureA = ftemp; } } if (foundCountA > 1) { if (context != null) { errorInA = true; /* String errorStrg = I18N.get("org.openjump.plugin.tools.IntersectPolygonLayersPlugIn.Found-more-than-one-source-feature-in-Layer"); context.getWorkbenchFrame().warnUser(errorStrg+ " " + GenericNames.LAYER_A); context.getWorkbenchFrame().getOutputFrame().createNewDocument(); context.getWorkbenchFrame().getOutputFrame().addText("IntersectPolygonLayersPlugIn: " + errorStrg+ ": " + GenericNames.LAYER_A + ". Reason: The Layer contains probably objects that overlay each other. Will set polygon values of item with FID: " + f.getID() + " to NaN. Use i)" + I18N.get("org.openjump.sigle.plugin.SpatialJoinPlugIn.Transfer-Attributes") + " or ii)" + I18N.get("org.openjump.core.ui.plugin.tools.JoinAttributesSpatiallyPlugIn.Join-Attributes-Spatially") + " functions to obtain atributes from " + GenericNames.LAYER_A); */ } } else if (foundCountA == 0) { //if (context != null) { // context.getWorkbenchFrame().warnUser("no corresponding // feature in Layer A"); //} } if (foundCountA > 0){ // -- before mapping check and set for error values if (errorInA){ featureA = resetFeatureValuesToNaN(featureA); } else{ // -- do mapping mapping.transferAttributes(featureA, null, f); } // -- set Geometry f.setGeometry((Geometry) geom.clone()); fd.add(f); } // else{ // System.out.println("polygon without correspondent"); // } } // -- return fd; } /** * All values are set to NaN. * @param f * @return a clone of f where all numeric and string attributes are set to NaN */ public static Feature resetFeatureValuesToNaN(Feature f){ //-- work only on a copy so the original feature isn't changed Feature ftemp = f.clone(true); FeatureSchema fs = ftemp.getSchema(); for (int i =0; i < fs.getAttributeCount(); i++){ AttributeType type = fs.getAttributeType(i); if (!type.equals(AttributeType.GEOMETRY)){ if(type.equals(AttributeType.DOUBLE)){ ftemp.setAttribute(i, Double.NaN); } if(type.equals(AttributeType.INTEGER)){ ftemp.setAttribute(i, Double.NaN); } if(type.equals(AttributeType.STRING)){ ftemp.setAttribute(i, "NaN"); } if(type.equals(AttributeType.OBJECT)){ ftemp.setAttribute(i, null); } if(type.equals(AttributeType.DATE)){ ftemp.setAttribute(i, null); } } } return ftemp; } private void setDialogValues(MultiInputDialog dialog, PlugInContext context) { dialog.setSideBarDescription(sDescription); // 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 >= 2. // [Jon Aquino] dialog.addLayerComboBox(LAYER1, layer1, context.getLayerManager()); //dialog.addDoubleField(sAccurracy, this.accurracy, 7); //dialog.addCheckBox(sTRANSFER, this.transferAtt); } private void getDialogValues(MultiInputDialog dialog) { layer1 = dialog.getLayer(LAYER1); //this.transferAtt = dialog.getBoolean(sTRANSFER); //this.accurracy = dialog.getDouble(sAccurracy); } }