Statistics
| Revision:

root / branches / v2_0_0_prep / libraries / libFMap_mapcontext / src / org / gvsig / fmap / mapcontext / ViewPort.java @ 37298

History | View | Annotate | Download (59.2 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.Geometry;
60
import org.gvsig.fmap.geom.GeometryLocator;
61
import org.gvsig.fmap.geom.GeometryManager;
62
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
63
import org.gvsig.fmap.geom.exception.CreateGeometryException;
64
import org.gvsig.fmap.geom.primitive.Envelope;
65
import org.gvsig.fmap.geom.primitive.Point;
66
import org.gvsig.fmap.mapcontext.events.ColorEvent;
67
import org.gvsig.fmap.mapcontext.events.ExtentEvent;
68
import org.gvsig.fmap.mapcontext.events.ProjectionEvent;
69
import org.gvsig.fmap.mapcontext.events.listeners.ViewPortListener;
70
import org.gvsig.timesupport.Time;
71
import org.gvsig.tools.ToolsLocator;
72
import org.gvsig.tools.dynobject.DynStruct;
73
import org.gvsig.tools.lang.Cloneable;
74
import org.gvsig.tools.persistence.PersistenceManager;
75
import org.gvsig.tools.persistence.Persistent;
76
import org.gvsig.tools.persistence.PersistentState;
77
import org.gvsig.tools.persistence.exception.PersistenceException;
78
import org.gvsig.tools.util.Callable;
79

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

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

    
142
    private static final GeometryManager geomManager =
143
        GeometryLocator.getGeometryManager();
144
    private static final Logger logger =
145
        LoggerFactory.getLogger(ViewPort.class);
146

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

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

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

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

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

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

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

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

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

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

    
311
    /**
312
     * <p>
313
     * Clipping area.
314
     * </p>
315
     */
316
    // private Rectangle2D clip;
317

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

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

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

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

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

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

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

    
409
    public ViewPort() {
410

    
411
    }
412

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

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

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

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

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

    
515
        try {
516
            trans.deltaTransform(pWorld, pScreen);
517
        } catch (Exception e) {
518
            System.err.print(e.getMessage());
519
        }
520

    
521
        return (int) (d * pScreen.x);
522
    }
523

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

    
545
        try {
546
            trans.transform(pWorld, pScreen);
547
        } catch (Exception e) {
548
            System.err.print(e.getMessage());
549
        }
550

    
551
        return pScreen;
552
    }
553

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

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

    
594
        return toMapPoint(pScreen);
595
    }
596

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

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

    
638
        return dist;
639
    }
640

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

    
662
        try {
663
            at = trans.createInverse();
664
            at.transform(pScreen, pWorld);
665
        } catch (NoninvertibleTransformException e) {
666
            throw new RuntimeException("Non invertible transform Exception", e);
667
        }
668

    
669
        return pWorld;
670
    }
671

    
672
    public Point convertToMapPoint(Point2D pScreen) {
673
            Point2D p = toMapPoint(pScreen);
674
                try {
675
                        return geomManager.createPoint(
676
                                p.getX(), 
677
                                p.getY(), 
678
                                Geometry.SUBTYPES.GEOM2D
679
                        );
680
                } catch (CreateGeometryException e) {
681
                        // FIXME: Use a most especific exception.
682
                        throw new RuntimeException(e);
683
                }
684
    }
685
    
686
    public Point convertToMapPoint(int x, int y) {
687
        Point2D pScreen = new Point2D.Double(x, y);
688

    
689
        return convertToMapPoint(pScreen);
690
    }
691

    
692
    
693
    /**
694
     * <p>
695
     * Returns the real distance (in <i>world coordinates</i>) at the graphic
696
     * layers of two 2D points (in <i>map coordinates</i>) of the plane where is
697
     * selected the <i>extent</i>.
698
     * </p>
699
     * <p>
700
     * If the projection of this view is UTM, considers the Earth curvature.
701
     * </p>
702
     * 
703
     * @param pt1
704
     *            a 2D point in <i>map coordinates</i>
705
     * @param pt2
706
     *            another 2D point in <i>map coordinates</i>
707
     * 
708
     * @return the distance in meters between the two points 2D
709
     * 
710
     * @see GeoCalcImpl#distanceVincenty(Point2D, Point2D)
711
     */
712
    public double distanceWorld(Point2D pt1, Point2D pt2) {
713
        double dist = -1;
714
        dist = pt1.distance(pt2);
715

    
716
        if ((proj != null) && !(proj instanceof UTM)) {
717
            dist =
718
                new GeoCalc(proj).distanceVincenty(proj.toGeo(pt1),
719
                    proj.toGeo(pt2));
720
            return dist;
721
        }
722
        return (dist * MapContext.getDistanceTrans2Meter()[getMapUnits()]);
723
    }
724

    
725
    /**
726
     * <p>
727
     * Sets as extent and adjusted extent of this view port, the previous.
728
     * Recalculating its parameters.
729
     * </p>
730
     * 
731
     * @see #getExtents()
732
     * @see #calculateAffineTransform()
733
     * @deprecated use {@link ViewPort#setPreviousEnvelope()}
734
     */
