Statistics
| Revision:

svn-gvsig-desktop / branches / dal_time_support / libraries / libFMap_mapcontext / src / org / gvsig / fmap / mapcontext / ViewPort.java @ 35134

History | View | Annotate | Download (58.8 KB)

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

    
43
import java.awt.Color;
44
import java.awt.Dimension;
45
import java.awt.Toolkit;
46
import java.awt.geom.AffineTransform;
47
import java.awt.geom.NoninvertibleTransformException;
48
import java.awt.geom.Point2D;
49
import java.awt.geom.Rectangle2D;
50
import java.util.ArrayList;
51

    
52
import org.cresques.cts.GeoCalc;
53
import org.cresques.cts.IProjection;
54
import org.cresques.cts.UTM;
55
import org.slf4j.Logger;
56
import org.slf4j.LoggerFactory;
57

    
58
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
59
import org.gvsig.fmap.geom.GeometryLocator;
60
import org.gvsig.fmap.geom.GeometryManager;
61
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
62
import org.gvsig.fmap.geom.primitive.Envelope;
63
import org.gvsig.fmap.mapcontext.events.ColorEvent;
64
import org.gvsig.fmap.mapcontext.events.ExtentEvent;
65
import org.gvsig.fmap.mapcontext.events.ProjectionEvent;
66
import org.gvsig.fmap.mapcontext.events.listeners.ViewPortListener;
67
import org.gvsig.timesupport.Time;
68
import org.gvsig.tools.ToolsLocator;
69
import org.gvsig.tools.dynobject.DynStruct;
70
import org.gvsig.tools.lang.Cloneable;
71
import org.gvsig.tools.persistence.PersistenceManager;
72
import org.gvsig.tools.persistence.Persistent;
73
import org.gvsig.tools.persistence.PersistentState;
74
import org.gvsig.tools.persistence.exception.PersistenceException;
75
import org.gvsig.tools.util.Callable;
76

    
77
/**
78
 * <p>
79
 * <code>ViewPort</code> class represents the logic needed to transform a
80
 * rectangular area of a map to the available area in screen to display it.
81
 * </p>
82
 * 
83
 * <p>
84
 * Includes an affine transformation, between the rectangular area selected of
85
 * the external map, in its own <i>map coordinates</i>, to the rectangular area
86
 * available of a view in <i>screen coordinates</i>.
87
 * </p>
88
 * 
89
 * <p>
90
 * Elements:
91
 * <ul>
92
 * <li><i>extent</i>: the area selected of the map, in <i>map coordinates</i>.
93
 * <li><i>imageSize</i>: width and height in pixels (<i>screen coordinates</i>)
94
 * of the area available in screen to display the area selected of the map.
95
 * <li><i>adjustedExtent</i>: the area selected must be an scale of
96
 * <i>imageSize</i>.<br>
97
 * This implies adapt the extent, preserving and centering it, and adding around
98
 * the needed area to fill all the image size. That added area will be extracted
99
 * from the original map, wherever exists, and filled with the background color
100
 * wherever not.
101
 * <li><i>scale</i>: the scale between the adjusted extent and the image size.
102
 * <li><i>backColor</i>: the default background color in the view, if there is
103
 * no map.
104
 * <li><i>trans</i>: the affine transformation.
105
 * <li><i>proj</i>: map projection used in this view.
106
 * <li><i>distanceUnits</i>: distance measurement units, of data in screen.
107
 * <li><i>mapUnits</i>: measurement units, of data in map.
108
 * <li><i>extents</i>: an {@link ExtentHistory ExtentHistory} with the last
109
 * previous extents.
110
 * <li><i>offset</i>: position in pixels of the available rectangular area,
111
 * where start drawing the map.
112
 * <li><i>dist1pixel</i>: the distance in <i>world coordinates</i> equivalent to
113
 * 1 pixel in the view with the current extent.
114
 * <li><i>dist3pixel</i>: the distance in <i>world coordinates</i> equivalent to
115
 * 3 pixels in the view with the current extent.
116
 * <li><i>listeners</i>: list with the {@link ViewPortListener ViewPortListener}
117
 * registered.
118
 * </ul>
119
 * </p>
120
 * 
121
 * @author Vicente Caballero Navarro
122
 */
