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

History | View | Annotate | Download (17.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.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.slf4j.Logger;
36
import org.slf4j.LoggerFactory;
37

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

    
53

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

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

    
65
    private static final Logger logger = LoggerFactory.getLogger(AbstractSpline.class);
66

    
67
    protected ArrayListCoordinateSequence coordinates;
68
    protected PointJTS anyVertex;
69
    protected static final double SUBSEGMENTS = 30.0;
70

    
71
    /**
72
     * @param type
73
     * @param subtype
74
     */
75
    public AbstractSpline(int type, int subtype) {
76
        super(type, subtype);
77
    }
78

    
79
    /**
80
    *
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
    /*
89
     * (non-Javadoc)
90
     *
91
     * @see org.gvsig.fmap.geom.jts.GeometryJTS#getJTS()
92
     */
93
    public com.vividsolutions.jts.geom.Geometry getJTS() {
94
        return JTSUtils.createJTSLineString(getSplineCoordinates());
95
    }
96

    
97
    protected abstract ArrayListCoordinateSequence getSplineCoordinates();
98

    
99

    
100
    static class Spline {
101
        private double y[];
102
        private double y2[];
103

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

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

    
136
    }
137

    
138
    /*
139
     * (non-Javadoc)
140
     *
141
     * @see
142
     * org.gvsig.fmap.geom.primitive.OrientablePrimitive#addVertex(org.gvsig
143
     * .fmap.geom.primitive.Point)
144
     */
145
    public void addVertex(Point point) {
146
        point = fixPoint(point);
147
        coordinates.add(((PointJTS) point).getJTSCoordinate());
148
        anyVertex = (PointJTS) point;
149
    }
150

    
151
    /*
152
     * (non-Javadoc)
153
     *
154
     * @see
155
     * org.gvsig.fmap.geom.primitive.Curve#setPoints(org.gvsig.fmap.geom.primitive
156
     * .Point, org.gvsig.fmap.geom.primitive.Point)
157
     */
158
    public void setPoints(Point initialPoint, Point endPoint) {
159
        initialPoint = fixPoint(initialPoint);
160
        endPoint = fixPoint(endPoint);
161
        coordinates.clear();
162
        addVertex(initialPoint);
163
        addVertex(endPoint);
164
        anyVertex = (PointJTS) endPoint;
165
    }
166

    
167
    /**
168
     * @param initialPoint
169
     * @return
170
     */
171
    protected abstract Point fixPoint(Point point);
172

    
173
    /*
174
     * (non-Javadoc)
175
     *
176
     * @see
177
     * org.gvsig.fmap.geom.primitive.OrientablePrimitive#getCoordinateAt(int,
178
     * int)
179
     */
180
    public double getCoordinateAt(int index, int dimension) {
181
        return coordinates.getOrdinate(index, dimension);
182
    }
183

    
184
    /*
185
     * (non-Javadoc)
186
     *
187
     * @see
188
     * org.gvsig.fmap.geom.primitive.OrientablePrimitive#setCoordinateAt(int,
189
     * int, double)
190
     */
191
    public void setCoordinateAt(int index, int dimension, double value) {
192
        coordinates.setOrdinate(index, dimension, value);
193
    }
194

    
195
    /*
196
     * (non-Javadoc)
197
     *
198
     * @see org.gvsig.fmap.geom.primitive.OrientablePrimitive#removeVertex(int)
199
     */
200
    public void removeVertex(int index) {
201
        coordinates.remove(index);
202
    }
203

    
204
    /*
205
     * (non-Javadoc)
206
     *
207
     * @see org.gvsig.fmap.geom.primitive.OrientablePrimitive#getNumVertices()
208
     */
209
    public int getNumVertices() {
210
        return coordinates.size();
211
    }
212

    
213
    /*
214
     * (non-Javadoc)
215
     *
216
     * @see org.gvsig.fmap.geom.primitive.OrientablePrimitive#insertVertex(int,
217
     * org.gvsig.fmap.geom.primitive.Point)
218
     */
219
    public void insertVertex(int index, Point p) {
220
        p = fixPoint(p);
221
        coordinates.add(index, ((PointJTS) p).getJTSCoordinate());
222
    }
223

    
224
    /*
225
     * (non-Javadoc)
226
     *
227
     * @see org.gvsig.fmap.geom.primitive.OrientablePrimitive#setVertex(int,
228
     * org.gvsig.fmap.geom.primitive.Point)
229
     */
230
    public void setVertex(int index, Point p) {
231
        p = fixPoint(p);
232
        coordinates.set(index, ((PointJTS) p).getJTSCoordinate());
233
    }
234

    
235
    /*
236
     * (non-Javadoc)
237
     *
238
     * @see
239
     * org.gvsig.fmap.geom.primitive.OrientablePrimitive#setGeneralPath(org.
240
     * gvsig.fmap.geom.primitive.GeneralPathX)
241
     */
242
    public void setGeneralPath(GeneralPathX generalPathX) {
243

    
244
        PathIterator it = generalPathX.getPathIterator(null);
245
        double[] segment = new double[6];
246
        int i = 0;
247
        while(!it.isDone()){
248
            int type = it.currentSegment(segment);
249
            if(i==0){
250
                switch (type) {
251
                case IGeneralPathX.SEG_MOVETO:
252
                    Point p = new Point2D(segment[0], segment[1]);
253
                    p = fixPoint(p);
254
                    coordinates.add(((PointJTS)p).getJTSCoordinate());
255
                    break;
256
                default:
257
                    String message = StringUtils.replace("Type of segment %(segment)s isn't SEG_MOVETO.","%(segment)s",String.valueOf(i));
258
                    logger.warn(message);
259
                    throw new RuntimeException(message);
260
                }
261
            } else {
262
                //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.
263
                Point p;
264
                switch (type) {
265
                case IGeneralPathX.SEG_LINETO:
266
                    p = new Point2D(segment[0], segment[1]);
267
                    p = fixPoint(p);
268
                    coordinates.add(((PointJTS)p).getJTSCoordinate());
269
                    break;
270
                case IGeneralPathX.SEG_QUADTO:
271
                    for (int j = 0; j <= 1; j++) {
272
                        p = new Point2D(segment[i], segment[i+1]);
273
                        p = fixPoint(p);
274
                        coordinates.add(((PointJTS) p).getJTSCoordinate());
275
                    }
276
                    break;
277
                case IGeneralPathX.SEG_CUBICTO:
278
                    for (int j = 0; j <= 2; j++) {
279
                        p = new Point2D(segment[i], segment[i+1]);
280
                        p = fixPoint(p);
281
                        coordinates.add(((PointJTS) p).getJTSCoordinate());
282
                    }
283
                    break;
284
                case IGeneralPathX.SEG_CLOSE:
285
                    coordinates.add(coordinates.get(0));
286
                    break;
287
                default:
288
                    String message = StringUtils.replace("The general path has a gap in segment %(segment)s.","%(segment)s",String.valueOf(i));
289
                    logger.warn(message);
290
                    throw new RuntimeException(message);
291
                }
292
            }
293
            it.next();
294
            i++;
295
        }
296
    }
297

    
298
    /*
299
     * (non-Javadoc)
300
     *
301
     * @see
302
     * org.gvsig.fmap.geom.primitive.OrientablePrimitive#addMoveToVertex(org
303
     * .gvsig.fmap.geom.primitive.Point)
304
     */
305
    public void addMoveToVertex(Point point) {
306
        throw new UnsupportedOperationException();
307
    }
308

    
309
    /*
310
     * (non-Javadoc)
311
     *
312
     * @see org.gvsig.fmap.geom.primitive.OrientablePrimitive#closePrimitive()
313
     */
314
    public void closePrimitive() {
315
        if (!coordinates.isEmpty() && !isClosed()) {
316
            coordinates.add((Coordinate)coordinates.get(0).clone());
317
        }
318
    }
319

    
320
    /*
321
     * (non-Javadoc)
322
     *
323
     * @see
324
     * org.gvsig.fmap.geom.primitive.OrientablePrimitive#ensureCapacity(int)
325
     */
326
    public void ensureCapacity(int capacity) {
327
        coordinates.ensureCapacity(capacity);
328
    }
329

    
330
    /*
331
     * (non-Javadoc)
332
     *
333
     * @see org.gvsig.fmap.geom.Geometry#reProject(org.cresques.cts.ICoordTrans)
334
     */
335
    public void reProject(ICoordTrans ct) {
336
        if (ct == null) {
337
            return;
338
        }
339
        ArrayListCoordinateSequence tmpCoordinates = new ArrayListCoordinateSequence();
340
        tmpCoordinates.ensureCapacity(coordinates.size());
341
        for (Iterator<Coordinate> iterator = coordinates.iterator(); iterator.hasNext();) {
342
            Coordinate coordinate = (Coordinate) iterator.next();
343

    
344
            java.awt.geom.Point2D p = new java.awt.geom.Point2D.Double(coordinate.x, coordinate.y);
345
            try {
346
                p = ct.convert(p, p);
347
                coordinate.x = p.getX();
348
                coordinate.y = p.getY();
349
                tmpCoordinates.add(coordinate);
350
            } catch (Exception exc) {
351
                /*
352
                 * This can happen when the reprojection lib is unable
353
                 * to reproject (for example the source point
354
                 * is out of the valid range and some computing
355
                 * problem happens)
356
                 */
357
            }
358
        }
359
        coordinates=tmpCoordinates;
360
        this.setProjection(ct.getPDest());
361
    }
362

    
363
    /*
364
     * (non-Javadoc)
365
     *
366
     * @see
367
     * org.gvsig.fmap.geom.Geometry#transform(java.awt.geom.AffineTransform)
368
     */
369
    public void transform(AffineTransform at) {
370
        if (at == null) {
371
            return;
372
        }
373

    
374
        for (Iterator<Coordinate> iterator = coordinates.iterator(); iterator.hasNext();) {
375
            Coordinate coordinate = (Coordinate) iterator.next();
376
            java.awt.geom.Point2D p = new java.awt.geom.Point2D.Double(coordinate.x, coordinate.y);
377

    
378
            at.transform(p, p);
379
            coordinate.x = p.getX();
380
            coordinate.y = p.getY();
381
        }
382
    }
383

    
384
    /*
385
     * (non-Javadoc)
386
     *
387
     * @see org.gvsig.fmap.geom.Geometry#getDimension()
388
     */
389
    public int getDimension() {
390
        return anyVertex.getDimension();
391
    }
392

    
393
    /*
394
     * (non-Javadoc)
395
     *
396
     * @see org.gvsig.fmap.geom.Geometry#getShape(java.awt.geom.AffineTransform)
397
     */
398
    public Shape getShape(AffineTransform affineTransform) {
399
        return new DefaultGeneralPathX(new SplineIterator(affineTransform),false,0);
400
    }
401

    
402
    /*
403
     * (non-Javadoc)
404
     *
405
     * @see org.gvsig.fmap.geom.Geometry#getShape()
406
     */
407
    public Shape getShape() {
408
        return getShape(null);
409
    }
410

    
411
    /*
412
     * (non-Javadoc)
413
     *
414
     * @see
415
     * org.gvsig.fmap.geom.Geometry#getPathIterator(java.awt.geom.AffineTransform
416
     * )
417
     */
418
    public PathIterator getPathIterator(AffineTransform at) {
419
        SplineIterator pi = new SplineIterator(at);
420
        return pi;
421
    }
422

    
423
    /*
424
     * (non-Javadoc)
425
     *
426
     * @see
427
     * org.gvsig.fmap.geom.Geometry#getPathIterator(java.awt.geom.AffineTransform
428
     * , double)
429
     */
430
    public PathIterator getPathIterator(AffineTransform at, double flatness) {
431
        return getPathIterator(at);
432
    }
433

    
434
    /*
435
     * (non-Javadoc)
436
     *
437
     * @see org.gvsig.fmap.geom.Geometry#getGeneralPath()
438
     */
439
    public GeneralPathX getGeneralPath() {
440
        return new DefaultGeneralPathX(new SplineIterator(null),false,0);
441
    }
442

    
443
    public class SplineIterator extends GeneralPathXIterator {
444

    
445
        /** Transform applied on the coordinates during iteration */
446
        private AffineTransform at;
447

    
448
        /** True when the point has been read once */
449
        private boolean done;
450
        private int index = 0;
451

    
452
        /**
453
         * Creates a new PointIterator object.
454
         *
455
         * @param p
456
         *            The polygon
457
         * @param at
458
         *            The affine transform applied to coordinates during
459
         *            iteration
460
         */
461
        public SplineIterator(AffineTransform at) {
462
            super(new GeneralPathX());
463
            if (at == null) {
464
                at = new AffineTransform();
465
            }
466

    
467
            this.at = at;
468
            done = false;
469
        }
470

    
471
        /**
472
         * Return the winding rule for determining the interior of the path.
473
         *
474
         * @return <code>WIND_EVEN_ODD</code> by default.
475
         */
476
        public int getWindingRule() {
477
            return PathIterator.WIND_EVEN_ODD;
478
        }
479

    
480
        /**
481
         * @see java.awt.geom.PathIterator#next()
482
         */
483
        public void next() {
484
            done = (getJTS().getCoordinates().length == ++index);
485
        }
486

    
487
        /**
488
         * @see java.awt.geom.PathIterator#isDone()
489
         */
490
        public boolean isDone() {
491
            return done;
492
        }
493

    
494
        /**
495
         * @see java.awt.geom.PathIterator#currentSegment(double[])
496
         */
497
        public int currentSegment(double[] coords) {
498
            Coordinate[] jtsCoordinates = getJTS().getCoordinates();
499
            coords[0] = jtsCoordinates[index].x;
500
            coords[1] = jtsCoordinates[index].y;
501
            at.transform(coords, 0, coords, 0, 1);
502

    
503
            if (index == 0) {
504
                return PathIterator.SEG_MOVETO;
505
            } else {
506
                return PathIterator.SEG_LINETO;
507
            }
508
        }
509

    
510
        /*
511
         * (non-Javadoc)
512
         *
513
         * @see java.awt.geom.PathIterator#currentSegment(float[])
514
         */
515
        public int currentSegment(float[] coords) {
516
            Coordinate[] jtsCoordinates = getJTS().getCoordinates();
517
            coords[0] = (float)jtsCoordinates[index].x;
518
            coords[1] = (float)jtsCoordinates[index].y;
519
            at.transform(coords, 0, coords, 0, 1);
520

    
521
            if (index == 0) {
522
                return PathIterator.SEG_MOVETO;
523
            } else {
524
                return PathIterator.SEG_LINETO;
525
            }
526
        }
527
    }
528

    
529

    
530
    /*
531
     * (non-Javadoc)
532
     *
533
     * @see org.gvsig.fmap.geom.jts.GeometryJTS#is3D()
534
     */
535
    public boolean is3D() {
536
        return anyVertex.is3D();
537
    }
538

    
539

    
540
    /* (non-Javadoc)
541
     * @see org.gvsig.fmap.geom.jts.GeometryJTS#flip()
542
     */
543
    public void flip() throws GeometryOperationNotSupportedException, GeometryOperationException {
544
        Collections.reverse(coordinates);
545
    }
546

    
547
    protected ArrayListCoordinateSequence cloneCoordinates() {
548
        ArrayListCoordinateSequence cloned = new ArrayListCoordinateSequence();
549
        cloned.ensureCapacity(coordinates.size());
550
        for (Iterator iterator = coordinates.iterator(); iterator.hasNext();) {
551
            Coordinate coordinate = (Coordinate) iterator.next();
552
            cloned.add((Coordinate)coordinate.clone());
553
        }
554
        return cloned;
555
    }
556

    
557

    
558
    protected boolean isClosed(){
559
        return coordinates.get(0).equals(coordinates.get(coordinates.size()-1));
560
    }
561

    
562

    
563
    @Override
564
    public boolean canBeTransformed(AffineTransform at) {
565
        return true;
566
    }
567

    
568
    @Override
569
    public boolean canBeReprojected(ICoordTrans ct) {
570
        return true;
571
    }
572
    
573
}