Statistics
| Revision:

svn-gvsig-desktop / branches / v2_0_0_prep / extensions / extEditing / src / com / iver / cit / gvsig / gui / cad / tools / split / SplitGraph.java @ 28857

History | View | Annotate | Download (9.8 KB)

1
/* Spatial Operations & Editing Tools for uDig
2
 *
3
 * Axios Engineering under a funding contract with:
4
 *      Diputación Foral de Gipuzkoa, Ordenación Territorial
5
 *
6
 *      http://b5m.gipuzkoa.net
7
 *      http://www.axios.es
8
 *
9
 * (C) 2006, Diputación Foral de Gipuzkoa, Ordenación Territorial (DFG-OT).
10
 * DFG-OT agrees to licence under Lesser General Public License (LGPL).
11
 *
12
 * You can redistribute it and/or modify it under the terms of the
13
 * GNU Lesser General Public License as published by the Free Software
14
 * Foundation; version 2.1 of the License.
15
 *
16
 * This library is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19
 * Lesser General Public License for more details.
20
 */
21
package com.iver.cit.gvsig.gui.cad.tools.split;
22

    
23
import java.util.ArrayList;
24
import java.util.List;
25

    
26
import org.slf4j.Logger;
27
import org.slf4j.LoggerFactory;
28

    
29
import com.vividsolutions.jts.geom.Coordinate;
30
import com.vividsolutions.jts.geom.CoordinateArrays;
31
import com.vividsolutions.jts.geom.Geometry;
32
import com.vividsolutions.jts.geom.GeometryFactory;
33
import com.vividsolutions.jts.geom.LineString;
34
import com.vividsolutions.jts.geom.Location;
35
import com.vividsolutions.jts.geom.Point;
36
import com.vividsolutions.jts.geom.Polygon;
37
import com.vividsolutions.jts.geomgraph.DirectedEdge;
38
import com.vividsolutions.jts.geomgraph.Edge;
39
import com.vividsolutions.jts.geomgraph.Label;
40
import com.vividsolutions.jts.geomgraph.NodeFactory;
41
import com.vividsolutions.jts.geomgraph.PlanarGraph;
42
import com.vividsolutions.jts.geomgraph.Position;
43
import com.vividsolutions.jts.geomgraph.Quadrant;
44

    
45
/**
46
 * A {@link PlanarGraph} that builds itself from a {@link Polygon} and a
47
 * {@link LineString splitting line}.
48
 * <p>
49
 * The resulting graph will have the following characteristics:
50
 * <ul>
51
 * <li>It will contain as many edges as linestrings in the boundary of the intersection geometry
52
 * between the polygon and the splitting line string.
53
 * <li>All edges will be labelled {@link Location#BOUNDARY} at {@link Position#ON}</li>
54
 * <li>The edges from the polygon's exterior ring will be labelled {@link Location#EXTERIOR} at the
55
 * {@link Position#LEFT}, {@link Location#INTERIOR} at {@link Position#RIGHT}</li>
56
 * <li>The edges from the polygon's holes will be labelled {@link Location#INTERIOR} at the
57
 * {@link Position#LEFT}, {@link Location#EXTERIOR} at {@link Position#RIGHT}</li>
58
 * </ul>
59
 * <p>
60
 * Note the provided polygon may result modified as the result of {@link Polygon#normalize()},
61
 * which is called in order to ensure propper orientation of the shell and holes.
62
 * </p>
63
 * </p>
64
 *
65
 * @author Gabriel Roldán, Axios Engineering
66
 * @author Mauricio Pazos, Axios Engineering
67
 * @since 1.1.0
68
 */
