Statistics
| Revision:

svn-gvsig-desktop / trunk / libraries / libFMap / src / com / iver / cit / gvsig / fmap / core / gt2 / FLiteShape.java @ 6507

History | View | Annotate | Download (32.2 KB)

1
/*
2
 * Created on 12-may-2005
3
 *
4
 * gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
5
 * 
6
 * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
7
 * 
8
 * This program is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU General Public License
10
 * as published by the Free Software Foundation; either version 2
11
 * of the License, or (at your option) any later version.
12
 *  
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 * 
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21
 *  
22
 * For more information, contact:
23
 *
24
 *  Generalitat Valenciana
25
 *   Conselleria d'Infraestructures i Transport
26
 *   Av. Blasco Ib??ez, 50
27
 *   46010 VALENCIA
28
 *   SPAIN
29
 *
30
 *      +34 963862235
31
 *   gvsig@gva.es
32
 *      www.gvsig.gva.es
33
 * 
34
 *    or
35
 * 
36
 *   IVER T.I. S.A
37
 *   Salamanca 50
38
 *   46005 Valencia
39
 *   Spain
40
 * 
41
 *   +34 963163400
42
 *   dac@iver.es
43
 */
44
package com.iver.cit.gvsig.fmap.core.gt2;
45

    
46

    
47
import java.awt.Rectangle;
48
import java.awt.geom.AffineTransform;
49
import java.awt.geom.PathIterator;
50
import java.awt.geom.Point2D;
51
import java.awt.geom.Rectangle2D;
52
import java.util.ArrayList;
53

    
54
import org.cresques.cts.ICoordTrans;
55
import org.geotools.geometry.coordinatesequence.InPlaceCoordinateSequenceTransformer;
56
import org.geotools.geometry.jts.CoordinateSequenceTransformer;
57
import org.geotools.referencing.operation.GeneralMatrix;
58
import org.opengis.referencing.FactoryException;
59
import org.opengis.referencing.operation.MathTransform;
60
import org.opengis.referencing.operation.MathTransformFactory;
61
import org.opengis.referencing.operation.TransformException;
62

    
63
import com.iver.cit.gvsig.fmap.core.AbstractHandler;
64
import com.iver.cit.gvsig.fmap.core.FShape;
65
import com.iver.cit.gvsig.fmap.core.Handler;
66
import com.iver.cit.gvsig.fmap.core.gt2.factory.FactoryFinder;
67
import com.vividsolutions.jts.geom.Coordinate;
68
import com.vividsolutions.jts.geom.Geometry;
69
import com.vividsolutions.jts.geom.GeometryCollection;
70
import com.vividsolutions.jts.geom.GeometryFactory;
71
import com.vividsolutions.jts.geom.LineString;
72
import com.vividsolutions.jts.geom.LinearRing;
73
import com.vividsolutions.jts.geom.MultiLineString;
74
import com.vividsolutions.jts.geom.MultiPolygon;
75
import com.vividsolutions.jts.geom.Point;
76
import com.vividsolutions.jts.geom.Polygon;
77
import com.vividsolutions.jts.geom.impl.PackedCoordinateSequenceFactory;
78

    
79

    
80
/**
81
 * A thin wrapper that adapts a JTS geometry to the Shape interface so that the
82
 * geometry can be used by java2d without coordinate cloning
83
 *
84
 * @author Andrea Aime
85
 * @version $Id: FLiteShape.java 5880 2006-06-16 10:44:01Z fjp $
86
 */
