Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.mapcontext / org.gvsig.fmap.mapcontext.api / src / main / java / org / gvsig / fmap / mapcontext / ViewPort.java @ 40559

History | View | Annotate | Download (58.9 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.fmap.mapcontext;
25

    
26
import java.awt.Color;
27
import java.awt.Dimension;
28
import java.awt.Toolkit;
29
import java.awt.geom.AffineTransform;
30
import java.awt.geom.NoninvertibleTransformException;
31
import java.awt.geom.Point2D;
32
import java.awt.geom.Rectangle2D;
33
import java.util.ArrayList;
34

    
35
import org.cresques.cts.GeoCalc;
36
import org.cresques.cts.IProjection;
37
import org.cresques.cts.UTM;
38
import org.slf4j.Logger;
39
import org.slf4j.LoggerFactory;
40

    
41
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
42
import org.gvsig.fmap.geom.Geometry;
43
import org.gvsig.fmap.geom.GeometryLocator;
44
import org.gvsig.fmap.geom.GeometryManager;
45
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
46
import org.gvsig.fmap.geom.exception.CreateGeometryException;
47
import org.gvsig.fmap.geom.primitive.Envelope;
48
import org.gvsig.fmap.geom.primitive.Point;
49
import org.gvsig.fmap.mapcontext.events.ColorEvent;
50
import org.gvsig.fmap.mapcontext.events.ExtentEvent;
51
import org.gvsig.fmap.mapcontext.events.ProjectionEvent;
52
import org.gvsig.fmap.mapcontext.events.listeners.ViewPortListener;
53
import org.gvsig.timesupport.Time;
54
import org.gvsig.tools.ToolsLocator;
55
import org.gvsig.tools.dynobject.DynStruct;
56
import org.gvsig.tools.lang.Cloneable;
57
import org.gvsig.tools.persistence.PersistenceManager;
58
import org.gvsig.tools.persistence.Persistent;
59
import org.gvsig.tools.persistence.PersistentState;
60
import org.gvsig.tools.persistence.exception.PersistenceException;
61
import org.gvsig.tools.util.Callable;
62

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

    
111
    private static final String FIELD_DISTANCE_AREA = "distanceArea";
112
    private static final String FIELD_IMAGE_SIZE = "imageSize";
113
    private static final String FIELD_PROJ = "proj";
114
    private static final String FIELD_OFFSET = "offset";
115
    private static final String FIELD_MAP_UNITS = "mapUnits";
116
    private static final String FIELD_EXTENT = "extent";
117
    private static final String FIELD_EXTENTS = "extents";
118
    private static final String FIELD_DISTANCE_UNITS = "distanceUnits";
119
    private static final String FIELD_DIST3PIXEL = "dist3pixel";
120
    private static final String FIELD_DIST1PIXEL = "dist1pixel";
121
    private static final String FIELD_CLIP = "clip";
122
    private static final String FIELD_BACK_COLOR = "backColor";
123
    private static final String FIELD_ADJUSTED_EXTENT = "adjustedExtent";
124

    
125
    private static final GeometryManager geomManager =
126
        GeometryLocator.getGeometryManager();
127
    private static final Logger logger =
128
        LoggerFactory.getLogger(ViewPort.class);
129

    
130
    /**
131
     * <p>
132
     * Screen resolution in <i>dots-per-inch</i>. Useful to calculate the
133
     * geographic scale of the view.
134
     * </p>
135
     * 
136
     * @see Toolkit#getScreenResolution()
137
     * @see #getScale()
138
     */
139
    private static int dpi = java.awt.Toolkit.getDefaultToolkit()
140
        .getScreenResolution();
141

    
142
    /**
143
     * <p>
144
     * Area selected by user using some tool.
145
     * </p>
146
     * 
147
     * <p>
148
     * When the zoom changes (for instance when using the zoom in or zoom out
149
     * tools, but also zooming to a selected feature or shape) the extent that
150
     * covers that area is the value returned by this method. It is not the
151
     * actual area shown in the view because it does not care about the aspect
152
     * ratio of the available area. However, any part of the real world
153
     * contained in this extent is shown in the view.
154
     * </p>
155
     * <p>
156
     * Probably this is not what you are looking for. If you are looking for the
157
     * complete extent currently shown, you must use
158
     * {@linkplain #getAdjustedExtent()} method which returns the extent that
159
     * contains this one but regarding the current view's aspect ratio.
160
     * </p>
161
     * 
162
     * @see #getExtent()
163
     * @see #setEnvelope(Envelope)
164
     */
165
    protected Rectangle2D extent;
166
    
167
    protected Time time;
168

    
169
    /**
170
     * <p>
171
     * Location and dimensions of the extent adjusted to the image size.
172
     * </p>
173
     * 
174
     * @see #getAdjustedExtent()
175
     */
176
    protected Rectangle2D adjustedExtent;
177

    
178
    /**
179
     * Draw version of the context. It's used for know when de componend has
180
     * changed any visualization property
181
     * 
182
     * @see getDrawVersion
183
     * @see updateDrawVersion
184
     */
185
    private long drawVersion = 0L;
186

    
187
    /**
188
     * <p>
189
     * History with the last extents of the view.
190
     * </p>
191
     * 
192
     * @see #setPreviousExtent()
193
     * @see #getExtents()
194
     */
195
    protected ExtentHistory extents = new ExtentHistory();
196

    
197
    /**
198
     * <p>
199
     * Size in <i>screen coordinates</i> of the rectangle where the image is
200
     * displayed.
201
     * </p>
202
     * <p>
203
     * Used by {@linkplain #calculateAffineTransform()} to calculate:<br>
204
     * 
205
     * <ul>
206
     * <li>The new {@link #scale scale} .
207
     * <li>The new {@link #adjustedExtent adjustableExtent} .
208
     * <li>The new {@link #trans trans} .
209
     * <li>The new real world coordinates equivalent to 1 pixel (
210
     * {@link #dist1pixel dist1pixel}) .
211
     * <li>The new real world coordinates equivalent to 3 pixels (
212
     * {@link #dist3pixel dist3pixel}) .
213
     * </ul>
214
     * </p>
215
     * 
216
     * @see #getImageSize()
217
     * @see #getImageHeight()
218
     * @see #getImageWidth()
219
     * @see #setImageSize(Dimension)
220
     */
221
    private Dimension imageSize;
222

    
223
    /**
224
     * <p>
225
     * the affine transformation between the {@link #extent extent} in <i>map 2D
226
     * coordinates</i> to the image area in the screen, in <i>screen 2D
227
     * coordinates</i> (pixels).
228
     * </p>
229
     * 
230
     * @see AffineTransform
231
     * 
232
     * @see #getAffineTransform()
233
     * @see #setAffineTransform(AffineTransform)
234
     * @see #calculateAffineTransform()
235
     */
236
    private AffineTransform trans = new AffineTransform();
237

    
238
    /**
239
     * <p>
240
     * Measurement unit used for measuring distances and displaying information.
241
     * </p>
242
     * 
243
     * @see #getDistanceUnits()
244
     * @see #setDistanceUnits(int)
245
     */
246
    private int distanceUnits = 1;
247
    /**
248
     * <p>
249
     * Measurement unit used for measuring areas and displaying information.
250
     * </p>
251
     * 
252
     * @see #getDistanceArea()
253
     * @see #setDistanceArea(int)
254
     */
255
    private int distanceArea = 1;
256
    /**
257
     * <p>
258
     * Measurement unit used by this view port for the map.
259
     * </p>
260
     * 
261
     * @see #getMapUnits()
262
     * @see #setMapUnits(int)
263
     */
264
    private int mapUnits = 1;
265

    
266
    /**
267
     * <p>
268
     * Array with the {@link ViewPortListener ViewPortListener}s registered to
269
     * this view port.
270
     * </p>
271
     * 
272
     * @see #addViewPortListener(ViewPortListener)
273
     * @see #removeViewPortListener(ViewPortListener)
274
     */
275
    private ArrayList listeners = new ArrayList();
276

    
277
    /**
278
     * <p>
279
     * The offset is the position where start drawing the map.
280
     * </p>
281
     * <p>
282
     * The offset of a <a href="http://www.gvsig.gva.es/">gvSIG</a>'s
283
     * <i>View</i> is always (0, 0) because the drawing area fits with the full
284
     * window area. But in a <a href="http://www.gvsig.gva.es/">gvSIG</a>'s
285
     * <i>Layout</i> it's up to the place where the <code>FFrameView</code> is
286
     * located.
287
     * </p>
288
     * 
289
     * @see #getOffset()
290
     * @see #setOffset(Point2D)
291
     */
292
    private Point2D offset = new Point2D.Double(0, 0);
293

    
294
    /**
295
     * <p>
296
     * Clipping area.
297
     * </p>
298
     */
299
    // private Rectangle2D clip;
300

    
301
    /**
302
     * <p>
303
     * Background color of this view.
304
     * </p>
305
     * 
306
     * @see #getBackColor()
307
     * @see #setBackColor(Color)
308
     */
309
    private Color backColor = null; // Color.WHITE;
310

    
311
    /**
312
     * <p>
313
     * Information about the map projection used in this view.
314
     * </p>
315
     * 
316
     * @see #getProjection()
317
     * @see #setProjection(IProjection)
318
     */
319
    private IProjection proj;
320

    
321
    /**
322
     * <p>
323
     * Represents the distance in <i>world coordinates</i> equivalent to 1 pixel
324
     * in the view with the current extent.
325
     * </p>
326
     * 
327
     * @see #getDist1pixel()
328
     * @see #setDist1pixel(double)
329
     */
330
    private double dist1pixel;
331

    
332
    /**
333
     * <p>
334
     * Represents the distance in <i>world coordinates</i> equivalent to 3
335
     * pixels in the view with the current extent.
336
     * </p>
337
     * 
338
     * @see #getDist3pixel()
339
     * @see #setDist3pixel(double)
340
     */
341
    private double dist3pixel;
342

    
343
    /**
344
     * <p>
345
     * Ratio between the size of <code>imageSize</code> and <code>extent</code>:
346
     * <br>
347
     * <i>
348
     * 
349
     * <pre>
350
     * min{(imageSize.getHeight()/extent.getHeight(), imageSize.getWidth()/extent.getWidth())}
351
     * </pre>
352
     * 
353
     * </i>
354
     * </p>
355
     */
356
    private double scale;
357

    
358
    /**
359
     * <p>
360
     * Clipping area.
361
     * </p>
362
     * 
363
     * @see #setClipRect(Rectangle2D)
364
     */
365
    private Rectangle2D cliprect;
366

    
367
    /**
368
     * <p>
369
     * Enables or disables the <i>"adjustable extent"</i> mode.
370
     * </p>
371
     * 
372
     * <p>
373
     * When calculates the affine transform, if
374
     * <ul>
375
     * <li><i>enabled</i>: the new <code>adjustedExtent</code> will have the (X,
376
     * Y) coordinates of the <code>extent</code> and an area that will be an
377
     * scale of the image size. That area will have different height or width
378
     * (not both) of the extent according the least ratio (height or width) in
379
     * 
380
     * <pre>
381
     * image.size/extent.size&quot;
382
     * </pre>.
383
     * <li><i>disabled</i>: the new <code>adjustedExtent</code> will be like
384
     * <code>extent</code>.
385
     * </ul>
386
     * </p>
387
     * 
388
     * @see #setAdjustable(boolean)
389
     */
390
    private boolean adjustableExtent = true;
391

    
392
    public ViewPort() {
393

    
394
    }
395

    
396
    /**
397
     * <p>
398
     * Creates a new view port with the information of the projection in
399
     * <code>proj</code> argument, and default configuration:
400
     * </p>
401
     * <p>
402
     * <ul>
403
     * <li><i><code>distanceUnits</code></i> = meters
404
     * <li><i><code>mapUnits</code></i> = meters
405
     * <li><i><code>backColor</code></i> = <i>undefined</i>
406
     * <li><i><code>offset</code></i> = <code>new Point2D.Double(0, 0);</code>
407
     * </ul>
408
     * </p>
409
     * 
410
     * @param proj
411
     *            information of the projection for this view port
412
     */
413
    public ViewPort(IProjection proj) {
414
        // Por defecto
415
        this.proj = proj;
416
    }
417

    
418
    /**
419
     * <p>
420
     * Changes the status of the <i>"adjustable extent"</i> option to enabled or
421
     * disabled.
422
     * </p>
423
     * 
424
     * <p>
425
     * If view port isn't adjustable, won't bear in mind the aspect ratio of the
426
     * available rectangular area to calculate the affine transform from the
427
     * original map in real coordinates. (Won't scale the image to adapt it to
428
     * the available rectangular area).
429
     * </p>
430
     * 
431
     * @param boolean the boolean to be set
432
     */
433
    public void setAdjustable(boolean adjustable) {
434
        if (adjustable == adjustableExtent) {
435
            return;
436
        }
437
        adjustableExtent = adjustable;
438
        this.updateDrawVersion();
439
    }
440

    
441
    /**
442
     * <p>
443
     * Appends the specified {@link ViewPortListener ViewPortListener} listener
444
     * if weren't.
445
     * </p>
446
     * 
447
     * @param arg0
448
     *            the listener to add
449
     * 
450
     * @return <code>true</code> if has been added successfully
451
     * 
452
     * @see #removeViewPortListener(ViewPortListener)
453
     */
454
    public boolean addViewPortListener(ViewPortListener arg0) {
455
        if (!listeners.contains(arg0)) {
456
            return listeners.add(arg0);
457
        }
458
        return false;
459
    }
460

    
461
    /**
462
     * <p>
463
     * Removes the specified {@link ViewPortListener ViewPortListener} listener,
464
     * if existed.
465
     * </p>
466
     * 
467
     * @param arg0
468
     *            the listener to remove
469
     * 
470
     * @return <code>true</code> if the contained the specified listener.
471
     * 
472
     * @see #addViewPortListener(ViewPortListener)
473
     */
474
    public boolean removeViewPortListener(ViewPortListener arg0) {
475
        return listeners.remove(arg0);
476
    }
477

    
478
    /**
479
     * <p>
480
     * Converts and returns the distance <code>d</code>, that is in <i>map
481
     * coordinates</i> to <i>screen coordinates</i> using a <i>delta
482
     * transform</i> with the transformation affine information in the
483
     * {@link #trans #trans} attribute.
484
     * </p>
485
     * 
486
     * @param d
487
     *            distance in <i>map coordinates</i>
488
     * 
489
     * @return distance equivalent in <i>screen coordinates</i>
490
     * 
491
     * @see #toMapDistance(int)
492
     * @see AffineTransform#deltaTransform(Point2D, Point2D)S
493
     */
494
    public int fromMapDistance(double d) {
495
        Point2D.Double pWorld = new Point2D.Double(1, 1);
496
        Point2D.Double pScreen = new Point2D.Double();
497

    
498
        try {
499
            trans.deltaTransform(pWorld, pScreen);
500
        } catch (Exception e) {
501
            System.err.print(e.getMessage());
502
        }
503

    
504
        return (int) (d * pScreen.x);
505
    }
506

    
507
    /**
508
     * <p>
509
     * Converts and returns the 2D point <code>(x,y)</code>, that is in <i>map
510
     * coordinates</i> to <i>screen coordinates</i> (pixels) using the affine
511
     * transformation in the {@link #trans #trans} attribute.
512
     * </p>
513
     * 
514
     * @param x
515
     *            the <code>x</code> <i>map coordinate</i> of a 2D point
516
     * @param y
517
     *            the <code>y</code> <i>map coordinate</i> of a 2D point
518
     * 
519
     * @return 2D point equivalent in <i>screen coordinates</i> (pixels)
520
     * 
521
     * @see #fromMapPoint(Point2D)
522
     * @see AffineTransform#transform(Point2D, Point2D)
523
     */
524
    public Point2D fromMapPoint(double x, double y) {
525
        Point2D.Double pWorld = new Point2D.Double(x, y);
526
        Point2D.Double pScreen = new Point2D.Double();
527

    
528
        try {
529
            trans.transform(pWorld, pScreen);
530
        } catch (Exception e) {
531
            System.err.print(e.getMessage());
532
        }
533

    
534
        return pScreen;
535
    }
536

    
537
    /**
538
     * <p>
539
     * Converts and returns the 2D point argument, that is in <i>map
540
     * coordinates</i> to <i>screen coordinates</i> (pixels) using the affine
541
     * transformation in the {@link #trans #trans} attribute.
542
     * </p>
543
     * 
544
     * @param point
545
     *            the 2D point in <i>map coordinates</i>
546
     * 
547
     * @return 2D point equivalent in <i>screen coordinates</i> (pixels)
548
     * 
549
     * @see #toMapPoint(Point2D)
550
     * @see #fromMapPoint(double, double)
551
     */
552
    public Point2D fromMapPoint(Point2D point) {
553
        return fromMapPoint(point.getX(), point.getY());
554
    }
555

    
556
    /**
557
     * <p>
558
     * Converts and returns the 2D point <code>(x,y)</code>, that is in
559
     * <i>screen coordinates</i> (pixels) to <i>map coordinates</i> using the
560
     * affine transformation in the {@link #trans #trans} attribute.
561
     * </p>
562
     * 
563
     * @param x
564
     *            the <code>x</code> <i>screen coordinate</i> of a 2D point
565
     * @param y
566
     *            the <code>y</code> <i>screen coordinate</i> of a 2D point
567
     * 
568
     * @return 2D point equivalent in <i>map coordinates</i>
569
     * 
570
     * @see #toMapPoint(Point2D)
571
     * @see #fromMapPoint(double, double)
572
     * @deprecated use {@link #convertToMapPoint(int, int)}
573
     */
574
    public Point2D toMapPoint(int x, int y) {
575
        Point2D pScreen = new Point2D.Double(x, y);
576

    
577
        return toMapPoint(pScreen);
578
    }
579

    
580
    /**
581
     * <p>
582
     * Converts and returns the {@link Rectangle2D Rectangle2D}, that is in
583
     * <i>screen coordinates</i> (pixels) to <i>map coordinates</i> using
584
     * {@linkplain #toMapDistance(int)}, and {@linkplain #toMapPoint(int, int)}.
585
     * </p>
586
     * 
587
     * @param r
588
     *            the 2D rectangle in <i>screen coordinates</i> (pixels)
589
     * @return 2D rectangle equivalent in <i>map coordinates</i>
590
     * 
591
     * @see #fromMapRectangle(Rectangle2D)
592
     * @see #toMapDistance(int)
593
     * @see #toMapPoint(int, int)
594
     */
595
    public Rectangle2D toMapRectangle(Rectangle2D r) {
596
        Rectangle2D rect = new Rectangle2D.Double();
597
        Point2D p1 = toMapPoint((int) r.getX(), (int) r.getY());
598
        Point2D p2 = toMapPoint((int) r.getMaxX(), (int) r.getMaxY());
599
        rect.setFrameFromDiagonal(p1, p2);
600
        return rect;
601
    }
602

    
603
    /**
604
     * <p>
605
     * Converts and returns the distance <code>d</code>, that is in <i>screen
606
     * coordinates</i> to <i>map coordinates</i> using the transformation affine
607
     * information in the {@link #trans #trans} attribute.
608
     * </p>
609
     * 
610
     * @param d
611
     *            distance in pixels
612
     * 
613
     * @return distance equivalent in <i>map coordinates</i>
614
     * 
615
     * @see #fromMapDistance(double)
616
     * @see AffineTransform
617
     */
618
    public double toMapDistance(int d) {
619
        double dist = d / trans.getScaleX();
620

    
621
        return dist;
622
    }
623

    
624
    /**
625
     * <p>
626
     * Converts and returns the 2D point argument, that is in <i>screen
627
     * coordinates</i> (pixels) to <i>map coordinates</i> using the inverse
628
     * affine transformation of the {@link #trans #trans} attribute.
629
     * </p>
630
     * 
631
     * @param pScreen
632
     *            the 2D point in <i>screen coordinates</i> (pixels)
633
     * 
634
     * @return 2D point equivalent in <i>map coordinates</i>
635
     * 
636
     * @see #toMapPoint(int, int)
637
     * @see AffineTransform#createInverse()
638
     * @see AffineTransform#transform(Point2D, Point2D)
639
     * @deprecated use {@link #convertToMapPoint(Point2D)}
640
     */
641
    public Point2D toMapPoint(Point2D pScreen) {
642
        Point2D.Double pWorld = new Point2D.Double();
643
        AffineTransform at;
644

    
645
        try {
646
            at = trans.createInverse();
647
            at.transform(pScreen, pWorld);
648
        } catch (NoninvertibleTransformException e) {
649
            throw new RuntimeException("Non invertible transform Exception", e);
650
        }
651

    
652
        return pWorld;
653
    }
654

    
655
    public Point convertToMapPoint(Point2D pScreen) {
656
            Point2D p = toMapPoint(pScreen);
657
                try {
658
                        return geomManager.createPoint(
659
                                p.getX(), 
660
                                p.getY(), 
661
                                Geometry.SUBTYPES.GEOM2D
662
                        );
663
                } catch (CreateGeometryException e) {
664
                        // FIXME: Use a most especific exception.
665
                        throw new RuntimeException(e);
666
                }
667
    }
668
    
669
    public Point convertToMapPoint(int x, int y) {
670
        Point2D pScreen = new Point2D.Double(x, y);
671

    
672
        return convertToMapPoint(pScreen);
673
    }
674

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

    
699
        if ((proj != null) && !(proj instanceof UTM)) {
700
            dist =
701
                new GeoCalc(proj).distanceVincenty(proj.toGeo(pt1),
702
                    proj.toGeo(pt2));
703
            return dist;
704
        }
705
        return (dist * MapContext.getDistanceTrans2Meter()[getMapUnits()]);
706
    }
