Statistics
| Revision:

root / branches / v2_0_0_prep / extensions / extEditing / src / org / gvsig / editing / gui / cad / tools / split / SplitGraph.java @ 29685

History | View | Annotate | Download (9.77 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 org.gvsig.editing.gui.cad.tools.split;
22

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

    
26
import org.slf4j.LoggerFactory;
27

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

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

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

    
72
    private final Polygon            polygon;
73

    
74
    private final LineString         splitter;
75

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
259
}