Statistics
| Revision:

svn-gvsig-desktop / trunk / libraries / libTopology / src / org / gvsig / jts / LineStringSplitter.java @ 19632

History | View | Annotate | Download (11.2 KB)

1
/*
2
 * Created on 07-sep-2007
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
 * Improvements to JTS (robustness problems)
49
 *
50
 *
51
 */
52
package org.gvsig.jts;
53

    
54
import java.util.ArrayList;
55
import java.util.Arrays;
56
import java.util.Collection;
57
import java.util.Collections;
58
import java.util.Comparator;
59
import java.util.HashMap;
60
import java.util.Iterator;
61
import java.util.List;
62

    
63
import com.vividsolutions.jts.geom.Coordinate;
64
import com.vividsolutions.jts.geom.Geometry;
65
import com.vividsolutions.jts.geom.LineString;
66
import com.vividsolutions.jts.geom.Polygon;
67
import com.vividsolutions.jts.linearref.LinearLocation;
68
import com.vividsolutions.jts.operation.polygonize.Polygonizer;
69

    
70
/**
71
 * 
72
 * From a given linestring and a set of "cut" points, split the linestring
73
 * resulting a new linestring between two consecutives (from the start) points.
74
 * 
75
 * Also remove self-intersections (when the linestring has cicles)
76
 * 
77
 * 
78
 * 
79
 * @author azabala
80
 *
81
 */