735
    public void setPreviousExtent() {
736
        setPreviousEnvelope();
737
    }
738

    
739
    /**
740
     * <p>
741
     * Sets as envelope and adjusted envelope of this view port, the previous.
742
     * Recalculating its parameters.
743
     * </p>
744
     * 
745
     * @see #getExtents()
746
     * @see #calculateAffineTransform()
747
     */
748
    public void setPreviousEnvelope() {
749
        this.updateDrawVersion();
750
        extent = extents.removePrev();
751

    
752
        // Calcula la transformaci�n af�n
753
        calculateAffineTransform();
754

    
755
        // Lanzamos los eventos de extent cambiado
756
        callExtentChanged(getAdjustedExtent());
757
    }
758

    
759
    /**
760
     * <p>
761
     * Gets the area selected by user using some tool.
762
     * </p>
763
     * 
764
     * <p>
765
     * When the zoom changes (for instance using the <i>zoom in</i> or <i>zoom
766
     * out</i> tools, but also zooming to a selected feature or shape) the
767
     * extent that covers that area is the value returned by this method. It is
768
     * not the actual area shown because it doesn't care about the aspect ratio
769
     * of the image size of the view. However, any part of the real world
770
     * contained in this extent is shown in the view.
771
     * </p>
772
     * 
773
     * <p>
774
     * If you are looking for the complete extent currently shown, you must use
775
     * the {@linkplain #getAdjustedExtent()} method.
776
     * </p>
777
     * 
778
     * @return the current extent
779
     * 
780
     * @see #setEnvelope(Envelope)
781
     * @see #getAdjustedExtent()
782
     * @see #setPreviousExtent()
783
     * @see #getExtents()
784
     * 
785
     * @deprecated use {@link ViewPort#getEnvelope()}
786
     */
787
    public Rectangle2D getExtent() {
788
        return extent;
789
    }
790

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

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

    
888
        if (this.extent != null && this.extent.equals(newExtent)) {
889
            return;
890
        }
891
        if (extent != null) {
892
            extents.put(extent);
893
        }
894
        this.updateDrawVersion();
895
        this.extent = newExtent;
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
     * Changes the <i>extent</i> and <i>adjusted extent</i> of this view port:<br>
907
     * <ul>
