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 @ 47432

History | View | Annotate | Download (16.7 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 com.vividsolutions.jts.geom.Coordinate;
26
import com.vividsolutions.jts.geom.CoordinateSequence;
27
import java.awt.Shape;
28
import java.awt.geom.AffineTransform;
29
import java.awt.geom.PathIterator;
30
import java.util.Collections;
31
import org.apache.commons.lang3.StringUtils;
32
import org.cresques.cts.ICoordTrans;
33
import org.gvsig.fmap.geom.Geometry;
34
import org.gvsig.fmap.geom.jts.gputils.DefaultGeneralPathX;
35
import org.gvsig.fmap.geom.jts.gputils.GeneralPathXIterator;
36
import org.gvsig.fmap.geom.jts.mgeom.MCoordinate;
37
import org.gvsig.fmap.geom.jts.primitive.curve.AbstractCurve;
38
import org.gvsig.fmap.geom.jts.primitive.point.Point2D;
39
import org.gvsig.fmap.geom.jts.primitive.point.PointJTS;
40
import org.gvsig.fmap.geom.jts.util.ArrayListCoordinateSequence;
41
import org.gvsig.fmap.geom.jts.util.JTSUtils;
42
import org.gvsig.fmap.geom.jts.util.ReadOnlyCoordinates;
43
import org.gvsig.fmap.geom.operation.GeometryOperationException;
44
import org.gvsig.fmap.geom.operation.GeometryOperationNotSupportedException;
45
import org.gvsig.fmap.geom.primitive.GeneralPathX;
46
import org.gvsig.fmap.geom.primitive.IGeneralPathX;
47
import org.gvsig.fmap.geom.primitive.OrientablePrimitive;
48
import org.gvsig.fmap.geom.primitive.Point;
49

    
50

    
51
/**
52
 * @author gvSIG Team
53
 *
54
 */
55
@SuppressWarnings("UseSpecificCatch")
56
public abstract class AbstractSpline extends AbstractCurve {
57

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

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

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

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

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

    
93
    protected abstract ArrayListCoordinateSequence getSplineCoordinates();
94

    
95

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

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

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

    
132
    }
133

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

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

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

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

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

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

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

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

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

    
185
    public void setGeneralPath(GeneralPathX generalPathX) {
186

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
329
    public class SplineIterator extends GeneralPathXIterator {
330

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

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

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

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

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

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

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

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

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

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

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

    
403

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

    
409

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

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

    
424
    @Override
425
    public Geometry force2D() throws GeometryOperationNotSupportedException, GeometryOperationException {
426
        ArrayListCoordinateSequence coordinates2D = new ArrayListCoordinateSequence();
427
        coordinates2D.ensureCapacity(coordinates.size());
428
        for (Coordinate coordinate : coordinates) {
429
            coordinates2D.add(new Coordinate(coordinate.x, coordinate.y));
430
        }
431
        Spline2D s = new Spline2D(coordinates2D);
432
        s.setProjection(this.getProjection());
433
        return s;
434
    }
435
    
436
    @Override
437
    public Geometry force2DM() throws GeometryOperationNotSupportedException, GeometryOperationException {
438
        ArrayListCoordinateSequence coordinates2DM = new ArrayListCoordinateSequence(coordinates.size());
439
        for (Coordinate coordinate : this.coordinates) {
440
            coordinates2DM.add(MCoordinate.create2dWithMeasure(
441
                    coordinate.x, 
442
                    coordinate.y, 
443
                    (coordinate instanceof MCoordinate)?coordinate.getOrdinate(CoordinateSequence.M):0
444
            ));
445
        }
446
        Spline2DM s = new Spline2DM(coordinates2DM);
447
        s.setProjection(this.getProjection());
448
        return s;
449
    }
450

    
451
    @Override
452
    public Geometry force3D() throws GeometryOperationNotSupportedException, GeometryOperationException {
453
        ArrayListCoordinateSequence coordinates3D = new ArrayListCoordinateSequence(coordinates.size());
454
        for (Coordinate coordinate : this.coordinates) {
455
            coordinates3D.add(new Coordinate(
456
                    coordinate.x, 
457
                    coordinate.y, 
458
                    Double.isNaN(coordinate.z)?0:coordinate.z
459
            ));
460
        }
461
        Spline3D s = new Spline3D(coordinates3D);
462
        s.setProjection(this.getProjection());
463
        return s;
464
    }
465

    
466
    @Override
467
    public Geometry force3DM() throws GeometryOperationNotSupportedException, GeometryOperationException {
468
        ArrayListCoordinateSequence coordinates3DM = new ArrayListCoordinateSequence(coordinates.size());
469
        for (Coordinate coordinate : this.coordinates) {
470
            coordinates3DM.add(MCoordinate.create3dWithMeasure(
471
                    coordinate.x, 
472
                    coordinate.y, 
473
                    (Double.isNaN(coordinate.z))?0:coordinate.z, 
474
                    (coordinate instanceof MCoordinate)?coordinate.getOrdinate(CoordinateSequence.M):0
475
            ));
476
        }
477
        Spline3DM s = new Spline3DM(coordinates3DM);
478
        s.setProjection(this.getProjection());
479
        return s;
480
    }
481
    
482

    
483
    protected boolean isClosed(){
484
        return coordinates.get(0).equals(coordinates.get(coordinates.size()-1));
485
    }
486

    
487

    
488
    @Override
489
    public boolean canBeTransformed(AffineTransform at) {
490
        return true;
491
    }
492

    
493
    @Override
494
    public boolean canBeReprojected(ICoordTrans ct) {
495
        return true;
496
    }
497
    
498
}