Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.geometry / org.gvsig.fmap.geometry.jts / src / main / java / org / gvsig / fmap / geom / jts / util / OpenJUMPUtils.java @ 47755

History | View | Annotate | Download (10.3 KB)

1
/* gvSIG. Desktop Geographic Information System.
2
 *
3
 * Copyright ? 2007-2015 gvSIG Association
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18
 * MA  02110-1301, USA.
19
 *
20
 * For any additional information, do not hesitate to contact us
21
 * at info AT gvsig.com, or visit our website www.gvsig.com.
22
 */
23
package org.gvsig.fmap.geom.jts.util;
24

    
25
import java.util.ArrayList;
26
import java.util.Collection;
27

    
28
import com.vividsolutions.jts.algorithm.distance.DistanceToPoint;
29
import com.vividsolutions.jts.algorithm.distance.PointPairDistance;
30
import com.vividsolutions.jts.geom.Coordinate;
31
import com.vividsolutions.jts.geom.LineString;
32
import com.vividsolutions.jts.operation.buffer.BufferOp;
33
import com.vividsolutions.jts.operation.buffer.BufferParameters;
34
import com.vividsolutions.jts.operation.linemerge.LineMerger;
35
import org.cresques.cts.IProjection;
36

    
37
import org.gvsig.fmap.geom.Geometry;
38

    
39
/**
40
 * Based on portions of code from OpenJump
41
 *
42
 */