908
     * <li>Executes {@linkplain #calculateAffineTransform()}: getting the new
909
     * scale, adjusted extent, affine transformation between map and screen
910
     * coordinates, the real world coordinates equivalent to 1 pixel, and the
911
     * real world coordinates equivalent to 3 pixels.
912
     * <li>Notifies to all {@link ViewPortListener ViewPortListener} registered
913
     * that the extent has changed.
914
     * </ul>
915
     * </p>
916
     * 
917
     * @see #setEnvelope(Envelope)
918
     * @see #calculateAffineTransform()
919
     */
920
    public void refreshExtent() {
921
        // this.scale = scale;
922

    
923
        // Calcula la transformaci�n af�n
924
        calculateAffineTransform();
925

    
926
        // Lanzamos los eventos de extent cambiado
927
        callExtentChanged(getAdjustedExtent());
928
    }
929

    
930
    /**
931
     * <p>
932
     * Calculates and returns using the current projection of this view port,
933
     * the scale that is the extent in <i>screen coordinates</i> from the image
934
     * in <i>map coordinates</i>.
935
     * </p>
936
     * 
937
     * @return the scale <i>extent / image size</i> projected by this view port
938
     * 
939
     * @deprecated since 07/09/07, use {@linkplain MapContext#getScaleView()}
940
     */
941
    public double getScale() {
942
        return proj.getScale(extent.getMinX(),
943
            extent.getMaxX(),
944
            imageSize.width,
945
            dpi);
946
    }
947

    
948
    /**
949
     * <p>
950
     * Affine transformation between <i>map 2D coordinates</i> to <i>screen 2D
951
     * coordinates</i> (pixels), preserving the "straightness" and "parallelism"
952
     * of the lines.
953
     * </p>
954
     * 
955
     * @return the affine transformation
956
     * 
957
     * @see #setAffineTransform(AffineTransform)
958
     * @see #calculateAffineTransform()
959
     */
960
    public AffineTransform getAffineTransform() {
961
        return trans;
962
    }
963

    
964
    /**
965
     * <p>
966
     * Returns the size of the image projected.
967
     * </p>
968
     * 
969
     * @return the image size
970
     * 
971
     * @see #setImageSize(Dimension)
972
     * @see #getImageHeight()
973
     * @see #getImageWidth()
974
     */
975
    public Dimension getImageSize() {
976
        return imageSize;
977
    }
978

    
979
    /**
980
     * <p>
981
     * Sets the size of the image projected, recalculating the parameters of
982
     * this view port.
983
     * </p>
984
     * 
985
     * @param imageSize
986
     *            the image size
987
     * 
988
     * @see #getImageSize()
989
     * @see #calculateAffineTransform()
990
     */
991
    public void setImageSize(Dimension imageSize) {
992

    
993
        if (this.imageSize == null || (!this.imageSize.equals(imageSize))) {
994
            this.updateDrawVersion();
995
            this.imageSize = imageSize;
996
            calculateAffineTransform();
997
        }
998
    }
999

    
1000
    /**
1001
     * <p>
1002
     * Notifies to all view port listeners registered, that the adjusted extent
1003
     * of this view port has changed.
1004
     * </p>
1005
     * 
1006
     * @param newRect
1007
     *            the new adjusted extend
1008
     * 
1009
     * @see #refreshExtent()
1010
     * @see #setEnvelope(Envelope)
1011
     * @see #setPreviousExtent()
1012
     * @see ExtentEvent
1013
     * @see ViewPortListener
1014
     */
1015
    protected void callExtentChanged(Envelope newRect) {
1016
        ExtentEvent ev = ExtentEvent.createExtentEvent(newRect);
1017

    
1018
        for (int i = 0; i < listeners.size(); i++) {
1019
            ViewPortListener listener = (ViewPortListener) listeners.get(i);
1020
            listener.extentChanged(ev);
1021
        }
1022
    }
1023
    
1024
    /**
1025
     * <p>
1026
     * Notifies to all view port listeners registered, that the time
1027
     * of this view port has changed.
1028
     * </p>
1029
     * 
1030
     * @param newTime
1031
     *            the new time
1032
     * 
1033
     * @see #refreshExtent()
1034
     * @see #setTime(Time)   
1035
     * @see ExtentEvent
1036
     * @see ViewPortListener
1037
     */
1038
    protected void callTimeChanged(Time newTime) {
1039
        ExtentEvent viewPortEvent = new ExtentEvent(newTime);
1040

    
1041
        for (int i = 0; i < listeners.size(); i++) {
1042
            ViewPortListener listener = (ViewPortListener) listeners.get(i);
1043
            listener.extentChanged(viewPortEvent);
1044
        }
1045
    }
1046

    
1047
    /**
1048
     * <p>
1049
     * Notifies to all view port listeners registered, that the background color
1050
     * of this view port has changed.
1051
     * </p>
1052
     * 
1053
     * @param c
1054
     *            the new background color
1055
     * 
1056
     * @see #setBackColor(Color)
1057
     * @see ColorEvent
1058
     * @see ViewPortListener
1059
     */
1060
    private void callColorChanged(Color c) {
1061
        ColorEvent ce = ColorEvent.createColorEvent(c);
1062

    
1063
        for (int i = 0; i < listeners.size(); i++) {
1064
            ViewPortListener listener = (ViewPortListener) listeners.get(i);
1065
            listener.backColorChanged(ce);
1066
        }
1067
    }
1068

    
1069
    /**
1070
     * <p>
1071
     * Notifies to all view port listeners registered, that the projection of
1072
     * this view port has changed.
1073
     * </p>
1074
     * 
1075
     * @param projection
1076
     *            the new projection
1077
     * 
1078
     * @see #setProjection(IProjection)
1079
     * @see ProjectionEvent
1080
     * @see ViewPortListener
1081
     */
1082
    private void callProjectionChanged(IProjection projection) {
1083
        ProjectionEvent ev = ProjectionEvent.createProjectionEvent(projection);
1084

    
1085
        for (int i = 0; i < listeners.size(); i++) {
1086
            ViewPortListener listener = (ViewPortListener) listeners.get(i);
1087
            listener.projectionChanged(ev);
1088
        }
1089
    }
1090

    
1091
    /**
1092
     * <p>
1093
     * Calculates the affine transformation between the {@link #extent extent}
1094
     * in <i>map 2D coordinates</i> to the image area in the screen, in
1095
     * <i>screen 2D coordinates</i> (pixels).
1096
     * </p>
1097
     * 
1098
     * <p>
1099
     * This process recalculates some parameters of this view port:<br>
1100
     * 
1101
     * <ul>
1102
     * <li>The new {@link #scale scale} .
1103
     * <li>The new {@link #adjustedExtent adjustedExtent} .
1104
     * <li>The new {@link #trans trans} .
1105
     * <li>The new real world coordinates equivalent to 1 pixel (
1106
     * {@link #dist1pixel dist1pixel}) .
1107
     * <li>The new real world coordinates equivalent to 3 pixels (
1108
     * {@link #dist3pixel dist3pixel}) .
1109
     * </ul>
1110
     * </p>
1111
     * 
1112
     * @see #getAffineTransform()
1113
     * @see #setAffineTransform(AffineTransform)
1114
     * @see #refreshExtent()
1115
     * @see #setEnvelope(Envelope)
1116
     * @see #setImageSize(Dimension)
1117
     * @see #setPreviousExtent()
1118
     * @see #createFromXML(XMLEntity)
1119
     * @see AffineTransform
1120
     */
1121
    private void calculateAffineTransform() {
1122
        if ((imageSize == null) || (extent == null) || (imageSize.width <= 0)
1123
            || (imageSize.height <= 0)) {
1124
            return;
1125
        }
1126

    
1127
        AffineTransform escalado = new AffineTransform();
1128
        AffineTransform translacion = new AffineTransform();
1129

    
1130
        double escalaX;
1131
        double escalaY;
1132

    
1133
        escalaX = imageSize.width / extent.getWidth();
1134
        escalaY = imageSize.height / extent.getHeight();
1135

    
1136
        double xCenter = extent.getCenterX();
1137
        double yCenter = extent.getCenterY();
1138
        double newHeight;
1139
        double newWidth;
1140

    
1141
        adjustedExtent = new Rectangle2D.Double();
1142

    
1143
        if (adjustableExtent) {
1144
            if (escalaX < escalaY) {
1145
                scale = escalaX;
1146
                newHeight = imageSize.height / scale;
1147
                adjustedExtent.setRect(xCenter - (extent.getWidth() / 2.0),
1148
                    yCenter - (newHeight / 2.0),
1149
                    extent.getWidth(),
1150
                    newHeight);
1151
            } else {
1152
                scale = escalaY;
1153
                newWidth = imageSize.width / scale;
1154
                adjustedExtent.setRect(xCenter - (newWidth / 2.0), yCenter
1155
                    - (extent.getHeight() / 2.0), newWidth, extent.getHeight());
1156
            }
1157
            escalado.setToScale(scale, -scale);
1158
        } else { // adjusted is same as extent
1159
            scale = escalaX;
1160
            adjustedExtent.setFrame(extent);
1161
            escalado.setToScale(escalaX, -escalaY);
1162
        }
1163
        Envelope env = getAdjustedExtent();
1164
        if (env == null) {
1165
            return;
1166
        }
1167
        translacion.setToTranslation(-env.getMinimum(0), -env.getMinimum(1)
1168
            - getAdjustedExtent().getLength(1));
1169

    
1170
        AffineTransform offsetTrans = new AffineTransform();
1171
        offsetTrans.setToTranslation(offset.getX(), offset.getY());
1172

    
1173
        trans.setToIdentity();
1174
        trans.concatenate(offsetTrans);
1175
        trans.concatenate(escalado);
1176

    
1177
        trans.concatenate(translacion);
1178

    
1179
        // Calculamos las distancias de 1 pixel y 3 pixel con esa
1180
        // transformaci�n
1181
        // de coordenadas, de forma que est�n precalculadas para cuando las
1182
        // necesitemos
1183
        AffineTransform at;
1184

    
1185
        try {
1186
            at = trans.createInverse();
1187

    
1188
            Point2D pPixel = new Point2D.Float(1, 1);
1189

    
1190
            Point2D.Float pProv = new Point2D.Float();
1191
            at.deltaTransform(pPixel, pProv);
1192

    
1193
            dist1pixel = pProv.x;
1194
            dist3pixel = 3 * pProv.x;
1195
        } catch (NoninvertibleTransformException e) {
1196
            System.err.println("transformada afin = " + trans.toString());
1197
            System.err.println("extent = " + extent.toString() + " imageSize= "
1198
                + imageSize.toString());
1199
            throw new RuntimeException("Non invertible transform Exception", e);
1200
        }
1201
    }
1202

    
1203
    /**
1204
     * <p>
1205
     * Sets the offset.
1206
     * </p>
1207
     * <p>
1208
     * The offset is the position where start drawing the map.
1209
     * </p>
1210
     * 
1211
     * @param p
1212
     *            2D point that represents the offset in pixels
1213
     * 
1214
     * @see #getOffset()
1215
     */
1216
    public void setOffset(Point2D p) {
1217
        if (!offset.equals(p)) {
1218
            this.updateDrawVersion();
1219
            offset = p;
1220
        }
1221
    }
1222

    
1223
    /**
1224
     * <p>
1225
     * Gets the offset.
1226
     * </p>
1227
     * <p>
1228
     * The offset is the position where start drawing the map.
1229
     * </p>
1230
     * 
1231
     * @return 2D point that represents the offset in pixels
1232
     * 
1233
     * @see #setOffset(Point2D)
1234
     */
1235
    public Point2D getOffset() {
1236
        return offset;
1237
    }
1238

    
1239
    /**
1240
     * <p>
1241
     * Sets the background color.
1242
     * </p>
1243
     * 
1244
     * @param c
1245
     *            the new background color
1246
     * 
1247
     * @see #getBackColor()
1248
     */
1249
    public void setBackColor(Color c) {
1250
        if (!c.equals(this.backColor)) {
1251
            this.updateDrawVersion();
1252
            backColor = c;
1253
            callColorChanged(backColor);
1254
        }
1255
    }
1256

    
1257
    /**
1258
     * <p>
1259
     * Gets the background color.
1260
     * </p>
1261
     * 
1262
     * @return the background color of the view
1263
     * 
1264
     * @see #setBackColor(Color)
1265
     */
1266
    public Color getBackColor() {
1267
        return backColor;
1268
    }
1269

    
1270
    /**
1271
     * <p>
1272
     * Returns the extent currently covered by the view adjusted (scaled) to the
1273
     * image size aspect.
1274
     * </p>
1275
     * 
1276
     * @return extent of the view adjusted to the image size aspect
1277
     * 
1278
     * @see #setAdjustable(boolean)
1279
     * @deprecated use {@link ViewPort#getAdjustedEnvelope()} instead
1280
     */
1281
    public Envelope getAdjustedExtent() {
1282
        return getAdjustedEnvelope();
1283
    }
1284

    
1285
    /**
1286
     * <p>
1287
     * Returns the envelope currently covered by the view adjusted (scaled) to
1288
     * the image size aspect.
1289
     * </p>
1290
     * 
1291
     * @return envelope of the view adjusted to the image size aspect
1292
     * 
1293
     * @see #setAdjustable(boolean)
1294
     */
1295
    public Envelope getAdjustedEnvelope() {
1296
        if (cliprect != null) {
1297
            Rectangle2D r = adjustedExtent.createIntersection(cliprect);
1298
            try {
1299
                return geomManager.createEnvelope(r.getX(),
1300
                    r.getY(),
1301
                    r.getMaxX(),
1302
                    r.getMaxY(),
1303
                    SUBTYPES.GEOM2D);
1304
            } catch (CreateEnvelopeException e) {
1305
                e.printStackTrace();
1306
                logger.error("Error adjusting the extent", e);
1307
            }
1308
        }
1309
        if (adjustedExtent != null) {
1310
            try {
1311
                return geomManager.createEnvelope(adjustedExtent.getX(),
1312
                    adjustedExtent.getY(),
1313
                    adjustedExtent.getMaxX(),
1314
                    adjustedExtent.getMaxY(),
1315
                    SUBTYPES.GEOM2D);
1316
            } catch (CreateEnvelopeException e) {
1317
                e.printStackTrace();
1318
                logger.error("Error adjusting the extent", e);
1319
            }
1320
        }
1321
        return null;
1322
    }
1323

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

    
1339
    /**
1340
     * <p>
1341
     * Returns the measurement unit of this view port used for measuring areas
1342
     * and displaying information.
1343
     * </p>
1344
     * 
1345
     * @return the measurement unit of this view used for measuring areas and
1346
     *         displaying information
1347
     * 
1348
     * @see #setDistanceUnits(int)
1349
     */
1350
    public int getDistanceArea() {
1351
        return distanceArea;
1352
    }
1353

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

    
1370
    /**
1371
     * <p>
1372
     * Sets the measurement unit of this view port used for measuring areas and
1373
     * displaying information.
1374
     * </p>
1375
     * 
1376
     * @param distanceUnits
1377
     *            the measurement unit of this view used for measuring areas and
1378
     *            displaying information
1379
     * 
1380
     * @see #getDistanceUnits()
1381
     */
1382
    public void setDistanceArea(int distanceArea) {
1383
        this.distanceArea = distanceArea;
1384
    }
1385

    
1386
    /**
1387
     * <p>
1388
     * Gets the measurement unit used by this view port for the map.
1389
     * </p>
1390
     * 
1391
     * @return Returns the current map measure unit
1392
     * 
1393
     * @see #setMapUnits(int)
1394
     */
1395
    public int getMapUnits() {
1396
        return mapUnits;
1397
    }
1398

    
1399
    /**
1400
     * <p>
1401
     * Sets the measurement unit used by this view port for the map.
1402
     * </p>
1403
     * 
1404
     * @param mapUnits
1405
     *            the new map measure unit
1406
     * 
1407
     * @see #getMapUnits()
1408
     */
1409
    public void setMapUnits(int mapUnits) {
1410
        this.mapUnits = mapUnits;
1411
    }
1412

    
1413
    /**
1414
     * <p>
1415
     * Gets the width in <i>screen coordinates</i> of the rectangle where the
1416
     * image is displayed.
1417
     * </p>
1418
     * <p>
1419
     * Used by {@linkplain #calculateAffineTransform()} to calculate:<br>
1420
     * 
1421
     * <ul>
1422
     * <li>The new {@link #scale scale} .
1423
     * <li>The new {@link #adjustedExtent adjustableExtent} .
1424
     * <li>The new {@link #trans trans} .
1425
     * <li>The new real world coordinates equivalent to 1 pixel (
1426
     * {@link #dist1pixel dist1pixel}) .
1427
     * <li>The new real world coordinates equivalent to 3 pixels (
1428
     * {@link #dist3pixel dist3pixel}) .
1429
     * </ul>
1430
     * </p>
1431
     * 
1432
     * @see #getImageHeight()
1433
     * @see #getImageSize()
1434
     * @see #setImageSize(Dimension)
1435
     */
1436
    public int getImageWidth() {
1437
        return imageSize.width;
1438
    }
1439

    
1440
    /**
1441
     * <p>
1442
     * Gets the height in <i>screen coordinates</i> of the rectangle where the
1443
     * image is displayed.
1444
     * </p>
1445
     * <p>
1446
     * Used by {@linkplain #calculateAffineTransform()} to calculate:<br>
1447
     * 
1448
     * <ul>
1449
     * <li>The new {@link #scale scale} .
1450
     * <li>The new {@link #adjustedExtent adjustableExtent} .
1451
     * <li>The new {@link #trans trans} .
1452
     * <li>The new real world coordinates equivalent to 1 pixel (
1453
     * {@link #dist1pixel dist1pixel}) .
1454
     * <li>The new real world coordinates equivalent to 3 pixels (
1455
     * {@link #dist3pixel dist3pixel}) .
1456
     * </ul>
1457
     * </p>
1458
     * 
1459
     * @see #getImageWidth()
1460
     * @see #getImageSize()
1461
     * @see #setImageSize(Dimension)
1462
     */
1463
    public int getImageHeight() {
1464
        return imageSize.height;
1465
    }
1466

    
1467
    /**
1468
     * <p>
1469
     * Gets the distance in <i>world coordinates</i> equivalent to 1 pixel in
1470
     * the view with the current extent.
1471
     * </p>
1472
     * 
1473
     * @return the distance
1474
     * 
1475
     * @see #setDist1pixel(double)
1476
     */
1477
    public double getDist1pixel() {
1478
        return dist1pixel;
1479
    }
1480

    
1481
    /**
1482
     * <p>
1483
     * Sets the distance in <i>world coordinates</i> equivalent to 1 pixel in
1484
     * the view with the current extent.
1485
     * </p>
1486
     * 
1487
     * @param dist1pixel
1488
     *            the distance
1489
     * 
1490
     * @see #getDist1pixel()
1491
     */
1492
    public void setDist1pixel(double dist1pixel) {
1493
        if (dist1pixel == this.dist1pixel) {
1494
            return;
1495
        }
1496
        this.updateDrawVersion();
1497
        this.dist1pixel = dist1pixel;
1498
    }
1499

    
1500
    /**
1501
     * <p>
1502
     * Gets the distance in <i>world coordinates</i> equivalent to 3 pixels in
1503
     * the view with the current extent.
1504
     * </p>
1505
     * 
1506
     * @return the distance
1507
     * 
1508
     * @see #setDist3pixel(double)
1509
     */
1510
    public double getDist3pixel() {
1511
        return dist3pixel;
1512
    }
1513

    
1514
    /**
1515
     * <p>
1516
     * Sets the distance in <i>world coordinates</i> equivalent to 3 pixels in
1517
     * the view with the current extent.
1518
     * </p>
1519
     * 
1520
     * @param dist3pixel
1521
     *            the distance
1522
     * 
1523
     * @see #getDist3pixel()
1524
     */
1525
    public void setDist3pixel(double dist3pixel) {
1526
        if (this.dist3pixel == dist3pixel) {
1527
            return;
1528
        }
1529
        this.updateDrawVersion();
1530
        this.dist3pixel = dist3pixel;
1531
    }
1532

    
1533
    /**
1534
     * <p>
1535
     * Returns the last previous extents of this view port.
1536
     * </p>
1537
     * 
1538
     * @return the last previous extents of this view port
1539
     * 
1540
     * @see #setPreviousExtent()
1541
     * @deprecated use {@link ViewPort#getEnvelopes()}
1542
     */
1543
    public ExtentHistory getExtents() {
1544
        return getEnvelopes();
1545
    }
1546

    
1547
    /**
1548
     * <p>
1549
     * Returns the last previous extents of this view port.
1550
     * </p>
1551
     * 
1552
     * @return the last previous extents of this view port
1553
     * 
1554
     * @see #setPreviousExtent()
1555
     */
1556
    public ExtentHistory getEnvelopes() {
1557
        return extents;
1558
    }
1559

    
1560
    /**
1561
     * <p>
1562
     * Gets the projection used in this view port.
1563
     * </p>
1564
     * 
1565
     * @return projection used in this view port
1566
     * 
1567
     * @see #setProjection(IProjection)
1568
     */
1569
    public IProjection getProjection() {
1570
        return proj;
1571
    }
1572

    
1573
    /**
1574
     * <p>
1575
     * Sets the projection to this view port.
1576
     * </p>
1577
     * 
1578
     * @param proj
1579
     *            the new projection
1580
     * 
1581
     * @see #getProjection()
1582
     */
1583
    public void setProjection(IProjection proj) {
1584
        if (this.proj == null || !this.proj.getAbrev().equals(proj.getAbrev())) {
1585
            this.updateDrawVersion();
1586
            this.proj = proj;
1587
            callProjectionChanged(proj);
1588
        }
1589
    }
1590

    
1591
    // -----------------------------------------------------------------------------------------------------------
1592
    // NOTA PARA DESARROLLADORES SOBRE EL M�TODO
1593
    // "public void setAffineTransform(AffineTransform at)"
1594
    // ==============================================================================================
1595
    // Only used for print, should be removed, redefining the {@link
1596
    // RasterAdapter RasterAdapter} interface,
1597
    // allowing it to receive a {@link ViewPortData ViewPortData} .
1598
    // -----------------------------------------------------------------------------------------------------------
1599

    
1600
    /**
1601
     * <p>
1602
     * Sets only the affine transform to this view port, without updating
1603
     * dependent attributes.
1604
     * </p>
1605
     * <p>
1606
     * <b><i>This method could be problematic!</i></b>
1607
     * </p>
1608
     * 
1609
     * @param at
1610
     *            the affine transform to set
1611
     * 
1612
     * @see #getAffineTransform()
1613
     * @see #calculateAffineTransform()
1614
     */
1615
    public void setAffineTransform(AffineTransform at) {
1616
        this.trans = at;
1617
    }
1618

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

    
1681
        state.set(FIELD_ADJUSTED_EXTENT, adjustedExtent);
1682
        state.set(FIELD_BACK_COLOR, backColor);
1683
        state.set(FIELD_CLIP, cliprect);
1684
        state.set(FIELD_DIST1PIXEL, dist1pixel);
1685
        state.set(FIELD_DIST3PIXEL, dist3pixel);
1686
        state.set(FIELD_DISTANCE_UNITS, distanceUnits);
1687
        state.set(FIELD_DISTANCE_AREA, distanceArea);
1688

    
1689
        state.set(FIELD_EXTENT, extent);
1690
        state.set(FIELD_EXTENTS, extents);
1691

    
1692
        state.set(FIELD_MAP_UNITS, mapUnits);
1693
        state.set(FIELD_OFFSET, offset);
1694

    
1695
        state.set(FIELD_PROJ, proj);
1696

    
1697
        state.set(FIELD_IMAGE_SIZE, imageSize);
1698
    }