707

    
708
    /**
709
     * <p>
710
     * Sets as extent and adjusted extent of this view port, the previous.
711
     * Recalculating its parameters.
712
     * </p>
713
     * 
714
     * @see #getExtents()
715
     * @see #calculateAffineTransform()
716
     * @deprecated use {@link ViewPort#setPreviousEnvelope()}
717
     */
718
    public void setPreviousExtent() {
719
        setPreviousEnvelope();
720
    }
721

    
722
    /**
723
     * <p>
724
     * Sets as envelope and adjusted envelope of this view port, the previous.
725
     * Recalculating its parameters.
726
     * </p>
727
     * 
728
     * @see #getExtents()
729
     * @see #calculateAffineTransform()
730
     */
731
    public void setPreviousEnvelope() {
732
        this.updateDrawVersion();
733
        extent = extents.removePrev();
734

    
735
        // Calcula la transformaci�n af�n
736
        calculateAffineTransform();
737

    
738
        // Lanzamos los eventos de extent cambiado
739
        callExtentChanged(getAdjustedExtent());
740
    }
741

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

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

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

    
871
        if (this.extent != null && this.extent.equals(newExtent)) {
872
            return;
873
        }
874
        if (extent != null) {
875
            extents.put(extent);
876
        }
877
        this.updateDrawVersion();
