Statistics
| Revision:

root / trunk / libraries / libTopology / src / org / gvsig / jts / GeometrySnapper.java @ 13812

History | View | Annotate | Download (9.17 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
 *
49
 */
50
package org.gvsig.jts;
51

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

    
55
import com.iver.cit.gvsig.util.SnappingCoordinateMap;
56
import com.vividsolutions.jts.algorithms.SnapCGAlgorithms;
57
import com.vividsolutions.jts.geom.Coordinate;
58
import com.vividsolutions.jts.geom.CoordinateList;
59
import com.vividsolutions.jts.geom.Geometry;
60
import com.vividsolutions.jts.geom.GeometryFactory;
61
import com.vividsolutions.jts.geom.LineString;
62
import com.vividsolutions.jts.geom.LinearRing;
63
import com.vividsolutions.jts.geom.Point;
64
import com.vividsolutions.jts.geom.Polygon;
65

    
66
/**
67
 * Moves coordinates of geometries (in a given tolerance) 
68
 * to force them to coincide with coordinates of other geometries 
69
 * in its proximity (the snap tolerance).
70
 * 
71
 * Also, it could normalize coordinates of the same geometry. By normalize
72
 * we mean to force the coherence of isClosed method and coordinates.
73
 * (for example, LINESTRING(100 100, 200 100, 200 200, 100 200, 100 100.01)
74
 *  return 'true' to isClosed call. After normalize the last coordinate
75
 *  will be (100 100))
76
 * 
77
 */
78
public class GeometrySnapper {
79

    
80
        private double snapTolerance;
81
        
82
        private GeometryFactory geomFactory = new GeometryFactory();
83

    
84
        
85
        public GeometrySnapper(double snapTolerance) {
86
                this.snapTolerance = snapTolerance;
87
        }
88
        
89
        /**
90
         * Utility class for compute coordinate average
91
         * */
92
        class SnapCoordinate {
93
                int weight;
94
                Coordinate coordinate;
95
        }
96

    
97
        /**
98
         * Snaps the coordinates of many geometries with the same weight
99
         * (all coordinates must move)
100
         * @param geoms
101
         * @return
102
         */
103
        public Geometry[] snap(Geometry[] geoms){
104
                int weight[] = new int[geoms.length];
105
                for(int i = 0; i < geoms.length; i++){
106
                        weight[i] = 1;
107
                }
108
                return snap(geoms, weight);
109
        }
110
        
111
        /**
112
         * Snaps all the coordinates of the given geometries
113
         * .
114
         * If many coords are in the same distance tolerance radius, it applies a weighted
115
         * average. If one geometry's weight is 0, it wont participate in the average.
116
         * 
117
         * @param geoms geometries whose coordinates we want to snap
118
         * @param weights weight of each geometry in the computation of the weighted average
119
         * 
120
         * */
121
        public Geometry[] snap(Geometry[] geoms, int[] weights) {
122
                SnappingCoordinateMap coordinateMap = new SnappingCoordinateMap(snapTolerance);
123
                
124
                Geometry[] solution = new Geometry[geoms.length];
125
                for(int i = 0; i < geoms.length; i++){
126
                        Geometry geom = geoms[i];
127
                        Coordinate[] coords = geom.getCoordinates();
128
                        CoordinateList coordList = new CoordinateList(coords);
129
                        
130
                        for(int j = 0; j < coords.length; j++){
131
                                Coordinate coord = coords[j];
132
                                Coordinate existingSolution = (Coordinate) coordinateMap.get(coord);
133
                                if(coordinateMap.get(coord) != null){//this coordinate has already been snapped
134
                                        coordList.set(j, existingSolution);
135
                                        continue;
136
                                }
137
                                
138
                                ArrayList coordsToSnap = new ArrayList();
139
                                SnapCoordinate snapCoord = new SnapCoordinate();
140
                                snapCoord.weight = weights[i];
141
                                snapCoord.coordinate = coord;
142
                                coordsToSnap.add(snapCoord);
143
                                
144
                                for (int k = 0; k < geoms.length; k++){
145
                                        Geometry geom2 = geoms[k];
146
                                        if(i == k)
147
                                                continue;
148
                                        Coordinate[] coords2 = geom2.getCoordinates();
149
                                        for( int z = 0; z < coords2.length; z++){
150
                                                Coordinate coord2 = coords2[z];
151
                                                if(SnapCGAlgorithms.snapEquals2D(coord, coord2, snapTolerance)){
152
                                                        snapCoord = new SnapCoordinate();
153
                                                        snapCoord.weight = weights[k];
154
                                                        snapCoord.coordinate = coord2;
155
                                                        coordsToSnap.add(snapCoord);
156
                                                }//if
157
                                        }//for z
158
                                }//for k
159
                                
160
                                Coordinate newCoord = snapAverage(coordsToSnap);
161
                                coordList.set(j, newCoord);
162
                                coordinateMap.put(newCoord, newCoord);
163
                        }//for j
164
                        
165
                        Coordinate[] newPts = coordList.toCoordinateArray();
166
                        solution[i] = createGeometry(newPts, geom);
167
                }//for i
168
                return solution;
169
        }
170
        
171
        /**
172
         * Creates a geometry of the same type as geom with newPts coordinates.
173
         * geom mustn be a GeometryCollection (MultiPolygon, etc.)
174
         * @param newPts
175
         * @param geom
176
         * @return
177
         */
178
        
179
        //TODO See how to manage the snap of polygon holes
180
        
181
        private Geometry createGeometry(Coordinate[] newPts, Geometry geom) {
182
                Geometry solution = null;
183
                if(geom instanceof Point){
184
                        if(newPts.length == 1)
185
                                return geomFactory.createPoint(newPts[0]);
186
                        else{
187
                                solution = geomFactory.createMultiPoint(newPts);
188
                        }
189
                }else if(geom instanceof LineString){
190
                        solution = geomFactory.createLineString(newPts);
191
                }else if(geom instanceof Polygon){
192
                        LinearRing linearRing = geomFactory.createLinearRing(newPts);
193
                        solution =  geomFactory.createPolygon(linearRing, null);
194
                }
195
                return solution;
196
        }
197

    
198
        public Coordinate snapAverage(List coordinates, int[] weights){
199
                List snapList = new ArrayList();
200
                for(int i = 0; i < coordinates.size(); i++){
201
                        Coordinate coord = (Coordinate) coordinates.get(i);
202
                        int weight = weights[i];
203
                        SnapCoordinate snap = new SnapCoordinate();
204
                        snap.weight = weight;
205
                        snap.coordinate = coord;
206
                        
207
                        snapList.add(snap);
208
                }
209
                
210
                return snapAverage(snapList);
211
        }
212
        
213
        
214
        private Coordinate snapAverage(List coordsToSnap){
215
                
216
                if(coordsToSnap.size() == 1)
217
                {
218
                        return ((SnapCoordinate)coordsToSnap.get(0)).coordinate;
219
                }
220
                
221
                double sumX = 0d;
222
                double sumY = 0d;
223
                double sumW = 0d;
224
                
225
                for(int i = 0; i < coordsToSnap.size(); i++){
226
                        SnapCoordinate snapCoord = (SnapCoordinate) coordsToSnap.get(i);
227
                        Coordinate coord = snapCoord.coordinate;
228
                        int weight = snapCoord.weight;
229
                        if(weight == 0)
230
                                continue;
231
                        
232
                        double weightD = 1d / (double)weight;
233
                        sumX += weightD * coord.x;
234
                        sumY += weightD * coord.y;
235
                        sumW += weightD;
236
                }
237
                
238
                double newX = sumX / sumW;
239
                double newY = sumY / sumW;
240
                
241
                return new Coordinate(newX, newY);
242
        }
243
                
244
        
245
        public boolean isClosed(Geometry a) {
246
                Coordinate[] coords = a.getCoordinates();
247
                Coordinate firstPoint = coords[0];
248
                Coordinate lastPoint = coords[coords.length - 1];
249
                return SnapCGAlgorithms.snapEquals2D(firstPoint, lastPoint,
250
                                snapTolerance);
251
        }
252
        
253
        /**
254
         * If geometry 'a' is not exactly closed, but its extremes are 
255
         * in a snap radius, its snap the last point to the first point of the
256
         * geometry
257
         * @param a
258
         * @return
259
         */
260
        public Geometry normalizeExtremes(Geometry a){
261
                Coordinate[] coords = a.getCoordinates();
262
                Coordinate firstPoint = coords[0];
263
                Coordinate lastPoint = coords[coords.length - 1];
264
                if(!firstPoint.equals2D(lastPoint) && isClosed(a)){
265
                    CoordinateList coordList = new CoordinateList(coords);
266
                    coordList.set(coords.length - 1, coords[0]);
267
                    return createGeometry(coordList.toCoordinateArray(), a);
268
                }else
269
                        return a;
270
        }
271
        
272
        public static boolean isClosed(Geometry a, double snap){
273
                Coordinate[] coords = a.getCoordinates();
274
                Coordinate firstPoint = coords[0];
275
                Coordinate lastPoint = coords[coords.length - 1];
276
                return SnapCGAlgorithms.snapEquals2D(firstPoint, lastPoint, snap);
277
        }
278
        
279
        
280
        /**
281
         * Snaps coordinates in srcPts with coordinates of snapPts.
282
         * This method forces the snap: the points of srcPoints are shifted to
283
         * snap with points in snapPts. No average is computed for coordinates.
284
         * @param srcPts
285
         * @param isClosed
286
         * @param snapPts
287
         * @return
288
         */
289

    
290
        private Coordinate[] snapTo(Coordinate[] srcPts, 
291
                                                        boolean isClosed,
292
                                                        Coordinate[] snapPts) {
293

    
294
                CoordinateList coordList = new CoordinateList(srcPts);
295
                int coordinateCount = coordList.size();
296
                if(isClosed)
297
                        coordinateCount --;
298
                for (int i = 0; i < coordinateCount; i++) {
299
                        Coordinate srcPt = (Coordinate) coordList.get(i);
300
                        Coordinate snapVert = findSnapForVertex(srcPt, snapPts);
301
                        if (snapVert != null) {
302
                                coordList.set(i, new Coordinate(snapVert));
303
                                if (i == 0 && isClosed)
304
                                        coordList.set(coordList.size() - 1,
305
                                                                new Coordinate(snapVert));
306
                        }//if
307
                }//for
308

    
309
                Coordinate[] newPts = coordList.toCoordinateArray();
310
                return newPts;
311
        }
312

    
313
        
314

    
315
        private Coordinate findSnapForVertex(Coordinate pt, Coordinate[] snapPts) {
316
                for (int i = 0; i < snapPts.length; i++) {
317
                        if (pt.equals2D(snapPts[i]))
318
                                return null;
319
                        if(SnapCGAlgorithms.snapEquals2D(pt, snapPts[i], snapTolerance)){
320
                                return snapPts[i];
321
                        }
322
                }
323
                return null;
324
        }
325
}