/*********************************************** * created on 5.May.2008 * last modified: 19.May.2008 * * author: sstein * license: LGPL * * description: * provides functions to merge/intersects an ArrayList of geometries * that may contain several (possibly different) geometries ***********************************************/ package org.openjump.core.geomutils.algorithm; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryCollection; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.LinearRing; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.geom.PrecisionModel; import com.vividsolutions.jump.task.TaskMonitor; import com.vividsolutions.jump.workbench.plugin.PlugInContext; public class IntersectGeometries { /* *//** * TODO: this method is not properly tested for mixed geometries and needs to be revised. * It can be used either for collections of LineString or Polygons * @param geomList * @param monitor can be null * @param context can be null * @return *//* public static ArrayList<Geometry> intersectGeometries(ArrayList<Geometry> geomList, TaskMonitor monitor, PlugInContext context){ ArrayList<Geometry> withoutIntersection = new ArrayList<Geometry>(); //-- resolve all GeometryCollections/Multigeometries ArrayList<Geometry> tempList = new ArrayList(); for (int i = 0; i < geomList.size(); i++) { Geometry geom = geomList.get(i); if (geom instanceof GeometryCollection){ ArrayList<Geometry> parts = GeometryConverter.explodeGeomsIfMultiG(geom); tempList.addAll(parts); } else{ tempList.add(geom); } } geomList = tempList; //-- note: no assumption that objects in one layer have contain overlaps int totalCount = 0; while(geomList.size() > 1){ //-- take alsways the first item until there is not more left Geometry g2Test = geomList.get(0); //--check against all others // this is very time consuming, but necessary if (monitor != null){ if (monitor.isCancelRequested()){ return withoutIntersection; } else{ monitor.report("n: " + geomList.size()); } } //-- avoid that already GeomCollections are inside while (g2Test instanceof GeometryCollection){ ArrayList<Geometry> parts = GeometryConverter.explodeGeomsIfMultiG(g2Test); geomList.addAll(parts); geomList.remove(0); g2Test = geomList.get(0); } //--start with the checks boolean checkNext = true; int count = 0; while(checkNext) { count++; totalCount++; Geometry gtemp = geomList.get(count); boolean checkRes = checkIntersectionByGeomTypeB(g2Test,gtemp); if (checkRes){ //-- stop cycle, and take new one first checkNext = false; Geometry gnewI = null; Geometry gnewNi = null; // if (checkRes == 1){ //-- get intersection and difference gnewI = g2Test.intersection(gtemp); //-- note: use difference as we // are only interested in returning parts of the // the this geometry and not the other gnewNi = g2Test.symDifference(gtemp); // } // else if (checkRes == 2){ // gnewI = gtemp.intersection(g2Test); // gnewNi = gtemp.symDifference(g2Test); // } //explode multi-geoms ArrayList<Geometry> intersection = GeometryConverter.explodeGeomsIfMultiG(gnewI); ArrayList<Geometry> nonIntersection = GeometryConverter.explodeGeomsIfMultiG(gnewNi); //-- remove the item (first the latter one, then the others) geomList.remove(count); geomList.remove(0); // System.out.println("size now:" + geomList.size()); //-- add instead the parts geomList.addAll(intersection); geomList.addAll(nonIntersection); //-- testing stuff // System.out.println("size new:" + geomList.size()); // System.out.println("tested g1: " + g2Test.getClass() + " g2: " + gtemp.getClass()); // checkIntersectionByGeomTypeB(g2Test,gtemp); //for checking again // FeatureCollection fc = FeatureDatasetFactory.createFromGeometry(geomList); // ArrayList<Geometry> tointersect = new ArrayList<Geometry>(); // tointersect.add(g2Test); tointersect.add(gtemp); // FeatureCollection fc2 = FeatureDatasetFactory.createFromGeometry(tointersect); // context.addLayer(StandardCategoryNames.WORKING, "tointersect", fc2); // context.addLayer(StandardCategoryNames.WORKING, "loop", fc); } if(((count+1) == geomList.size()) && (checkNext == true)){ //-- nothing more to test // so we can add this geometry to the finalList //System.out.println("added"); withoutIntersection.add((Geometry)g2Test.clone()); //-- we remove it from the list of items geomList.remove(0); checkNext = false; } // if(totalCount == 1000000){ // checkNext = false; // //System.out.println("stopped"); // } } } //-- add the last (or only) one withoutIntersection.add(geomList.get(0)); return withoutIntersection; }*/ /** * the method intersects all polygons in the geometry list with each other. An intersection is only * proceeded if it returns another polygon (i.e. the intersection area > 0). Unfortunately the method * returns results where some polygons may contain spikes. For this reason it may be better to create an * an intersection of Linestrings (derived from the Polygons) and then use the Polygonizer (see IntersectPolygonLayersPlugIn). * * @param geomList list of geometries to process * @param accurracy this parameter is currently not used and replaced by the use of a fixed precision model * @param monitor can be null * @param context can be null * @return an ArrayList of processed geometries */ public static ArrayList<Geometry> intersectPolygons(ArrayList<Geometry> geomList, double accurracy, TaskMonitor monitor, PlugInContext context){ ArrayList<Geometry> withoutIntersection = new ArrayList<Geometry>(); //-- resolve all GeometryCollections/Multigeometries ArrayList<Geometry> tempList = new ArrayList(); for (int i = 0; i < geomList.size(); i++) { Geometry geom = geomList.get(i); if (geom instanceof GeometryCollection){ ArrayList<Geometry> parts = GeometryConverter.explodeGeomsIfMultiG(geom); tempList.addAll(parts); } else{ tempList.add(geom); } } geomList = tempList; //-- use a fixed precision model //double scaleFactor = (1.0/accurracy)/10.0; //devide additionally by 10 to remove one digit more //PrecisionModel pm = new PrecisionModel(scaleFactor); PrecisionModel pm = new PrecisionModel(PrecisionModel.FIXED); GeometryFactory gf = new GeometryFactory(pm); tempList = new ArrayList(); for (Iterator iterator = geomList.iterator(); iterator.hasNext();) { Geometry geomOld = (Geometry) iterator.next(); Geometry geomNew = createGeometryWithFixedPrecision(gf,geomOld); if (geomNew != null){ tempList.add(geomNew); } else{ //-- bad luck for geometry collections, we leave them out } } geomList = tempList; //-- note: no assumption that objects in one layer have contain overlaps int totalCount = 0; while(geomList.size() > 1){ //-- take alsways the first item until there is not more left Geometry g2Test = geomList.get(0); //--check against all others // this is very time consuming, but necessary if (monitor != null){ if (monitor.isCancelRequested()){ return withoutIntersection; } else{ monitor.report("n: " + geomList.size()); } } //-- avoid that already GeomCollections are inside while (g2Test instanceof GeometryCollection){ ArrayList<Geometry> parts = GeometryConverter.explodeGeomsIfMultiG(g2Test); geomList.addAll(parts); geomList.remove(0); g2Test = geomList.get(0); } //--start with the checks boolean checkNext = true; int count = 0; while(checkNext) { count++; totalCount++; Geometry gtemp = geomList.get(count); boolean checkRes = checkPolygonIntersection(g2Test,gtemp); if (checkRes){ //-- stop cycle, and take new one first checkNext = false; Geometry gnewI = null; Geometry gnewNi = null; //-- get intersection and difference gnewI = g2Test.intersection(gtemp); //-- note: use difference as we // are only interested in returning parts of the // the this geometry and not the other gnewNi = g2Test.symDifference(gtemp); //explode multi-geoms ArrayList<Geometry> intersection = GeometryConverter.explodeGeomsIfMultiG(gnewI); ArrayList<Geometry> nonIntersection = GeometryConverter.explodeGeomsIfMultiG(gnewNi); //-- remove the item (first the latter one, then the others) geomList.remove(count); geomList.remove(0); geomList.addAll(intersection); geomList.addAll(nonIntersection); } if(((count+1) == geomList.size()) && (checkNext == true)){ //-- nothing more to test // so we can add this geometry to the finalList withoutIntersection.add((Geometry)g2Test.clone()); //-- we remove it from the list of items geomList.remove(0); checkNext = false; } } } //-- add the last (or only) one withoutIntersection.add(geomList.get(0)); return withoutIntersection; } /** * * @param gf the GeometryFactory that has a specific (fixed) precision (create the PrecisionModel and afterwards the GeometryFactory before hand) * @param geomOld the Geometry is not allowed to be a geometry collection * @return null if input was a geometry collection */ private static Geometry createGeometryWithFixedPrecision( GeometryFactory gf, Geometry geomOld) { if (geomOld instanceof Polygon){ Polygon po = (Polygon)geomOld; LinearRing shell = gf.createLinearRing(po.getExteriorRing().getCoordinates()); LinearRing[] holes = null; if (po.getNumInteriorRing() > 0){ holes = new LinearRing[po.getNumInteriorRing()]; for (int i = 0; i < po.getNumInteriorRing(); i++) { holes[i] = gf.createLinearRing(po.getInteriorRingN(i).getCoordinates()); } } return gf.createPolygon(shell, holes); } else if (geomOld instanceof Point){ return gf.createPoint(((Point)geomOld).getCoordinate()); } else if (geomOld instanceof LineString){ return gf.createLineString(((LineString)geomOld).getCoordinates()); } else{ return null; } } /** * Evaluates if two geometries intersect. E.g. for two polygons it evaluates if * the intersection is an area and not just a line. * <br> * This method is used since <i>area1.intersection(area2)</i> does return also a positive result * if the intersection is on the boundary (i.e. no intersection of the interior). * <br> * Note1: the order of how the geometries are provided matters! It tested such that * <i>g1.SpatialCritearia(g2)</i>. * <br> * NOTE 2: the function has not been checked for mixed * geometry types * * @param g1 first geometry * @param g2 second geometry * @return true if more a geometry is split in at least 2 parts * * TODO : enable check of mixed geometries. */ public static boolean checkIntersectionByGeomTypeB(Geometry g1, Geometry g2){ //-- g1 is Polygon if (g1 instanceof Polygon){ Geometry intersection = g1.intersection(g2); if(g2 instanceof Polygon){ Geometry diff = g1.difference(g2); if (intersection.getArea() > 0){ return true; } else{ return false; } } else if(g2 instanceof LineString){ Geometry diff = g1.difference(g2); if (diff instanceof GeometryCollection){ if (((GeometryCollection)diff).getNumGeometries() > 1){ return true; } else{ return false; } } else{ return false; } } else{//could be geometry collection or point if (g1.intersects(g2)){ return true; } else{ return false; } } } //-- g1 is LineString else if (g1 instanceof LineString){ if(g2 instanceof Polygon){ Geometry diff = g2.difference(g1); if (diff instanceof GeometryCollection){ if (((GeometryCollection)diff).getNumGeometries() > 1){ return true; } else{ return false; } } else{ return false; } } else if(g2 instanceof LineString){ Geometry diff = g1.difference(g2); if (diff instanceof GeometryCollection){ if (((GeometryCollection)diff).getNumGeometries() > 1){ return true; } else{ return false; } } else{ return false; } } else if(g2 instanceof Point){ Geometry diff = g1.difference(g2); if (diff instanceof GeometryCollection){ if (((GeometryCollection)diff).getNumGeometries() > 1){ return true; } else{ return false; } } else{ return false; } } else{//could be geometry collection if (g1.intersects(g2)){ return true; } else{ return false; } } } else if (g1 instanceof Point){ if (g2 instanceof LineString){ //we don't want to test points with anything else //as the intersection can only be a point //but we may want to to test linestrings against points //as this may result in two resulting linestrings Geometry diff = g2.difference(g2); if (((GeometryCollection)diff).getNumGeometries() > 1){ return true; } else{ return false; } } else{ return false; } } //-- otherwise else{//could be geometry collection if (g1.intersects(g2)){ return true; } else{ return false; } } } /** * Evaluates if two polygon geometries intersect. E.g. for two polygons it evaluates if * the intersection is an area and not just a line. * <br> * This method is used since <i>area1.intersection(area2)</i> does return also a positive result * if the intersection is on the boundary (i.e. no intersection of the interior). * <br> * Note1: the order of how the geometries are provided matters! It tested such that * <i>g1.SpatialCritearia(g2)</i>. * <br> * NOTE 2: the function has not been checked for mixed geometry types * * @param g1 first geometry * @param g2 second geometry * @return true if area(intersection) > 0 * * TODO : enable check of mixed geometries. */ public static boolean checkPolygonIntersection(Geometry g1, Geometry g2){ //-- g1 is Polygon if (g1 instanceof Polygon){ Geometry intersection = g1.intersection(g2); if(g2 instanceof Polygon){ Geometry diff = g1.difference(g2); if (intersection.getArea() > 0){ return true; } else{ return false; } } else{ return false; } } else{ return false; } } /** * Nodes a collection of linestrings. * Noding is done via JTS union, which is reasonably effective but * may exhibit robustness failures. \n * note: the method is taken from PolygonizerPlugIn * * @param lines the linear geometries to node * @return a collection of linear geometries, noded together */ public static Collection nodeLines(Collection lines) { GeometryFactory fact = new GeometryFactory(); Geometry linesGeom = fact.createMultiLineString(fact.toLineStringArray(lines)); Geometry unionInput = fact.createMultiLineString(null); // force the unionInput to be non-empty if possible, to ensure union is not optimized away Geometry point = extractPoint(lines); if (point != null) unionInput = point; Geometry noded = linesGeom.union(unionInput); List nodedList = new ArrayList(); nodedList.add(noded); return nodedList; } private static Geometry extractPoint(Collection lines) { int minPts = Integer.MAX_VALUE; Geometry point = null; // extract first point from first non-empty geometry for (Iterator i = lines.iterator(); i.hasNext(); ) { Geometry g = (Geometry) i.next(); if (! g.isEmpty()) { Coordinate p = g.getCoordinate(); point = g.getFactory().createPoint(p); } } return point; } }