82
public class LineStringSplitter {
83
        
84
        
85
        /*
86
         * This inner class is repeated in LineCleanVisitor of
87
         * GeoprocessingExtensions and here.
88
         * 
89
         * TODO Remove one (for that, move this class to libFMap)
90
         * 
91
         */
92

    
93
        static class LineIntersection {
94
                Coordinate coordinate;
95

    
96
                double lenght;
97
        }
98

    
99
        private static final double DEFAULT_SNAP_TOLERANCE = 0d;
100

    
101
        
102
        
103
        private static LineString[] splitClosedLineString(LineString lineString,
104
                        Coordinate[] splitPoints){
105
                return splitClosedLineString(lineString, splitPoints,DEFAULT_SNAP_TOLERANCE);
106
        }
107
        
108
        
109
        
110
        private static LineString[] splitClosedLineString(LineString lineString,
111
                        Coordinate[] splitPoints, double snapTolerance) {
112

    
113
                Polygonizer polygonizer = new Polygonizer();
114
                LineString[] lineStrings = splitSimple(lineString, splitPoints); 
115
                
116
                ArrayList<LineString> closedLineStrings = new ArrayList<LineString>();
117
                ArrayList<LineString> unclosedLineStrings = new ArrayList<LineString>();
118
                
119
                for(int i = 0; i < lineStrings.length; i++){
120
                        LineString geom = lineStrings[i];
121
                        if(JtsUtil.isClosed(geom, snapTolerance)){
122
                                closedLineStrings.add(geom);
123
                        }else{
124
                                unclosedLineStrings.add(geom);
125
                        }
126
                }
127
                
128
                //JTS Polygonizer requires a exact coincident in coordinates.
129
                //We try to apply a previous snap
130
                //MUST WE APPLY A COORDINATE CRACKING??
131
                
132
                LineString[] unclosedLS = new LineString[unclosedLineStrings.size()];
133
                unclosedLineStrings.toArray(unclosedLS);
134
                GeometrySnapper snapper = new GeometrySnapper(snapTolerance);
135
                Geometry[] snappedLS = snapper.snap(unclosedLS);
136
                List<Geometry> snappedList = Arrays.asList(snappedLS);
137
                
138
                
139
                polygonizer.add(snappedList);
140
                Collection polygons = polygonizer.getPolygons();
141
                
142
                Iterator polyIt = polygons.iterator();
143
                ArrayList outerRingsList = new ArrayList();
144
                while (polyIt.hasNext()) {
145
                        outerRingsList.add(((Polygon) polyIt.next()).getExteriorRing());
146
                }
147
                
148
                Iterator closedLineStringIt =closedLineStrings.iterator();
149
                while(closedLineStringIt.hasNext()){
150
                        outerRingsList.add(closedLineStringIt.next());
151
                }
152
                
153
                LineString[] solution = new LineString[outerRingsList.size()];
154
                outerRingsList.toArray(solution);
155
                return solution;
156
        }
157
        
158
        
159
        //FIXME Introduce snap tolerance concept
160
        
161
        private static LineString[] splitUnclosedLineString(LineString lineString,
162
                        Coordinate[] splitPoints, double snapTolerance) {
163
                
164
                ArrayList lineStringList = new ArrayList();
165
                RobustLengthIndexedLine lengthLine = new RobustLengthIndexedLine(lineString);
166
                ArrayList<LineIntersection> nodeIntersections = new ArrayList<LineIntersection>();
167
                double linearReferencingIndex = 0d;
168
                for (int i = 0; i < splitPoints.length; i++) {
169
                        Coordinate coord = splitPoints[i];
170
                        double lengthOfNode = lengthLine.indexOfAfter(coord,
171
                                        linearReferencingIndex);
172
                        linearReferencingIndex = lengthOfNode;
173

    
174
                        LineIntersection inters = new LineIntersection();
175
                        inters.coordinate = coord;
176
                        inters.lenght = lengthOfNode;
177
                        nodeIntersections.add(inters);
178
                }// for
179

    
180
                // We sort the intersections by distance along the line
181
                // (dynamic
182
                // segmentation)
183
                Collections.sort(nodeIntersections, new Comparator() {
184
                        public int compare(Object arg0, Object arg1) {
185
                                LineIntersection l1 = (LineIntersection) arg0;
186
                                LineIntersection l2 = (LineIntersection) arg1;
187
                                if (l1.lenght > l2.lenght)
188
                                        return 1;
189
                                else if (l1.lenght < l2.lenght)
190
                                        return -1;
191
                                else
192
                                        return 0;
193
                        }
194
                });
195

    
196
                LinearLocation lastLocation = null;
197
                LineIntersection lastIntersection = null;
198
                RobustLocationIndexedLine indexedLine = new RobustLocationIndexedLine(lineString);
199
                for (int i = 0; i < nodeIntersections.size(); i++) {
200
                        Geometry geometry = null;
201
                        LineIntersection li = (LineIntersection) nodeIntersections.get(i);
202
                        // LinearLocation location = indexedLine.indexOf(li.coordinate);
203
                        LinearLocation location = indexedLine.indexOfAfter(indexedLine,
204
                                        li.coordinate, lastLocation);
205

    
206
                        if (lastLocation == null) {
207
                                LinearLocation from = new LinearLocation(0, 0d);
208
                                /*
209
                                 * we build a linestring with all points between the first point
210
                                 * until the first selfintersection
211
                                 */
212
                                geometry = indexedLine.extractLine(from, location);
213
                                lastLocation = location;
214
                                lastIntersection = li;
215
                        } else {
216
                                /*
217
                                 * Its not the first selfintersection. We build a linestring
218
                                 * with all points between two self intersections
219
                                 */
220
                                LinearLocation locationFrom = lastLocation;
221
                                geometry = indexedLine.extractLine(locationFrom, location);
222
                                lastLocation = location;
223
                                lastIntersection = li;
224

    
225
                        }
226
                        lineStringList.add(geometry);
227
                }// for
228
                LinearLocation endLocation = new LinearLocation();
229
                endLocation.setToEnd(lineString);
230
                lineStringList.add(indexedLine.extractLine(lastLocation, endLocation));
231

    
232
                LineString[] solution = new LineString[lineStringList.size()];
233
                lineStringList.toArray(solution);
234
                return solution;
235
        }
236

    
237
        
238
//        FIXME Introduce snap tolerance concept
239
        
240
        /**
241
         * Splits the specified lineString with the specified points.
242
         * (One point will give two lines, two points three lines, etc
243
         * 
244
         * @param lineString
245
         * @param points
246
         * */
247
        
248
        public static LineString[] splitSimple(LineString lineString, Coordinate[] points) {
249
                LineString[] splittedLS = null;
250
                RobustLengthIndexedLine lengthLine = new RobustLengthIndexedLine(lineString);
251
                /*
252
                 * LenghtIndexedLine indexOfAlter method returns the index of a line greater than the specified index.
253
                 * For this reason, we must save in a cache the last computed index for a given coord, and pass
254
                 * this precomputed index to the indexOfAlter method
255
                 * */
256
                HashMap coordsIndex = new HashMap();
257
                ArrayList nodeIntersections = new ArrayList();
258
                for (int i = 0; i < points.length; i++) {
259
                        Coordinate coord = points[i];
260
                        Double index = (Double) coordsIndex.get(coord);
261
                        double indexD = 0d;
262
                        if(index != null)
263
                                indexD = index.doubleValue();
264
                        double computedIndex = lengthLine.indexOfAfter(coord, indexD);
265
                        coordsIndex.put(coord, new Double(computedIndex));
266
                                        LineIntersection inters = new LineIntersection();
267
                        inters.coordinate = coord;
268
                        inters.lenght = computedIndex;
269
                        nodeIntersections.add(inters);
270
                }// for
271

    
272
                // We sort the intersections by distance along the line
273
                // (dynamic
274
                // segmentation)
275
                Collections.sort(nodeIntersections, new Comparator() {
276
                        public int compare(Object arg0, Object arg1) {
277
                                LineIntersection l1 = (LineIntersection) arg0;
278
                                LineIntersection l2 = (LineIntersection) arg1;
279
                                if (l1.lenght > l2.lenght)
280
                                        return 1;
281
                                else if (l1.lenght < l2.lenght)
282
                                        return -1;
283
                                else
284
                                        return 0;
285
                        }
286
                });
287

    
288
                LinearLocation lastLocation = null;
289
                LineIntersection lastIntersection = null;
290
                RobustLocationIndexedLine indexedLine = new RobustLocationIndexedLine(lineString);
291
                ArrayList splittedLsList = new ArrayList();
292
                for (int i = 0; i < nodeIntersections.size(); i++) {
293
                        Geometry solution = null;
294
                        LineIntersection li = (LineIntersection) nodeIntersections.get(i);
295
                        LinearLocation location = indexedLine.indexOfAfter(indexedLine,
296
                                        li.coordinate, lastLocation);
297
                        if (lastLocation == null) {
298
                                LinearLocation from = new LinearLocation(0, 0d);
299
                                solution = indexedLine.extractLine(from, location);
300
                                lastLocation = location;
301
                                lastIntersection = li;
302

    
303
                        } else {
304
                                LinearLocation locationFrom = lastLocation;
305
                                solution = indexedLine.extractLine(locationFrom, location);
306
                                lastLocation = location;
307
                                lastIntersection = li;
308
                        }
309
                        splittedLsList.add(solution);
310
                }// for
311
                LinearLocation endLocation = new LinearLocation();
312
                endLocation.setToEnd(lineString);
313
                Geometry geo = indexedLine.extractLine(lastLocation, endLocation);
314
                splittedLsList.add(geo);
315
                splittedLS = new LineString[splittedLsList.size()];
316
                splittedLsList.toArray(splittedLS);
317
                
318
                return splittedLS;
319

    
320
        }
321

    
322
        /**
323
         * to propper work, LineStringSplitter needs coordinates to split a line. in
324
         * a self intersected line case, we must to repeat self interesections
325
         * coordinates, because self intersections produce cycles
326
         */
327

    
328
        public static Coordinate[] duplicateSelfIntersections(
329
                        Coordinate[] selfIntersections) {
330
                Coordinate[] solution = new Coordinate[selfIntersections.length * 2];
331
                for (int i = 0; i < selfIntersections.length; i++) {
332
                        solution[2 * i] = selfIntersections[i];
333
                        solution[(2 * i) + 1] = selfIntersections[i];
334
                }
335
                return solution;
336
        }
337
        
338
        
339
//        FIXME Introduce snap tolerance concept
340
        
341

    
342
        public static LineString[] removeSelfIntersections(LineString lineString,
343
                        Coordinate[] splitPoints, double snapTolerance) {
344
                if (splitPoints.length < 1) {
345
                        return new LineString[] { lineString };
346
                }
347
                
348
                Coordinate[] duplicatedSelfIntersections = duplicateSelfIntersections(splitPoints);
349
                //TODO Create a test case for a linestring not closed for precision reasons
350
                //but closed with a given snap tolerance
351
                if(JtsUtil.isClosed(lineString, snapTolerance)){
352
//                if (lineString.isClosed()) {
353
                        return splitClosedLineString(lineString,
354
                                        duplicatedSelfIntersections, snapTolerance);
355
                } else {
356
                        return splitUnclosedLineString(lineString,
357
                                        duplicatedSelfIntersections, snapTolerance);
358
                }
359

    
360
        }
361
        
362
        
363
        public static LineString[] removeSelfIntersections(LineString lineString,
364
                        Coordinate[] splitPoints){
365
                return removeSelfIntersections(lineString, splitPoints, DEFAULT_SNAP_TOLERANCE);
366
        }
367
}