123
public class ViewPort implements Persistent, Cloneable {
124

    
125
    private static final String FIELD_DISTANCE_AREA = "distanceArea";
126
    private static final String FIELD_IMAGE_SIZE = "imageSize";
127
    private static final String FIELD_PROJ = "proj";
128
    private static final String FIELD_OFFSET = "offset";
129
    private static final String FIELD_MAP_UNITS = "mapUnits";
130
    private static final String FIELD_EXTENT = "extent";
131
    private static final String FIELD_EXTENTS = "extents";
132
    private static final String FIELD_DISTANCE_UNITS = "distanceUnits";
133
    private static final String FIELD_DIST3PIXEL = "dist3pixel";
134
    private static final String FIELD_DIST1PIXEL = "dist1pixel";
135
    private static final String FIELD_CLIP = "clip";
136
    private static final String FIELD_BACK_COLOR = "backColor";
137
    private static final String FIELD_ADJUSTED_EXTENT = "adjustedExtent";
138

    
139
    private static final GeometryManager geomManager =
140
        GeometryLocator.getGeometryManager();
141
    private static final Logger logger =
142
        LoggerFactory.getLogger(ViewPort.class);
143

    
144
    /**
145
     * <p>
146
     * Screen resolution in <i>dots-per-inch</i>. Useful to calculate the
147
     * geographic scale of the view.
148
     * </p>
149
     * 
150
     * @see Toolkit#getScreenResolution()
151
     * @see #getScale()
152
     */
153
    private static int dpi = java.awt.Toolkit.getDefaultToolkit()
154
        .getScreenResolution();
155

    
156
    /**
157
     * <p>
158
     * Area selected by user using some tool.
159
     * </p>
160
     * 
161
     * <p>
162
     * When the zoom changes (for instance when using the zoom in or zoom out
163
     * tools, but also zooming to a selected feature or shape) the extent that
164
     * covers that area is the value returned by this method. It is not the
165
     * actual area shown in the view because it does not care about the aspect
166
     * ratio of the available area. However, any part of the real world
167
     * contained in this extent is shown in the view.
168
     * </p>
169
     * <p>
170
     * Probably this is not what you are looking for. If you are looking for the
171
     * complete extent currently shown, you must use
172
     * {@linkplain #getAdjustedExtent()} method which returns the extent that
173
     * contains this one but regarding the current view's aspect ratio.
174
     * </p>
175
     * 
176
     * @see #getExtent()
177
     * @see #setEnvelope(Envelope)
178
     */
179
    protected Rectangle2D extent;
180
    
181
    protected Time time;
182

    
183
    /**
184
     * <p>
185
     * Location and dimensions of the extent adjusted to the image size.
186
     * </p>
187
     * 
188
     * @see #getAdjustedExtent()
189
     */
190
    protected Rectangle2D adjustedExtent;
191

    
192
    /**
193
     * Draw version of the context. It's used for know when de componend has
194
     * changed any visualization property
195
     * 
196
     * @see getDrawVersion
197
     * @see updateDrawVersion
198
     */
199
    private long drawVersion = 0L;
200

    
201
    /**
202
     * <p>
203
     * History with the last extents of the view.
204
     * </p>
205
     * 
206
     * @see #setPreviousExtent()
207
     * @see #getExtents()
208
     */
209
    protected ExtentHistory extents = new ExtentHistory();
210

    
211
    /**
212
     * <p>
213
     * Size in <i>screen coordinates</i> of the rectangle where the image is
214
     * displayed.
215
     * </p>
216
     * <p>
217
     * Used by {@linkplain #calculateAffineTransform()} to calculate:<br>
218
     * 
219
     * <ul>
220
     * <li>The new {@link #scale scale} .
221
     * <li>The new {@link #adjustedExtent adjustableExtent} .
222
     * <li>The new {@link #trans trans} .
223
     * <li>The new real world coordinates equivalent to 1 pixel (
224
     * {@link #dist1pixel dist1pixel}) .
225
     * <li>The new real world coordinates equivalent to 3 pixels (
226
     * {@link #dist3pixel dist3pixel}) .
227
     * </ul>
228
     * </p>
229
     * 
230
     * @see #getImageSize()
231
     * @see #getImageHeight()
232
     * @see #getImageWidth()
233
     * @see #setImageSize(Dimension)
234
     */
235
    private Dimension imageSize;
236

    
237
    /**
238
     * <p>
239
     * the affine transformation between the {@link #extent extent} in <i>map 2D
240
     * coordinates</i> to the image area in the screen, in <i>screen 2D
241
     * coordinates</i> (pixels).
242
     * </p>
243
     * 
244
     * @see AffineTransform
245
     * 
246
     * @see #getAffineTransform()
247
     * @see #setAffineTransform(AffineTransform)
248
     * @see #calculateAffineTransform()
249
     */
250
    private AffineTransform trans = new AffineTransform();
251

    
252
    /**
253
     * <p>
254
     * Measurement unit used for measuring distances and displaying information.
255
     * </p>
256
     * 
257
     * @see #getDistanceUnits()
258
     * @see #setDistanceUnits(int)
259
     */
260
    private int distanceUnits = 1;
261
    /**
262
     * <p>
263
     * Measurement unit used for measuring areas and displaying information.
264
     * </p>
265
     * 
266
     * @see #getDistanceArea()
267
     * @see #setDistanceArea(int)
268
     */
269
    private int distanceArea = 1;
270
    /**
271
     * <p>
272
     * Measurement unit used by this view port for the map.
273
     * </p>
274
     * 
275
     * @see #getMapUnits()
276
     * @see #setMapUnits(int)
277
     */
278
    private int mapUnits = 1;
279

    
280
    /**
281
     * <p>
282
     * Array with the {@link ViewPortListener ViewPortListener}s registered to
283
     * this view port.
284
     * </p>
285
     * 
286
     * @see #addViewPortListener(ViewPortListener)
287
     * @see #removeViewPortListener(ViewPortListener)
288
     */
289
    private ArrayList listeners = new ArrayList();
290

    
291
    /**
292
     * <p>
293
     * The offset is the position where start drawing the map.
294
     * </p>
295
     * <p>
296
     * The offset of a <a href="http://www.gvsig.gva.es/">gvSIG</a>'s
297
     * <i>View</i> is always (0, 0) because the drawing area fits with the full
298
     * window area. But in a <a href="http://www.gvsig.gva.es/">gvSIG</a>'s
299
     * <i>Layout</i> it's up to the place where the <code>FFrameView</code> is
300
     * located.
301
     * </p>
302
     * 
303
     * @see #getOffset()
304
     * @see #setOffset(Point2D)
305
     */
306
    private Point2D offset = new Point2D.Double(0, 0);
307

    
308
    /**
309
     * <p>
310
     * Clipping area.
311
     * </p>
312
     */
313
    // private Rectangle2D clip;
314

    
315
    /**
316
     * <p>
317
     * Background color of this view.
318
     * </p>
319
     * 
320
     * @see #getBackColor()
321
     * @see #setBackColor(Color)
322
     */
323
    private Color backColor = null; // Color.WHITE;
324

    
325
    /**
326
     * <p>
327
     * Information about the map projection used in this view.
328
     * </p>
329
     * 
330
     * @see #getProjection()
331
     * @see #setProjection(IProjection)
332
     */
333
    private IProjection proj;
334

    
335
    /**
336
     * <p>
337
     * Represents the distance in <i>world coordinates</i> equivalent to 1 pixel
338
     * in the view with the current extent.
339
     * </p>
340
     * 
341
     * @see #getDist1pixel()
342
     * @see #setDist1pixel(double)
343
     */
344
    private double dist1pixel;
345

    
346
    /**
347
     * <p>
348
     * Represents the distance in <i>world coordinates</i> equivalent to 3
349
     * pixels in the view with the current extent.
350
     * </p>
351
     * 
352
     * @see #getDist3pixel()
353
     * @see #setDist3pixel(double)
354
     */
355
    private double dist3pixel;
356

    
357
    /**
358
     * <p>
359
     * Ratio between the size of <code>imageSize</code> and <code>extent</code>:
360
     * <br>
361
     * <i>
362
     * 
363
     * <pre>
364
     * min{(imageSize.getHeight()/extent.getHeight(), imageSize.getWidth()/extent.getWidth())}
365
     * </pre>
366
     * 
367
     * </i>
368
     * </p>
369
     */
370
    private double scale;
371

    
372
    /**
373
     * <p>
374
     * Clipping area.
375
     * </p>
376
     * 
377
     * @see #setClipRect(Rectangle2D)
378
     */
379
    private Rectangle2D cliprect;
380

    
381
    /**
382
     * <p>
383
     * Enables or disables the <i>"adjustable extent"</i> mode.
384
     * </p>
385
     * 
386
     * <p>
387
     * When calculates the affine transform, if
388
     * <ul>
389
     * <li><i>enabled</i>: the new <code>adjustedExtent</code> will have the (X,
390
     * Y) coordinates of the <code>extent</code> and an area that will be an
391
     * scale of the image size. That area will have different height or width
392
     * (not both) of the extent according the least ratio (height or width) in
393
     * 
394
     * <pre>
395
     * image.size/extent.size&quot;
396
     * </pre>.
397
     * <li><i>disabled</i>: the new <code>adjustedExtent</code> will be like
398
     * <code>extent</code>.
399
     * </ul>
400
     * </p>
401
     * 
402
     * @see #setAdjustable(boolean)
403
     */
404
    private boolean adjustableExtent = true;
405

    
406
    public ViewPort() {
407

    
408
    }
409

    
410
    /**
411
     * <p>
412
     * Creates a new view port with the information of the projection in
413
     * <code>proj</code> argument, and default configuration:
414
     * </p>
415
     * <p>
416
     * <ul>
417
     * <li><i><code>distanceUnits</code></i> = meters
418
     * <li><i><code>mapUnits</code></i> = meters
419
     * <li><i><code>backColor</code></i> = <i>undefined</i>
420
     * <li><i><code>offset</code></i> = <code>new Point2D.Double(0, 0);</code>
421
     * </ul>
422
     * </p>
423
     * 
424
     * @param proj
425
     *            information of the projection for this view port
426
     */
427
    public ViewPort(IProjection proj) {
428
        // Por defecto
429
        this.proj = proj;
430
    }
431

    
432
    /**
433
     * <p>
434
     * Changes the status of the <i>"adjustable extent"</i> option to enabled or
435
     * disabled.
436
     * </p>
437
     * 
438
     * <p>
439
     * If view port isn't adjustable, won't bear in mind the aspect ratio of the
440
     * available rectangular area to calculate the affine transform from the
441
     * original map in real coordinates. (Won't scale the image to adapt it to
442
     * the available rectangular area).
443
     * </p>
444
     * 
445
     * @param boolean the boolean to be set
446
     */
447
    public void setAdjustable(boolean adjustable) {
448
        if (adjustable == adjustableExtent) {
449
            return;
450
        }
451
        adjustableExtent = adjustable;
452
        this.updateDrawVersion();
453
    }
454

    
455
    /**
456
     * <p>
457
     * Appends the specified {@link ViewPortListener ViewPortListener} listener
458
     * if weren't.
459
     * </p>
460
     * 
461
     * @param arg0
462
     *            the listener to add
463
     * 
464
     * @return <code>true</code> if has been added successfully
465
     * 
466
     * @see #removeViewPortListener(ViewPortListener)
467
     */
468
    public boolean addViewPortListener(ViewPortListener arg0) {
469
        if (!listeners.contains(arg0)) {
470
            return listeners.add(arg0);
471
        }
472
        return false;
473
    }
474

    
475
    /**
476
     * <p>
477
     * Removes the specified {@link ViewPortListener ViewPortListener} listener,
478
     * if existed.
479
     * </p>
480
     * 
481
     * @param arg0
482
     *            the listener to remove
483
     * 
484
     * @return <code>true</code> if the contained the specified listener.
485
     * 
486
     * @see #addViewPortListener(ViewPortListener)
487
     */
488
    public boolean removeViewPortListener(ViewPortListener arg0) {
489
        return listeners.remove(arg0);
490
    }
491

    
492
    /**
493
     * <p>
494
     * Converts and returns the distance <code>d</code>, that is in <i>map
495
     * coordinates</i> to <i>screen coordinates</i> using a <i>delta
496
     * transform</i> with the transformation affine information in the
497
     * {@link #trans #trans} attribute.
498
     * </p>
499
     * 
500
     * @param d
501
     *            distance in <i>map coordinates</i>
502
     * 
503
     * @return distance equivalent in <i>screen coordinates</i>
504
     * 
505
     * @see #toMapDistance(int)
506
     * @see AffineTransform#deltaTransform(Point2D, Point2D)S
507
     */
508
    public int fromMapDistance(double d) {
509
        Point2D.Double pWorld = new Point2D.Double(1, 1);
510
        Point2D.Double pScreen = new Point2D.Double();
511

    
512
        try {
513
            trans.deltaTransform(pWorld, pScreen);
514
        } catch (Exception e) {
515
            System.err.print(e.getMessage());
516
        }
517

    
518
        return (int) (d * pScreen.x);
519
    }
520

    
521
    /**
522
     * <p>
523
     * Converts and returns the 2D point <code>(x,y)</code>, that is in <i>map
524
     * coordinates</i> to <i>screen coordinates</i> (pixels) using the affine
525
     * transformation in the {@link #trans #trans} attribute.
526
     * </p>
527
     * 
528
     * @param x
529
     *            the <code>x</code> <i>map coordinate</i> of a 2D point
530
     * @param y
531
     *            the <code>y</code> <i>map coordinate</i> of a 2D point
532
     * 
533
     * @return 2D point equivalent in <i>screen coordinates</i> (pixels)
534
     * 
535
     * @see #fromMapPoint(Point2D)
536
     * @see AffineTransform#transform(Point2D, Point2D)
537
     */
538
    public Point2D fromMapPoint(double x, double y) {
539
        Point2D.Double pWorld = new Point2D.Double(x, y);
540
        Point2D.Double pScreen = new Point2D.Double();
541

    
542
        try {
543
            trans.transform(pWorld, pScreen);
544
        } catch (Exception e) {
545
            System.err.print(e.getMessage());
546
        }
547

    
548
        return pScreen;
549
    }
550

    
551
    /**
552
     * <p>
553
     * Converts and returns the 2D point argument, that is in <i>map
554
     * coordinates</i> to <i>screen coordinates</i> (pixels) using the affine
555
     * transformation in the {@link #trans #trans} attribute.
556
     * </p>
557
     * 
558
     * @param point
559
     *            the 2D point in <i>map coordinates</i>
560
     * 
561
     * @return 2D point equivalent in <i>screen coordinates</i> (pixels)
562
     * 
563
     * @see #toMapPoint(Point2D)
564
     * @see #fromMapPoint(double, double)
565
     */
566
    public Point2D fromMapPoint(Point2D point) {
567
        return fromMapPoint(point.getX(), point.getY());
568
    }
569

    
570
    /**
571
     * <p>
572
     * Converts and returns the 2D point <code>(x,y)</code>, that is in
573
     * <i>screen coordinates</i> (pixels) to <i>map coordinates</i> using the
574
     * affine transformation in the {@link #trans #trans} attribute.
575
     * </p>
576
     * 
577
     * @param x
578
     *            the <code>x</code> <i>screen coordinate</i> of a 2D point
579
     * @param y
580
     *            the <code>y</code> <i>screen coordinate</i> of a 2D point
581
     * 
582
     * @return 2D point equivalent in <i>map coordinates</i>
583
     * 
584
     * @see #toMapPoint(Point2D)
585
     * @see #fromMapPoint(double, double)
586
     */
587
    public Point2D toMapPoint(int x, int y) {
588
        Point2D pScreen = new Point2D.Double(x, y);
589

    
590
        return toMapPoint(pScreen);
591
    }
592

    
593
    /**
594
     * <p>
595
     * Converts and returns the {@link Rectangle2D Rectangle2D}, that is in
596
     * <i>screen coordinates</i> (pixels) to <i>map coordinates</i> using
597
     * {@linkplain #toMapDistance(int)}, and {@linkplain #toMapPoint(int, int)}.
598
     * </p>
599
     * 
600
     * @param r
601
     *            the 2D rectangle in <i>screen coordinates</i> (pixels)
602
     * @return 2D rectangle equivalent in <i>map coordinates</i>
603
     * 
604
     * @see #fromMapRectangle(Rectangle2D)
605
     * @see #toMapDistance(int)
606
     * @see #toMapPoint(int, int)
607
     */
608
    public Rectangle2D toMapRectangle(Rectangle2D r) {
609
        Rectangle2D rect = new Rectangle2D.Double();
610
        Point2D p1 = toMapPoint((int) r.getX(), (int) r.getY());
611
        Point2D p2 = toMapPoint((int) r.getMaxX(), (int) r.getMaxY());
612
        rect.setFrameFromDiagonal(p1, p2);
613
        return rect;
614
    }
615

    
616
    /**
617
     * <p>
618
     * Converts and returns the distance <code>d</code>, that is in <i>screen
619
     * coordinates</i> to <i>map coordinates</i> using the transformation affine
620
     * information in the {@link #trans #trans} attribute.
621
     * </p>
622
     * 
623
     * @param d
624
     *            distance in pixels
625
     * 
626
     * @return distance equivalent in <i>map coordinates</i>
627
     * 
628
     * @see #fromMapDistance(double)
629
     * @see AffineTransform
630
     */
631
    public double toMapDistance(int d) {
632
        double dist = d / trans.getScaleX();
633

    
634
        return dist;
635
    }
636

    
637
    /**
638
     * <p>
639
     * Converts and returns the 2D point argument, that is in <i>screen
640
     * coordinates</i> (pixels) to <i>map coordinates</i> using the inverse
641
     * affine transformation of the {@link #trans #trans} attribute.
642
     * </p>
643
     * 
644
     * @param pScreen
645
     *            the 2D point in <i>screen coordinates</i> (pixels)
646
     * 
647
     * @return 2D point equivalent in <i>map coordinates</i>
648
     * 
649
     * @see #toMapPoint(int, int)
650
     * @see AffineTransform#createInverse()
651
     * @see AffineTransform#transform(Point2D, Point2D)
652
     */
653
    public Point2D toMapPoint(Point2D pScreen) {
654
        Point2D.Double pWorld = new Point2D.Double();
655
        AffineTransform at;
656

    
657
        try {
658
            at = trans.createInverse();
659
            at.transform(pScreen, pWorld);
660
        } catch (NoninvertibleTransformException e) {
661
            throw new RuntimeException("Non invertible transform Exception", e);
662
        }
663

    
664
        return pWorld;
665
    }
666

    
667
    /**
668
     * <p>
669
     * Returns the real distance (in <i>world coordinates</i>) at the graphic
670
     * layers of two 2D points (in <i>map coordinates</i>) of the plane where is
671
     * selected the <i>extent</i>.
672
     * </p>
673
     * <p>
674
     * If the projection of this view is UTM, considers the Earth curvature.
675
     * </p>
676
     * 
677
     * @param pt1
678
     *            a 2D point in <i>map coordinates</i>
679
     * @param pt2
680
     *            another 2D point in <i>map coordinates</i>
681
     * 
682
     * @return the distance in meters between the two points 2D
683
     * 
684
     * @see GeoCalcImpl#distanceVincenty(Point2D, Point2D)
685
     */
686
    public double distanceWorld(Point2D pt1, Point2D pt2) {
687
        double dist = -1;
688
        dist = pt1.distance(pt2);
689

    
690
        if ((proj != null) && !(proj instanceof UTM)) {
691
            dist =
692
                new GeoCalc(proj).distanceVincenty(proj.toGeo(pt1),
693
                    proj.toGeo(pt2));
694
            return dist;
695
        }
696
        return (dist * MapContext.getDistanceTrans2Meter()[getMapUnits()]);
697
    }
698

    
699
    /**
700
     * <p>
701
     * Sets as extent and adjusted extent of this view port, the previous.
702
     * Recalculating its parameters.
703
     * </p>
704
     * 
705
     * @see #getExtents()
706
     * @see #calculateAffineTransform()
707
     * @deprecated use {@link ViewPort#setPreviousEnvelope()}
708
     */
709
    public void setPreviousExtent() {
710
        setPreviousEnvelope();
711
    }
712

    
713
    /**
714
     * <p>
715
     * Sets as envelope and adjusted envelope of this view port, the previous.
716
     * Recalculating its parameters.
717
     * </p>
718
     * 
719
     * @see #getExtents()
720
     * @see #calculateAffineTransform()
721
     */
722
    public void setPreviousEnvelope() {
723
        this.updateDrawVersion();
724
        extent = extents.removePrev();
725

    
726
        // Calcula la transformaci?n af?n
727
        calculateAffineTransform();
728

    
729
        // Lanzamos los eventos de extent cambiado
730
        callExtentChanged(getAdjustedExtent());
731
    }
732

    
733
    /**
734
     * <p>
735
     * Gets the area selected by user using some tool.
736
     * </p>
737
     * 
738
     * <p>
739
     * When the zoom changes (for instance using the <i>zoom in</i> or <i>zoom
740
     * out</i> tools, but also zooming to a selected feature or shape) the
741
     * extent that covers that area is the value returned by this method. It is
742
     * not the actual area shown because it doesn't care about the aspect ratio
743
     * of the image size of the view. However, any part of the real world
744
     * contained in this extent is shown in the view.
745
     * </p>
746
     * 
747
     * <p>
748
     * If you are looking for the complete extent currently shown, you must use
749
     * the {@linkplain #getAdjustedExtent()} method.
750
     * </p>
751
     * 
752
     * @return the current extent
753
     * 
754
     * @see #setEnvelope(Envelope)
755
     * @see #getAdjustedExtent()
756
     * @see #setPreviousExtent()
757
     * @see #getExtents()
758
     * 
759
     * @deprecated use {@link ViewPort#getEnvelope()}
760
     */
761
    public Rectangle2D getExtent() {
762
        return extent;
763
    }
764

    
765
    /**
766
     * <p>
767
     * Gets the envelope selected by user using some tool.
768
     * </p>
769
     * 
770
     * <p>
771
     * When the zoom changes (for instance using the <i>zoom in</i> or <i>zoom
772
     * out</i> tools, but also zooming to a selected feature or shape) the
773
     * envelope that covers that area is the value returned by this method. It
774
     * is not the actual envelope shown because it doesn't care about the aspect
775
     * ratio of the image size of the view. However, any part of the real world
776
     * contained in this envelope is shown in the view.
777
     * </p>
778
     * 
779
     * <p>
780
     * If you are looking for the complete extent currently shown, you must use
781
     * the {@linkplain #getAdjustedEnvelope()} method.
782
     * </p>
783
     * 
784
     * @return the current envelope
785
     * 
786
     * @see #setEnvelope(Envelope)
787
     * @see #getAdjustedEnvelope()
788
     * @see #setPreviousEnvelope()
789
     * @see #getEnvelopes()
790
     */
791
    public Envelope getEnvelope() {
792
        if (this.extent == null) {
793
            return null;
794
        }
795
        try {
796
            return geomManager.createEnvelope(extent.getMinX(),
797
                extent.getMinY(),
798
                extent.getMaxX(),
799
                extent.getMaxY(),
800
                SUBTYPES.GEOM2D);
801
            // This class has to use Envelope instead of Rectangle2D. This catch
802
            // will disappear
803
        } catch (CreateEnvelopeException e) {
804
            logger.error("Error creating the envelope");
805
        }
806
        return null;
807
    }
808

    
809
    /**
810
     * <p>
811
     * Changes the <i>extent</i> and <i>adjusted extent</i> of this view port:<br>
812
     * <ul>
813
     * <li>Stores the previous extent.
814
     * <li>Calculates the new extent using <code>r</code>:
815
     * 
816
     * <pre>
817
     * extent =
818
     *     new Rectangle2D.Double(r.getMinX() - 0.1,
819
     *         r.getMinY() - 0.1,
820
     *         r.getWidth() + 0.2,
821
     *         r.getHeight() + 0.2);
822
     * </pre>
823
     * 
824
     * <li>Executes {@linkplain #calculateAffineTransform()}: getting the new
825
     * scale, adjusted extent, affine transformation between map and screen
826
     * coordinates, the real world coordinates equivalent to 1 pixel, and the
827
     * real world coordinates equivalent to 3 pixels.
828
     * <li>Notifies all {@link ViewPortListener ViewPortListener} registered
829
     * that the extent has changed.
830
     * </ul>
831
     * </p>
832
     * 
833
     * @param r
834
     *            the new extent
835
     * 
836
     * @see #getExtent()
837
     * @see #getExtents()
838
     * @see #calculateAffineTransform()
839
     * @see #setPreviousExtent()
840
     */
841
    public void setEnvelope(Envelope r) {
842
        Rectangle2D newExtent = null;
843
        // Esto comprueba que el extent no es de anchura o altura = "0"
844
        // y si es as? lo redimensiona.
845
        if (r != null) {
846
            if ((r.getMaximum(0) - r.getMinimum(0) == 0)
847
                || (r.getMaximum(1) - r.getMinimum(1) == 0)) {
848
                newExtent =
849
                    new Rectangle2D.Double(r.getMinimum(0) - 0.1,
850
                        r.getMinimum(1) - 0.1,
851
                        r.getMaximum(0) - r.getMinimum(0) + 0.2,
852
                        r.getMaximum(1) - r.getMinimum(1) + 0.2);
853
            } else {
854
                newExtent =
855
                    new Rectangle2D.Double(r.getMinimum(0),
856
                        r.getMinimum(1),
857
                        Math.abs(r.getMaximum(0) - r.getMinimum(0)),
858
                        Math.abs(r.getMaximum(1) - r.getMinimum(1)));
859
            }
860
        }
861

    
862
        if (this.extent != null && this.extent.equals(newExtent)) {
863
            return;
864
        }
865
        if (extent != null) {
866
            extents.put(extent);
867
        }
868
        this.updateDrawVersion();
869
        this.extent = newExtent;
870

    
871
        // Calcula la transformaci?n af?n
872
        calculateAffineTransform();
873

    
874
        // Lanzamos los eventos de extent cambiado
875
        callExtentChanged(getAdjustedExtent());
876
    }
877

    
878
    /**
879
     * <p>
880
     * Changes the <i>extent</i> and <i>adjusted extent</i> of this view port:<br>
881
     * <ul>
882
     * <li>Executes {@linkplain #calculateAffineTransform()}: getting the new
883
     * scale, adjusted extent, affine transformation between map and screen
884
     * coordinates, the real world coordinates equivalent to 1 pixel, and the
885
     * real world coordinates equivalent to 3 pixels.
886
     * <li>Notifies to all {@link ViewPortListener ViewPortListener} registered
887
     * that the extent has changed.
888
     * </ul>
889
     * </p>
890
     * 
891
     * @see #setEnvelope(Envelope)
892
     * @see #calculateAffineTransform()
893
     */
894
    public void refreshExtent() {
895
        // this.scale = scale;
896

    
897
        // Calcula la transformaci?n af?n
898
        calculateAffineTransform();
899

    
900
        // Lanzamos los eventos de extent cambiado
901
        callExtentChanged(getAdjustedExtent());
902
    }
903

    
904
    /**
905
     * <p>
906
     * Calculates and returns using the current projection of this view port,
907
     * the scale that is the extent in <i>screen coordinates</i> from the image
908
     * in <i>map coordinates</i>.
909
     * </p>
910
     * 
911
     * @return the scale <i>extent / image size</i> projected by this view port
912
     * 
913
     * @deprecated since 07/09/07, use {@linkplain MapContext#getScaleView()}
914
     */
915
    public double getScale() {
916
        return proj.getScale(extent.getMinX(),
917
            extent.getMaxX(),
918
            imageSize.width,
919
            dpi);
920
    }
921

    
922
    /**
923
     * <p>
924
     * Affine transformation between <i>map 2D coordinates</i> to <i>screen 2D
925
     * coordinates</i> (pixels), preserving the "straightness" and "parallelism"
926
     * of the lines.
927
     * </p>
928
     * 
929
     * @return the affine transformation
930
     * 
931
     * @see #setAffineTransform(AffineTransform)
932
     * @see #calculateAffineTransform()
933
     */
934
    public AffineTransform getAffineTransform() {
935
        return trans;
936
    }
937

    
938
    /**
939
     * <p>
940
     * Returns the size of the image projected.
941
     * </p>
942
     * 
943
     * @return the image size
944
     * 
945
     * @see #setImageSize(Dimension)
946
     * @see #getImageHeight()
947
     * @see #getImageWidth()
948
     */
949
    public Dimension getImageSize() {
950
        return imageSize;
951
    }
952

    
953
    /**
954
     * <p>
955
     * Sets the size of the image projected, recalculating the parameters of
956
     * this view port.
957
     * </p>
958
     * 
959
     * @param imageSize
960
     *            the image size
961
     * 
962
     * @see #getImageSize()
963
     * @see #calculateAffineTransform()
964
     */
965
    public void setImageSize(Dimension imageSize) {
966

    
967
        if (this.imageSize == null || (!this.imageSize.equals(imageSize))) {
968
            this.updateDrawVersion();
969
            this.imageSize = imageSize;
970
            calculateAffineTransform();
971
        }
972
    }
973

    
974
    /**
975
     * <p>
976
     * Notifies to all view port listeners registered, that the adjusted extent
977
     * of this view port has changed.
978
     * </p>
979
     * 
980
     * @param newRect
981
     *            the new adjusted extend
982
     * 
983
     * @see #refreshExtent()
984
     * @see #setEnvelope(Envelope)
985
     * @see #setPreviousExtent()
986
     * @see ExtentEvent
987
     * @see ViewPortListener
988
     */
989
    protected void callExtentChanged(Envelope newRect) {
990
        ExtentEvent ev = ExtentEvent.createExtentEvent(newRect);
991

    
992
        for (int i = 0; i < listeners.size(); i++) {
993
            ViewPortListener listener = (ViewPortListener) listeners.get(i);
994
            listener.extentChanged(ev);
995
        }
996
    }
997

    
998
    /**
999
     * <p>
1000
     * Notifies to all view port listeners registered, that the background color
1001
     * of this view port has changed.
1002
     * </p>
1003
     * 
1004
     * @param c
1005
     *            the new background color
1006
     * 
1007
     * @see #setBackColor(Color)
1008
     * @see ColorEvent
1009
     * @see ViewPortListener
1010
     */
1011
    private void callColorChanged(Color c) {
1012
        ColorEvent ce = ColorEvent.createColorEvent(c);
1013

    
1014
        for (int i = 0; i < listeners.size(); i++) {
1015
            ViewPortListener listener = (ViewPortListener) listeners.get(i);
1016
            listener.backColorChanged(ce);
1017
        }
1018
    }
1019

    
1020
    /**
1021
     * <p>
1022
     * Notifies to all view port listeners registered, that the projection of
1023
     * this view port has changed.
1024
     * </p>
1025
     * 
1026
     * @param projection
1027
     *            the new projection
1028
     * 
1029
     * @see #setProjection(IProjection)
1030
     * @see ProjectionEvent
1031
     * @see ViewPortListener
1032
     */
1033
    private void callProjectionChanged(IProjection projection) {
1034
        ProjectionEvent ev = ProjectionEvent.createProjectionEvent(projection);
1035

    
1036
        for (int i = 0; i < listeners.size(); i++) {
1037
            ViewPortListener listener = (ViewPortListener) listeners.get(i);
1038
            listener.projectionChanged(ev);
1039
        }
1040
    }
1041

    
1042
    /**
1043
     * <p>
1044
     * Calculates the affine transformation between the {@link #extent extent}
1045
     * in <i>map 2D coordinates</i> to the image area in the screen, in
1046
     * <i>screen 2D coordinates</i> (pixels).
1047
     * </p>
1048
     * 
1049
     * <p>
1050
     * This process recalculates some parameters of this view port:<br>
1051
     * 
1052
     * <ul>
1053
     * <li>The new {@link #scale scale} .
1054
     * <li>The new {@link #adjustedExtent adjustedExtent} .
1055
     * <li>The new {@link #trans trans} .
1056
     * <li>The new real world coordinates equivalent to 1 pixel (
1057
     * {@link #dist1pixel dist1pixel}) .
1058
     * <li>The new real world coordinates equivalent to 3 pixels (
1059
     * {@link #dist3pixel dist3pixel}) .
1060
     * </ul>
1061
     * </p>
1062
     * 
1063
     * @see #getAffineTransform()
1064
     * @see #setAffineTransform(AffineTransform)
1065
     * @see #refreshExtent()
1066
     * @see #setEnvelope(Envelope)
1067
     * @see #setImageSize(Dimension)
1068
     * @see #setPreviousExtent()
1069
     * @see #createFromXML(XMLEntity)
1070
     * @see AffineTransform
1071
     */
1072
    private void calculateAffineTransform() {
1073
        if ((imageSize == null) || (extent == null) || (imageSize.width <= 0)
1074
            || (imageSize.height <= 0)) {
1075
            return;
1076
        }
1077

    
1078
        AffineTransform escalado = new AffineTransform();
1079
        AffineTransform translacion = new AffineTransform();
1080

    
1081
        double escalaX;
1082
        double escalaY;
1083

    
1084
        escalaX = imageSize.width / extent.getWidth();
1085
        escalaY = imageSize.height / extent.getHeight();
1086

    
1087
        double xCenter = extent.getCenterX();
1088
        double yCenter = extent.getCenterY();
1089
        double newHeight;
1090
        double newWidth;
1091

    
1092
        adjustedExtent = new Rectangle2D.Double();
1093

    
1094
        if (adjustableExtent) {
1095
            if (escalaX < escalaY) {
1096
                scale = escalaX;
1097
                newHeight = imageSize.height / scale;
1098
                adjustedExtent.setRect(xCenter - (extent.getWidth() / 2.0),
1099
                    yCenter - (newHeight / 2.0),
1100
                    extent.getWidth(),
1101
                    newHeight);
1102
            } else {
1103
                scale = escalaY;
1104
                newWidth = imageSize.width / scale;
1105
                adjustedExtent.setRect(xCenter - (newWidth / 2.0), yCenter
1106
                    - (extent.getHeight() / 2.0), newWidth, extent.getHeight());
1107
            }
1108
            escalado.setToScale(scale, -scale);
1109
        } else { // adjusted is same as extent
1110
            scale = escalaX;
1111
            adjustedExtent.setFrame(extent);
1112
            escalado.setToScale(escalaX, -escalaY);
1113
        }
1114
        Envelope env = getAdjustedExtent();
1115
        if (env == null) {
1116
            return;
1117
        }
1118
        translacion.setToTranslation(-env.getMinimum(0), -env.getMinimum(1)
1119
            - getAdjustedExtent().getLength(1));
1120

    
1121
        AffineTransform offsetTrans = new AffineTransform();
1122
        offsetTrans.setToTranslation(offset.getX(), offset.getY());
1123

    
1124
        trans.setToIdentity();
1125
        trans.concatenate(offsetTrans);
1126
        trans.concatenate(escalado);
1127

    
1128
        trans.concatenate(translacion);
1129

    
1130
        // Calculamos las distancias de 1 pixel y 3 pixel con esa
1131
        // transformaci?n
1132
        // de coordenadas, de forma que est?n precalculadas para cuando las
1133
        // necesitemos
1134
        AffineTransform at;
1135

    
1136
        try {
1137
            at = trans.createInverse();
1138

    
1139
            Point2D pPixel = new Point2D.Float(1, 1);
1140

    
1141
            Point2D.Float pProv = new Point2D.Float();
1142
            at.deltaTransform(pPixel, pProv);
1143

    
1144
            dist1pixel = pProv.x;
1145
            dist3pixel = 3 * pProv.x;
1146
        } catch (NoninvertibleTransformException e) {
1147
            System.err.println("transformada afin = " + trans.toString());
1148
            System.err.println("extent = " + extent.toString() + " imageSize= "
1149
                + imageSize.toString());
1150
            throw new RuntimeException("Non invertible transform Exception", e);
1151
        }
1152
    }
1153

    
1154
    /**
1155
     * <p>
1156
     * Sets the offset.
1157
     * </p>
1158
     * <p>
1159
     * The offset is the position where start drawing the map.
1160
     * </p>
1161
     * 
1162
     * @param p
1163
     *            2D point that represents the offset in pixels
1164
     * 
1165
     * @see #getOffset()
1166
     */
1167
    public void setOffset(Point2D p) {
1168
        if (!offset.equals(p)) {
1169
            this.updateDrawVersion();
1170
            offset = p;
1171
        }
1172
    }
1173

    
1174
    /**
1175
     * <p>
1176
     * Gets the offset.
1177
     * </p>
1178
     * <p>
1179
     * The offset is the position where start drawing the map.
1180
     * </p>
1181
     * 
1182
     * @return 2D point that represents the offset in pixels
1183
     * 
1184
     * @see #setOffset(Point2D)
1185
     */
1186
    public Point2D getOffset() {
1187
        return offset;
1188
    }
1189

    
1190
    /**
1191
     * <p>
1192
     * Sets the background color.
1193
     * </p>
1194
     * 
1195
     * @param c
1196
     *            the new background color
1197
     * 
1198
     * @see #getBackColor()
1199
     */
1200
    public void setBackColor(Color c) {
1201
        if (!c.equals(this.backColor)) {
1202
            this.updateDrawVersion();
1203
            backColor = c;
1204
            callColorChanged(backColor);
1205
        }
1206
    }
1207

    
1208
    /**
1209
     * <p>
1210
     * Gets the background color.
1211
     * </p>
1212
     * 
1213
     * @return the background color of the view
1214
     * 
1215
     * @see #setBackColor(Color)
1216
     */
1217
    public Color getBackColor() {
1218
        return backColor;
1219
    }
1220

    
1221
    /**
1222
     * <p>
1223
     * Returns the extent currently covered by the view adjusted (scaled) to the
1224
     * image size aspect.
1225
     * </p>
1226
     * 
1227
     * @return extent of the view adjusted to the image size aspect
1228
     * 
1229
     * @see #setAdjustable(boolean)
1230
     * @deprecated use {@link ViewPort#getAdjustedEnvelope()} instead
1231
     */
1232
    public Envelope getAdjustedExtent() {
1233
        return getAdjustedEnvelope();
1234
    }
1235

    
1236
    /**
1237
     * <p>
1238
     * Returns the envelope currently covered by the view adjusted (scaled) to
1239
     * the image size aspect.
1240
     * </p>
1241
     * 
1242
     * @return envelope of the view adjusted to the image size aspect
1243
     * 
1244
     * @see #setAdjustable(boolean)
1245
     */
1246
    public Envelope getAdjustedEnvelope() {
1247
        if (cliprect != null) {
1248
            Rectangle2D r = adjustedExtent.createIntersection(cliprect);
1249
            try {
1250
                return geomManager.createEnvelope(r.getX(),
1251
                    r.getY(),
1252
                    r.getMaxX(),
1253
                    r.getMaxY(),
1254
                    SUBTYPES.GEOM2D);
1255
            } catch (CreateEnvelopeException e) {
1256
                e.printStackTrace();
1257
                logger.error("Error adjusting the extent", e);
1258
            }
1259
        }
1260
        if (adjustedExtent != null) {
1261
            try {
1262
                return geomManager.createEnvelope(adjustedExtent.getX(),
1263
                    adjustedExtent.getY(),
1264
                    adjustedExtent.getMaxX(),
1265
                    adjustedExtent.getMaxY(),
1266
                    SUBTYPES.GEOM2D);
1267
            } catch (CreateEnvelopeException e) {
1268
                e.printStackTrace();
1269
                logger.error("Error adjusting the extent", e);
1270
            }
1271
        }
1272
        return null;
1273
    }
1274

    
1275
    /**
1276
     * <p>
1277
     * Returns the measurement unit of this view port used for measuring
1278
     * distances and displaying information.
1279
     * </p>
1280
     * 
1281
     * @return the measurement unit of this view used for measuring distances
1282
     *         and displaying information
1283
     * 
1284
     * @see #setDistanceUnits(int)
1285
     */
1286
    public int getDistanceUnits() {
1287
        return distanceUnits;
1288
    }
1289

    
1290
    /**
1291
     * <p>
1292
     * Returns the measurement unit of this view port used for measuring areas
1293
     * and displaying information.
1294
     * </p>
1295
     * 
1296
     * @return the measurement unit of this view used for measuring areas and
1297
     *         displaying information
1298
     * 
1299
     * @see #setDistanceUnits(int)
1300
     */
1301
    public int getDistanceArea() {
1302
        return distanceArea;
1303
    }
1304

    
1305
    /**
1306
     * <p>
1307
     * Sets the measurement unit of this view port used for measuring distances
1308
     * and displaying information.
1309
     * </p>
1310
     * 
1311
     * @param distanceUnits
1312
     *            the measurement unit of this view used for measuring distances
1313
     *            and displaying information
1314
     * 
1315
     * @see #getDistanceUnits()
1316
     */
1317
    public void setDistanceUnits(int distanceUnits) {
1318
        this.distanceUnits = distanceUnits;
1319
    }
1320

    
1321
    /**
1322
     * <p>
1323
     * Sets the measurement unit of this view port used for measuring areas and
1324
     * displaying information.
1325
     * </p>
1326
     * 
1327
     * @param distanceUnits
1328
     *            the measurement unit of this view used for measuring areas and
1329
     *            displaying information
1330
     * 
1331
     * @see #getDistanceUnits()
1332
     */
1333
    public void setDistanceArea(int distanceArea) {
1334
        this.distanceArea = distanceArea;
1335
    }
1336

    
1337
    /**
1338
     * <p>
1339
     * Gets the measurement unit used by this view port for the map.
1340
     * </p>
1341
     * 
1342
     * @return Returns the current map measure unit
1343
     * 
1344
     * @see #setMapUnits(int)
1345
     */
1346
    public int getMapUnits() {
1347
        return mapUnits;
1348
    }
1349

    
1350
    /**
1351
     * <p>
1352
     * Sets the measurement unit used by this view port for the map.
1353
     * </p>
1354
     * 
1355
     * @param mapUnits
1356
     *            the new map measure unit
1357
     * 
1358
     * @see #getMapUnits()
1359
     */
1360
    public void setMapUnits(int mapUnits) {
1361
        this.mapUnits = mapUnits;
1362
    }
1363

    
1364
    /**
1365
     * <p>
1366
     * Gets the width in <i>screen coordinates</i> of the rectangle where the
1367
     * image is displayed.
1368
     * </p>
1369
     * <p>
1370
     * Used by {@linkplain #calculateAffineTransform()} to calculate:<br>
1371
     * 
1372
     * <ul>
1373
     * <li>The new {@link #scale scale} .
1374
     * <li>The new {@link #adjustedExtent adjustableExtent} .
1375
     * <li>The new {@link #trans trans} .
1376
     * <li>The new real world coordinates equivalent to 1 pixel (
1377
     * {@link #dist1pixel dist1pixel}) .
1378
     * <li>The new real world coordinates equivalent to 3 pixels (
1379
     * {@link #dist3pixel dist3pixel}) .
1380
     * </ul>
1381
     * </p>
1382
     * 
1383
     * @see #getImageHeight()
1384
     * @see #getImageSize()
1385
     * @see #setImageSize(Dimension)
1386
     */
1387
    public int getImageWidth() {
1388
        return imageSize.width;
1389
    }
1390

    
1391
    /**
1392
     * <p>
1393
     * Gets the height in <i>screen coordinates</i> of the rectangle where the
1394
     * image is displayed.
1395
     * </p>
1396
     * <p>
1397
     * Used by {@linkplain #calculateAffineTransform()} to calculate:<br>
1398
     * 
1399
     * <ul>
1400
     * <li>The new {@link #scale scale} .
1401
     * <li>The new {@link #adjustedExtent adjustableExtent} .
1402
     * <li>The new {@link #trans trans} .
1403
     * <li>The new real world coordinates equivalent to 1 pixel (
1404
     * {@link #dist1pixel dist1pixel}) .
1405
     * <li>The new real world coordinates equivalent to 3 pixels (
1406
     * {@link #dist3pixel dist3pixel}) .
1407
     * </ul>
1408
     * </p>
1409
     * 
1410
     * @see #getImageWidth()
1411
     * @see #getImageSize()
1412
     * @see #setImageSize(Dimension)
1413
     */
1414
    public int getImageHeight() {
1415
        return imageSize.height;
1416
    }
1417

    
1418
    /**
1419
     * <p>
1420
     * Gets the distance in <i>world coordinates</i> equivalent to 1 pixel in
1421
     * the view with the current extent.
1422
     * </p>
1423
     * 
1424
     * @return the distance
1425
     * 
1426
     * @see #setDist1pixel(double)
1427
     */
1428
    public double getDist1pixel() {
1429
        return dist1pixel;
1430
    }
1431

    
1432
    /**
1433
     * <p>
1434
     * Sets the distance in <i>world coordinates</i> equivalent to 1 pixel in
1435
     * the view with the current extent.
1436
     * </p>
1437
     * 
1438
     * @param dist1pixel
1439
     *            the distance
1440
     * 
1441
     * @see #getDist1pixel()
1442
     */
1443
    public void setDist1pixel(double dist1pixel) {
1444
        if (dist1pixel == this.dist1pixel) {
1445
            return;
1446
        }
1447
        this.updateDrawVersion();
1448
        this.dist1pixel = dist1pixel;
1449
    }
1450

    
1451
    /**
1452
     * <p>
1453
     * Gets the distance in <i>world coordinates</i> equivalent to 3 pixels in
1454
     * the view with the current extent.
1455
     * </p>
1456
     * 
1457
     * @return the distance
1458
     * 
1459
     * @see #setDist3pixel(double)
1460
     */
1461
    public double getDist3pixel() {
1462
        return dist3pixel;
1463
    }
1464

    
1465
    /**
1466
     * <p>
1467
     * Sets the distance in <i>world coordinates</i> equivalent to 3 pixels in
1468
     * the view with the current extent.
1469
     * </p>
1470
     * 
1471
     * @param dist3pixel
1472
     *            the distance
1473
     * 
1474
     * @see #getDist3pixel()
1475
     */
1476
    public void setDist3pixel(double dist3pixel) {
1477
        if (this.dist3pixel == dist3pixel) {
1478
            return;
1479
        }
1480
        this.updateDrawVersion();
1481
        this.dist3pixel = dist3pixel;
1482
    }
1483

    
1484
    /**
1485
     * <p>
1486
     * Returns the last previous extents of this view port.
1487
     * </p>
1488
     * 
1489
     * @return the last previous extents of this view port
1490
     * 
1491
     * @see #setPreviousExtent()
1492
     * @deprecated use {@link ViewPort#getEnvelopes()}
1493
     */
1494
    public ExtentHistory getExtents() {
1495
        return getEnvelopes();
1496
    }
1497

    
1498
    /**
1499
     * <p>
1500
     * Returns the last previous extents of this view port.
1501
     * </p>
1502
     * 
1503
     * @return the last previous extents of this view port
1504
     * 
1505
     * @see #setPreviousExtent()
1506
     */
1507
    public ExtentHistory getEnvelopes() {
1508
        return extents;
1509
    }
1510

    
1511
    /**
1512
     * <p>
1513
     * Gets the projection used in this view port.
1514
     * </p>
1515
     * 
1516
     * @return projection used in this view port
1517
     * 
1518
     * @see #setProjection(IProjection)
1519
     */
1520
    public IProjection getProjection() {
1521
        return proj;
1522
    }
1523

    
1524
    /**
1525
     * <p>
1526
     * Sets the projection to this view port.
1527
     * </p>
1528
     * 
1529
     * @param proj
1530
     *            the new projection
1531
     * 
1532
     * @see #getProjection()
1533
     */
1534
    public void setProjection(IProjection proj) {
1535
        if (this.proj == null || !this.proj.getAbrev().equals(proj.getAbrev())) {
1536
            this.updateDrawVersion();
1537
            this.proj = proj;
1538
            callProjectionChanged(proj);
1539
        }
1540
    }
1541

    
1542
    // -----------------------------------------------------------------------------------------------------------
1543
    // NOTA PARA DESARROLLADORES SOBRE EL M?TODO
1544
    // "public void setAffineTransform(AffineTransform at)"
1545
    // ==============================================================================================
1546
    // Only used for print, should be removed, redefining the {@link
1547
    // RasterAdapter RasterAdapter} interface,
1548
    // allowing it to receive a {@link ViewPortData ViewPortData} .
1549
    // -----------------------------------------------------------------------------------------------------------
1550

    
1551
    /**
1552
     * <p>
1553
     * Sets only the affine transform to this view port, without updating
1554
     * dependent attributes.
1555
     * </p>
1556
     * <p>
1557
     * <b><i>This method could be problematic!</i></b>
1558
     * </p>
1559
     * 
1560
     * @param at
1561
     *            the affine transform to set
1562
     * 
1563
     * @see #getAffineTransform()
1564
     * @see #calculateAffineTransform()
1565
     */
1566
    public void setAffineTransform(AffineTransform at) {
1567
        this.trans = at;
1568
    }
1569

    
1570
    /**
1571
     * <p>
1572
     * Returns an XML entity that represents this view port instance:<br>
1573
     * <ul>
1574
     * <li>Properties:
1575
     * <ul>
1576
     * <li><i>className</i>: name of this class.
1577
     * <li>If defined, the adjusted extent:
1578
     * <ul>
1579
     * <li><i>adjustedExtentX</i>: X coordinate of the adjusted extent.
1580
     * <li><i>adjustedExtentY</i>: Y coordinate of the adjusted extent.
1581
     * <li><i>adjustedExtentW</i>: width of the adjusted extent.
1582
     * <li><i>adjustedExtentH</i>: height of the adjusted extent.
1583
     * </ul>
1584
     * <li>If defined, the background color:
1585
     * <ul>
1586
     * <li><i>backColor</i>: background color.
1587
     * </ul>
1588
     * <li>If defined, the clip:
1589
     * <ul>
1590
     * <li><i>clipX</i>: X coordinate of the clip.
1591
     * <li><i>clipY</i>: Y coordinate of clip.
1592
     * <li><i>clipW</i>: width of the clip.
1593
     * <li><i>clipH</i>: height of the clip.
1594
     * </ul>
1595
     * <li><i>dist1pixel</i>: the distance in world coordinates equivalent to 1
1596
     * pixel in the view.
1597
     * <li><i>dist3pixel</i>: the distance in world coordinates equivalent to 3
1598
     * pixels in the view.
1599
     * <li><i>distanceUnits</i>: the distance measurement unit.
1600
     * <li>If defined, the extent:
1601
     * <ul>
1602
     * <li><i>extentX</i>: X coordinate of the extent.
1603
     * <li><i>extentY</i>: Y coordinate of the extent.
1604
     * <li><i>extentW</i>: width of the extent.
1605
     * <li><i>extentH</i>: height of the extent.
1606
     * </ul>
1607
     * <li><i>mapUnits</i>: the map measurement unit.
1608
     * <li><i>offsetX</i>: X coordinate of the offset.
1609
     * <li><i>offsetY</i>: Y coordinate of the offset.
1610
     * <li>If defined, the projection:
1611
     * <ul>
1612
     * <li>If its defined, the projection:
1613
     * <ul>
1614
     * <li><i>proj</i>: the projection.</li>
1615
     * </ul>
1616
     * </ul>
1617
     * <li><i>scale</i>: ratio between the size of <code>imageSize</code> and
1618
     * <code>extent</code>.
1619
     * </ul>
1620
     * <li>Child branches:
1621
     * <ul>
1622
     * <li>XML entity of the internal {@link ExtentHistory ExtentHistory} .
1623
     * </ul>
1624
     * </ul>
1625
     * 
1626
     * @return the XML entity
1627
     * 
1628
     * @see #createFromXML(XMLEntity)
1629
     */
1630
    public void saveToState(PersistentState state) throws PersistenceException {
1631

    
1632
        state.set(FIELD_ADJUSTED_EXTENT, adjustedExtent);
1633
        state.set(FIELD_BACK_COLOR, backColor);
1634
        state.set(FIELD_CLIP, cliprect);
1635
        state.set(FIELD_DIST1PIXEL, dist1pixel);
1636
        state.set(FIELD_DIST3PIXEL, dist3pixel);
1637
        state.set(FIELD_DISTANCE_UNITS, distanceUnits);
1638
        state.set(FIELD_DISTANCE_AREA, distanceArea);
1639

    
1640
        state.set(FIELD_EXTENT, extent);
1641
        state.set(FIELD_EXTENTS, extents);
1642

    
1643
        state.set(FIELD_MAP_UNITS, mapUnits);
1644
        state.set(FIELD_OFFSET, offset);
1645

    
1646
        state.set(FIELD_PROJ, proj);
1647

    
1648
        state.set(FIELD_IMAGE_SIZE, imageSize);
1649
    }
1650

    
1651
    public void loadFromState(PersistentState state) throws PersistenceException {
1652

    
1653
        adjustedExtent = (Rectangle2D) state.get(FIELD_ADJUSTED_EXTENT);
1654
        backColor = (Color) state.get(FIELD_BACK_COLOR);
1655
        cliprect = (Rectangle2D) state.get(FIELD_CLIP);
1656
        dist1pixel = state.getDouble(FIELD_DIST1PIXEL);
1657
        dist3pixel = state.getDouble(FIELD_DIST3PIXEL);
1658
        distanceUnits = state.getInt(FIELD_DISTANCE_UNITS);
1659
        extents = (ExtentHistory) state.get(FIELD_EXTENTS);
1660
        extent = (Rectangle2D) state.get(FIELD_EXTENT);
1661
        mapUnits = state.getInt(FIELD_MAP_UNITS);
1662
        offset = (Point2D) state.get(FIELD_OFFSET);
1663
        proj = (IProjection) state.get(FIELD_PROJ);
1664
        imageSize = (Dimension) state.get(FIELD_IMAGE_SIZE);
1665
        distanceArea = state.getInt(FIELD_DISTANCE_AREA);
1666

    
1667
        refreshExtent();
1668
    }
1669

    
1670
        public static class RegisterPersistence implements Callable {
1671

    
1672
                public Object call() throws Exception {
1673
                        PersistenceManager manager = ToolsLocator.getPersistenceManager();
1674
                        if( manager.getDefinition("ViewPort")==null ) {
1675
                        DynStruct definition =
1676
                            manager.addDefinition(ViewPort.class,
1677
                                "ViewPort",
1678
                                "ViewPort Persistence definition",
1679
                                null,
1680
                                null);
1681

    
1682
                        definition.addDynFieldObject(FIELD_ADJUSTED_EXTENT)
1683
                            .setClassOfValue(Rectangle2D.class)
1684
                            .setMandatory(false);
1685

    
1686
                        definition.addDynFieldObject(FIELD_BACK_COLOR)
1687
                            .setClassOfValue(Color.class)
1688
                            .setMandatory(false);
1689

    
1690
                        definition.addDynFieldObject(FIELD_CLIP)
1691
                            .setClassOfValue(Rectangle2D.class)
1692
                            .setMandatory(false);
1693

    
1694
                        definition.addDynFieldDouble(FIELD_DIST1PIXEL).setMandatory(true);
1695

    
1696
                        definition.addDynFieldDouble(FIELD_DIST3PIXEL).setMandatory(true);
1697

    
1698
                        definition.addDynFieldInt(FIELD_DISTANCE_UNITS).setMandatory(true);
1699

    
1700
                        definition.addDynFieldInt(FIELD_DISTANCE_AREA).setMandatory(false);
1701

    
1702
                        definition.addDynFieldObject(FIELD_EXTENT)
1703
                            .setClassOfValue(Rectangle2D.class)
1704
                            .setMandatory(false);
1705

    
1706
                        definition.addDynFieldObject(FIELD_EXTENTS)
1707
                            .setClassOfValue(ExtentHistory.class)
1708
                            .setMandatory(true);
1709

    
1710
                        definition.addDynFieldInt(FIELD_MAP_UNITS).setMandatory(true);
1711

    
1712
                        definition.addDynFieldObject(FIELD_OFFSET)
1713
                            .setClassOfValue(Point2D.class)
1714
                            .setMandatory(false);
1715

    
1716
                        definition.addDynFieldObject(FIELD_PROJ)
1717
                            .setClassOfValue(IProjection.class)
1718
                            .setMandatory(true);
1719

    
1720
                        definition.addDynFieldObject(FIELD_IMAGE_SIZE)
1721
                            .setClassOfValue(Dimension.class)
1722
                            .setMandatory(false);
1723
                        }
1724
                        return Boolean.TRUE;
1725
                }
1726
                
1727
        }
1728

    
1729
        /**
1730
     * Clone the view port without clone the listeners nor the extent history.
1731
     * 
1732
     * @return the cloned view port
1733
     */
1734
    public Object clone() throws CloneNotSupportedException {
1735

    
1736
        ViewPort clonedViewPort = (ViewPort) super.clone();
1737
        clonedViewPort.listeners = new ArrayList();
1738
        clonedViewPort.extents = new ExtentHistory();
1739

    
1740
        if (this.adjustedExtent!=null){
1741
            clonedViewPort.adjustedExtent =
1742
                (Rectangle2D) this.adjustedExtent.clone();
1743
        }
1744

    
1745
        if (this.cliprect!=null){
1746
            clonedViewPort.cliprect = (Rectangle2D) this.cliprect.clone();
1747
        }
1748
        
1749
        if (this.extent!=null) {
1750
            clonedViewPort.extent = (Rectangle2D) this.extent.clone();
1751
        }
1752
        if (this.imageSize!=null){
1753
            clonedViewPort.imageSize = (Dimension) this.imageSize.clone();
1754
        }
1755
        
1756
        if (this.offset!=null){
1757
            clonedViewPort.offset = (Point2D) this.offset.clone();
1758
        }
1759
        if (proj!=null){
1760
            clonedViewPort.proj = (IProjection) this.proj.clone();
1761
        }
1762
        
1763
        clonedViewPort.trans = (AffineTransform) this.trans.clone();
1764

    
1765
        return clonedViewPort;
1766
    }
1767

    
1768
    /**
1769
     * <p>
1770
     * Returns a <code>String</code> representation of the main values of this
1771
     * view port: <code>{@linkplain #extent}</code>,
1772
     * <code>{@linkplain #adjustedExtent}</code>,
1773
     * <code>{@linkplain #imageSize}</code>, <code>{@linkplain #scale}</code>,
1774
     * and <code>{@linkplain #trans}</code>.
1775
     * </p>
1776
     * 
1777
     * @return a <code>string</code> representation of the main values of this
1778
     *         view port
1779
     */
1780
    public String toString() {
1781

    
1782
        String str;
1783
        str =
1784
            "Datos del viewPort:\nExtent=" + extent + "\nadjustedExtent="
1785
                + adjustedExtent + "\nimageSize=" + imageSize + "\nescale="
1786
                + scale + "\ntrans=" + trans;
1787

    
1788
        return str;
1789
    }
1790

    
1791
    /**
1792
     * <p>
1793
     * Sets the position and size of the clipping rectangle.
1794
     * </p>
1795
     * 
1796
     * @param rectView
1797
     *            the clipping rectangle to set
1798
     */
1799
    public void setClipRect(Rectangle2D rectView) {
1800
        this.updateDrawVersion();
1801
        cliprect = rectView;
1802
    }
1803

    
1804
    /**
1805
     * <p>
1806
     * Converts and returns the {@link Rectangle2D Rectangle2D}, that is in
1807
     * <i>map coordinates</i> to <i>screen coordinates</i> (pixels) using an
1808
     * <i>inverse transform</i> with the transformation affine information in
1809
     * the {@link #trans #trans} attribute.
1810
     * </p>
1811
     * 
1812
     * @param r
1813
     *            the 2D rectangle in <i>map coordinates</i>
1814
     * @return 2D rectangle equivalent in <i>screen coordinates</i> (pixels)
1815
     * 
1816
     * @see #toMapRectangle(Rectangle2D)
1817
     * @see #fromMapDistance(double)
1818
     * @see #fromMapPoint(Point2D)
1819
     */
1820
    public Rectangle2D fromMapRectangle(Rectangle2D r) {
1821
        Rectangle2D rect = new Rectangle2D.Double();
1822
        Point2D p1 = fromMapPoint((int) r.getX(), (int) r.getY());
1823
        Point2D p2 = fromMapPoint((int) r.getMaxX(), (int) r.getMaxY());
1824
        rect.setFrameFromDiagonal(p1, p2);
1825
        return rect;
1826
    }
1827

    
1828
    /**
1829
     * <p>
1830
     * Recalculates the current <code>{@linkplain #extent}</code> using an
1831
     * scale. It's necessary execute {@linkplain #refreshExtent()} after.
1832
     * </p>
1833
     * 
1834
     * @param s
1835
     *            the scale to set
1836
     * 
1837
     * @deprecated since 07/09/07, use
1838
     *             {@linkplain MapContext#setScaleView(long)}
1839
     */
1840
    public void setScale(long s) {
1841
        double x = extent.getX();
1842
        double y = extent.getY();
1843
        double escalaX = imageSize.width / extent.getWidth();
1844
        // double w = imageSize.width / s;
1845
        // double h = imageSize.height / s;
1846
        double difw = escalaX / s;
1847

    
1848
        double x1 = (-x * difw) - x + extent.getWidth() / 2;
1849
        double y1 = (-y * difw) - y + extent.getHeight() / 2;
1850
        double w1 = extent.getWidth() * difw;
1851
        double h1 = extent.getHeight() * difw;
1852
        extent.setRect(-x1, -y1, w1, h1);
1853
    }
1854

    
1855
    public long getDrawVersion() {
1856
        return this.drawVersion;
1857
    }
1858

    
1859
    protected void updateDrawVersion() {
1860
        this.drawVersion++;
1861
    }
1862

    
1863
//    /**
1864
//     * Obtiene la representaci?n de un color como String
1865
//     * 
1866
//     * @param c
1867
//     *            Color
1868
//     * 
1869
//     * @return String
1870
//     */
1871
//    public static String color2String(Color c) {
1872
//        if (c == null)
1873
//            return null;
1874
//        return c.getRed() + "," + c.getGreen() + "," + c.getBlue() + ","
1875
//            + c.getAlpha();
1876
//    }
1877
//
1878
//    /**
1879
//     * Obtiene el color de un string generado con color2String
1880
//     * 
1881
//     * @param stringColor
1882
//     *            string
1883
//     * 
1884
//     * @return Color
1885
//     */
1886
//    public static Color string2Color(String stringColor) {
1887
//        if (stringColor == null || stringColor.equals("null"))
1888
//            return null;
1889
//        String[] ints = new String[4];
1890
//
1891
//        ints = CompatLocator.getStringUtils().split(stringColor, ",");
1892
//
1893
//        int[] ret = new int[4];
1894
//
1895
//        for (int i = 0; i < ret.length; i++) {
1896
//            ret[i] = new Integer(ints[i]).intValue();
1897
//        }
1898
//
1899
//        return new Color(ret[0], ret[1], ret[2], ret[3]);
1900
//    }
1901
    
1902
    public Time getTime() {
1903
        return time;
1904
    }
1905
    
1906
    public void setTime(Time time) {
1907
        this.time = time;
1908
    }
1909
}