878
        this.extent = newExtent;
879

    
880
        // Calcula la transformaci�n af�n
881
        calculateAffineTransform();
882

    
883
        // Lanzamos los eventos de extent cambiado
884
        callExtentChanged(getAdjustedExtent());
885
    }
886

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

    
906
        // Calcula la transformaci�n af�n
907
        calculateAffineTransform();
908

    
909
        // Lanzamos los eventos de extent cambiado
910
        callExtentChanged(getAdjustedExtent());
911
    }
912

    
913
    /**
914
     * <p>
915
     * Calculates and returns using the current projection of this view port,
916
     * the scale that is the extent in <i>screen coordinates</i> from the image
917
     * in <i>map coordinates</i>.
918
     * </p>
919
     * 
920
     * @return the scale <i>extent / image size</i> projected by this view port
921
     * 
922
     * @deprecated since 07/09/07, use {@linkplain MapContext#getScaleView()}
923
     */
924
    public double getScale() {
925
        return proj.getScale(extent.getMinX(),
926
            extent.getMaxX(),
927
            imageSize.width,
928
            dpi);
929
    }
930

    
931
    /**
932
     * <p>
933
     * Affine transformation between <i>map 2D coordinates</i> to <i>screen 2D
934
     * coordinates</i> (pixels), preserving the "straightness" and "parallelism"
935
     * of the lines.
936
     * </p>
937
     * 
938
     * @return the affine transformation
939
     * 
940
     * @see #setAffineTransform(AffineTransform)
941
     * @see #calculateAffineTransform()
942
     */