69
class SplitGraph extends PlanarGraph {
70

    
71
    private static final NodeFactory NODE_FACTORY = new SplitGraphNodeFactory();
72

    
73
    private final Polygon            polygon;
74

    
75
    private final LineString         splitter;
76

    
77
    public SplitGraph( Polygon polygon, LineString splitter ) {
78
        super(NODE_FACTORY);
79
        this.polygon = polygon;
80
        // after normalize() we know the shell is oriented CW and the holes CCW
81
        this.polygon.normalize();
82

    
83
        this.splitter = normalizeSplitter(splitter);
84
        buildGraph();
85
    }
86

    
87
    private LineString normalizeSplitter( final LineString original ) {
88
        // ensure the splitter has no endpoints lying inside the polygon
89
        LineString splitter = removeInteriorEndPoints(polygon, original);
90
        // endure the splitter is directed clockwise, as its going
91
        // to become part of the shell boundary when the tesselation
92
        // process eliminates other shell edges from the graph
93
        Coordinate[] splitterCoords = splitter.getCoordinates();
94
        Coordinate coord0 = splitterCoords[0];
95
        Coordinate coord1 = splitterCoords[1];
96

    
97
        // determine the orientation of the coordinate sequence given the
98
        // quadrant of its first vector
99
        // 1 | 0
100
        // --+--
101
        // 2 | 3
102
        final int quadrant = Quadrant.quadrant(coord0, coord1);
103
        boolean isCounterClockWise = 1 == quadrant || 2 == quadrant;
104
        if (isCounterClockWise) {
105
            CoordinateArrays.reverse(splitterCoords);
106
            GeometryFactory gf = original.getFactory();
107
            splitter = gf.createLineString(splitterCoords);
108
        }
109
        return splitter;
110
    }
111

    
112
    /**
113
     * Removes the given edge and its related {@link DirectedEdge}'s from this graph
114
     *
115
     * @param edge the edge to remove
116
     * @throws IllegalArgumentException if no enclosing DirectedEdge is found for <code>edge</code>
117
     * @see #remove(DirectedEdge)
118
     */
119
    public void remove( final SplitEdge edge ) {
120
        DirectedEdge edgeEnd = (DirectedEdge) findEdgeEnd(edge);
121
        if (edgeEnd == null) {
122
            throw new IllegalArgumentException("No enclosing edge end found for " + edge);
123
        }
124
        remove(edgeEnd);
125
    }
126

    
127
    /**
128
     * Removes a DirectedEdge, its sym and its {@link SplitEdge} from this graph. May lead to the
129
     * graph containing dangling nodes.
130
     *
131
     * @param edgeEnd
132
     */
133
    public void remove( final DirectedEdge edgeEnd ) {
134
        if (edgeEnd == null) {
135
            throw new NullPointerException();
136
        }
137
        if (edgeEndList.remove(edgeEnd)) {
138
            DirectedEdge sym = edgeEnd.getSym();
139
            edgeEndList.remove(sym);
140

    
141
            // shared edge between both ends
142
            Edge edge = edgeEnd.getEdge();
143
            edges.remove(edge);
144

    
145
            // node of directed edge end
146
            SplitGraphNode node = (SplitGraphNode) edgeEnd.getNode();
147
            // node of symetric directed edge end
148
            SplitGraphNode endNode = (SplitGraphNode) sym.getNode();
149
            node.remove(edgeEnd);
150
            endNode.remove(sym);
151
        }
152
    }
153

    
154
    /**
155
     * Builds a linestrnig from splitter such that it contains no endpoints lying inside the polygon
156
     *
157
     * @param polygon
158
     * @param splitter
159
     * @return
160
     */
161
    private LineString removeInteriorEndPoints( Polygon polygon, LineString splitter ) {
162
        final Coordinate[] coords = splitter.getCoordinates();
163
        final GeometryFactory gf = splitter.getFactory();
164
        int useFrom;
165
        int useTo;
166

    
167
        for( useFrom = 0; useFrom < coords.length; useFrom++ ) {
168
            Point p = gf.createPoint(coords[useFrom]);
169
            if (!polygon.contains(p)) {
170
                break;
171
            }
172
        }
173
        for( useTo = coords.length - 1; useTo >= useFrom; useTo-- ) {
174
            Point p = gf.createPoint(coords[useTo]);
175
            if (!polygon.contains(p)) {
176
                break;
177
            }
178
        }
179

    
180
        if (useFrom == useTo) {
181
            throw new IllegalArgumentException("Line lies completely inside polygon");
182
        }
183

    
184
        int length = 1 + (useTo - useFrom);
185
        Coordinate[] crossingLineCoords = new Coordinate[length];
186
        System.arraycopy(coords, useFrom, crossingLineCoords, 0, length);
187
        LineString surelyCrossingLine = gf.createLineString(crossingLineCoords);
188
        return surelyCrossingLine;
189
    }
190

    
191
    /**
192
     * <pre>
193
     * <code>
194
     *
195
     *                  +----------o-----------+
196
     *                  |          |           |
197
     *                  |          |           |
198
     *                  |    +-----------+     |
199
     *                  |    |     |     |     |
200
     *                  |    |     |     |     |
201
     *                  |    |     |     |     |
202
     *                  |    o__\__o_____|     |
203
     *                  |       /  |           |
204
     *                 /|\        /|\          |
205
     *                  o__________o___________|
206
     *
207
     *
208
     * </code>
209
     * </pre>
210
     *
211
     * @throws Exception
212
     */
213
    private void buildGraph() {
214
        Geometry intersectingLineStrings = polygon.intersection(splitter);
215
        Geometry nodedShell = polygon.getExteriorRing().difference(splitter);
216

    
217
        LineString[] interiorRings = new LineString[polygon.getNumInteriorRing()];
218
        for( int i = 0; i < polygon.getNumInteriorRing(); i++ ) {
219
            LineString interiorRingN = polygon.getInteriorRingN(i);
220
            interiorRings[i] = interiorRingN;
221
        }
222
        GeometryFactory factory = polygon.getFactory();
223
        Geometry interiorRingCollection = factory.createMultiLineString(interiorRings);
224
        Geometry nodedHoles = interiorRingCollection.difference(splitter);
225

    
226
        // shell segments oriented CW means exterior at the left, interior at
227
        // the right
228
        addEdges(nodedShell, Location.BOUNDARY, Location.EXTERIOR, Location.INTERIOR);
229
        // hole segments oriented CCW means interior at the left, exterior at
230
        // the right
231
        addEdges(nodedHoles, Location.BOUNDARY, Location.INTERIOR, Location.EXTERIOR);
232
        // splitter intersection segments have interior location at both left
233
        // and right
234
        addEdges(intersectingLineStrings, Location.BOUNDARY, Location.INTERIOR, Location.INTERIOR);
235
    }
236

    
237
    private void addEdges( Geometry linearGeom, int onLoc, int leftLoc, int rightLoc ) {
238
        final int nParts = linearGeom.getNumGeometries();
239
        Geometry currGeom;
240
        Coordinate[] coords;
241
        List edges = new ArrayList();
242
        for( int i = 0; i < nParts; i++ ) {
243
            currGeom = linearGeom.getGeometryN(i);
244
            coords = currGeom.getCoordinates();
245
            if (coords.length<2)
246
                    continue;
247
            Label label = new Label(onLoc, leftLoc, rightLoc);
248
            Edge edge = new SplitEdge(coords, label);
249
            edges.add(edge);
250
        }
251
        // for each edge in the list, adds two DirectedEdge, one reflecting
252
        // the given edge and other the opposite
253
        try{
254
                super.addEdges(edges);
255
        }catch (Exception e) {
256
                        LoggerFactory.getLogger(this.getClass()).error(e.getLocalizedMessage(),e);
257
                }
258
    }
259

    
260
}