Statistics
| Revision:

svn-gvsig-desktop / trunk / libraries / libTopology / src / org / gvsig / topology / topologyrules / jtsisvalidrules / PolygonHolesMustBeInShell.java @ 19327

History | View | Annotate | Download (8.99 KB)

1
/*
2
 * Created on 10-abr-2006
3
 *
4
 * gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
5
 *
6
 * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
7
 *
8
 * This program is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU General Public License
10
 * as published by the Free Software Foundation; either version 2
11
 * of the License, or (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
21
 *
22
 * For more information, contact:
23
 *
24
 *  Generalitat Valenciana
25
 *   Conselleria d'Infraestructures i Transport
26
 *   Av. Blasco Ib??ez, 50
27
 *   46010 VALENCIA
28
 *   SPAIN
29
 *
30
 *      +34 963862235
31
 *   gvsig@gva.es
32
 *      www.gvsig.gva.es
33
 *
34
 *    or
35
 *
36
 *   IVER T.I. S.A
37
 *   Salamanca 50
38
 *   46005 Valencia
39
 *   Spain
40
 *
41
 *   +34 963163400
42
 *   dac@iver.es
43
 */
44
/* CVS MESSAGES:
45
 *
46
 * $Id: 
47
 * $Log: 
48
 */
49
package org.gvsig.topology.topologyrules.jtsisvalidrules;
50

    
51
import java.util.ArrayList;
52
import java.util.List;
53

    
54
import org.gvsig.jts.JtsUtil;
55
import org.gvsig.topology.AbstractTopologyRule;
56
import org.gvsig.topology.Messages;
57
import org.gvsig.topology.Topology;
58
import org.gvsig.topology.TopologyError;
59
import org.gvsig.topology.TopologyRuleDefinitionException;
60
import org.gvsig.topology.topologyrules.JtsValidRule;
61

    
62
import com.hardcode.gdbms.driver.exceptions.ReadDriverException;
63
import com.iver.cit.gvsig.fmap.core.FShape;
64
import com.iver.cit.gvsig.fmap.core.IFeature;
65
import com.iver.cit.gvsig.fmap.core.IGeometry;
66
import com.iver.cit.gvsig.fmap.core.v02.FConverter;
67
import com.iver.cit.gvsig.fmap.layers.FLyrVect;
68
import com.iver.utiles.XMLEntity;
69
import com.vividsolutions.jts.algorithm.PointInRing;
70
import com.vividsolutions.jts.algorithms.SnapCGAlgorithms;
71
import com.vividsolutions.jts.geom.Coordinate;
72
import com.vividsolutions.jts.geom.Geometry;
73
import com.vividsolutions.jts.geom.GeometryCollection;
74
import com.vividsolutions.jts.geom.LinearRing;
75
import com.vividsolutions.jts.geom.MultiPolygon;
76
import com.vividsolutions.jts.geom.Polygon;
77
import com.vividsolutions.jts.geomgraph.Edge;
78
import com.vividsolutions.jts.geomgraph.EdgeIntersectionList;
79
import com.vividsolutions.jts.geomgraph.GeometryGraph;
80
import com.vividsolutions.jts.precision.EnhancedPrecisionOp;
81

    
82
/**
83
 * This rule checks that all polygon holes must be contained in the exterior shell.
84
 * 
85
 * @author Alvaro Zabala
86
 *
87
 */
