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

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

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

    
54

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

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

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

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

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

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

    
98
    protected abstract ArrayListCoordinateSequence getSplineCoordinates();
99

    
100

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

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

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

    
137
    }
138

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
444
    public class SplineIterator extends GeneralPathXIterator {
445

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

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

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

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

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

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

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

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

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

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

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

    
530

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

    
540

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

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

    
558
    @Override
559
    public Geometry force2D() throws GeometryOperationNotSupportedException, GeometryOperationException {
560
        ArrayListCoordinateSequence coordinates2D = new ArrayListCoordinateSequence();
561
        coordinates2D.ensureCapacity(coordinates.size());
562
        for (Iterator iterator = coordinates.iterator(); iterator.hasNext();) {
563
            Coordinate coordinate = (Coordinate) iterator.next();
564
            coordinates2D.add(new Coordinate(coordinate.x, coordinate.y));
565
        }
566
        Spline2D s = new Spline2D(coordinates2D);
567
        s.setProjection(this.getProjection());
568
        return s;
569
    }
570

    
571
    protected boolean isClosed(){
572
        return coordinates.get(0).equals(coordinates.get(coordinates.size()-1));
573
    }
574

    
575

    
576
    @Override
577
    public boolean canBeTransformed(AffineTransform at) {
578
        return true;
579
    }
580

    
581
    @Override
582
    public boolean canBeReprojected(ICoordTrans ct) {
583
        return true;
584
    }
585
    
586
}