1699

    
1700
    public void loadFromState(PersistentState state) throws PersistenceException {
1701

    
1702
        adjustedExtent = (Rectangle2D) state.get(FIELD_ADJUSTED_EXTENT);
1703
        backColor = (Color) state.get(FIELD_BACK_COLOR);
1704
        cliprect = (Rectangle2D) state.get(FIELD_CLIP);
1705
        dist1pixel = state.getDouble(FIELD_DIST1PIXEL);
1706
        dist3pixel = state.getDouble(FIELD_DIST3PIXEL);
1707
        distanceUnits = state.getInt(FIELD_DISTANCE_UNITS);
1708
        extents = (ExtentHistory) state.get(FIELD_EXTENTS);
1709
        extent = (Rectangle2D) state.get(FIELD_EXTENT);
1710
        mapUnits = state.getInt(FIELD_MAP_UNITS);
1711
        offset = (Point2D) state.get(FIELD_OFFSET);
1712
        proj = (IProjection) state.get(FIELD_PROJ);
1713
        imageSize = (Dimension) state.get(FIELD_IMAGE_SIZE);
1714
        distanceArea = state.getInt(FIELD_DISTANCE_AREA);
1715

    
1716
        refreshExtent();
1717
    }
1718

    
1719
        public static class RegisterPersistence implements Callable {
1720

    
1721
                public Object call() throws Exception {
1722
                        PersistenceManager manager = ToolsLocator.getPersistenceManager();
1723
                        if( manager.getDefinition("ViewPort")==null ) {
1724
                        DynStruct definition =
1725
                            manager.addDefinition(ViewPort.class,
1726
                                "ViewPort",
1727
                                "ViewPort Persistence definition",
1728
                                null,
1729
                                null);
1730

    
1731
                        definition.addDynFieldObject(FIELD_ADJUSTED_EXTENT)
1732
                            .setClassOfValue(Rectangle2D.class)
1733
                            .setMandatory(false);
1734

    
1735
                        definition.addDynFieldObject(FIELD_BACK_COLOR)
1736
                            .setClassOfValue(Color.class)
1737
                            .setMandatory(false);
1738

    
1739
                        definition.addDynFieldObject(FIELD_CLIP)
1740
                            .setClassOfValue(Rectangle2D.class)
1741
                            .setMandatory(false);
1742

    
1743
                        definition.addDynFieldDouble(FIELD_DIST1PIXEL).setMandatory(true);
1744

    
1745
                        definition.addDynFieldDouble(FIELD_DIST3PIXEL).setMandatory(true);
1746

    
1747
                        definition.addDynFieldInt(FIELD_DISTANCE_UNITS).setMandatory(true);
1748

    
1749
                        definition.addDynFieldInt(FIELD_DISTANCE_AREA).setMandatory(false);
1750

    
1751
                        definition.addDynFieldObject(FIELD_EXTENT)
1752
                            .setClassOfValue(Rectangle2D.class)
1753
                            .setMandatory(false);
1754

    
1755
                        definition.addDynFieldObject(FIELD_EXTENTS)
1756
                            .setClassOfValue(ExtentHistory.class)
1757
                            .setMandatory(true);
1758

    
1759
                        definition.addDynFieldInt(FIELD_MAP_UNITS).setMandatory(true);
1760

    
1761
                        definition.addDynFieldObject(FIELD_OFFSET)
1762
                            .setClassOfValue(Point2D.class)
1763
                            .setMandatory(false);
1764

    
1765
                        definition.addDynFieldObject(FIELD_PROJ)
1766
                            .setClassOfValue(IProjection.class)
1767
                            .setMandatory(true);
1768

    
1769
                        definition.addDynFieldObject(FIELD_IMAGE_SIZE)
1770
                            .setClassOfValue(Dimension.class)
1771
                            .setMandatory(false);
1772
                        }
1773
                        return Boolean.TRUE;
1774
                }
1775
                
1776
        }