943
    public AffineTransform getAffineTransform() {
944
        return trans;
945
    }
946

    
947
    /**
948
     * <p>
949
     * Returns the size of the image projected.
950
     * </p>
951
     * 
952
     * @return the image size
953
     * 
954
     * @see #setImageSize(Dimension)
955
     * @see #getImageHeight()
956
     * @see #getImageWidth()
957
     */
958
    public Dimension getImageSize() {
959
        return imageSize;
960
    }
961

    
962
    /**
963
     * <p>
964
     * Sets the size of the image projected, recalculating the parameters of
965
     * this view port.
966
     * </p>
967
     * 
968
     * @param imageSize
969
     *            the image size
970
     * 
971
     * @see #getImageSize()
972
     * @see #calculateAffineTransform()
973
     */
974
    public void setImageSize(Dimension imageSize) {
975

    
976
        if (this.imageSize == null || (!this.imageSize.equals(imageSize))) {
977
            this.updateDrawVersion();
978
            this.imageSize = imageSize;
979
            calculateAffineTransform();
980
        }
981
    }
982

    
983
    /**
984
     * <p>
985
     * Notifies to all view port listeners registered, that the adjusted extent
986
     * of this view port has changed.
987
     * </p>
988
     * 
989
     * @param newRect
990
     *            the new adjusted extend
991
     * 
992
     * @see #refreshExtent()
993
     * @see #setEnvelope(Envelope)
994
     * @see #setPreviousExtent()
995
     * @see ExtentEvent
996
     * @see ViewPortListener
997
     */
998
    protected void callExtentChanged(Envelope newRect) {
999
        ExtentEvent ev = ExtentEvent.createExtentEvent(newRect);
1000

    
1001
        for (int i = 0; i < listeners.size(); i++) {
1002
            ViewPortListener listener = (ViewPortListener) listeners.get(i);
1003
            listener.extentChanged(ev);
1004
        }
1005
    }
1006
    
1007
    /**
1008
     * <p>
1009
     * Notifies to all view port listeners registered, that the time
1010
     * of this view port has changed.
1011
     * </p>
1012
     * 
1013
     * @param newTime
1014
     *            the new time
1015
     * 
1016
     * @see #refreshExtent()
1017
     * @see #setTime(Time)   
1018
     * @see ExtentEvent
1019
     * @see ViewPortListener
1020
     */
1021
    protected void callTimeChanged(Time newTime) {
1022
        ExtentEvent viewPortEvent = new ExtentEvent(newTime);
1023

    
1024
        for (int i = 0; i < listeners.size(); i++) {
1025
            ViewPortListener listener = (ViewPortListener) listeners.get(i);
1026
            listener.extentChanged(viewPortEvent);
1027
        }
1028
    }
1029

    
1030
    /**
1031
     * <p>
1032
     * Notifies to all view port listeners registered, that the background color
1033
     * of this view port has changed.
1034
     * </p>
1035
     * 
1036
     * @param c
1037
     *            the new background color
1038
     * 
1039
     * @see #setBackColor(Color)
1040
     * @see ColorEvent
1041
     * @see ViewPortListener
1042
     */
1043
    private void callColorChanged(Color c) {
1044
        ColorEvent ce = ColorEvent.createColorEvent(c);
1045

    
1046
        for (int i = 0; i < listeners.size(); i++) {
1047
            ViewPortListener listener = (ViewPortListener) listeners.get(i);
1048
            listener.backColorChanged(ce);
1049
        }
1050
    }
1051

    
1052
    /**
1053
     * <p>
1054
     * Notifies to all view port listeners registered, that the projection of
1055
     * this view port has changed.
1056
     * </p>
1057
     * 
1058
     * @param projection
1059
     *            the new projection
1060
     * 
1061
     * @see #setProjection(IProjection)
1062
     * @see ProjectionEvent
1063
     * @see ViewPortListener
1064
     */
1065
    private void callProjectionChanged(IProjection projection) {
1066
        ProjectionEvent ev = ProjectionEvent.createProjectionEvent(projection);
1067

    
1068
        for (int i = 0; i < listeners.size(); i++) {
1069
            ViewPortListener listener = (ViewPortListener) listeners.get(i);
1070
            listener.projectionChanged(ev);
1071
        }
1072
    }
