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