1777

    
1778
        /**
1779
     * Clone the view port without clone the listeners nor the extent history.
1780
     * 
1781
     * @return the cloned view port
1782
     */
1783
    public Object clone() throws CloneNotSupportedException {
1784

    
1785
        ViewPort clonedViewPort = (ViewPort) super.clone();
1786
        clonedViewPort.listeners = new ArrayList();
1787
        clonedViewPort.extents = new ExtentHistory();
1788

    
1789
        if (this.adjustedExtent!=null){
1790
            clonedViewPort.adjustedExtent =
1791
                (Rectangle2D) this.adjustedExtent.clone();
1792
        }
1793

    
1794
        if (this.cliprect!=null){
1795
            clonedViewPort.cliprect = (Rectangle2D) this.cliprect.clone();
1796
        }
1797
        
1798
        if (this.extent!=null) {
1799
            clonedViewPort.extent = (Rectangle2D) this.extent.clone();
1800
        }
1801
        if (this.imageSize!=null){
1802
            clonedViewPort.imageSize = (Dimension) this.imageSize.clone();
1803
        }
1804
        
1805
        if (this.offset!=null){
1806
            clonedViewPort.offset = (Point2D) this.offset.clone();
1807
        }
1808
        if (proj!=null){
1809
            clonedViewPort.proj = (IProjection) this.proj.clone();
1810
        }
1811
        
1812
        clonedViewPort.trans = (AffineTransform) this.trans.clone();
1813

    
1814
        return clonedViewPort;
1815
    }