1073

    
1074
    /**
1075
     * <p>
1076
     * Calculates the affine transformation between the {@link #extent extent}
1077
     * in <i>map 2D coordinates</i> to the image area in the screen, in
1078
     * <i>screen 2D coordinates</i> (pixels).
1079
     * </p>
1080
     * 
1081
     * <p>
1082
     * This process recalculates some parameters of this view port:<br>
1083
     * 
1084
     * <ul>
1085
     * <li>The new {@link #scale scale} .
1086
     * <li>The new {@link #adjustedExtent adjustedExtent} .
1087
     * <li>The new {@link #trans trans} .
1088
     * <li>The new real world coordinates equivalent to 1 pixel (
1089
     * {@link #dist1pixel dist1pixel}) .
1090
     * <li>The new real world coordinates equivalent to 3 pixels (
1091
     * {@link #dist3pixel dist3pixel}) .
1092
     * </ul>
1093
     * </p>
1094
     * 
1095
     * @see #getAffineTransform()
1096
     * @see #setAffineTransform(AffineTransform)
1097
     * @see #refreshExtent()
1098
     * @see #setEnvelope(Envelope)
1099
     * @see #setImageSize(Dimension)
1100
     * @see #setPreviousExtent()
1101
     * @see #createFromXML(XMLEntity)
1102
     * @see AffineTransform
1103
     */
1104
    private void calculateAffineTransform() {
1105
        if ((imageSize == null) || (extent == null) || (imageSize.width <= 0)
1106
            || (imageSize.height <= 0)) {
1107
            return;
1108
        }
1109

    
1110
        AffineTransform escalado = new AffineTransform();
1111
        AffineTransform translacion = new AffineTransform();
1112

    
1113
        double escalaX;
1114
        double escalaY;
1115

    
1116
        escalaX = imageSize.width / extent.getWidth();
1117
        escalaY = imageSize.height / extent.getHeight();
1118

    
1119
        double xCenter = extent.getCenterX();
1120
        double yCenter = extent.getCenterY();
1121
        double newHeight;
1122
        double newWidth;
1123

    
1124
        adjustedExtent = new Rectangle2D.Double();
1125

    
1126
        if (adjustableExtent) {
1127
            if (escalaX < escalaY) {
1128
                scale = escalaX;
1129
                newHeight = imageSize.height / scale;
1130
                adjustedExtent.setRect(xCenter - (extent.getWidth() / 2.0),
1131
                    yCenter - (newHeight / 2.0),
1132
                    extent.getWidth(),
1133
                    newHeight);
1134
            } else {
1135
                scale = escalaY;
1136
                newWidth = imageSize.width / scale;
1137
                adjustedExtent.setRect(xCenter - (newWidth / 2.0), yCenter
1138
                    - (extent.getHeight() / 2.0), newWidth, extent.getHeight());
1139
            }
1140
            escalado.setToScale(scale, -scale);
1141
        } else { // adjusted is same as extent
1142
            scale = escalaX;
1143
            adjustedExtent.setFrame(extent);
1144
            escalado.setToScale(escalaX, -escalaY);
1145
        }
1146
        Envelope env = getAdjustedExtent();
1147
        if (env == null) {
1148
            return;
1149
        }
1150
        translacion.setToTranslation(-env.getMinimum(0), -env.getMinimum(1)
1151
            - getAdjustedExtent().getLength(1));
1152

    
1153
        AffineTransform offsetTrans = new AffineTransform();
1154
        offsetTrans.setToTranslation(offset.getX(), offset.getY());
1155

    
1156
        trans.setToIdentity();
1157
        trans.concatenate(offsetTrans);
1158
        trans.concatenate(escalado);
1159

    
1160
        trans.concatenate(translacion);
1161

    
1162
        // Calculamos las distancias de 1 pixel y 3 pixel con esa
1163
        // transformaci�n
1164
        // de coordenadas, de forma que est�n precalculadas para cuando las
1165
        // necesitemos
1166
        AffineTransform at;
1167

    
1168
        try {
1169
            at = trans.createInverse();
1170

    
1171
            Point2D pPixel = new Point2D.Float(1, 1);
1172

    
1173
            Point2D.Float pProv = new Point2D.Float();
1174
            at.deltaTransform(pPixel, pProv);
1175

    
1176
            dist1pixel = pProv.x;
1177
            dist3pixel = 3 * pProv.x;
1178
        } catch (NoninvertibleTransformException e) {
1179
            System.err.println("transformada afin = " + trans.toString());
1180
            System.err.println("extent = " + extent.toString() + " imageSize= "
1181
                + imageSize.toString());
1182
            throw new RuntimeException("Non invertible transform Exception", e);
1183
        }
1184
    }
1185

    
1186
    /**
1187
     * <p>
1188
     * Sets the offset.
1189
     * </p>
1190
     * <p>
1191
     * The offset is the position where start drawing the map.
1192
     * </p>
1193
     * 
1194
     * @param p
1195
     *            2D point that represents the offset in pixels
1196
     * 
1197
     * @see #getOffset()
1198
     */
1199
    public void setOffset(Point2D p) {
1200
        if (!offset.equals(p)) {
1201
            this.updateDrawVersion();
1202
            offset = p;
1203
        }
1204
    }
1205

    
1206
    /**
1207
     * <p>
1208
     * Gets the offset.
1209
     * </p>
1210
     * <p>
1211
     * The offset is the position where start drawing the map.
1212
     * </p>
1213
     * 
1214
     * @return 2D point that represents the offset in pixels
1215
     * 
1216
     * @see #setOffset(Point2D)
1217
     */
1218
    public Point2D getOffset() {
1219
        return offset;
1220
    }
1221

    
1222
    /**
1223
     * <p>
1224
     * Sets the background color.
1225
     * </p>
1226
     * 
1227
     * @param c
1228
     *            the new background color
1229
     * 
1230
     * @see #getBackColor()
1231
     */
1232
    public void setBackColor(Color c) {
1233
        if (!c.equals(this.backColor)) {
1234
            this.updateDrawVersion();
1235
            backColor = c;
1236
            callColorChanged(backColor);
1237
        }
1238
    }
1239

    
1240
    /**
1241
     * <p>
1242
     * Gets the background color.
1243
     * </p>
1244
     * 
1245
     * @return the background color of the view
1246
     * 
1247
     * @see #setBackColor(Color)
1248
     */
1249
    public Color getBackColor() {
1250
        return backColor;
1251
    }
1252

    
1253
    /**
1254
     * <p>
1255
     * Returns the extent currently covered by the view adjusted (scaled) to the
1256
     * image size aspect.
1257
     * </p>
1258
     * 
1259
     * @return extent of the view adjusted to the image size aspect
1260
     * 
1261
     * @see #setAdjustable(boolean)
1262
     * @deprecated use {@link ViewPort#getAdjustedEnvelope()} instead
1263
     */
1264
    public Envelope getAdjustedExtent() {
1265
        return getAdjustedEnvelope();
1266
    }
1267

    
1268
    /**
1269
     * <p>
1270
     * Returns the envelope currently covered by the view adjusted (scaled) to
1271
     * the image size aspect.
1272
     * </p>
1273
     * 
1274
     * @return envelope of the view adjusted to the image size aspect
1275
     * 
1276
     * @see #setAdjustable(boolean)
1277
     */