43
public class OpenJUMPUtils {
44

    
45
    /**
46
     * Creates a offset of a open line at a distance by removing segments that do not match the limit of the buffer.
47
     *
48
     * @param proj
49
     * @param coordinates
50
     * @param distance
51
     * @return
52
     */
53
    public static Geometry offsetCleanOpenLine(IProjection proj, ArrayListCoordinateSequence coordinates, double distance) {
54
        com.vividsolutions.jts.geom.GeometryFactory factory = JTSUtils.getFactory(coordinates);
55

    
56
        LineString jtsGeom = JTSUtils.createJTSLineString(proj, coordinates);
57

    
58
        BufferParameters bufParams = JTSUtils.getBufferParameters();
59

    
60
        com.vividsolutions.jts.geom.Geometry sidedBuffer =
61
            new BufferOp(jtsGeom, bufParams).getResultGeometry(distance).getBoundary();
62
        Collection<LineString> offsetSegments = new ArrayList<>();
63
        // Segments located entirely under this distance are excluded
64
        int quadrantSegments = bufParams.getQuadrantSegments();
65
        double lowerBound = Math.abs(distance) * Math.sin(Math.PI / (4 * quadrantSegments));
66
        // Segments located entirely over this distance are included
67
        // note that the theoretical approximation made with quadrantSegments
68
        // is offset*cos(PI/(4*quadrantSegments) but
69
        // offset*cos(PI/(2*quadrantSegments)
70
        // is used to make sure to include segments located on the boundary
71
        double upperBound = Math.abs(distance) * Math.cos(Math.PI / (2 * quadrantSegments));
72
        for (int i = 0; i < sidedBuffer.getNumGeometries(); i++) {
73
            Coordinate[] cc = sidedBuffer.getGeometryN(i).getCoordinates();
74
            PointPairDistance ppd = new PointPairDistance();
75
            DistanceToPoint.computeDistance(jtsGeom, cc[0], ppd);
76
            double dj = ppd.getDistance();
77
            for (int j = 1; j < cc.length; j++) {
78
                double di = dj;
79
                ppd = new PointPairDistance();
80
                DistanceToPoint.computeDistance(jtsGeom, cc[j], ppd);
81
                dj = ppd.getDistance();
82
                // segment along or touching the source geometry : eclude it
83
                if (Math.max(di, dj) < lowerBound || di == 0 || dj == 0) {
84
                    continue;
85
                }
86
                // segment along the buffer boundary : include it
87
                else if (Math.min(di, dj) > upperBound) {
88
                    LineString segment = jtsGeom.getFactory().createLineString(new Coordinate[] { cc[j - 1], cc[j] });
89
                    offsetSegments.add(segment);
90
                }
91
                // segment entirely located inside the buffer : exclude it
92
                else if (Math.min(di, dj) > lowerBound && Math.max(di, dj) < upperBound) {
93
                    continue;
94
                }
95
                // segment with a end at the offset distance and the other
96
                // located within the buffer : divide it
97
                else {
98
                    // One of the coordinates is closed to but not on the source
99
                    // curve and the other is more or less closed to offset
100
                    // distance
101
                    divide(offsetSegments, jtsGeom, cc[j - 1], cc[j], di, dj, lowerBound, upperBound);
102
                }
103
            }
104
        }
105
        Collection<LineString> offsetCurves = new ArrayList<LineString>();
106
        offsetCurves.addAll(merge(offsetSegments));
107

    
108
        return JTSUtils.createGeometry(proj, factory.buildGeometry(offsetCurves));
109
    }
110
    
111
    /**
112
     * Creates a offset of a open line at a distance by removing segments that do not match the limit of the buffer.
113
     *
114
     * @param proj
115
     * @param coordinates
116
     * @param bufParams
117
     * @param distance
118
     * @return
119
     */
120
    public static Geometry offsetCleanOpenLine(IProjection proj, ArrayListCoordinateSequence coordinates, BufferParameters bufParams, double distance) {
121
        com.vividsolutions.jts.geom.GeometryFactory factory = JTSUtils.getFactory(coordinates);
122

    
123
        LineString jtsGeom = JTSUtils.createJTSLineString(proj, coordinates);
124

    
125
//        BufferParameters bufParams = JTSUtils.getBufferParameters();
126

    
127
        com.vividsolutions.jts.geom.Geometry sidedBuffer =
128
            new BufferOp(jtsGeom, bufParams).getResultGeometry(distance).getBoundary();
129
        Collection<LineString> offsetSegments = new ArrayList<>();
130
        // Segments located entirely under this distance are excluded
131
        int quadrantSegments = bufParams.getQuadrantSegments();
132
        double lowerBound = Math.abs(distance) * Math.sin(Math.PI / (4 * quadrantSegments));
133
        // Segments located entirely over this distance are included
134
        // note that the theoretical approximation made with quadrantSegments
135
        // is offset*cos(PI/(4*quadrantSegments) but
136
        // offset*cos(PI/(2*quadrantSegments)
137
        // is used to make sure to include segments located on the boundary
138
        double upperBound = Math.abs(distance) * Math.cos(Math.PI / (2 * quadrantSegments));
139
        for (int i = 0; i < sidedBuffer.getNumGeometries(); i++) {
140
            Coordinate[] cc = sidedBuffer.getGeometryN(i).getCoordinates();
141
            PointPairDistance ppd = new PointPairDistance();
142
            DistanceToPoint.computeDistance(jtsGeom, cc[0], ppd);
143
            double dj = ppd.getDistance();
144
            for (int j = 1; j < cc.length; j++) {
145
                double di = dj;
146
                ppd = new PointPairDistance();
147
                DistanceToPoint.computeDistance(jtsGeom, cc[j], ppd);
148
                dj = ppd.getDistance();
149
                // segment along or touching the source geometry : eclude it
150
                if (Math.max(di, dj) < lowerBound || di == 0 || dj == 0) {
151
                    continue;
152
                }
153
                // segment along the buffer boundary : include it
154
                else if (Math.min(di, dj) > upperBound) {
155
                    LineString segment = jtsGeom.getFactory().createLineString(new Coordinate[] { cc[j - 1], cc[j] });
156
                    offsetSegments.add(segment);
157
                }
158
                // segment entirely located inside the buffer : exclude it
159
                else if (Math.min(di, dj) > lowerBound && Math.max(di, dj) < upperBound) {
160
                    continue;
161
                }
162
                // segment with a end at the offset distance and the other
163
                // located within the buffer : divide it
164
                else {
165
                    // One of the coordinates is closed to but not on the source
166
                    // curve and the other is more or less closed to offset
167
                    // distance
168
                    divide(offsetSegments, jtsGeom, cc[j - 1], cc[j], di, dj, lowerBound, upperBound);
169
                }
170
            }
171
        }
172
        Collection<LineString> offsetCurves = new ArrayList<LineString>();
173
        offsetCurves.addAll(merge(offsetSegments));
174

    
175
        return JTSUtils.createGeometry(proj, factory.buildGeometry(offsetCurves));
176
    }
177

    
178
    /**
179
     * Recursive function to split segments located on the single-side buffer
180
     * boundary, but having a part of them inside the full buffer.
181
     *
182
     * @param offsetSegments
183
     * @param sourceCurve
184
     * @param c1
185
     * @param c2
186
     * @param d1
187
     * @param d2
188
     * @param lb
189
     * @param ub
190
     */
191
    private static void divide(Collection<LineString> offsetSegments, com.vividsolutions.jts.geom.Geometry sourceCurve,
192
            Coordinate c1, Coordinate c2, double d1, double d2, double lb, double ub) {
193
        // I stop recursion for segment < 2*lb to exclude small segments
194
        // perpendicular but very close to the boundary
195
        if (c1.distance(c2) < 2*lb) return;
196

    
197
        Coordinate c = new Coordinate((c1.x+c2.x)/2.0, (c1.y+c2.y)/2.0);
198
        PointPairDistance ppd = new PointPairDistance();
199
        DistanceToPoint.computeDistance(sourceCurve, c, ppd);
200
        double d = ppd.getDistance();
201
        if (Math.max(d1, d) < lb) {}
202
        else if (Math.min(d1, d) > lb && Math.max(d1, d) < ub) {}
203
        else if (Math.min(d1, d) > ub) {
204
            LineString segment = sourceCurve.getFactory().createLineString(
205
                        new Coordinate[]{c1, c});
206
            offsetSegments.add(segment);
207
        }
208
        else {
209
            divide(offsetSegments, sourceCurve, c1, c, d1, d, lb, ub);
210
        }
211
        if (Math.max(d, d2) < lb) {}
212
        else if (Math.min(d, d2) > lb && Math.max(d, d2) < ub) {}
213
        else if (Math.min(d, d2) > ub) {
214
            LineString segment = sourceCurve.getFactory().createLineString(
215
                        new Coordinate[]{c, c2});
216
            offsetSegments.add(segment);
217
        }
218
        else {
219
            divide(offsetSegments, sourceCurve, c, c2, d, d2, lb, ub);
220
        }
221
    }
222

    
223
    @SuppressWarnings("unchecked")
224
    private static Collection<LineString> merge(Collection<LineString> linestrings) {
225
        LineMerger merger = new LineMerger();
226
        merger.add(linestrings);
227
        return merger.getMergedLineStrings();
228
    }
229
}