Statistics
| Revision:

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

History | View | Annotate | Download (32.1 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.IFinalHandler;
67
import com.iver.cit.gvsig.fmap.core.gt2.factory.FactoryFinder;
68
import com.vividsolutions.jts.geom.Coordinate;
69
import com.vividsolutions.jts.geom.Geometry;
70
import com.vividsolutions.jts.geom.GeometryCollection;
71
import com.vividsolutions.jts.geom.GeometryFactory;
72
import com.vividsolutions.jts.geom.LineString;
73
import com.vividsolutions.jts.geom.LinearRing;
74
import com.vividsolutions.jts.geom.MultiLineString;
75
import com.vividsolutions.jts.geom.MultiPolygon;
76
import com.vividsolutions.jts.geom.Point;
77
import com.vividsolutions.jts.geom.Polygon;
78
import com.vividsolutions.jts.geom.impl.PackedCoordinateSequenceFactory;
79

    
80

    
81
/**
82
 * A thin wrapper that adapts a JTS geometry to the Shape interface so that the
83
 * geometry can be used by java2d without coordinate cloning
84
 *
85
 * @author Andrea Aime
86
 * @version $Id: FLiteShape.java 8949 2006-11-22 11:40:05Z caballero $
87
 */
88
public class FLiteShape implements FShape, Cloneable {
89
    /** The wrapped JTS geometry */
90
    private Geometry geometry;
91

    
92
    /** The transform needed to go from the object space to the device space */
93
    private AffineTransform affineTransform = null;
94
    private boolean generalize = false;
95
    private double maxDistance = 1;
96

    
97
    // cached iterators
98
    private LineIterator lineIterator = new LineIterator();
99
    private GeomCollectionIterator collIterator = new GeomCollectionIterator();
100

    
101
    private float xScale;
102

    
103
    private float yScale;
104

    
105
    private GeometryFactory geomFac;
106

    
107
    private MathTransform mathTransform;
108

    
109
    private static final AffineTransform IDENTITY = new AffineTransform();
110

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

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

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

    
149

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

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

    
212
    public FLiteShape(Geometry geom)
213
    {
214
        this(geom, null, false);
215
    }
216

    
217
    /**
218
     * Creates a new LiteShape object.
219
     *
220
     * @param geom - the wrapped geometry
221
     * @param at - the transformation applied to the geometry in order to get to the shape points
222
     * @param generalize - set to true if the geometry need to be generalized
223
     *        during rendering
224
     * @param maxDistance - distance used in the generalization process
225
     * @throws TransformException
226
     */
227
    public FLiteShape(Geometry geom, AffineTransform at, boolean generalize){
228
        if( geom!=null)
229
            this.geometry =getGeometryFactory().createGeometry(geom);
230
        if( at!=null )
231
            this.affineTransform = at;
232
        else
233
            this.affineTransform=new AffineTransform();
234
        this.generalize = generalize;
235
            try {
236
                if( geometry!=null)
237
                    transformGeometry(geometry);
238
            } catch (Exception e) {
239
                affineTransform=at;
240
                geometry=geom;
241
            }
242
        xScale = (float) Math.sqrt(
243
                (affineTransform.getScaleX() * affineTransform.getScaleX())
244
                + (affineTransform.getShearX() * affineTransform.getShearX()));
245
        yScale = (float) Math.sqrt(
246
                (affineTransform.getScaleY() * affineTransform.getScaleY())
247
                + (affineTransform.getShearY() * affineTransform.getShearY()));
248
    }
249

    
250
    private void transformGeometry(Geometry geometry) throws TransformException, FactoryException {
251

    
252
        if( mathTransform==null || mathTransform.isIdentity() ){
253
            if( !affineTransform.isIdentity() ){
254
                MathTransformFactory factory=FactoryFinder.getMathTransformFactory(null);
255
                mathTransform=factory.createAffineTransform(new GeneralMatrix(affineTransform));
256
                affineTransform=IDENTITY;
257
            }
258
        }else if( !affineTransform.isIdentity() ){
259
            MathTransformFactory factory=FactoryFinder.getMathTransformFactory(null);
260
            factory.createConcatenatedTransform(mathTransform, factory.createAffineTransform(new GeneralMatrix(affineTransform)));
261
            affineTransform=IDENTITY;
262
        }
263

    
264
        if( mathTransform==null || mathTransform.isIdentity() )
265
            return;
266

    
267

    
268
        CoordinateSequenceTransformer transformer=new InPlaceCoordinateSequenceTransformer();
269
        if (geometry instanceof GeometryCollection) {
270
            GeometryCollection collection=(GeometryCollection)geometry;
271
            for (int i = 0; i < collection.getNumGeometries(); i++) {
272
                transformGeometry(collection.getGeometryN(i));
273
            }
274
        }else if (geometry instanceof Point) {
275
            transformer.transform(((Point)geometry).getCoordinateSequence(), mathTransform);
276
        }else if (geometry instanceof Polygon) {
277
            Polygon polygon=(Polygon) geometry;
278
            transformGeometry(polygon.getExteriorRing());
279
            for (int i = 0; i < polygon.getNumInteriorRing(); i++) {
280
                transformGeometry(polygon.getInteriorRingN(i));
281
            }
282
        } else if (geometry instanceof LineString) {
283
            transformer.transform(((LineString)geometry).getCoordinateSequence(), mathTransform);
284
        }
285
    }
286

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

    
292
        return geomFac;
293
    }
294

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

    
328
    }
329

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
623
        if (this.geometry instanceof Polygon) {
624

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

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

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

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

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

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

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

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

    
821
        return geometry.getFactory().createPolygon(lr, null);
822
    }
823

    
824
    /**
825
     * Returns the affine transform for this lite shape
826
     * @return
827
     */
828
    public AffineTransform getAffineTransform() {
829
        return affineTransform;
830
    }
831

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

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

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

    
853

    
854
        return type;
855
    }
856

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

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

    
878
    }
879

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

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

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

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