1278
    public Envelope getAdjustedEnvelope() {
1279
        if (cliprect != null) {
1280
            Rectangle2D r = adjustedExtent.createIntersection(cliprect);
1281
            try {
1282
                return geomManager.createEnvelope(r.getX(),
1283
                    r.getY(),
1284
                    r.getMaxX(),
1285
                    r.getMaxY(),
1286
                    SUBTYPES.GEOM2D);
1287
            } catch (CreateEnvelopeException e) {
1288
                e.printStackTrace();
1289
                logger.error("Error adjusting the extent", e);
1290
            }
1291
        }
1292
        if (adjustedExtent != null) {
1293
            try {
1294
                return geomManager.createEnvelope(adjustedExtent.getX(),
1295
                    adjustedExtent.getY(),
1296
                    adjustedExtent.getMaxX(),
1297
                    adjustedExtent.getMaxY(),
1298
                    SUBTYPES.GEOM2D);
1299
            } catch (CreateEnvelopeException e) {
1300
                e.printStackTrace();
1301
                logger.error("Error adjusting the extent", e);
1302
            }
1303
        }
1304
        return null;
1305
    }
1306

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

    
1322
    /**
1323
     * <p>
1324
     * Returns the measurement unit of this view port used for measuring areas
1325
     * and displaying information.
1326
     * </p>
1327
     * 
1328
     * @return the measurement unit of this view used for measuring areas and
1329
     *         displaying information
1330
     * 
1331
     * @see #setDistanceUnits(int)
1332
     */
1333
    public int getDistanceArea() {
1334
        return distanceArea;
1335
    }
1336

    
1337
    /**
1338
     * <p>
1339
     * Sets the measurement unit of this view port used for measuring distances
1340
     * and displaying information.
1341
     * </p>
1342
     * 
1343
     * @param distanceUnits
1344
     *            the measurement unit of this view used for measuring distances
1345
     *            and displaying information
1346
     * 
1347
     * @see #getDistanceUnits()
1348
     */
1349
    public void setDistanceUnits(int distanceUnits) {
1350
        this.distanceUnits = distanceUnits;
1351
    }
1352

    
1353
    /**
1354
     * <p>
1355
     * Sets the measurement unit of this view port used for measuring areas and
1356
     * displaying information.
1357
     * </p>
1358
     * 
1359
     * @param distanceUnits
1360
     *            the measurement unit of this view used for measuring areas and
1361
     *            displaying information
1362
     * 
1363
     * @see #getDistanceUnits()
1364
     */
1365
    public void setDistanceArea(int distanceArea) {
1366
        this.distanceArea = distanceArea;
1367
    }
1368

    
1369
    /**
1370
     * <p>
1371
     * Gets the measurement unit used by this view port for the map.
1372
     * </p>
1373
     * 
1374
     * @return Returns the current map measure unit
1375
     * 
1376
     * @see #setMapUnits(int)
1377
     */
1378
    public int getMapUnits() {
1379
        return mapUnits;
1380
    }
1381

    
1382
    /**
1383
     * <p>
1384
     * Sets the measurement unit used by this view port for the map.
1385
     * </p>
1386
     * 
1387
     * @param mapUnits
1388
     *            the new map measure unit
1389
     * 
1390
     * @see #getMapUnits()
1391
     */
1392
    public void setMapUnits(int mapUnits) {
1393
        this.mapUnits = mapUnits;
1394
    }
1395

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

    
1423
    /**
1424
     * <p>
1425
     * Gets the height in <i>screen coordinates</i> of the rectangle where the
1426
     * image is displayed.
1427
     * </p>
1428
     * <p>
1429
     * Used by {@linkplain #calculateAffineTransform()} to calculate:<br>
1430
     * 
1431
     * <ul>
1432
     * <li>The new {@link #scale scale} .
1433
     * <li>The new {@link #adjustedExtent adjustableExtent} .
1434
     * <li>The new {@link #trans trans} .
1435
     * <li>The new real world coordinates equivalent to 1 pixel (
1436
     * {@link #dist1pixel dist1pixel}) .
1437
     * <li>The new real world coordinates equivalent to 3 pixels (
1438
     * {@link #dist3pixel dist3pixel}) .
1439
     * </ul>
1440
     * </p>
1441
     * 
1442
     * @see #getImageWidth()
1443
     * @see #getImageSize()
1444
     * @see #setImageSize(Dimension)
1445
     */
1446
    public int getImageHeight() {
1447
        return imageSize.height;
1448
    }
1449

    
1450
    /**
1451
     * <p>
1452
     * Gets the distance in <i>world coordinates</i> equivalent to 1 pixel in
1453
     * the view with the current extent.
1454
     * </p>
1455
     * 
1456
     * @return the distance
1457
     * 
1458
     * @see #setDist1pixel(double)
1459
     */
1460
    public double getDist1pixel() {
1461
        return dist1pixel;
1462
    }
1463

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

    
1483
    /**
1484
     * <p>
1485
     * Gets the distance in <i>world coordinates</i> equivalent to 3 pixels in
1486
     * the view with the current extent.
1487
     * </p>
1488
     * 
1489
     * @return the distance
1490
     * 
1491
     * @see #setDist3pixel(double)
1492
     */
1493
    public double getDist3pixel() {
1494
        return dist3pixel;
1495
    }
1496

    
1497
    /**
1498
     * <p>
1499
     * Sets the distance in <i>world coordinates</i> equivalent to 3 pixels in
1500
     * the view with the current extent.
1501
     * </p>
1502
     * 
1503
     * @param dist3pixel
1504
     *            the distance
1505
     * 
1506
     * @see #getDist3pixel()
1507
     */
1508
    public void setDist3pixel(double dist3pixel) {
1509
        if (this.dist3pixel == dist3pixel) {
1510
            return;
1511
        }
1512
        this.updateDrawVersion();
1513
        this.dist3pixel = dist3pixel;
1514
    }
1515

    
1516
    /**
1517
     * <p>
1518
     * Returns the last previous extents of this view port.
1519
     * </p>
1520
     * 
1521
     * @return the last previous extents of this view port
1522
     * 
1523
     * @see #setPreviousExtent()
1524
     * @deprecated use {@link ViewPort#getEnvelopes()}
1525
     */
1526
    public ExtentHistory getExtents() {
1527
        return getEnvelopes();
1528
    }
1529

    
1530
    /**
1531
     * <p>
1532
     * Returns the last previous extents of this view port.
1533
     * </p>
1534
     * 
1535
     * @return the last previous extents of this view port
1536
     * 
1537
     * @see #setPreviousExtent()
1538
     */
1539
    public ExtentHistory getEnvelopes() {
1540
        return extents;
1541
    }
1542

    
1543
    /**
1544
     * <p>
1545
     * Gets the projection used in this view port.
1546
     * </p>
1547
     * 
1548
     * @return projection used in this view port
1549
     * 
1550
     * @see #setProjection(IProjection)
1551
     */
1552
    public IProjection getProjection() {
1553
        return proj;
1554
    }
1555

    
1556
    /**
1557
     * <p>
1558
     * Sets the projection to this view port.
1559
     * </p>
1560
     * 
1561
     * @param proj
1562
     *            the new projection
1563
     * 
1564
     * @see #getProjection()
1565
     */