88
public class PolygonHolesMustBeInShell extends AbstractTopologyRule {
89
        
90
        private double snapTolerance;
91
        
92
        private JtsValidRule parentRule;
93
        
94
        /**
95
         * Implementation of PointInRing that applies snap tolerance
96
         * 
97
         * TODO Move to an external class 
98
         */
99
        private class SnapPointInRing implements PointInRing {
100

    
101
                LinearRing ring;
102
                double snapTolerance;
103
                
104
                
105
                
106
                SnapPointInRing(LinearRing ring, double snapTolerance){
107
                        this.ring = ring;
108
                        this.snapTolerance = snapTolerance;
109
                }
110
                
111
                public boolean isInside(Coordinate pt) {
112
                        return SnapCGAlgorithms.isPointInRing(pt, ring.getCoordinates(), snapTolerance);
113
                }
114
                
115
        }
116
        
117
        public PolygonHolesMustBeInShell(Topology topology, FLyrVect originLyr, double snapTolerance){
118
                super(topology, originLyr);
119
                this.snapTolerance = snapTolerance;
120
        }
121
        
122
        public PolygonHolesMustBeInShell(FLyrVect originLyr, double snapTolerance){
123
                this(null, originLyr, snapTolerance);
124
        }
125
        
126
        public PolygonHolesMustBeInShell(){}
127
        
128
        public String getName() {
129
                return Messages.getText("POLYGON_HOLES_MUST_BE_IN_SHELL");
130
        }
131

    
132
        public void checkPreconditions() throws TopologyRuleDefinitionException {
133
                try {
134
                        int shapeType = this.originLyr.getShapeType();
135
                        if(shapeType == FShape.POLYGON || shapeType == FShape.MULTI)
136
                                throw new TopologyRuleDefinitionException();
137
                } catch (ReadDriverException e) {
138
                        e.printStackTrace();
139
                        throw new TopologyRuleDefinitionException("Error leyendo el tipo de geometria del driver",e);
140
                }        
141
        
142
        
143
        }
144

    
145
        public void validateFeature(IFeature feature) {
146
                Geometry jtsGeo = feature.getGeometry().toJTSGeometry();
147
                if (jtsGeo instanceof Polygon) {
148
                        checkHolesInShell((Polygon)jtsGeo, new GeometryGraph(0, jtsGeo), feature);
149
                } else if (jtsGeo instanceof MultiPolygon) {
150
                        MultiPolygon multiPoly = (MultiPolygon)jtsGeo;
151
                        for(int i = 0; i < multiPoly.getNumGeometries(); i++){
152
                                Polygon polygon = (Polygon) multiPoly.getGeometryN(i);
153
                                checkHolesInShell( polygon, new GeometryGraph(0, polygon), feature);
154
                        }
155
                }else if(jtsGeo instanceof GeometryCollection){
156
                        MultiPolygon multiPoly = JtsUtil.convertIfPossible((GeometryCollection)jtsGeo);
157
                        for(int i = 0; i < multiPoly.getNumGeometries(); i++){
158
                                Polygon polygon = (Polygon) multiPoly.getGeometryN(i);
159
                                checkHolesInShell( polygon, new GeometryGraph(0, polygon), feature);
160
                        }
161
                }
162
        }
163

    
164
        private void checkHolesInShell(Polygon p, GeometryGraph graph, IFeature feature) {
165
                LinearRing shell = (LinearRing) p.getExteriorRing();
166
                Polygon shellAsPoly = null;
167
//                PointInRing pir = null;
168
//                if(snapTolerance == 0d)
169
//                        pir = new MCPointInRing(shell);
170
//                else
171
//                        pir = new SnapPointInRing(shell, snapTolerance);
172

    
173
                for (int i = 0; i < p.getNumInteriorRing(); i++) {
174
                        LinearRing hole = (LinearRing) p.getInteriorRingN(i);
175
                        
176
                        //TODO Probably this wont work if we dont use 
177
                        //libFMap's SnappingGeometryGraph
178
                        List<Coordinate> holePts = findPtNotNode(hole.getCoordinates(), shell,
179
                                        graph);
180
                        /*
181
                         * If no non-node hole vertex can be found, the hole must split the
182
                         * polygon into disconnected interiors. This will be caught by a
183
                         * subsequent check.
184
                         */
185
                        if (holePts == null || holePts.size() == 0)
186
                                return;
187

    
188
                        /*
189
                        List<Coordinate> outsideCoordinates = new ArrayList<Coordinate>();
190
                        for(int j = 0; j < holePts.size(); j++){
191
                                Coordinate coordinate = holePts.get(j);
192
                                boolean outside = !pir.isInside(coordinate);
193
                                if (outside) {
194
                                        outsideCoordinates.add(coordinate);
195
                                }
196
                        }//for
197
                        
198
                        int outsideCoordsSize = outsideCoordinates.size();
199
                        if( outsideCoordsSize > 0){
200
                                Coordinate start = outsideCoordinates.get(0);
201
                                GeneralPathX gpx = new GeneralPathX();
202
                                gpx.moveTo(start.x, start.y);
203
                                for(int j = 1; j < outsideCoordsSize; j++){
204
                                        Coordinate coord = outsideCoordinates.get(j);
205
                                        gpx.lineTo(coord.x, coord.y);
206
                                }
207
                                Coordinate end = outsideCoordinates.get(outsideCoordsSize - 1);
208
                                if(! SnapCGAlgorithms.snapEquals2D(start, end, snapTolerance)){
209
                                        gpx.closePath();
210
                                }
211
                                IGeometry igeo = ShapeFactory.createPolygon2D(gpx);
212
                                IFeature[] features = {feature};
213
                                TopologyError topologyError = new TopologyError(igeo, this, features);
214
                                addTopologyError(topologyError);
215
                        }//if
216
                        */
217
                        
218
                        //At this point hole is not contained in shell
219
                        if(shellAsPoly == null){
220
                                shellAsPoly = JtsUtil.geomFactory.createPolygon(shell, null);
221
                        }
222
                        
223
                        Polygon holeAsPoly = JtsUtil.geomFactory.createPolygon(hole, null);
224
                        Geometry errorGeometry = EnhancedPrecisionOp.difference(shellAsPoly, holeAsPoly);
225
                        IGeometry errorGeo = FConverter.jts_to_igeometry(errorGeometry);
226
                        AbstractTopologyRule violatedRule = null;
227
                        if(this.parentRule != null)
228
                                violatedRule = parentRule;
229
                        else
230
                                violatedRule = this;
231
                        TopologyError topologyError = 
232
                                new TopologyError(errorGeo, violatedRule, feature, topology);
233
                        addTopologyError(topologyError);
234
                        
235
                }//for holes
236
        }
237

    
238
        
239
        //TODO Move to a util class (repeated code)
240
        List<Coordinate> findPtNotNode(Coordinate[] testCoords, 
241
                                                                LinearRing searchRing,
242
                                                                GeometryGraph graph) {
243
                
244
                List<Coordinate> solution = new ArrayList<Coordinate>();
245
                // find edge corresponding to searchRing.
246
                Edge searchEdge = graph.findEdge(searchRing);
247
                // find a point in the testCoords which is not a node of the searchRing
248
                EdgeIntersectionList eiList = searchEdge.getEdgeIntersectionList();
249
                // somewhat inefficient - is there a better way? (Use a node map, for
250
                // instance?)
251
                for (int i = 0; i < testCoords.length; i++) {
252
                        Coordinate pt = testCoords[i];
253
                        if (!eiList.isIntersection(pt))
254
                                solution.add(pt);
255
                }
256
                if(solution.size() == 0)
257
                        return null;
258
                else 
259
                        return solution;
260
        }
261
        
262
        public XMLEntity getXMLEntity(){
263
                XMLEntity xml = super.getXMLEntity();
264
                xml.putProperty("snapTolerance", snapTolerance);
265
                return xml;
266
        }
267
            
268
        public void setXMLEntity(XMLEntity xml){
269
                super.setXMLEntity(xml);
270
                
271
                if(xml.contains("snapTolerance")){
272
                        snapTolerance = xml.getDoubleProperty("snapTolerance");
273
                }
274
        }
275

    
276
        public JtsValidRule getParentRule() {
277
                return parentRule;
278
        }
279

    
280
        public void setParentRule(JtsValidRule parentRule) {
281
                this.parentRule = parentRule;
282
        }
283
}