1 /* 2 * Copyright (c) 2016 Vivid Solutions. 3 * 4 * All rights reserved. This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * and Eclipse Distribution License v. 1.0 which accompanies this distribution. 7 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html 8 * and the Eclipse Distribution License is available at 9 * 10 * http://www.eclipse.org/org/documents/edl-v10.php. 11 */ 12 package org.locationtech.jtslab.clean; 13 14 import java.util.ArrayList; 15 import java.util.List; 16 17 import org.locationtech.jts.geom.Geometry; 18 import org.locationtech.jts.geom.GeometryFactory; 19 import org.locationtech.jts.geom.LinearRing; 20 import org.locationtech.jts.geom.Polygon; 21 import org.locationtech.jts.geom.prep.PreparedGeometry; 22 import org.locationtech.jts.geom.prep.PreparedGeometryFactory; 23 import org.locationtech.jts.geom.util.GeometryMapper; 24 import org.locationtech.jts.geom.util.GeometryMapper.MapOp; 25 26 /** 27 * Removes holes which are invalid due to not being wholly covered by the parent shell. 28 * <p> 29 * Notes: 30 * <ul> 31 * <li>Does not remove holes which are invalid due to touching other rings at more than one point 32 * <li>Does not remove holes which are nested inside another hole 33 * </ul> 34 * 35 * @author Martin Davis 36 * 37 */ 38 public class InvalidHoleRemover { 39 40 /** 41 * Removes invalid holes from the polygons in a geometry. 42 * 43 * @param geom the geometry to clean 44 * @return the geometry with invalid holes removed 45 */ clean(Geometry geom)46 public static Geometry clean(Geometry geom) { 47 InvalidHoleRemover pihr = new InvalidHoleRemover(geom); 48 return pihr.getResult(); 49 } 50 51 private Geometry geom; 52 53 /** 54 * Creates a new invalid hole remover instance. 55 * 56 * @param geom the geometry to process 57 */ InvalidHoleRemover(Geometry geom)58 public InvalidHoleRemover(Geometry geom) { 59 this.geom = geom; 60 } 61 62 /** 63 * Gets the cleaned geometry. 64 * 65 * @return the geometry with invalid holes removed. 66 */ getResult()67 public Geometry getResult() 68 { 69 return GeometryMapper.map(geom, new InvalidHoleRemoverMapOp()); 70 } 71 72 private static class InvalidHoleRemoverMapOp implements MapOp { 73 map(Geometry geom)74 public Geometry map(Geometry geom) { 75 if (geom instanceof Polygon) 76 return PolygonInvalidHoleRemover.clean((Polygon) geom); 77 return geom; 78 } 79 80 } 81 82 private static class PolygonInvalidHoleRemover { 83 clean(Polygon poly)84 public static Polygon clean(Polygon poly) { 85 PolygonInvalidHoleRemover pihr = new PolygonInvalidHoleRemover(poly); 86 return pihr.getResult(); 87 } 88 89 private Polygon poly; 90 PolygonInvalidHoleRemover(Polygon poly)91 public PolygonInvalidHoleRemover(Polygon poly) { 92 this.poly = poly; 93 } 94 getResult()95 public Polygon getResult() 96 { 97 GeometryFactory gf = poly.getFactory(); 98 Polygon shell = gf.createPolygon(poly.getExteriorRing()); 99 PreparedGeometry shellPrep = PreparedGeometryFactory.prepare(shell); 100 101 List holes = new ArrayList(); 102 for (int i = 0; i < poly.getNumInteriorRing(); i++) { 103 LinearRing hole = poly.getInteriorRingN(i); 104 if (shellPrep.covers(hole)) { 105 holes.add(hole); 106 } 107 } 108 // all holes valid, so return original 109 if (holes.size() == poly.getNumInteriorRing()) 110 return poly; 111 112 // return new polygon with covered holes only 113 Polygon result = gf.createPolygon(poly.getExteriorRing(), 114 GeometryFactory.toLinearRingArray(holes)); 115 return result; 116 } 117 118 } 119 } 120