1566
    public void setProjection(IProjection proj) {
1567
        if (this.proj == null || !this.proj.getAbrev().equals(proj.getAbrev())) {
1568
            this.updateDrawVersion();
1569
            this.proj = proj;
1570
            callProjectionChanged(proj);
1571
        }
1572
    }
1573

    
1574
    // -----------------------------------------------------------------------------------------------------------
1575
    // NOTA PARA DESARROLLADORES SOBRE EL M�TODO
1576
    // "public void setAffineTransform(AffineTransform at)"
1577
    // ==============================================================================================
1578
    // Only used for print, should be removed, redefining the {@link
1579
    // RasterAdapter RasterAdapter} interface,
1580
    // allowing it to receive a {@link ViewPortData ViewPortData} .
1581
    // -----------------------------------------------------------------------------------------------------------
1582

    
1583
    /**
1584
     * <p>
1585
     * Sets only the affine transform to this view port, without updating
1586
     * dependent attributes.
1587
     * </p>
1588
     * <p>
1589
     * <b><i>This method could be problematic!</i></b>
1590
     * </p>
1591
     * 
1592
     * @param at
1593
     *            the affine transform to set
1594
     * 
1595
     * @see #getAffineTransform()
1596
     * @see #calculateAffineTransform()
1597
     */
1598
    public void setAffineTransform(AffineTransform at) {
1599
        this.trans = at;
1600
    }
1601

    
1602
    /**
1603
     * <p>
1604
     * Returns an XML entity that represents this view port instance:<br>
1605
     * <ul>
1606
     * <li>Properties:
1607
     * <ul>
1608
     * <li><i>className</i>: name of this class.
1609
     * <li>If defined, the adjusted extent:
1610
     * <ul>
1611
     * <li><i>adjustedExtentX</i>: X coordinate of the adjusted extent.
1612
     * <li><i>adjustedExtentY</i>: Y coordinate of the adjusted extent.
1613
     * <li><i>adjustedExtentW</i>: width of the adjusted extent.
1614
     * <li><i>adjustedExtentH</i>: height of the adjusted extent.
1615
     * </ul>
1616
     * <li>If defined, the background color:
1617
     * <ul>
1618
     * <li><i>backColor</i>: background color.
1619
     * </ul>
1620
     * <li>If defined, the clip:
1621
     * <ul>
1622
     * <li><i>clipX</i>: X coordinate of the clip.
1623
     * <li><i>clipY</i>: Y coordinate of clip.
1624
     * <li><i>clipW</i>: width of the clip.
1625
     * <li><i>clipH</i>: height of the clip.
1626
     * </ul>
1627
     * <li><i>dist1pixel</i>: the distance in world coordinates equivalent to 1
1628
     * pixel in the view.
1629
     * <li><i>dist3pixel</i>: the distance in world coordinates equivalent to 3
1630
     * pixels in the view.
1631
     * <li><i>distanceUnits</i>: the distance measurement unit.
1632
     * <li>If defined, the extent:
1633
     * <ul>
1634
     * <li><i>extentX</i>: X coordinate of the extent.
1635
     * <li><i>extentY</i>: Y coordinate of the extent.
1636
     * <li><i>extentW</i>: width of the extent.
1637
     * <li><i>extentH</i>: height of the extent.
1638
     * </ul>
1639
     * <li><i>mapUnits</i>: the map measurement unit.
1640
     * <li><i>offsetX</i>: X coordinate of the offset.
1641
     * <li><i>offsetY</i>: Y coordinate of the offset.
1642
     * <li>If defined, the projection:
1643
     * <ul>
1644
     * <li>If its defined, the projection:
1645
     * <ul>
1646
     * <li><i>proj</i>: the projection.</li>
1647
     * </ul>
1648
     * </ul>
1649
     * <li><i>scale</i>: ratio between the size of <code>imageSize</code> and
1650
     * <code>extent</code>.
1651
     * </ul>
1652
     * <li>Child branches:
1653
     * <ul>
1654
     * <li>XML entity of the internal {@link ExtentHistory ExtentHistory} .
1655
     * </ul>
1656
     * </ul>
1657
     * 
1658
     * @return the XML entity
1659
     * 
1660
     * @see #createFromXML(XMLEntity)
1661
     */
1662
    public void saveToState(PersistentState state) throws PersistenceException {
1663

    
1664
        state.set(FIELD_ADJUSTED_EXTENT, adjustedExtent);
1665
        state.set(FIELD_BACK_COLOR, backColor);
1666
        state.set(FIELD_CLIP, cliprect);
1667
        state.set(FIELD_DIST1PIXEL, dist1pixel);
1668
        state.set(FIELD_DIST3PIXEL, dist3pixel);
1669
        state.set(FIELD_DISTANCE_UNITS, distanceUnits);
1670
        state.set(FIELD_DISTANCE_AREA, distanceArea);
1671

    
1672
        state.set(FIELD_EXTENT, extent);
1673
        state.set(FIELD_EXTENTS, extents);
1674

    
1675
        state.set(FIELD_MAP_UNITS, mapUnits);
1676
        state.set(FIELD_OFFSET, offset);
1677

    
1678
        state.set(FIELD_PROJ, proj);
1679

    
1680
        state.set(FIELD_IMAGE_SIZE, imageSize);
1681
    }
1682

    
1683
    public void loadFromState(PersistentState state) throws PersistenceException {
1684

    
1685
        adjustedExtent = (Rectangle2D) state.get(FIELD_ADJUSTED_EXTENT);
1686
        backColor = (Color) state.get(FIELD_BACK_COLOR);
1687
        cliprect = (Rectangle2D) state.get(FIELD_CLIP);
1688
        dist1pixel = state.getDouble(FIELD_DIST1PIXEL);
1689
        dist3pixel = state.getDouble(FIELD_DIST3PIXEL);
1690
        distanceUnits = state.getInt(FIELD_DISTANCE_UNITS);
1691
        extents = (ExtentHistory) state.get(FIELD_EXTENTS);
1692
        extent = (Rectangle2D) state.get(FIELD_EXTENT);
1693
        mapUnits = state.getInt(FIELD_MAP_UNITS);
1694
        offset = (Point2D) state.get(FIELD_OFFSET);
1695
        proj = (IProjection) state.get(FIELD_PROJ);
1696
        imageSize = (Dimension) state.get(FIELD_IMAGE_SIZE);
1697
        distanceArea = state.getInt(FIELD_DISTANCE_AREA);
1698

    
1699
        refreshExtent();
1700
    }