1816

    
1817
    /**
1818
     * <p>
1819
     * Returns a <code>String</code> representation of the main values of this
1820
     * view port: <code>{@linkplain #extent}</code>,
1821
     * <code>{@linkplain #adjustedExtent}</code>,
1822
     * <code>{@linkplain #imageSize}</code>, <code>{@linkplain #scale}</code>,
1823
     * and <code>{@linkplain #trans}</code>.
1824
     * </p>
1825
     * 
1826
     * @return a <code>string</code> representation of the main values of this
1827
     *         view port
1828
     */
1829
    public String toString() {
1830

    
1831
        String str;
1832
        str =
1833
            "Datos del viewPort:\nExtent=" + extent + "\nadjustedExtent="
1834
                + adjustedExtent + "\nimageSize=" + imageSize + "\nescale="
1835
                + scale + "\ntrans=" + trans;
1836

    
1837
        return str;
1838
    }
1839

    
1840
    /**
1841
     * <p>
1842
     * Sets the position and size of the clipping rectangle.
1843
     * </p>
1844
     * 
1845
     * @param rectView
1846
     *            the clipping rectangle to set
1847
     */
1848
    public void setClipRect(Rectangle2D rectView) {
1849
        this.updateDrawVersion();
1850
        cliprect = rectView;
1851
    }
