Statistics
| Revision:

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

History | View | Annotate | Download (30.8 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

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

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

    
76

    
77
/**
78
 * A thin wrapper that adapts a JTS geometry to the Shape interface so that the
79
 * geometry can be used by java2d without coordinate cloning
80
 *
81
 * @author Andrea Aime
82
 * @version $Id: FLiteShape.java 2943 2005-09-22 11:27:52Z fjp $
83
 */
84
public class FLiteShape implements FShape, Cloneable {
85
    /** The wrapped JTS geometry */
86
    private Geometry geometry;
87

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

    
97
    private float xScale;
98

    
99
    private float yScale;
100
    
101
    private GeometryFactory geomFac;
102

    
103
    private MathTransform mathTransform;
104

    
105
    private static final AffineTransform IDENTITY = new AffineTransform();
106

    
107
    /**
108
     * Creates a new LiteShape object.
109
     *
110
     * @param geom - the wrapped geometry
111
     * @param at - the transformation applied to the geometry in order to get to the shape points
112
     * @param generalize - set to true if the geometry need to be generalized
113
     *        during rendering
114
     * @param maxDistance - distance used in the generalization process
115
     * @throws TransformException 
116
     * @throws FactoryException 
117
     */
118
    public FLiteShape(Geometry geom, AffineTransform at, MathTransform mathTransform, boolean generalize,
119
        double maxDistance) throws TransformException, FactoryException {
120
        this(geom, at, mathTransform, generalize);
121
        this.maxDistance = maxDistance;
122
    }
123

    
124
    /**
125
     * Creates a new LiteShape object.
126
     *
127
     * @param geom - the wrapped geometry
128
     * @param at - the transformation applied to the geometry in order to get to the shape points
129
     * @param generalize - set to true if the geometry need to be generalized
130
     *        during rendering
131
     * @param maxDistance - distance used in the generalization process
132
     * @throws TransformException 
133
     * @throws FactoryException 
134
     */
135
    public FLiteShape(Geometry geom, AffineTransform at, MathTransform mathTransform, boolean generalize) throws TransformException, FactoryException {
136
        if( geom!=null)
137
            this.geometry =getGeometryFactory().createGeometry(geom);
138
        if( at!=null )
139
            this.affineTransform = at;
140
        else
141
            this.affineTransform=IDENTITY;
142
            this.mathTransform=mathTransform;
143
            if( geometry!=null)
144
                transformGeometry(geometry);
145
        this.generalize = generalize;
146
        xScale = (float) Math.sqrt(
147
                (affineTransform.getScaleX() * affineTransform.getScaleX())
148
                + (affineTransform.getShearX() * affineTransform.getShearX()));
149
        yScale = (float) Math.sqrt(
150
                (affineTransform.getScaleY() * affineTransform.getScaleY())
151
                + (affineTransform.getShearY() * affineTransform.getShearY()));
152
    }
153
    /**
154
     * Creates a new LiteShape object.
155
     *
156
     * @param geom - the wrapped geometry
157
     * @param at - the transformation applied to the geometry in order to get to the shape points
158
     * @param generalize - set to true if the geometry need to be generalized
159
     *        during rendering
160
     * @param maxDistance - distance used in the generalization process
161
     * @throws TransformException 
162
     */
163
    public FLiteShape(Geometry geom, AffineTransform at, boolean generalize,
164
        double maxDistance){
165
        this(geom, at, generalize);
166
        this.maxDistance = maxDistance;
167
    }
168
    
169
    public FLiteShape(Geometry geom)
170
    {
171
        this(geom, null, false);
172
    }
173

    
174
    /**
175
     * Creates a new LiteShape object.
176
     *
177
     * @param geom - the wrapped geometry
178
     * @param at - the transformation applied to the geometry in order to get to the shape points
179
     * @param generalize - set to true if the geometry need to be generalized
180
     *        during rendering
181
     * @param maxDistance - distance used in the generalization process
182
     * @throws TransformException 
183
     */
184
    public FLiteShape(Geometry geom, AffineTransform at, boolean generalize){
185
        if( geom!=null)
186
            this.geometry =getGeometryFactory().createGeometry(geom);
187
        if( at!=null )
188
            this.affineTransform = at;
189
        else
190
            this.affineTransform=new AffineTransform();
191
        this.generalize = generalize;
192
            try {
193
                if( geometry!=null)
194
                    transformGeometry(geometry);
195
            } catch (Exception e) {
196
                affineTransform=at;
197
                geometry=geom;
198
            }
199
        xScale = (float) Math.sqrt(
200
                (affineTransform.getScaleX() * affineTransform.getScaleX())
201
                + (affineTransform.getShearX() * affineTransform.getShearX()));
202
        yScale = (float) Math.sqrt(
203
                (affineTransform.getScaleY() * affineTransform.getScaleY())
204
                + (affineTransform.getShearY() * affineTransform.getShearY()));
205
    }
206
    
207
    private void transformGeometry(Geometry geometry) throws TransformException, FactoryException {
208
        
209
        if( mathTransform==null || mathTransform.isIdentity() ){
210
            if( !affineTransform.isIdentity() ){
211
                MathTransformFactory factory=FactoryFinder.getMathTransformFactory(null);
212
                mathTransform=factory.createAffineTransform(new GeneralMatrix(affineTransform));
213
                affineTransform=IDENTITY;
214
            }
215
        }else if( !affineTransform.isIdentity() ){
216
            MathTransformFactory factory=FactoryFinder.getMathTransformFactory(null);
217
            factory.createConcatenatedTransform(mathTransform, factory.createAffineTransform(new GeneralMatrix(affineTransform)));
218
            affineTransform=IDENTITY;
219
        }
220
        
221
        if( mathTransform==null || mathTransform.isIdentity() )
222
            return;
223
        
224
        
225
        CoordinateSequenceTransformer transformer=new InPlaceCoordinateSequenceTransformer();
226
        if (geometry instanceof GeometryCollection) {
227
            GeometryCollection collection=(GeometryCollection)geometry;
228
            for (int i = 0; i < collection.getNumGeometries(); i++) {
229
                transformGeometry(collection.getGeometryN(i));
230
            }
231
        }else if (geometry instanceof Point) {
232
            transformer.transform(((Point)geometry).getCoordinateSequence(), mathTransform);
233
        }else if (geometry instanceof Polygon) {     
234
            Polygon polygon=(Polygon) geometry;
235
            transformGeometry(polygon.getExteriorRing());
236
            for (int i = 0; i < polygon.getNumInteriorRing(); i++) {
237
                transformGeometry(polygon.getInteriorRingN(i));
238
            }
239
        } else if (geometry instanceof LineString) {
240
            transformer.transform(((LineString)geometry).getCoordinateSequence(), mathTransform);
241
        } 
242
    }
243

    
244
    private GeometryFactory getGeometryFactory() {
245
        if (geomFac == null) {
246
            geomFac = new GeometryFactory(new PackedCoordinateSequenceFactory());
247
        }
248

    
249
        return geomFac;
250
    }
251

    
252
    /**
253
     * Sets the geometry contained in this lite shape. Convenient to reuse this
254
     * object instead of creating it again and again during rendering
255
     *
256
     * @param g
257
     * @throws TransformException 
258
     * @throws FactoryException 
259
     */
260
    public void setGeometry(Geometry g) throws TransformException, FactoryException {
261
        if( g!=null){
262
            this.geometry =getGeometryFactory().createGeometry(g);
263
            transformGeometry(geometry);
264
        }
265
    }
266
    public void transform(AffineTransform at)
267
    {
268
        affineTransform=at;
269
        try {
270
            transformGeometry(geometry);
271
        } catch (TransformException e) {
272
            // TODO Auto-generated catch block
273
            e.printStackTrace();
274
        } catch (FactoryException e) {
275
            // TODO Auto-generated catch block
276
            e.printStackTrace();
277
        }        
278
        xScale = (float) Math.sqrt(
279
            (affineTransform.getScaleX() * affineTransform.getScaleX())
280
            + (affineTransform.getShearX() * affineTransform.getShearX()));
281
        yScale = (float) Math.sqrt(
282
            (affineTransform.getScaleY() * affineTransform.getScaleY())
283
            + (affineTransform.getShearY() * affineTransform.getShearY()));
284
        
285
    }
286

    
287
    /**
288
     * Tests if the interior of the <code>Shape</code> entirely contains the
289
     * specified <code>Rectangle2D</code>. This method might conservatively
290
     * return <code>false</code> when:
291
     * 
292
     * <ul>
293
     * <li>
294
     * the <code>intersect</code> method returns <code>true</code> and
295
     * </li>
296
     * <li>
297
     * the calculations to determine whether or not the <code>Shape</code>
298
     * entirely contains the <code>Rectangle2D</code> are prohibitively
299
     * expensive.
300
     * </li>
301
     * </ul>
302
     * 
303
     * This means that this method might return <code>false</code> even though
304
     * the <code>Shape</code> contains the <code>Rectangle2D</code>. The
305
     * <code>Area</code> class can be used to perform more accurate
306
     * computations of geometric intersection for any <code>Shape</code>
307
     * object if a more precise answer is required.
308
     *
309
     * @param r The specified <code>Rectangle2D</code>
310
     *
311
     * @return <code>true</code> if the interior of the <code>Shape</code>
312
     *         entirely contains the <code>Rectangle2D</code>;
313
     *         <code>false</code> otherwise or, if the <code>Shape</code>
314
     *         contains the <code>Rectangle2D</code> and the
315
     *         <code>intersects</code> method returns <code>true</code> and
316
     *         the containment calculations would be too expensive to perform.
317
     *
318
     * @see #contains(double, double, double, double)
319
     */
320
    public boolean contains(Rectangle2D r) {
321
        Geometry rect = rectangleToGeometry(r);
322

    
323
        return geometry.contains(rect);
324
    }
325

    
326
    /**
327
     * Tests if a specified {@link Point2D} is inside the boundary of the
328
     * <code>Shape</code>.
329
     *
330
     * @param p a specified <code>Point2D</code>
331
     *
332
     * @return <code>true</code> if the specified <code>Point2D</code> is
333
     *         inside the boundary of the <code>Shape</code>;
334
     *         <code>false</code> otherwise.
335
     */
336
    public boolean contains(Point2D p) {
337
        Coordinate coord = new Coordinate(p.getX(), p.getY());
338
        Geometry point = geometry.getFactory().createPoint(coord);
339

    
340
        return geometry.contains(point);
341
    }
342

    
343
    /**
344
     * Tests if the specified coordinates are inside the boundary of the
345
     * <code>Shape</code>.
346
     *
347
     * @param x the specified coordinates, x value
348
     * @param y the specified coordinates, y value
349
     *
350
     * @return <code>true</code> if the specified coordinates are inside the
351
     *         <code>Shape</code> boundary; <code>false</code> otherwise.
352
     */
353
    public boolean contains(double x, double y) {
354
        Coordinate coord = new Coordinate(x, y);
355
        Geometry point = geometry.getFactory().createPoint(coord);
356

    
357
        return geometry.contains(point);
358
    }
359

    
360
    /**
361
     * Tests if the interior of the <code>Shape</code> entirely contains the
362
     * specified rectangular area.  All coordinates that lie inside the
363
     * rectangular area must lie within the <code>Shape</code> for the entire
364
     * rectanglar area to be considered contained within the
365
     * <code>Shape</code>.
366
     * 
367
     * <p>
368
     * This method might conservatively return <code>false</code> when:
369
     * 
370
     * <ul>
371
     * <li>
372
     * the <code>intersect</code> method returns <code>true</code> and
373
     * </li>
374
     * <li>
375
     * the calculations to determine whether or not the <code>Shape</code>
376
     * entirely contains the rectangular area are prohibitively expensive.
377
     * </li>
378
     * </ul>
379
     * 
380
     * This means that this method might return <code>false</code> even though
381
     * the <code>Shape</code> contains the rectangular area. The
382
     * <code>Area</code> class can be used to perform more accurate
383
     * computations of geometric intersection for any <code>Shape</code>
384
     * object if a more precise answer is required.
385
     * </p>
386
     *
387
     * @param x the coordinates of the specified rectangular area, x value
388
     * @param y the coordinates of the specified rectangular area, y value
389
     * @param w the width of the specified rectangular area
390
     * @param h the height of the specified rectangular area
391
     *
392
     * @return <code>true</code> if the interior of the <code>Shape</code>
393
     *         entirely contains the specified rectangular area;
394
     *         <code>false</code> otherwise or, if the <code>Shape</code>
395
     *         contains the rectangular area and the <code>intersects</code>
396
     *         method returns <code>true</code> and the containment
397
     *         calculations would be too expensive to perform.
398
     *
399
     * @see java.awt.geom.Area
400
     * @see #intersects
401
     */
402
    public boolean contains(double x, double y, double w, double h) {
403
        Geometry rect = createRectangle(x, y, w, h);
404

    
405
        return geometry.contains(rect);
406
    }
407

    
408
    /**
409
     * Returns an integer {@link Rectangle} that completely encloses the
410
     * <code>Shape</code>.  Note that there is no guarantee that the returned
411
     * <code>Rectangle</code> is the smallest bounding box that encloses the
412
     * <code>Shape</code>, only that the <code>Shape</code> lies entirely
413
     * within the indicated  <code>Rectangle</code>.  The returned
414
     * <code>Rectangle</code> might also fail to completely enclose the
415
     * <code>Shape</code> if the <code>Shape</code> overflows the limited
416
     * range of the integer data type.  The <code>getBounds2D</code> method
417
     * generally returns a tighter bounding box due to its greater flexibility
418
     * in representation.
419
     *
420
     * @return an integer <code>Rectangle</code> that completely encloses the
421
     *         <code>Shape</code>.
422
     *
423
     * @see #getBounds2D
424
     */
425
    public Rectangle getBounds() {
426
        Coordinate[] coords = geometry.getEnvelope().getCoordinates();
427

    
428
        // get out corners. the documentation doens't specify in which
429
        // order the bounding box coordinates are returned
430
        double x1;
431

    
432
        // get out corners. the documentation doens't specify in which
433
        // order the bounding box coordinates are returned
434
        double y1;
435

    
436
        // get out corners. the documentation doens't specify in which
437
        // order the bounding box coordinates are returned
438
        double x2;
439

    
440
        // get out corners. the documentation doens't specify in which
441
        // order the bounding box coordinates are returned
442
        double y2;
443
        x1 = x2 = coords[0].x;
444
        y1 = y2 = coords[0].y;
445

    
446
        for (int i = 1; i < 3; i++) {
447
            double x = coords[i].x;
448
            double y = coords[i].y;
449

    
450
            if (x < x1) {
451
                x1 = x;
452
            }
453

    
454
            if (x > x2) {
455
                x2 = x;
456
            }
457

    
458
            if (y < y1) {
459
                y1 = y;
460
            }
461

    
462
            if (y > y2) {
463
                y2 = y;
464
            }
465
        }
466

    
467
        x1 = Math.ceil(x1);
468
        x2 = Math.floor(x2);
469
        y1 = Math.ceil(y1);
470
        y2 = Math.floor(y2);
471

    
472
        return new Rectangle((int) x1, (int) y1, (int) (x2 - x1),
473
            (int) (y2 - y1));
474
    }
475

    
476
    /**
477
     * Returns a high precision and more accurate bounding box of the
478
     * <code>Shape</code> than the <code>getBounds</code> method. Note that
479
     * there is no guarantee that the returned {@link Rectangle2D} is the
480
     * smallest bounding box that encloses the <code>Shape</code>, only that
481
     * the <code>Shape</code> lies entirely within the indicated
482
     * <code>Rectangle2D</code>.  The bounding box returned by this method is
483
     * usually tighter than that returned by the <code>getBounds</code> method
484
     * and never fails due to overflow problems since the return value can be
485
     * an instance of the <code>Rectangle2D</code> that uses double precision
486
     * values to store the dimensions.
487
     *
488
     * @return an instance of <code>Rectangle2D</code> that is a high-precision
489
     *         bounding box of the <code>Shape</code>.
490
     *
491
     * @see #getBounds
492
     */
493
    public Rectangle2D getBounds2D() {
494
        Coordinate[] coords = geometry.getEnvelope().getCoordinates();
495

    
496
        // get out corners. the documentation doens't specify in which
497
        // order the bounding box coordinates are returned
498
        double x1;
499
        double y1;
500
        double x2;
501
        double y2;
502

    
503
        x1 = x2 = coords[0].x;
504
        y1 = y2 = coords[0].y;
505

    
506
        for (int i = 1; i < 3; i++) {
507
            double x = coords[i].x;
508
            double y = coords[i].y;
509

    
510
            if (x < x1) {
511
                x1 = x;
512
            }
513

    
514
            if (x > x2) {
515
                x2 = x;
516
            }
517

    
518
            if (y < y1) {
519
                y1 = y;
520
            }
521

    
522
            if (y > y2) {
523
                y2 = y;
524
            }
525
        }
526

    
527
        return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
528
    }
529

    
530
    /**
531
     * Returns an iterator object that iterates along the <code>Shape</code>
532
     * boundary and provides access to the geometry of the <code>Shape</code>
533
     * outline.  If an optional {@link AffineTransform} is specified, the
534
     * coordinates returned in the iteration are transformed accordingly.
535
     * 
536
     * <p>
537
     * Each call to this method returns a fresh <code>PathIterator</code>
538
     * object that traverses the geometry of the <code>Shape</code> object
539
     * independently from any other <code>PathIterator</code> objects in use
540
     * at the same time.
541
     * </p>
542
     * 
543
     * <p>
544
     * It is recommended, but not guaranteed, that objects implementing the
545
     * <code>Shape</code> interface isolate iterations that are in process
546
     * from any changes that might occur to the original object's geometry
547
     * during such iterations.
548
     * </p>
549
     * 
550
     * <p>
551
     * Before using a particular implementation of the <code>Shape</code>
552
     * interface in more than one thread simultaneously, refer to its
553
     * documentation to verify that it guarantees that iterations are isolated
554
     * from modifications.
555
     * </p>
556
     *
557
     * @param at an optional <code>AffineTransform</code> to be applied to the
558
     *        coordinates as they are returned in the iteration, or
559
     *        <code>null</code> if untransformed coordinates are desired
560
     *
561
     * @return a new <code>PathIterator</code> object, which independently
562
     *         traverses the geometry of the <code>Shape</code>.
563
     */
564
    public PathIterator getPathIterator(AffineTransform at) {
565
        AbstractLiteIterator pi = null;
566

    
567
        AffineTransform combined = null;
568
        if ((at == null) || at.isIdentity()) {
569
            combined = affineTransform;
570
        } else {
571
            combined = new AffineTransform(affineTransform);
572
            combined.concatenate(at);
573
        }
574

    
575
        // return iterator according to the kind of geometry we include
576
        if (this.geometry instanceof Point) {
577
            pi = new PointIterator((Point) geometry, combined);
578
        }
579

    
580
        if (this.geometry instanceof Polygon) {             
581

    
582
            pi = new PolygonIterator((Polygon) geometry, combined, generalize,
583
                    maxDistance);
584
        } else if (this.geometry instanceof LinearRing) {
585
            lineIterator.init((LinearRing) geometry, combined, generalize,
586
                    (float) maxDistance);
587
            pi = lineIterator;
588
        } else if (this.geometry instanceof LineString) {
589
//          if(((LineString) geometry).getCoordinateSequence() instanceof PackedCoordinateSequence.Double)
590
//              pi = new PackedLineIterator((LineString) geometry, combined, generalize,
591
//                      (float) maxDistance);
592
//          else
593
            if(combined == affineTransform)
594
                lineIterator.init((LineString) geometry, combined, generalize,
595
                        (float) maxDistance, xScale, yScale);
596
            else 
597
                lineIterator.init((LineString) geometry, combined, generalize,
598
                        (float) maxDistance);
599
            pi = lineIterator;
600
        } else if (this.geometry instanceof GeometryCollection) {
601
            collIterator.init((GeometryCollection) geometry,
602
                    combined, generalize, maxDistance);
603
            pi = collIterator;
604
        }
605
        return pi;
606
    }
607

    
608
    /**
609
     * Returns an iterator object that iterates along the <code>Shape</code>
610
     * boundary and provides access to a flattened view of the
611
     * <code>Shape</code> outline geometry.
612
     * 
613
     * <p>
614
     * Only SEG_MOVETO, SEG_LINETO, and SEG_CLOSE point types are returned by
615
     * the iterator.
616
     * </p>
617
     * 
618
     * <p>
619
     * If an optional <code>AffineTransform</code> is specified, the
620
     * coordinates returned in the iteration are transformed accordingly.
621
     * </p>
622
     * 
623
     * <p>
624
     * The amount of subdivision of the curved segments is controlled by the
625
     * <code>flatness</code> parameter, which specifies the maximum distance
626
     * that any point on the unflattened transformed curve can deviate from
627
     * the returned flattened path segments. Note that a limit on the accuracy
628
     * of the flattened path might be silently imposed, causing very small
629
     * flattening parameters to be treated as larger values.  This limit, if
630
     * there is one, is defined by the particular implementation that is used.
631
     * </p>
632
     * 
633
     * <p>
634
     * Each call to this method returns a fresh <code>PathIterator</code>
635
     * object that traverses the <code>Shape</code> object geometry
636
     * independently from any other <code>PathIterator</code> objects in use
637
     * at the same time.
638
     * </p>
639
     * 
640
     * <p>
641
     * It is recommended, but not guaranteed, that objects implementing the
642
     * <code>Shape</code> interface isolate iterations that are in process
643
     * from any changes that might occur to the original object's geometry
644
     * during such iterations.
645
     * </p>
646
     * 
647
     * <p>
648
     * Before using a particular implementation of this interface in more than
649
     * one thread simultaneously, refer to its documentation to verify that it
650
     * guarantees that iterations are isolated from modifications.
651
     * </p>
652
     *
653
     * @param at an optional <code>AffineTransform</code> to be applied to the
654
     *        coordinates as they are returned in the iteration, or
655
     *        <code>null</code> if untransformed coordinates are desired
656
     * @param flatness the maximum distance that the line segments used to
657
     *        approximate the curved segments are allowed to deviate from any
658
     *        point on the original curve
659
     *
660
     * @return a new <code>PathIterator</code> that independently traverses the
661
     *         <code>Shape</code> geometry.
662
     */
663
    public PathIterator getPathIterator(AffineTransform at, double flatness) {
664
        return getPathIterator(at);
665
    }
666

    
667
    /**
668
     * Tests if the interior of the <code>Shape</code> intersects the interior
669
     * of a specified <code>Rectangle2D</code>. This method might
670
     * conservatively return <code>true</code> when:
671
     * 
672
     * <ul>
673
     * <li>
674
     * there is a high probability that the <code>Rectangle2D</code> and the
675
     * <code>Shape</code> intersect, but
676
     * </li>
677
     * <li>
678
     * the calculations to accurately determine this intersection are
679
     * prohibitively expensive.
680
     * </li>
681
     * </ul>
682
     * 
683
     * This means that this method might return <code>true</code> even though
684
     * the <code>Rectangle2D</code> does not intersect the <code>Shape</code>.
685
     *
686
     * @param r the specified <code>Rectangle2D</code>
687
     *
688
     * @return <code>true</code> if the interior of the <code>Shape</code> and
689
     *         the interior of the specified <code>Rectangle2D</code>
690
     *         intersect, or are both highly likely to intersect and
691
     *         intersection     calculations would be too expensive to
692
     *         perform; <code>false</code>     otherwise.
693
     *
694
     * @see #intersects(double, double, double, double)
695
     */
696
    public boolean intersects(Rectangle2D r) {
697
        Geometry rect = rectangleToGeometry(r);
698

    
699
        return geometry.intersects(rect);
700
    }
701

    
702
    /**
703
     * Tests if the interior of the <code>Shape</code> intersects the interior
704
     * of a specified rectangular area. The rectangular area is considered to
705
     * intersect the <code>Shape</code> if any point is contained in both the
706
     * interior of the <code>Shape</code> and the specified rectangular area.
707
     * 
708
     * <p>
709
     * This method might conservatively return <code>true</code> when:
710
     * 
711
     * <ul>
712
     * <li>
713
     * there is a high probability that the rectangular area and the
714
     * <code>Shape</code> intersect, but
715
     * </li>
716
     * <li>
717
     * the calculations to accurately determine this intersection are
718
     * prohibitively expensive.
719
     * </li>
720
     * </ul>
721
     * 
722
     * This means that this method might return <code>true</code> even though
723
     * the rectangular area does not intersect the <code>Shape</code>. The
724
     * {@link java.awt.geom.Area Area} class can be used to perform more
725
     * accurate computations of geometric intersection for any
726
     * <code>Shape</code> object if a more precise answer is required.
727
     * </p>
728
     *
729
     * @param x the coordinates of the specified rectangular area, x value
730
     * @param y the coordinates of the specified rectangular area, y value
731
     * @param w the width of the specified rectangular area
732
     * @param h the height of the specified rectangular area
733
     *
734
     * @return <code>true</code> if the interior of the <code>Shape</code> and
735
     *         the interior of the rectangular area intersect, or are both
736
     *         highly likely to intersect and intersection calculations would
737
     *         be too expensive to perform; <code>false</code> otherwise.
738
     *
739
     * @see java.awt.geom.Area
740
     */
741
    public boolean intersects(double x, double y, double w, double h) {
742
        Geometry rect = createRectangle(x, y, w, h);
743

    
744
        return geometry.intersects(rect);
745
    }
746

    
747
    /**
748
     * Converts the Rectangle2D passed as parameter in a jts Geometry object
749
     *
750
     * @param r the rectangle to be converted
751
     *
752
     * @return a geometry with the same vertices as the rectangle
753
     */
754
    private Geometry rectangleToGeometry(Rectangle2D r) {
755
        return createRectangle(r.getMinX(), r.getMinY(), r.getWidth(),
756
            r.getHeight());
757
    }
758

    
759
    /**
760
     * Creates a jts Geometry object representing a rectangle with the given
761
     * parameters
762
     *
763
     * @param x left coordinate
764
     * @param y bottom coordinate
765
     * @param w width
766
     * @param h height
767
     *
768
     * @return a rectangle with the specified position and size
769
     */
770
    private Geometry createRectangle(double x, double y, double w, double h) {
771
        Coordinate[] coords = {
772
                new Coordinate(x, y), new Coordinate(x, y + h),
773
                new Coordinate(x + w, y + h), new Coordinate(x + w, y),
774
                new Coordinate(x, y)
775
            };
776
        LinearRing lr = geometry.getFactory().createLinearRing(coords);
777

    
778
        return geometry.getFactory().createPolygon(lr, null);
779
    }
780
    
781
    /**
782
     * Returns the affine transform for this lite shape
783
     * @return
784
     */
785
    public AffineTransform getAffineTransform() {
786
        return affineTransform;
787
    }
788

    
789
    public MathTransform getMathTransform() {
790
        return mathTransform;
791
    }
792

    
793
    public Geometry getGeometry() {
794
        return geometry;
795
    }
796

    
797
    public int getShapeType() {
798
        int type = -1;
799
        if (geometry instanceof LineString)
800
            type = FShape.LINE;
801
        if (geometry instanceof Polygon)
802
            type = FShape.POLYGON;
803
        if (geometry instanceof Point)
804
            type = FShape.POINT;
805
        if (geometry instanceof MultiPolygon)
806
            type = FShape.POLYGON;
807
        if (geometry instanceof MultiLineString)
808
            type = FShape.LINE;
809

    
810
        
811
        return type;
812
    }
813

    
814
    public FShape cloneFShape() {
815
        try {
816
            return (FShape) this.clone();
817
        } catch (CloneNotSupportedException e) {
818
            // TODO Auto-generated catch block
819
            e.printStackTrace();
820
        }
821
        return null;
822
    }
823

    
824
    public void reProject(ICoordTrans ct) {
825
        // TODO
826
        /* Point2D pt = new Point2D.Double();
827
        for (int i = 0; i < numCoords; i+=2)
828
        {
829
            pt.setLocation(pointCoords[i], pointCoords[i+1]);
830
            pt = ct.convert(pt,null);
831
            pointCoords[i] = pt.getX();
832
            pointCoords[i+1] = pt.getY();
833
        } */
834
        
835
    }
836
}