87
public class FLiteShape implements FShape, Cloneable {
88
    /** The wrapped JTS geometry */
89
    private Geometry geometry;
90

    
91
    /** The transform needed to go from the object space to the device space */
92
    private AffineTransform affineTransform = null;
93
    private boolean generalize = false;
94
    private double maxDistance = 1;
95
    
96
    // cached iterators
97
    private LineIterator lineIterator = new LineIterator();
98
    private GeomCollectionIterator collIterator = new GeomCollectionIterator();
99

    
100
    private float xScale;
101

    
102
    private float yScale;
103
    
104
    private GeometryFactory geomFac;
105

    
106
    private MathTransform mathTransform;
107

    
108
    private static final AffineTransform IDENTITY = new AffineTransform();
109
    
110
        class PointHandler extends AbstractHandler {
111
                /**
112
                 * Crea un nuevo PointHandler.
113
                 *
114
                 * @param x DOCUMENT ME!
115
                 * @param y DOCUMENT ME!
116
                 */
117
                Coordinate c;
118
                public PointHandler(int i,Coordinate coord) {
119
                        point = new Point2D.Double(coord.x, coord.y);
120
                        index=i;
121
                        c = coord;
122
                }
123

    
124
                /**
125
                 * DOCUMENT ME!
126
                 *
127
                 * @param x DOCUMENT ME!
128
                 * @param y DOCUMENT ME!
129
                 *
130
                 * @return DOCUMENT ME!
131
                 */
132
                public void move(double x, double y) {                            
133
                        c.x+=x;
134
                        c.y+=y;
135
                        geometry.geometryChanged();
136
                }
137

    
138
                /**
139
                 * @see com.iver.cit.gvsig.fmap.core.Handler#set(double, double)
140
                 */
141
                public void set(double x, double y) {
142
                        c.x=x;
143
                        c.y=y;
144
                        geometry.geometryChanged();
145
                }
146
        }
147
    
148

    
149
    /**
150
     * Creates a new LiteShape object.
151
     *
152
     * @param geom - the wrapped geometry
153
     * @param at - the transformation applied to the geometry in order to get to the shape points
154
     * @param generalize - set to true if the geometry need to be generalized
155
     *        during rendering
156
     * @param maxDistance - distance used in the generalization process
157
     * @throws TransformException 
158
     * @throws FactoryException 
159
     */
160
    public FLiteShape(Geometry geom, AffineTransform at, MathTransform mathTransform, boolean generalize,
161
        double maxDistance) throws TransformException, FactoryException {
162
        this(geom, at, mathTransform, generalize);
163
        this.maxDistance = maxDistance;
164
    }
165

    
166
    /**
167
     * Creates a new LiteShape object.
168
     *
169
     * @param geom - the wrapped geometry
170
     * @param at - the transformation applied to the geometry in order to get to the shape points
171
     * @param generalize - set to true if the geometry need to be generalized
172
     *        during rendering
173
     * @param maxDistance - distance used in the generalization process
174
     * @throws TransformException 
175
     * @throws FactoryException 
176
     */
177
    public FLiteShape(Geometry geom, AffineTransform at, MathTransform mathTransform, boolean generalize) throws TransformException, FactoryException {
178
        if( geom!=null)
179
            this.geometry =getGeometryFactory().createGeometry(geom);
180
        if( at!=null )
181
            this.affineTransform = at;
182
        else
183
            this.affineTransform=IDENTITY;
184
            this.mathTransform=mathTransform;
185
            if( geometry!=null)
186
                transformGeometry(geometry);
187
        this.generalize = generalize;
188
        xScale = (float) Math.sqrt(
189
                (affineTransform.getScaleX() * affineTransform.getScaleX())
190
                + (affineTransform.getShearX() * affineTransform.getShearX()));
191
        yScale = (float) Math.sqrt(
192
                (affineTransform.getScaleY() * affineTransform.getScaleY())
193
                + (affineTransform.getShearY() * affineTransform.getShearY()));
194
    }
195
    /**
196
     * Creates a new LiteShape object.
197
     *
198
     * @param geom - the wrapped geometry
199
     * @param at - the transformation applied to the geometry in order to get to the shape points
200
     * @param generalize - set to true if the geometry need to be generalized
201
     *        during rendering
202
     * @param maxDistance - distance used in the generalization process
203
     * @throws TransformException 
204
     */
205
    public FLiteShape(Geometry geom, AffineTransform at, boolean generalize,
206
        double maxDistance){
207
        this(geom, at, generalize);
208
        this.maxDistance = maxDistance;
209
    }
210
    
211
    public FLiteShape(Geometry geom)
212
    {
213
        this(geom, null, false);
214
    }
215

    
216
    /**
217
     * Creates a new LiteShape object.
218
     *
219
     * @param geom - the wrapped geometry
220
     * @param at - the transformation applied to the geometry in order to get to the shape points
221
     * @param generalize - set to true if the geometry need to be generalized
222
     *        during rendering
223
     * @param maxDistance - distance used in the generalization process
224
     * @throws TransformException 
225
     */
226
    public FLiteShape(Geometry geom, AffineTransform at, boolean generalize){
227
        if( geom!=null)
228
            this.geometry =getGeometryFactory().createGeometry(geom);
229
        if( at!=null )
230
            this.affineTransform = at;
231
        else
232
            this.affineTransform=new AffineTransform();
233
        this.generalize = generalize;
234
            try {
235
                if( geometry!=null)
236
                    transformGeometry(geometry);
237
            } catch (Exception e) {
238
                affineTransform=at;
239
                geometry=geom;
240
            }
241
        xScale = (float) Math.sqrt(
242
                (affineTransform.getScaleX() * affineTransform.getScaleX())
243
                + (affineTransform.getShearX() * affineTransform.getShearX()));
244
        yScale = (float) Math.sqrt(
245
                (affineTransform.getScaleY() * affineTransform.getScaleY())
246
                + (affineTransform.getShearY() * affineTransform.getShearY()));
247
    }
248
    
249
    private void transformGeometry(Geometry geometry) throws TransformException, FactoryException {
250
        
251
        if( mathTransform==null || mathTransform.isIdentity() ){
252
            if( !affineTransform.isIdentity() ){
253
                MathTransformFactory factory=FactoryFinder.getMathTransformFactory(null);
254
                mathTransform=factory.createAffineTransform(new GeneralMatrix(affineTransform));
255
                affineTransform=IDENTITY;
256
            }
257
        }else if( !affineTransform.isIdentity() ){
258
            MathTransformFactory factory=FactoryFinder.getMathTransformFactory(null);
259
            factory.createConcatenatedTransform(mathTransform, factory.createAffineTransform(new GeneralMatrix(affineTransform)));
260
            affineTransform=IDENTITY;
261
        }
262
        
263
        if( mathTransform==null || mathTransform.isIdentity() )
264
            return;
265
        
266
        
267
        CoordinateSequenceTransformer transformer=new InPlaceCoordinateSequenceTransformer();
268
        if (geometry instanceof GeometryCollection) {
269
            GeometryCollection collection=(GeometryCollection)geometry;
270
            for (int i = 0; i < collection.getNumGeometries(); i++) {
271
                transformGeometry(collection.getGeometryN(i));
272
            }
273
        }else if (geometry instanceof Point) {
274
            transformer.transform(((Point)geometry).getCoordinateSequence(), mathTransform);
275
        }else if (geometry instanceof Polygon) {     
276
            Polygon polygon=(Polygon) geometry;
277
            transformGeometry(polygon.getExteriorRing());
278
            for (int i = 0; i < polygon.getNumInteriorRing(); i++) {
279
                transformGeometry(polygon.getInteriorRingN(i));
280
            }
281
        } else if (geometry instanceof LineString) {
282
            transformer.transform(((LineString)geometry).getCoordinateSequence(), mathTransform);
283
        } 
284
    }
285

    
286
    private GeometryFactory getGeometryFactory() {
287
        if (geomFac == null) {
288
            geomFac = new GeometryFactory(new PackedCoordinateSequenceFactory());
289
        }
290

    
291
        return geomFac;
292
    }
293

    
294
    /**
295
     * Sets the geometry contained in this lite shape. Convenient to reuse this
296
     * object instead of creating it again and again during rendering
297
     *
298
     * @param g
299
     * @throws TransformException 
300
     * @throws FactoryException 
301
     */
302
    public void setGeometry(Geometry g) throws TransformException, FactoryException {
303
        if( g!=null){
304
            this.geometry =getGeometryFactory().createGeometry(g);
305
            transformGeometry(geometry);
306
        }
307
    }
308
    public void transform(AffineTransform at)
309
    {
310
        affineTransform=at;
311
        try {
312
            transformGeometry(geometry);
313
        } catch (TransformException e) {
314
            // TODO Auto-generated catch block
315
            e.printStackTrace();
316
        } catch (FactoryException e) {
317
            // TODO Auto-generated catch block
318
            e.printStackTrace();
319
        }        
320
        xScale = (float) Math.sqrt(
321
            (affineTransform.getScaleX() * affineTransform.getScaleX())
322
            + (affineTransform.getShearX() * affineTransform.getShearX()));
323
        yScale = (float) Math.sqrt(
324
            (affineTransform.getScaleY() * affineTransform.getScaleY())
325
            + (affineTransform.getShearY() * affineTransform.getShearY()));
326
        
327
    }
328

    
329
    /**
330
     * Tests if the interior of the <code>Shape</code> entirely contains the
331
     * specified <code>Rectangle2D</code>. This method might conservatively
332
     * return <code>false</code> when:
333
     * 
334
     * <ul>
335
     * <li>
336
     * the <code>intersect</code> method returns <code>true</code> and
337
     * </li>
338
     * <li>
339
     * the calculations to determine whether or not the <code>Shape</code>
340
     * entirely contains the <code>Rectangle2D</code> are prohibitively
341
     * expensive.
342
     * </li>
343
     * </ul>
344
     * 
345
     * This means that this method might return <code>false</code> even though
346
     * the <code>Shape</code> contains the <code>Rectangle2D</code>. The
347
     * <code>Area</code> class can be used to perform more accurate
348
     * computations of geometric intersection for any <code>Shape</code>
349
     * object if a more precise answer is required.
350
     *
351
     * @param r The specified <code>Rectangle2D</code>
352
     *
353
     * @return <code>true</code> if the interior of the <code>Shape</code>
354
     *         entirely contains the <code>Rectangle2D</code>;
355
     *         <code>false</code> otherwise or, if the <code>Shape</code>
356
     *         contains the <code>Rectangle2D</code> and the
357
     *         <code>intersects</code> method returns <code>true</code> and
358
     *         the containment calculations would be too expensive to perform.
359
     *
360
     * @see #contains(double, double, double, double)
361
     */
362
    public boolean contains(Rectangle2D r) {
363
        Geometry rect = rectangleToGeometry(r);
364

    
365
        return geometry.contains(rect);
366
    }
367

    
368
    /**
369
     * Tests if a specified {@link Point2D} is inside the boundary of the
370
     * <code>Shape</code>.
371
     *
372
     * @param p a specified <code>Point2D</code>
373
     *
374
     * @return <code>true</code> if the specified <code>Point2D</code> is
375
     *         inside the boundary of the <code>Shape</code>;
376
     *         <code>false</code> otherwise.
377
     */
378
    public boolean contains(Point2D p) {
379
        Coordinate coord = new Coordinate(p.getX(), p.getY());
380
        Geometry point = geometry.getFactory().createPoint(coord);
381

    
382
        return geometry.contains(point);
383
    }
384

    
385
    /**
386
     * Tests if the specified coordinates are inside the boundary of the
387
     * <code>Shape</code>.
388
     *
389
     * @param x the specified coordinates, x value
390
     * @param y the specified coordinates, y value
391
     *
392
     * @return <code>true</code> if the specified coordinates are inside the
393
     *         <code>Shape</code> boundary; <code>false</code> otherwise.
394
     */
395
    public boolean contains(double x, double y) {
396
        Coordinate coord = new Coordinate(x, y);
397
        Geometry point = geometry.getFactory().createPoint(coord);
398

    
399
        return geometry.contains(point);
400
    }
401

    
402
    /**
403
     * Tests if the interior of the <code>Shape</code> entirely contains the
404
     * specified rectangular area.  All coordinates that lie inside the
405
     * rectangular area must lie within the <code>Shape</code> for the entire
406
     * rectanglar area to be considered contained within the
407
     * <code>Shape</code>.
408
     * 
409
     * <p>
410
     * This method might conservatively return <code>false</code> when:
411
     * 
412
     * <ul>
413
     * <li>
414
     * the <code>intersect</code> method returns <code>true</code> and
415
     * </li>
416
     * <li>
417
     * the calculations to determine whether or not the <code>Shape</code>
418
     * entirely contains the rectangular area are prohibitively expensive.
419
     * </li>
420
     * </ul>
421
     * 
422
     * This means that this method might return <code>false</code> even though
423
     * the <code>Shape</code> contains the rectangular area. The
424
     * <code>Area</code> class can be used to perform more accurate
425
     * computations of geometric intersection for any <code>Shape</code>
426
     * object if a more precise answer is required.
427
     * </p>
428
     *
429
     * @param x the coordinates of the specified rectangular area, x value
430
     * @param y the coordinates of the specified rectangular area, y value
431
     * @param w the width of the specified rectangular area
432
     * @param h the height of the specified rectangular area
433
     *
434
     * @return <code>true</code> if the interior of the <code>Shape</code>
435
     *         entirely contains the specified rectangular area;
436
     *         <code>false</code> otherwise or, if the <code>Shape</code>
437
     *         contains the rectangular area and the <code>intersects</code>
438
     *         method returns <code>true</code> and the containment
439
     *         calculations would be too expensive to perform.
440
     *
441
     * @see java.awt.geom.Area
442
     * @see #intersects
443
     */
444
    public boolean contains(double x, double y, double w, double h) {
445
        Geometry rect = createRectangle(x, y, w, h);
446

    
447
        return geometry.contains(rect);
448
    }
449

    
450
    /**
451
     * Returns an integer {@link Rectangle} that completely encloses the
452
     * <code>Shape</code>.  Note that there is no guarantee that the returned
453
     * <code>Rectangle</code> is the smallest bounding box that encloses the
454
     * <code>Shape</code>, only that the <code>Shape</code> lies entirely
455
     * within the indicated  <code>Rectangle</code>.  The returned
456
     * <code>Rectangle</code> might also fail to completely enclose the
457
     * <code>Shape</code> if the <code>Shape</code> overflows the limited
458
     * range of the integer data type.  The <code>getBounds2D</code> method
459
     * generally returns a tighter bounding box due to its greater flexibility
460
     * in representation.
461
     *
462
     * @return an integer <code>Rectangle</code> that completely encloses the
463
     *         <code>Shape</code>.
464
     *
465
     * @see #getBounds2D
466
     */
467
    public Rectangle getBounds() {
468
        Coordinate[] coords = geometry.getEnvelope().getCoordinates();
469

    
470
        // get out corners. the documentation doens't specify in which
471
        // order the bounding box coordinates are returned
472
        double x1;
473

    
474
        // get out corners. the documentation doens't specify in which
475
        // order the bounding box coordinates are returned
476
        double y1;
477

    
478
        // get out corners. the documentation doens't specify in which
479
        // order the bounding box coordinates are returned
480
        double x2;
481

    
482
        // get out corners. the documentation doens't specify in which
483
        // order the bounding box coordinates are returned
484
        double y2;
485
        x1 = x2 = coords[0].x;
486
        y1 = y2 = coords[0].y;
487

    
488
        for (int i = 1; i < 3; i++) {
489
            double x = coords[i].x;
490
            double y = coords[i].y;
491

    
492
            if (x < x1) {
493
                x1 = x;
494
            }
495

    
496
            if (x > x2) {
497
                x2 = x;
498
            }
499

    
500
            if (y < y1) {
501
                y1 = y;
502
            }
503

    
504
            if (y > y2) {
505
                y2 = y;
506
            }
507
        }
508

    
509
        x1 = Math.ceil(x1);
510
        x2 = Math.floor(x2);
511
        y1 = Math.ceil(y1);
512
        y2 = Math.floor(y2);
513

    
514
        return new Rectangle((int) x1, (int) y1, (int) (x2 - x1),
515
            (int) (y2 - y1));
516
    }
517

    
518
    /**
519
     * Returns a high precision and more accurate bounding box of the
520
     * <code>Shape</code> than the <code>getBounds</code> method. Note that
521
     * there is no guarantee that the returned {@link Rectangle2D} is the
522
     * smallest bounding box that encloses the <code>Shape</code>, only that
523
     * the <code>Shape</code> lies entirely within the indicated
524
     * <code>Rectangle2D</code>.  The bounding box returned by this method is
525
     * usually tighter than that returned by the <code>getBounds</code> method
526
     * and never fails due to overflow problems since the return value can be
527
     * an instance of the <code>Rectangle2D</code> that uses double precision
528
     * values to store the dimensions.
529
     *
530
     * @return an instance of <code>Rectangle2D</code> that is a high-precision
531
     *         bounding box of the <code>Shape</code>.
532
     *
533
     * @see #getBounds
534
     */
535
    public Rectangle2D getBounds2D() {
536
        Coordinate[] coords = geometry.getEnvelope().getCoordinates();
537

    
538
        // get out corners. the documentation doens't specify in which
539
        // order the bounding box coordinates are returned
540
        double x1;
541
        double y1;
542
        double x2;
543
        double y2;
544

    
545
        x1 = x2 = coords[0].x;
546
        y1 = y2 = coords[0].y;
547

    
548
        for (int i = 1; i < 3; i++) {
549
            double x = coords[i].x;
550
            double y = coords[i].y;
551

    
552
            if (x < x1) {
553
                x1 = x;
554
            }
555

    
556
            if (x > x2) {
557
                x2 = x;
558
            }
559

    
560
            if (y < y1) {
561
                y1 = y;
562
            }
563

    
564
            if (y > y2) {
565
                y2 = y;
566
            }
567
        }
568

    
569
        return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
570
    }
571

    
572
    /**
573
     * Returns an iterator object that iterates along the <code>Shape</code>
574
     * boundary and provides access to the geometry of the <code>Shape</code>
575
     * outline.  If an optional {@link AffineTransform} is specified, the
576
     * coordinates returned in the iteration are transformed accordingly.
577
     * 
578
     * <p>
579
     * Each call to this method returns a fresh <code>PathIterator</code>
580
     * object that traverses the geometry of the <code>Shape</code> object
581
     * independently from any other <code>PathIterator</code> objects in use
582
     * at the same time.
583
     * </p>
584
     * 
585
     * <p>
586
     * It is recommended, but not guaranteed, that objects implementing the
587
     * <code>Shape</code> interface isolate iterations that are in process
588
     * from any changes that might occur to the original object's geometry
589
     * during such iterations.
590
     * </p>
591
     * 
592
     * <p>
593
     * Before using a particular implementation of the <code>Shape</code>
594
     * interface in more than one thread simultaneously, refer to its
595
     * documentation to verify that it guarantees that iterations are isolated
596
     * from modifications.
597
     * </p>
598
     *
599
     * @param at an optional <code>AffineTransform</code> to be applied to the
600
     *        coordinates as they are returned in the iteration, or
601
     *        <code>null</code> if untransformed coordinates are desired
602
     *
603
     * @return a new <code>PathIterator</code> object, which independently
604
     *         traverses the geometry of the <code>Shape</code>.
605
     */
606
    public PathIterator getPathIterator(AffineTransform at) {
607
        AbstractLiteIterator pi = null;
608

    
609
        AffineTransform combined = null;
610
        if ((at == null) || at.isIdentity()) {
611
            combined = affineTransform;
612
        } else {
613
            combined = new AffineTransform(affineTransform);
614
            combined.concatenate(at);
615
        }
616

    
617
        // return iterator according to the kind of geometry we include
618
        if (this.geometry instanceof Point) {
619
            pi = new PointIterator((Point) geometry, combined);
620
        }
621

    
622
        if (this.geometry instanceof Polygon) {             
623

    
624
            pi = new PolygonIterator((Polygon) geometry, combined, generalize,
625
                    maxDistance);
626
        } else if (this.geometry instanceof LinearRing) {
627
            lineIterator.init((LinearRing) geometry, combined, generalize,
628
                    (float) maxDistance);
629
            pi = lineIterator;
630
        } else if (this.geometry instanceof LineString) {
631
//          if(((LineString) geometry).getCoordinateSequence() instanceof PackedCoordinateSequence.Double)
632
//              pi = new PackedLineIterator((LineString) geometry, combined, generalize,
633
//                      (float) maxDistance);
634
//          else
635
            if(combined == affineTransform)
636
                lineIterator.init((LineString) geometry, combined, generalize,
637
                        (float) maxDistance, xScale, yScale);
638
            else 
639
                lineIterator.init((LineString) geometry, combined, generalize,
640
                        (float) maxDistance);
641
            pi = lineIterator;
642
        } else if (this.geometry instanceof GeometryCollection) {
643
            collIterator.init((GeometryCollection) geometry,
644
                    combined, generalize, maxDistance);
645
            pi = collIterator;
646
        }
647
        return pi;
648
    }
649

    
650
    /**
651
     * Returns an iterator object that iterates along the <code>Shape</code>
652
     * boundary and provides access to a flattened view of the
653
     * <code>Shape</code> outline geometry.
654
     * 
655
     * <p>
656
     * Only SEG_MOVETO, SEG_LINETO, and SEG_CLOSE point types are returned by
657
     * the iterator.
658
     * </p>
659
     * 
660
     * <p>
661
     * If an optional <code>AffineTransform</code> is specified, the
662
     * coordinates returned in the iteration are transformed accordingly.
663
     * </p>
664
     * 
665
     * <p>
666
     * The amount of subdivision of the curved segments is controlled by the
667
     * <code>flatness</code> parameter, which specifies the maximum distance
668
     * that any point on the unflattened transformed curve can deviate from
669
     * the returned flattened path segments. Note that a limit on the accuracy
670
     * of the flattened path might be silently imposed, causing very small
671
     * flattening parameters to be treated as larger values.  This limit, if
672
     * there is one, is defined by the particular implementation that is used.
673
     * </p>
674
     * 
675
     * <p>
676
     * Each call to this method returns a fresh <code>PathIterator</code>
677
     * object that traverses the <code>Shape</code> object geometry
678
     * independently from any other <code>PathIterator</code> objects in use
679
     * at the same time.
680
     * </p>
681
     * 
682
     * <p>
683
     * It is recommended, but not guaranteed, that objects implementing the
684
     * <code>Shape</code> interface isolate iterations that are in process
685
     * from any changes that might occur to the original object's geometry
686
     * during such iterations.
687
     * </p>
688
     * 
689
     * <p>
690
     * Before using a particular implementation of this interface in more than
691
     * one thread simultaneously, refer to its documentation to verify that it
692
     * guarantees that iterations are isolated from modifications.
693
     * </p>
694
     *
695
     * @param at an optional <code>AffineTransform</code> to be applied to the
696
     *        coordinates as they are returned in the iteration, or
697
     *        <code>null</code> if untransformed coordinates are desired
698
     * @param flatness the maximum distance that the line segments used to
699
     *        approximate the curved segments are allowed to deviate from any
700
     *        point on the original curve
701
     *
702
     * @return a new <code>PathIterator</code> that independently traverses the
703
     *         <code>Shape</code> geometry.
704
     */
705
    public PathIterator getPathIterator(AffineTransform at, double flatness) {
706
        return getPathIterator(at);
707
    }
708

    
709
    /**
710
     * Tests if the interior of the <code>Shape</code> intersects the interior
711
     * of a specified <code>Rectangle2D</code>. This method might
712
     * conservatively return <code>true</code> when:
713
     * 
714
     * <ul>
715
     * <li>
716
     * there is a high probability that the <code>Rectangle2D</code> and the
717
     * <code>Shape</code> intersect, but
718
     * </li>
719
     * <li>
720
     * the calculations to accurately determine this intersection are
721
     * prohibitively expensive.
722
     * </li>
723
     * </ul>
724
     * 
725
     * This means that this method might return <code>true</code> even though
726
     * the <code>Rectangle2D</code> does not intersect the <code>Shape</code>.
727
     *
728
     * @param r the specified <code>Rectangle2D</code>
729
     *
730
     * @return <code>true</code> if the interior of the <code>Shape</code> and
731
     *         the interior of the specified <code>Rectangle2D</code>
732
     *         intersect, or are both highly likely to intersect and
733
     *         intersection     calculations would be too expensive to
734
     *         perform; <code>false</code>     otherwise.
735
     *
736
     * @see #intersects(double, double, double, double)
737
     */
738
    public boolean intersects(Rectangle2D r) {
739
        Geometry rect = rectangleToGeometry(r);
740

    
741
        return geometry.intersects(rect);
742
    }
743

    
744
    /**
745
     * Tests if the interior of the <code>Shape</code> intersects the interior
746
     * of a specified rectangular area. The rectangular area is considered to
747
     * intersect the <code>Shape</code> if any point is contained in both the
748
     * interior of the <code>Shape</code> and the specified rectangular area.
749
     * 
750
     * <p>
751
     * This method might conservatively return <code>true</code> when:
752
     * 
753
     * <ul>
754
     * <li>
755
     * there is a high probability that the rectangular area and the
756
     * <code>Shape</code> intersect, but
757
     * </li>
758
     * <li>
759
     * the calculations to accurately determine this intersection are
760
     * prohibitively expensive.
761
     * </li>
762
     * </ul>
763
     * 
764
     * This means that this method might return <code>true</code> even though
765
     * the rectangular area does not intersect the <code>Shape</code>. The
766
     * {@link java.awt.geom.Area Area} class can be used to perform more
767
     * accurate computations of geometric intersection for any
768
     * <code>Shape</code> object if a more precise answer is required.
769
     * </p>
770
     *
771
     * @param x the coordinates of the specified rectangular area, x value
772
     * @param y the coordinates of the specified rectangular area, y value
773
     * @param w the width of the specified rectangular area
774
     * @param h the height of the specified rectangular area
775
     *
776
     * @return <code>true</code> if the interior of the <code>Shape</code> and
777
     *         the interior of the rectangular area intersect, or are both
778
     *         highly likely to intersect and intersection calculations would
779
     *         be too expensive to perform; <code>false</code> otherwise.
780
     *
781
     * @see java.awt.geom.Area
782
     */
783
    public boolean intersects(double x, double y, double w, double h) {
784
        Geometry rect = createRectangle(x, y, w, h);
785

    
786
        return geometry.intersects(rect);
787
    }
788

    
789
    /**
790
     * Converts the Rectangle2D passed as parameter in a jts Geometry object
791
     *
792
     * @param r the rectangle to be converted
793
     *
794
     * @return a geometry with the same vertices as the rectangle
795
     */
796
    private Geometry rectangleToGeometry(Rectangle2D r) {
797
        return createRectangle(r.getMinX(), r.getMinY(), r.getWidth(),
798
            r.getHeight());
799
    }
800

    
801
    /**
802
     * Creates a jts Geometry object representing a rectangle with the given
803
     * parameters
804
     *
805
     * @param x left coordinate
806
     * @param y bottom coordinate
807
     * @param w width
808
     * @param h height
809
     *
810
     * @return a rectangle with the specified position and size
811
     */
812
    private Geometry createRectangle(double x, double y, double w, double h) {
813
        Coordinate[] coords = {
814
                new Coordinate(x, y), new Coordinate(x, y + h),
815
                new Coordinate(x + w, y + h), new Coordinate(x + w, y),
816
                new Coordinate(x, y)
817
            };
818
        LinearRing lr = geometry.getFactory().createLinearRing(coords);
819

    
820
        return geometry.getFactory().createPolygon(lr, null);
821
    }
822
    
823
    /**
824
     * Returns the affine transform for this lite shape
825
     * @return
826
     */
827
    public AffineTransform getAffineTransform() {
828
        return affineTransform;
829
    }
830

    
831
    public MathTransform getMathTransform() {
832
        return mathTransform;
833
    }
834

    
835
    public Geometry getGeometry() {
836
        return geometry;
837
    }
838

    
839
    public int getShapeType() {
840
        int type = -1;
841
        if (geometry instanceof LineString)
842
            type = FShape.LINE;
843
        if (geometry instanceof Polygon)
844
            type = FShape.POLYGON;
845
        if (geometry instanceof Point)
846
            type = FShape.POINT;
847
        if (geometry instanceof MultiPolygon)
848
            type = FShape.POLYGON;
849
        if (geometry instanceof MultiLineString)
850
            type = FShape.LINE;
851

    
852
        
853
        return type;
854
    }
855

    
856
    public FShape cloneFShape() {
857
        try {
858
            return (FShape) this.clone();
859
        } catch (CloneNotSupportedException e) {
860
            // TODO Auto-generated catch block
861
            e.printStackTrace();
862
        }
863
        return null;
864
    }
865

    
866
    public void reProject(ICoordTrans ct) {
867
        // TODO
868
        /* Point2D pt = new Point2D.Double();
869
        for (int i = 0; i < numCoords; i+=2)
870
        {
871
            pt.setLocation(pointCoords[i], pointCoords[i+1]);
872
            pt = ct.convert(pt,null);
873
            pointCoords[i] = pt.getX();
874
            pointCoords[i+1] = pt.getY();
875
        } */
876
        
877
    }
878

    
879
        public Handler[] getStretchingHandlers() {
880
                        ArrayList handlers = new ArrayList();
881
                        Coordinate[] coords = geometry.getCoordinates();
882
                        for (int i=0; i<coords.length; i++)
883
                        {
884
                                handlers.add(new PointHandler(i,coords[i]));
885
                        }
886

    
887
                        return (Handler[]) handlers.toArray(new Handler[0]);
888
        }
889

    
890
        public Handler[] getSelectHandlers() {
891
                ArrayList handlers = new ArrayList();
892
                Coordinate[] coords = geometry.getCoordinates();
893
                for (int i=0; i<coords.length; i++)
894
                {
895
                        handlers.add(new PointHandler(i,coords[i]));
896
                }
897

    
898
                return (Handler[]) handlers.toArray(new Handler[0]);
899
        }
900
}