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 / primitive / curve / spline / AbstractSpline.java @ 44617

History | View | Annotate | Download (14.6 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.primitive.curve.spline;
24

    
25
import java.awt.Shape;
26
import java.awt.geom.AffineTransform;
27
import java.awt.geom.PathIterator;
28
import java.util.Collections;
29
import java.util.Iterator;
30

    
31
import com.vividsolutions.jts.geom.Coordinate;
32

    
33
import org.apache.commons.lang3.StringUtils;
34
import org.cresques.cts.ICoordTrans;
35
import org.gvsig.fmap.geom.Geometry;
36

    
37
import org.gvsig.fmap.geom.jts.gputils.DefaultGeneralPathX;
38
import org.gvsig.fmap.geom.jts.gputils.GeneralPathXIterator;
39
import org.gvsig.fmap.geom.jts.primitive.curve.AbstractCurve;
40
import org.gvsig.fmap.geom.jts.primitive.point.Point2D;
41
import org.gvsig.fmap.geom.jts.primitive.point.PointJTS;
42
import org.gvsig.fmap.geom.jts.util.ArrayListCoordinateSequence;
43
import org.gvsig.fmap.geom.jts.util.JTSUtils;
44
import org.gvsig.fmap.geom.jts.util.ReadOnlyCoordinates;
45
import org.gvsig.fmap.geom.operation.GeometryOperationException;
46
import org.gvsig.fmap.geom.operation.GeometryOperationNotSupportedException;
47
import org.gvsig.fmap.geom.primitive.GeneralPathX;
48
import org.gvsig.fmap.geom.primitive.IGeneralPathX;
49
import org.gvsig.fmap.geom.primitive.OrientablePrimitive;
50
import org.gvsig.fmap.geom.primitive.Point;
51

    
52

    
53
/**
54
 * @author fdiaz
55
 *
56
 */
57
public abstract class AbstractSpline extends AbstractCurve {
58

    
59
    /**
60
     *
61
     */
62
    private static final long serialVersionUID = -1562503359430991082L;
63

    
64
    protected ArrayListCoordinateSequence coordinates;
65
    protected PointJTS anyVertex;
66
    protected static final double SUBSEGMENTS = 30.0;
67

    
68
    /**
69
     * @param type
70
     * @param subtype
71
     */
72
    public AbstractSpline(int type, int subtype) {
73
        super(type, subtype);
74
    }
75

    
76
    /**
77
    *
78
     * @param type
79
     * @param subtype
80
     * @param coordinates
81
     * @param aVertex
82
    */
83
   public AbstractSpline(int type, int subtype, Coordinate[] coordinates, PointJTS aVertex) {
84
       this(type, subtype);
85
       this.coordinates = new ArrayListCoordinateSequence(new ReadOnlyCoordinates(coordinates));
86
       anyVertex = aVertex;
87
   }
88

    
89
    @Override
90
    public com.vividsolutions.jts.geom.Geometry getJTS() {
91
        return JTSUtils.createJTSLineString(getSplineCoordinates());
92
    }
93

    
94
    protected abstract ArrayListCoordinateSequence getSplineCoordinates();
95

    
96

    
97
    static class Spline {
98
        private final double y[];
99
        private final double y2[];
100

    
101
        /**
102
         * The constructor calculates the second derivatives of the interpolating function
103
         * at the tabulated points xi, with xi = (i, y[i]).
104
         * Based on numerical recipes in C, http://www.library.cornell.edu/nr/bookcpdf/c3-3.pdf .
105
         * @param y Array of y coordinates for cubic-spline interpolation.
106
         */
107
        public Spline(double y[]) {
108
            this.y = y;
109
            int n = y.length;
110
            y2 = new double[n];
111
            double u[] = new double[n];
112
            for (int i = 1; i < n - 1; i++) {
113
                y2[i] = -1.0 / (4.0 + y2[i - 1]);
114
                u[i] = (6.0 * (y[i + 1] - 2.0 * y[i] + y[i - 1]) - u[i - 1]) / (4.0 + y2[i - 1]);
115
            }
116
            for (int i = n - 2; i >= 0; i--) {
117
                y2[i] = y2[i] * y2[i + 1] + u[i];
118
            }
119
        }
120

    
121
        /**
122
         * Returns a cubic-spline interpolated value y for the point between
123
         * point (n, y[n]) and (n+1, y[n+1), with t ranging from 0 for (n, y[n])
124
         * to 1 for (n+1, y[n+1]).
125
         * @param n The start point.
126
         * @param t The distance to the next point (0..1).
127
         * @return A cubic-spline interpolated value.
128
         */
129
        public double fn(int n, double t) {
130
            return t * y[n + 1] - ((t - 1.0) * t * ((t - 2.0) * y2[n] - (t + 1.0) * y2[n + 1])) / 6.0 + y[n] - t * y[n];
131
        }
132

    
133
    }
134

    
135
    public OrientablePrimitive addVertex(Point point) {
136
        point = fixPoint(point);
137
        coordinates.add(((PointJTS) point).getJTSCoordinate());
138
        anyVertex = (PointJTS) point;
139
        return (OrientablePrimitive) this;
140
    }
141

    
142
    public void setPoints(Point initialPoint, Point endPoint) {
143
        initialPoint = fixPoint(initialPoint);
144
        endPoint = fixPoint(endPoint);
145
        coordinates.clear();
146
        addVertex(initialPoint);
147
        addVertex(endPoint);
148
        anyVertex = (PointJTS) endPoint;
149
    }
150

    
151
    /**
152
     * @param point
153
     * @return
154
     */
155
    protected abstract Point fixPoint(Point point);
156

    
157
    public double getCoordinateAt(int index, int dimension) {
158
        return coordinates.getOrdinate(index, dimension);
159
    }
160

    
161
    public OrientablePrimitive setCoordinateAt(int index, int dimension, double value) {
162
        coordinates.setOrdinate(index, dimension, value);
163
        return (OrientablePrimitive) this;
164
    }
165

    
166
    public void removeVertex(int index) {
167
        coordinates.remove(index);
168
    }
169

    
170
    public int getNumVertices() {
171
        return coordinates.size();
172
    }
173

    
174
    public OrientablePrimitive insertVertex(int index, Point p) {
175
        p = fixPoint(p);
176
        coordinates.add(index, ((PointJTS) p).getJTSCoordinate());
177
        return (OrientablePrimitive) this;
178
    }
179

    
180
    public OrientablePrimitive setVertex(int index, Point p) {
181
        p = fixPoint(p);
182
        coordinates.set(index, ((PointJTS) p).getJTSCoordinate());
183
        return (OrientablePrimitive) this;
184
    }
185

    
186
    public void setGeneralPath(GeneralPathX generalPathX) {
187

    
188
        PathIterator it = generalPathX.getPathIterator(null);
189
        double[] segment = new double[6];
190
        int i = 0;
191
        while(!it.isDone()){
192
            int type = it.currentSegment(segment);
193
            if(i==0){
194
                switch (type) {
195
                case IGeneralPathX.SEG_MOVETO:
196
                    Point p = new Point2D(segment[0], segment[1]);
197
                    p = fixPoint(p);
198
                    coordinates.add(((PointJTS)p).getJTSCoordinate());
199
                    break;
200
                default:
201
                    String message = StringUtils.replace("Type of segment %(segment)s isn't SEG_MOVETO.","%(segment)s",String.valueOf(i));
202
                    logger.warn(message);
203
                    throw new RuntimeException(message);
204
                }
205
            } else {
206
                //Dudo de que los casos SEG_QUADTO y SEG_CUBICTO est?n bien pero se hac?a lo mismo en la librer?a de geometr?as vieja.
207
                Point p;
208
                switch (type) {
209
                case IGeneralPathX.SEG_LINETO:
210
                    p = new Point2D(segment[0], segment[1]);
211
                    p = fixPoint(p);
212
                    coordinates.add(((PointJTS)p).getJTSCoordinate());
213
                    break;
214
                case IGeneralPathX.SEG_QUADTO:
215
                    for (int j = 0; j <= 1; j++) {
216
                        p = new Point2D(segment[i], segment[i+1]);
217
                        p = fixPoint(p);
218
                        coordinates.add(((PointJTS) p).getJTSCoordinate());
219
                    }
220
                    break;
221
                case IGeneralPathX.SEG_CUBICTO:
222
                    for (int j = 0; j <= 2; j++) {
223
                        p = new Point2D(segment[i], segment[i+1]);
224
                        p = fixPoint(p);
225
                        coordinates.add(((PointJTS) p).getJTSCoordinate());
226
                    }
227
                    break;
228
                case IGeneralPathX.SEG_CLOSE:
229
                    coordinates.add(coordinates.get(0));
230
                    break;
231
                default:
232
                    String message = StringUtils.replace("The general path has a gap in segment %(segment)s.","%(segment)s",String.valueOf(i));
233
                    logger.warn(message);
234
                    throw new RuntimeException(message);
235
                }
236
            }
237
            it.next();
238
            i++;
239
        }
240
    }
241

    
242
    public void addMoveToVertex(Point point) {
243
        throw new UnsupportedOperationException();
244
    }
245

    
246
    public void closePrimitive() {
247
        if (!coordinates.isEmpty() && !isClosed()) {
248
            coordinates.add((Coordinate)coordinates.get(0).clone());
249
        }
250
    }
251

    
252
    public OrientablePrimitive ensureCapacity(int capacity) {
253
        coordinates.ensureCapacity(capacity);
254
        return (OrientablePrimitive) this;
255
    }
256

    
257
    @Override
258
    public void reProject(ICoordTrans ct) {
259
        if (ct == null) {
260
            return;
261
        }
262
        ArrayListCoordinateSequence tmpCoordinates = new ArrayListCoordinateSequence();
263
        tmpCoordinates.ensureCapacity(coordinates.size());
264
        for (Coordinate coordinate : coordinates) {
265
            java.awt.geom.Point2D p = new java.awt.geom.Point2D.Double(coordinate.x, coordinate.y);
266
            try {
267
                p = ct.convert(p, p);
268
                coordinate.x = p.getX();
269
                coordinate.y = p.getY();
270
                tmpCoordinates.add(coordinate);
271
            } catch (Exception exc) {
272
                /*
273
                * This can happen when the reprojection lib is unable
274
                * to reproject (for example the source point
275
                * is out of the valid range and some computing
276
                * problem happens)
277
                */
278
            }
279
        }
280
        coordinates=tmpCoordinates;
281
        this.setProjection(ct.getPDest());
282
    }
283

    
284
    @Override
285
    public void transform(AffineTransform at) {
286
        if (at == null) {
287
            return;
288
        }
289

    
290
        for (Coordinate coordinate : coordinates) {
291
            java.awt.geom.Point2D p = new java.awt.geom.Point2D.Double(coordinate.x, coordinate.y);
292

    
293
            at.transform(p, p);
294
            coordinate.x = p.getX();
295
            coordinate.y = p.getY();
296
        }
297
    }
298

    
299
    @Override
300
    public int getDimension() {
301
        return anyVertex.getDimension();
302
    }
303

    
304
    @Override
305
    public Shape getShape(AffineTransform affineTransform) {
306
        return new DefaultGeneralPathX(new SplineIterator(affineTransform),false,0);
307
    }
308

    
309
    @Override
310
    public Shape getShape() {
311
        return getShape(null);
312
    }
313

    
314
    @Override
315
    public PathIterator getPathIterator(AffineTransform at) {
316
        SplineIterator pi = new SplineIterator(at);
317
        return pi;
318
    }
319

    
320
    @Override
321
    public PathIterator getPathIterator(AffineTransform at, double flatness) {
322
        return getPathIterator(at);
323
    }
324

    
325
    @Override
326
    public GeneralPathX getGeneralPath() {
327
        return new DefaultGeneralPathX(new SplineIterator(null),false,0);
328
    }
329

    
330
    public class SplineIterator extends GeneralPathXIterator {
331

    
332
        /** Transform applied on the coordinates during iteration */
333
        private final AffineTransform at;
334

    
335
        /** True when the point has been read once */
336
        private boolean done;
337
        private int index = 0;
338

    
339
        /**
340
         * Creates a new PointIterator object.
341
         * @param at
342
         *            The affine transform applied to coordinates during
343
         *            iteration
344
         */
345
        public SplineIterator(AffineTransform at) {
346
            super(new GeneralPathX());
347
            if (at == null) {
348
                at = new AffineTransform();
349
            }
350

    
351
            this.at = at;
352
            done = false;
353
        }
354

    
355
        /**
356
         * Return the winding rule for determining the interior of the path.
357
         *
358
         * @return <code>WIND_EVEN_ODD</code> by default.
359
         */
360
        @Override
361
        public int getWindingRule() {
362
            return PathIterator.WIND_EVEN_ODD;
363
        }
364

    
365
        @Override
366
        public void next() {
367
            done = (getJTS().getCoordinates().length == ++index);
368
        }
369

    
370
        @Override
371
        public boolean isDone() {
372
            return done;
373
        }
374

    
375
        @Override
376
        public int currentSegment(double[] coords) {
377
            Coordinate[] jtsCoordinates = getJTS().getCoordinates();
378
            coords[0] = jtsCoordinates[index].x;
379
            coords[1] = jtsCoordinates[index].y;
380
            at.transform(coords, 0, coords, 0, 1);
381

    
382
            if (index == 0) {
383
                return PathIterator.SEG_MOVETO;
384
            } else {
385
                return PathIterator.SEG_LINETO;
386
            }
387
        }
388

    
389
        @Override
390
        public int currentSegment(float[] coords) {
391
            Coordinate[] jtsCoordinates = getJTS().getCoordinates();
392
            coords[0] = (float)jtsCoordinates[index].x;
393
            coords[1] = (float)jtsCoordinates[index].y;
394
            at.transform(coords, 0, coords, 0, 1);
395

    
396
            if (index == 0) {
397
                return PathIterator.SEG_MOVETO;
398
            } else {
399
                return PathIterator.SEG_LINETO;
400
            }
401
        }
402
    }
403

    
404

    
405
    @Override
406
    public boolean is3D() {
407
        return anyVertex.is3D();
408
    }
409

    
410

    
411
    @Override
412
    public void flip() throws GeometryOperationNotSupportedException, GeometryOperationException {
413
        Collections.reverse(coordinates);
414
    }
415

    
416
    protected ArrayListCoordinateSequence cloneCoordinates() {
417
        ArrayListCoordinateSequence cloned = new ArrayListCoordinateSequence();
418
        cloned.ensureCapacity(coordinates.size());
419
        for (Coordinate coordinate : coordinates) {
420
            cloned.add((Coordinate)coordinate.clone());
421
        }
422
        return cloned;
423
    }
424

    
425
    @Override
426
    public Geometry force2D() throws GeometryOperationNotSupportedException, GeometryOperationException {
427
        ArrayListCoordinateSequence coordinates2D = new ArrayListCoordinateSequence();
428
        coordinates2D.ensureCapacity(coordinates.size());
429
        for (Coordinate coordinate : coordinates) {
430
            coordinates2D.add(new Coordinate(coordinate.x, coordinate.y));
431
        }
432
        Spline2D s = new Spline2D(coordinates2D);
433
        s.setProjection(this.getProjection());
434
        return s;
435
    }
436

    
437
    protected boolean isClosed(){
438
        return coordinates.get(0).equals(coordinates.get(coordinates.size()-1));
439
    }
440

    
441

    
442
    @Override
443
    public boolean canBeTransformed(AffineTransform at) {
444
        return true;
445
    }
446

    
447
    @Override
448
    public boolean canBeReprojected(ICoordTrans ct) {
449
        return true;
450
    }
451
    
452
}