1701

    
1702
        public static class RegisterPersistence implements Callable {
1703

    
1704
                public Object call() throws Exception {
1705
                        PersistenceManager manager = ToolsLocator.getPersistenceManager();
1706
                        if( manager.getDefinition("ViewPort")==null ) {
1707
                        DynStruct definition =
1708
                            manager.addDefinition(ViewPort.class,
1709
                                "ViewPort",
1710
                                "ViewPort Persistence definition",
1711
                                null,
1712
                                null);
1713

    
1714
                        definition.addDynFieldObject(FIELD_ADJUSTED_EXTENT)
1715
                            .setClassOfValue(Rectangle2D.class)
1716
                            .setMandatory(false);
1717

    
1718
                        definition.addDynFieldObject(FIELD_BACK_COLOR)
1719
                            .setClassOfValue(Color.class)
1720
                            .setMandatory(false);
1721

    
1722
                        definition.addDynFieldObject(FIELD_CLIP)
1723
                            .setClassOfValue(Rectangle2D.class)
1724
                            .setMandatory(false);
1725

    
1726
                        definition.addDynFieldDouble(FIELD_DIST1PIXEL).setMandatory(true);
1727

    
1728
                        definition.addDynFieldDouble(FIELD_DIST3PIXEL).setMandatory(true);
1729

    
1730
                        definition.addDynFieldInt(FIELD_DISTANCE_UNITS).setMandatory(true);
1731

    
1732
                        definition.addDynFieldInt(FIELD_DISTANCE_AREA).setMandatory(false);
1733

    
1734
                        definition.addDynFieldObject(FIELD_EXTENT)
1735
                            .setClassOfValue(Rectangle2D.class)
1736
                            .setMandatory(false);
1737

    
1738
                        definition.addDynFieldObject(FIELD_EXTENTS)
1739
                            .setClassOfValue(ExtentHistory.class)
1740
                            .setMandatory(true);
1741

    
1742
                        definition.addDynFieldInt(FIELD_MAP_UNITS).setMandatory(true);
1743

    
1744
                        definition.addDynFieldObject(FIELD_OFFSET)
1745
                            .setClassOfValue(Point2D.class)
1746
                            .setMandatory(false);
1747

    
1748
                        definition.addDynFieldObject(FIELD_PROJ)
1749
                            .setClassOfValue(IProjection.class)
1750
                            .setMandatory(true);
1751

    
1752
                        definition.addDynFieldObject(FIELD_IMAGE_SIZE)
1753
                            .setClassOfValue(Dimension.class)
1754
                            .setMandatory(false);
1755
                        }
1756
                        return Boolean.TRUE;
1757
                }
1758
                
1759
        }
1760

    
1761
        /**
1762
     * Clone the view port without clone the listeners nor the extent history.
1763
     * 
1764
     * @return the cloned view port
1765
     */
1766
    public Object clone() throws CloneNotSupportedException {
1767

    
1768
        ViewPort clonedViewPort = (ViewPort) super.clone();
1769
        clonedViewPort.listeners = new ArrayList();
1770
        clonedViewPort.extents = new ExtentHistory();
1771

    
1772
        if (this.adjustedExtent!=null){
1773
            clonedViewPort.adjustedExtent =
1774
                (Rectangle2D) this.adjustedExtent.clone();
1775
        }
1776

    
1777
        if (this.cliprect!=null){
1778
            clonedViewPort.cliprect = (Rectangle2D) this.cliprect.clone();
1779
        }
1780
        
1781
        if (this.extent!=null) {
1782
            clonedViewPort.extent = (Rectangle2D) this.extent.clone();
1783
        }
1784
        if (this.imageSize!=null){
1785
            clonedViewPort.imageSize = (Dimension) this.imageSize.clone();
1786
        }
1787
        
1788
        if (this.offset!=null){
1789
            clonedViewPort.offset = (Point2D) this.offset.clone();
1790
        }
1791
        if (proj!=null){
1792
            clonedViewPort.proj = (IProjection) this.proj.clone();
1793
        }
1794
        
1795
        clonedViewPort.trans = (AffineTransform) this.trans.clone();
1796

    
1797
        return clonedViewPort;
1798
    }
1799

    
1800
    /**
1801
     * <p>
1802
     * Returns a <code>String</code> representation of the main values of this
1803
     * view port: <code>{@linkplain #extent}</code>,
1804
     * <code>{@linkplain #adjustedExtent}</code>,
1805
     * <code>{@linkplain #imageSize}</code>, <code>{@linkplain #scale}</code>,
1806
     * and <code>{@linkplain #trans}</code>.
1807
     * </p>
1808
     * 
1809
     * @return a <code>string</code> representation of the main values of this
1810
     *         view port
1811
     */
1812
    public String toString() {
1813

    
1814
        String str;
1815
        str =
1816
            "Datos del viewPort:\nExtent=" + extent + "\nadjustedExtent="
1817
                + adjustedExtent + "\nimageSize=" + imageSize + "\nescale="
1818
                + scale + "\ntrans=" + trans;
1819

    
1820
        return str;
1821
    }
1822

    
1823
    /**
1824
     * <p>
1825
     * Sets the position and size of the clipping rectangle.
1826
     * </p>
1827
     * 
1828
     * @param rectView
1829
     *            the clipping rectangle to set
1830
     */
1831
    public void setClipRect(Rectangle2D rectView) {
1832
        this.updateDrawVersion();
1833
        cliprect = rectView;
1834
    }
1835

    
1836
    /**
1837
     * <p>
1838
     * Converts and returns the {@link Rectangle2D Rectangle2D}, that is in
1839
     * <i>map coordinates</i> to <i>screen coordinates</i> (pixels) using an
1840
     * <i>inverse transform</i> with the transformation affine information in
1841
     * the {@link #trans #trans} attribute.
1842
     * </p>
1843
     * 
1844
     * @param r
1845
     *            the 2D rectangle in <i>map coordinates</i>
1846
     * @return 2D rectangle equivalent in <i>screen coordinates</i> (pixels)
1847
     * 
1848
     * @see #toMapRectangle(Rectangle2D)
1849
     * @see #fromMapDistance(double)
1850
     * @see #fromMapPoint(Point2D)
1851
     */
1852
    public Rectangle2D fromMapRectangle(Rectangle2D r) {
1853
        Rectangle2D rect = new Rectangle2D.Double();
1854
        Point2D p1 = fromMapPoint((int) r.getX(), (int) r.getY());
1855
        Point2D p2 = fromMapPoint((int) r.getMaxX(), (int) r.getMaxY());
1856
        rect.setFrameFromDiagonal(p1, p2);
1857
        return rect;
1858
    }
1859

    
1860
    /**
1861
     * <p>
1862
     * Recalculates the current <code>{@linkplain #extent}</code> using an
1863
     * scale. It's necessary execute {@linkplain #refreshExtent()} after.
1864
     * </p>
1865
     * 
1866
     * @param s
1867
     *            the scale to set
1868
     * 
1869
     * @deprecated since 07/09/07, use
1870
     *             {@linkplain MapContext#setScaleView(long)}
1871
     */
1872
    public void setScale(long s) {
1873
        double x = extent.getX();
1874
        double y = extent.getY();
1875
        double escalaX = imageSize.width / extent.getWidth();
1876
        // double w = imageSize.width / s;
1877
        // double h = imageSize.height / s;
1878
        double difw = escalaX / s;
1879

    
1880
        double x1 = (-x * difw) - x + extent.getWidth() / 2;
1881
        double y1 = (-y * difw) - y + extent.getHeight() / 2;
1882
        double w1 = extent.getWidth() * difw;
1883
        double h1 = extent.getHeight() * difw;
1884
        extent.setRect(-x1, -y1, w1, h1);
1885
    }
1886

    
1887
    public long getDrawVersion() {
1888
        return this.drawVersion;
1889
    }
1890

    
1891
    protected void updateDrawVersion() {
1892
        this.drawVersion++;
1893
    }
1894

    
1895
    public Time getTime() {
1896
        return time;
1897
    }
1898
    
1899
    public void setTime(Time time) {
1900
        this.time = time;
1901
        this.updateDrawVersion();
1902
        callTimeChanged(time);
1903
    }
1904
}