1852

    
1853
    /**
1854
     * <p>
1855
     * Converts and returns the {@link Rectangle2D Rectangle2D}, that is in
1856
     * <i>map coordinates</i> to <i>screen coordinates</i> (pixels) using an
1857
     * <i>inverse transform</i> with the transformation affine information in
1858
     * the {@link #trans #trans} attribute.
1859
     * </p>
1860
     * 
1861
     * @param r
1862
     *            the 2D rectangle in <i>map coordinates</i>
1863
     * @return 2D rectangle equivalent in <i>screen coordinates</i> (pixels)
1864
     * 
1865
     * @see #toMapRectangle(Rectangle2D)
1866
     * @see #fromMapDistance(double)
1867
     * @see #fromMapPoint(Point2D)
1868
     */
1869
    public Rectangle2D fromMapRectangle(Rectangle2D r) {
1870
        Rectangle2D rect = new Rectangle2D.Double();
1871
        Point2D p1 = fromMapPoint((int) r.getX(), (int) r.getY());
1872
        Point2D p2 = fromMapPoint((int) r.getMaxX(), (int) r.getMaxY());
1873
        rect.setFrameFromDiagonal(p1, p2);
1874
        return rect;
1875
    }
1876

    
1877
    /**
1878
     * <p>
1879
     * Recalculates the current <code>{@linkplain #extent}</code> using an
1880
     * scale. It's necessary execute {@linkplain #refreshExtent()} after.
1881
     * </p>
1882
     * 
1883
     * @param s
1884
     *            the scale to set
1885
     * 
1886
     * @deprecated since 07/09/07, use
1887
     *             {@linkplain MapContext#setScaleView(long)}
1888
     */
1889
    public void setScale(long s) {
1890
        double x = extent.getX();
1891
        double y = extent.getY();
1892
        double escalaX = imageSize.width / extent.getWidth();
1893
        // double w = imageSize.width / s;
1894
        // double h = imageSize.height / s;
1895
        double difw = escalaX / s;
1896

    
1897
        double x1 = (-x * difw) - x + extent.getWidth() / 2;
1898
        double y1 = (-y * difw) - y + extent.getHeight() / 2;
1899
        double w1 = extent.getWidth() * difw;
1900
        double h1 = extent.getHeight() * difw;
1901
        extent.setRect(-x1, -y1, w1, h1);
1902
    }
1903

    
1904
    public long getDrawVersion() {
1905
        return this.drawVersion;
1906
    }
1907

    
1908
    protected void updateDrawVersion() {
1909
        this.drawVersion++;
1910
    }
1911

    
1912
    public Time getTime() {
1913
        return time;
1914
    }
1915
    
1916
    public void setTime(Time time) {
1917
        this.time = time;
1918
        this.updateDrawVersion();
1919
        callTimeChanged(time);
1920
    }
1921
}