Statistics
| Revision:

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

History | View | Annotate | Download (52.1 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.gvsig.compat.CompatLocator;
56
import org.gvsig.fmap.crs.CRSFactory;
57
import org.gvsig.fmap.geom.GeometryLocator;
58
import org.gvsig.fmap.geom.GeometryManager;
59
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
60
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
61
import org.gvsig.fmap.geom.primitive.Envelope;
62
import org.gvsig.fmap.mapcontext.events.ColorEvent;
63
import org.gvsig.fmap.mapcontext.events.ExtentEvent;
64
import org.gvsig.fmap.mapcontext.events.ProjectionEvent;
65
import org.gvsig.fmap.mapcontext.events.listeners.ViewPortListener;
66
import org.gvsig.tools.ToolsLocator;
67
import org.gvsig.tools.dynobject.DynStruct;
68
import org.gvsig.tools.persistence.PersistenceManager;
69
import org.gvsig.tools.persistence.Persistent;
70
import org.gvsig.tools.persistence.PersistentState;
71
import org.gvsig.tools.persistence.exception.PersistenceException;
72
import org.slf4j.Logger;
73
import org.slf4j.LoggerFactory;
74

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

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

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

    
163
        /**
164
         * <p>
165
         * Location and dimensions of the extent adjusted to the image size.
166
         * </p>
167
         * 
168
         * @see #getAdjustedExtent()
169
         */
170
        protected Rectangle2D adjustedExtent;
171

    
172
        /**
173
         * Draw version of the context. It's used for know when de componend has
174
         * changed any visualization property
175
         * 
176
         * @see getDrawVersion
177
         * @see updateDrawVersion
178
         */
179
        private long drawVersion = 0L;
180

    
181
        /**
182
         * <p>
183
         * History with the last extents of the view.
184
         * </p>
185
         * 
186
         * @see #setPreviousExtent()
187
         * @see #getExtents()
188
         */
189
        protected ExtentHistory extents = new ExtentHistory();
190

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

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

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

    
260
        /**
261
         * <p>
262
         * Array with the {@link ViewPortListener ViewPortListener}s registered to
263
         * this view port.
264
         * </p>
265
         * 
266
         * @see #addViewPortListener(ViewPortListener)
267
         * @see #removeViewPortListener(ViewPortListener)
268
         */
269
        private ArrayList listeners = new ArrayList();
270

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

    
288
        /**
289
         * <p>
290
         * Clipping area.
291
         * </p>
292
         */
293
        // private Rectangle2D clip;
294

    
295
        /**
296
         * <p>
297
         * Background color of this view.
298
         * </p>
299
         * 
300
         * @see #getBackColor()
301
         * @see #setBackColor(Color)
302
         */
303
        private Color backColor = null; // Color.WHITE;
304

    
305
        /**
306
         * <p>
307
         * Information about the map projection used in this view.
308
         * </p>
309
         * 
310
         * @see #getProjection()
311
         * @see #setProjection(IProjection)
312
         */
313
        private IProjection proj;
314

    
315
        /**
316
         * <p>
317
         * Represents the distance in <i>world coordinates</i> equivalent to 1 pixel
318
         * in the view with the current extent.
319
         * </p>
320
         * 
321
         * @see #getDist1pixel()
322
         * @see #setDist1pixel(double)
323
         */
324
        private double dist1pixel;
325

    
326
        /**
327
         * <p>
328
         * Represents the distance in <i>world coordinates</i> equivalent to 3
329
         * pixels in the view with the current extent.
330
         * </p>
331
         * 
332
         * @see #getDist3pixel()
333
         * @see #setDist3pixel(double)
334
         */
335
        private double dist3pixel;
336

    
337
        /**
338
         * <p>
339
         * Ratio between the size of <code>imageSize</code> and <code>extent</code>:
340
         * <br>
341
         * <i>
342
         * 
343
         * <pre>
344
         * min{(imageSize.getHeight()/extent.getHeight(), imageSize.getWidth()/extent.getWidth())}
345
         * </pre>
346
         * 
347
         * </i>
348
         * </p>
349
         */
350
        private double scale;
351

    
352
        /**
353
         * <p>
354
         * Clipping area.
355
         * </p>
356
         * 
357
         * @see #setClipRect(Rectangle2D)
358
         */
359
        private Rectangle2D cliprect;
360

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

    
386
        public ViewPort() {
387
                
388
        }
389
        /**
390
         * <p>
391
         * Creates a new view port with the information of the projection in
392
         * <code>proj</code> argument, and default configuration:
393
         * </p>
394
         * <p>
395
         * <ul>
396
         * <li><i><code>distanceUnits</code></i> = meters
397
         * <li><i><code>mapUnits</code></i> = meters
398
         * <li><i><code>backColor</code></i> = <i>undefined</i>
399
         * <li><i><code>offset</code></i> = <code>new Point2D.Double(0, 0);</code>
400
         * </ul>
401
         * </p>
402
         * 
403
         * @param proj
404
         *            information of the projection for this view port
405
         */
406
        public ViewPort(IProjection proj) {
407
                // Por defecto
408
                this.proj = proj;
409
        }
410

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

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

    
454
        /**
455
         * <p>
456
         * Removes the specified {@link ViewPortListener ViewPortListener} listener,
457
         * if existed.
458
         * </p>
459
         * 
460
         * @param arg0
461
         *            the listener to remove
462
         * 
463
         * @return <code>true</code> if the contained the specified listener.
464
         * 
465
         * @see #addViewPortListener(ViewPortListener)
466
         */
467
        public boolean removeViewPortListener(ViewPortListener arg0) {
468
                return listeners.remove(arg0);
469
        }
470

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

    
491
                try {
492
                        trans.deltaTransform(pWorld, pScreen);
493
                } catch (Exception e) {
494
                        System.err.print(e.getMessage());
495
                }
496

    
497
                return (int) (d * pScreen.x);
498
        }
499

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

    
521
                try {
522
                        trans.transform(pWorld, pScreen);
523
                } catch (Exception e) {
524
                        System.err.print(e.getMessage());
525
                }
526

    
527
                return pScreen;
528
        }
529

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

    
549
        /**
550
         * <p>
551
         * Converts and returns the 2D point <code>(x,y)</code>, that is in
552
         * <i>screen coordinates</i> (pixels) to <i>map coordinates</i> using the
553
         * affine transformation in the {@link #trans #trans} attribute.
554
         * </p>
555
         * 
556
         * @param x
557
         *            the <code>x</code> <i>screen coordinate</i> of a 2D point
558
         * @param y
559
         *            the <code>y</code> <i>screen coordinate</i> of a 2D point
560
         * 
561
         * @return 2D point equivalent in <i>map coordinates</i>
562
         * 
563
         * @see #toMapPoint(Point2D)
564
         * @see #fromMapPoint(double, double)
565
         */
566
        public Point2D toMapPoint(int x, int y) {
567
                Point2D pScreen = new Point2D.Double(x, y);
568

    
569
                return toMapPoint(pScreen);
570
        }
571

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

    
595
        /**
596
         * <p>
597
         * Converts and returns the distance <code>d</code>, that is in <i>screen
598
         * coordinates</i> to <i>map coordinates</i> using the transformation affine
599
         * information in the {@link #trans #trans} attribute.
600
         * </p>
601
         * 
602
         * @param d
603
         *            distance in pixels
604
         * 
605
         * @return distance equivalent in <i>map coordinates</i>
606
         * 
607
         * @see #fromMapDistance(double)
608
         * @see AffineTransform
609
         */
610
        public double toMapDistance(int d) {
611
                double dist = d / trans.getScaleX();
612

    
613
                return dist;
614
        }
615

    
616
        /**
617
         * <p>
618
         * Converts and returns the 2D point argument, that is in <i>screen
619
         * coordinates</i> (pixels) to <i>map coordinates</i> using the inverse
620
         * affine transformation of the {@link #trans #trans} attribute.
621
         * </p>
622
         * 
623
         * @param pScreen
624
         *            the 2D point in <i>screen coordinates</i> (pixels)
625
         * 
626
         * @return 2D point equivalent in <i>map coordinates</i>
627
         * 
628
         * @see #toMapPoint(int, int)
629
         * @see AffineTransform#createInverse()
630
         * @see AffineTransform#transform(Point2D, Point2D)
631
         */
632
        public Point2D toMapPoint(Point2D pScreen) {
633
                Point2D.Double pWorld = new Point2D.Double();
634
                AffineTransform at;
635

    
636
                try {
637
                        at = trans.createInverse();
638
                        at.transform(pScreen, pWorld);
639
                } catch (NoninvertibleTransformException e) {
640
                        throw new RuntimeException("Non invertible transform Exception", e);
641
                }
642

    
643
                return pWorld;
644
        }
645

    
646
        /**
647
         * <p>
648
         * Returns the real distance (in <i>world coordinates</i>) at the graphic
649
         * layers of two 2D points (in <i>map coordinates</i>) of the plane where is
650
         * selected the <i>extent</i>.
651
         * </p>
652
         * <p>
653
         * If the projection of this view is UTM, considers the Earth curvature.
654
         * </p>
655
         * 
656
         * @param pt1
657
         *            a 2D point in <i>map coordinates</i>
658
         * @param pt2
659
         *            another 2D point in <i>map coordinates</i>
660
         * 
661
         * @return the distance in meters between the two points 2D
662
         * 
663
         * @see GeoCalcImpl#distanceVincenty(Point2D, Point2D)
664
         */
665
        public double distanceWorld(Point2D pt1, Point2D pt2) {
666
                double dist = -1;
667
                dist = pt1.distance(pt2);
668

    
669
                if ((proj != null) && !(proj instanceof UTM)) {
670
                        dist = new GeoCalc(proj).distanceVincenty(proj.toGeo(pt1), proj
671
                                        .toGeo(pt2));
672
                        return dist;
673
                }
674
                return (dist * MapContext.getDistanceTrans2Meter()[getMapUnits()]);
675
        }
676

    
677
        /**
678
         * <p>
679
         * Sets as extent and adjusted extent of this view port, the previous.
680
         * Recalculating its parameters.
681
         * </p>
682
         * 
683
         * @see #getExtents()
684
         * @see #calculateAffineTransform()
685
         * @deprecated use {@link ViewPort#setPreviousEnvelope()}
686
         */
687
        public void setPreviousExtent() {
688
                setPreviousEnvelope();
689
        }
690

    
691
        /**
692
         * <p>
693
         * Sets as envelope and adjusted envelope of this view port, the previous.
694
         * Recalculating its parameters.
695
         * </p>
696
         * 
697
         * @see #getExtents()
698
         * @see #calculateAffineTransform()
699
         */
700
        public void setPreviousEnvelope() {
701
                this.updateDrawVersion();
702
                extent = extents.removePrev();
703

    
704
                // Calcula la transformaci?n af?n
705
                calculateAffineTransform();
706

    
707
                // Lanzamos los eventos de extent cambiado
708
                callExtentChanged(getAdjustedExtent());
709
        }
710

    
711
        /**
712
         * <p>
713
         * Gets the area selected by user using some tool.
714
         * </p>
715
         * 
716
         * <p>
717
         * When the zoom changes (for instance using the <i>zoom in</i> or <i>zoom
718
         * out</i> tools, but also zooming to a selected feature or shape) the
719
         * extent that covers that area is the value returned by this method. It is
720
         * not the actual area shown because it doesn't care about the aspect ratio
721
         * of the image size of the view. However, any part of the real world
722
         * contained in this extent is shown in the view.
723
         * </p>
724
         * 
725
         * <p>
726
         * If you are looking for the complete extent currently shown, you must use
727
         * the {@linkplain #getAdjustedExtent()} method.
728
         * </p>
729
         * 
730
         * @return the current extent
731
         * 
732
         * @see #setEnvelope(Envelope)
733
         * @see #getAdjustedExtent()
734
         * @see #setPreviousExtent()
735
         * @see #getExtents()
736
         * 
737
         * @deprecated use {@link ViewPort#getEnvelope()}
738
         */
739
        public Rectangle2D getExtent() {
740
                return extent;
741
        }
742

    
743
        /**
744
         * <p>
745
         * Gets the envelope selected by user using some tool.
746
         * </p>
747
         * 
748
         * <p>
749
         * When the zoom changes (for instance using the <i>zoom in</i> or <i>zoom
750
         * out</i> tools, but also zooming to a selected feature or shape) the
751
         * envelope that covers that area is the value returned by this method. It
752
         * is not the actual envelope shown because it doesn't care about the aspect
753
         * ratio of the image size of the view. However, any part of the real world
754
         * contained in this envelope is shown in the view.
755
         * </p>
756
         * 
757
         * <p>
758
         * If you are looking for the complete extent currently shown, you must use
759
         * the {@linkplain #getAdjustedEnvelope()} method.
760
         * </p>
761
         * 
762
         * @return the current envelope
763
         * 
764
         * @see #setEnvelope(Envelope)
765
         * @see #getAdjustedEnvelope()
766
         * @see #setPreviousEnvelope()
767
         * @see #getEnvelopes()
768
         */
769
        public Envelope getEnvelope() {
770
                if( this.extent == null ) {
771
                        return null;
772
                }
773
                try {
774
                        return geomManager.createEnvelope(extent.getMinX(), extent
775
                                        .getMinY(), extent.getMaxX(), extent.getMaxY(),
776
                                        SUBTYPES.GEOM2D);
777
                        // This class has to use Envelope instead of Rectangle2D. This catch
778
                        // will disappear
779
                } catch (CreateEnvelopeException e) {
780
                        logger.error("Error creating the envelope");
781
                }
782
                return null;
783
        }
784

    
785
        /**
786
         * <p>
787
         * Changes the <i>extent</i> and <i>adjusted extent</i> of this view port:<br>
788
         * <ul>
789
         * <li>Stores the previous extent.
790
         * <li>Calculates the new extent using <code>r</code>:
791
         * 
792
         * <pre>
793
         * extent = new Rectangle2D.Double(r.getMinX() - 0.1, r.getMinY() - 0.1, r
794
         *                 .getWidth() + 0.2, r.getHeight() + 0.2);
795
         * </pre>
796
         * 
797
         * <li>Executes {@linkplain #calculateAffineTransform()}: getting the new
798
         * scale, adjusted extent, affine transformation between map and screen
799
         * coordinates, the real world coordinates equivalent to 1 pixel, and the
800
         * real world coordinates equivalent to 3 pixels.
801
         * <li>Notifies all {@link ViewPortListener ViewPortListener} registered
802
         * that the extent has changed.
803
         * </ul>
804
         * </p>
805
         * 
806
         * @param r
807
         *            the new extent
808
         * 
809
         * @see #getExtent()
810
         * @see #getExtents()
811
         * @see #calculateAffineTransform()
812
         * @see #setPreviousExtent()
813
         */
814
        public void setEnvelope(Envelope r) {
815
                Rectangle2D newExtent = null;
816
                // Esto comprueba que el extent no es de anchura o altura = "0"
817
                // y si es as? lo redimensiona.
818
                if (r != null) {
819
                        if ((r.getMaximum(0) - r.getMinimum(0) == 0)
820
                                        || (r.getMaximum(1) - r.getMinimum(1) == 0)) {
821
                                newExtent = new Rectangle2D.Double(r.getMinimum(0) - 0.1, r
822
                                                .getMinimum(1) - 0.1, r.getMaximum(0) - r.getMinimum(0)
823
                                                + 0.2, r.getMaximum(1) - r.getMinimum(1) + 0.2);
824
                        } else {
825
                                newExtent = new Rectangle2D.Double(r.getMinimum(0), r
826
                                                .getMinimum(1), Math.abs(r.getMaximum(0)
827
                                                - r.getMinimum(0)), Math.abs(r.getMaximum(1)
828
                                                - r.getMinimum(1)));
829
                        }
830
                }
831

    
832
                if (this.extent != null && this.extent.equals(newExtent)) {
833
                        return;
834
                }
835
                if (extent != null) {
836
                        extents.put(extent);
837
                }
838
                this.updateDrawVersion();
839
                this.extent = newExtent;
840

    
841
                // Calcula la transformaci?n af?n
842
                calculateAffineTransform();
843

    
844
                // Lanzamos los eventos de extent cambiado
845
                callExtentChanged(getAdjustedExtent());
846
        }
847

    
848
        /**
849
         * <p>
850
         * Changes the <i>extent</i> and <i>adjusted extent</i> of this view port:<br>
851
         * <ul>
852
         * <li>Executes {@linkplain #calculateAffineTransform()}: getting the new
853
         * scale, adjusted extent, affine transformation between map and screen
854
         * coordinates, the real world coordinates equivalent to 1 pixel, and the
855
         * real world coordinates equivalent to 3 pixels.
856
         * <li>Notifies to all {@link ViewPortListener ViewPortListener} registered
857
         * that the extent has changed.
858
         * </ul>
859
         * </p>
860
         * 
861
         * @see #setEnvelope(Envelope)
862
         * @see #calculateAffineTransform()
863
         */
864
        public void refreshExtent() {
865
                // this.scale = scale;
866

    
867
                // Calcula la transformaci?n af?n
868
                calculateAffineTransform();
869

    
870
                // Lanzamos los eventos de extent cambiado
871
                callExtentChanged(getAdjustedExtent());
872
        }
873

    
874
        /**
875
         * <p>
876
         * Calculates and returns using the current projection of this view port,
877
         * the scale that is the extent in <i>screen coordinates</i> from the image
878
         * in <i>map coordinates</i>.
879
         * </p>
880
         * 
881
         * @return the scale <i>extent / image size</i> projected by this view port
882
         * 
883
         * @deprecated since 07/09/07, use {@linkplain MapContext#getScaleView()}
884
         */
885
        public double getScale() {
886
                return proj.getScale(extent.getMinX(), extent.getMaxX(),
887
                                imageSize.width, dpi);
888
        }
889

    
890
        /**
891
         * <p>
892
         * Affine transformation between <i>map 2D coordinates</i> to <i>screen 2D
893
         * coordinates</i> (pixels), preserving the "straightness" and "parallelism"
894
         * of the lines.
895
         * </p>
896
         * 
897
         * @return the affine transformation
898
         * 
899
         * @see #setAffineTransform(AffineTransform)
900
         * @see #calculateAffineTransform()
901
         */
902
        public AffineTransform getAffineTransform() {
903
                return trans;
904
        }
905

    
906
        /**
907
         * <p>
908
         * Returns the size of the image projected.
909
         * </p>
910
         * 
911
         * @return the image size
912
         * 
913
         * @see #setImageSize(Dimension)
914
         * @see #getImageHeight()
915
         * @see #getImageWidth()
916
         */
917
        public Dimension getImageSize() {
918
                return imageSize;
919
        }
920

    
921
        /**
922
         * <p>
923
         * Sets the size of the image projected, recalculating the parameters of
924
         * this view port.
925
         * </p>
926
         * 
927
         * @param imageSize
928
         *            the image size
929
         * 
930
         * @see #getImageSize()
931
         * @see #calculateAffineTransform()
932
         */
933
        public void setImageSize(Dimension imageSize) {
934

    
935
                if (this.imageSize == null || (!this.imageSize.equals(imageSize))) {
936
                        this.updateDrawVersion();
937
                        this.imageSize = imageSize;
938
                        calculateAffineTransform();
939
                }
940
        }
941

    
942
        /**
943
         * <p>
944
         * Notifies to all view port listeners registered, that the adjusted extent
945
         * of this view port has changed.
946
         * </p>
947
         * 
948
         * @param newRect
949
         *            the new adjusted extend
950
         * 
951
         * @see #refreshExtent()
952
         * @see #setEnvelope(Envelope)
953
         * @see #setPreviousExtent()
954
         * @see ExtentEvent
955
         * @see ViewPortListener
956
         */
957
        protected void callExtentChanged(Envelope newRect) {
958
                ExtentEvent ev = ExtentEvent.createExtentEvent(newRect);
959

    
960
                for (int i = 0; i < listeners.size(); i++) {
961
                        ViewPortListener listener = (ViewPortListener) listeners.get(i);
962
                        listener.extentChanged(ev);
963
                }
964
        }
965

    
966
        /**
967
         * <p>
968
         * Notifies to all view port listeners registered, that the background color
969
         * of this view port has changed.
970
         * </p>
971
         * 
972
         * @param c
973
         *            the new background color
974
         * 
975
         * @see #setBackColor(Color)
976
         * @see ColorEvent
977
         * @see ViewPortListener
978
         */
979
        private void callColorChanged(Color c) {
980
                ColorEvent ce = ColorEvent.createColorEvent(c);
981

    
982
                for (int i = 0; i < listeners.size(); i++) {
983
                        ViewPortListener listener = (ViewPortListener) listeners.get(i);
984
                        listener.backColorChanged(ce);
985
                }
986
        }
987

    
988
        /**
989
         * <p>
990
         * Notifies to all view port listeners registered, that the projection of
991
         * this view port has changed.
992
         * </p>
993
         * 
994
         * @param projection
995
         *            the new projection
996
         * 
997
         * @see #setProjection(IProjection)
998
         * @see ProjectionEvent
999
         * @see ViewPortListener
1000
         */
1001
        private void callProjectionChanged(IProjection projection) {
1002
                ProjectionEvent ev = ProjectionEvent.createProjectionEvent(projection);
1003

    
1004
                for (int i = 0; i < listeners.size(); i++) {
1005
                        ViewPortListener listener = (ViewPortListener) listeners.get(i);
1006
                        listener.projectionChanged(ev);
1007
                }
1008
        }
1009

    
1010
        /**
1011
         * <p>
1012
         * Calculates the affine transformation between the {@link #extent extent}
1013
         * in <i>map 2D coordinates</i> to the image area in the screen, in
1014
         * <i>screen 2D coordinates</i> (pixels).
1015
         * </p>
1016
         * 
1017
         * <p>
1018
         * This process recalculates some parameters of this view port:<br>
1019
         * 
1020
         * <ul>
1021
         * <li>The new {@link #scale scale} .
1022
         * <li>The new {@link #adjustedExtent adjustedExtent} .
1023
         * <li>The new {@link #trans trans} .
1024
         * <li>The new real world coordinates equivalent to 1 pixel (
1025
         * {@link #dist1pixel dist1pixel}) .
1026
         * <li>The new real world coordinates equivalent to 3 pixels (
1027
         * {@link #dist3pixel dist3pixel}) .
1028
         * </ul>
1029
         * </p>
1030
         * 
1031
         * @see #getAffineTransform()
1032
         * @see #setAffineTransform(AffineTransform)
1033
         * @see #refreshExtent()
1034
         * @see #setEnvelope(Envelope)
1035
         * @see #setImageSize(Dimension)
1036
         * @see #setPreviousExtent()
1037
         * @see #createFromXML(XMLEntity)
1038
         * @see AffineTransform
1039
         */
1040
        private void calculateAffineTransform() {
1041
                if ((imageSize == null) || (extent == null) || (imageSize.width <= 0)
1042
                                || (imageSize.height <= 0)) {
1043
                        return;
1044
                }
1045

    
1046
                AffineTransform escalado = new AffineTransform();
1047
                AffineTransform translacion = new AffineTransform();
1048

    
1049
                double escalaX;
1050
                double escalaY;
1051

    
1052
                escalaX = imageSize.width / extent.getWidth();
1053
                escalaY = imageSize.height / extent.getHeight();
1054

    
1055
                double xCenter = extent.getCenterX();
1056
                double yCenter = extent.getCenterY();
1057
                double newHeight;
1058
                double newWidth;
1059

    
1060
                adjustedExtent = new Rectangle2D.Double();
1061

    
1062
                if (adjustableExtent) {
1063
                        if (escalaX < escalaY) {
1064
                                scale = escalaX;
1065
                                newHeight = imageSize.height / scale;
1066
                                adjustedExtent.setRect(xCenter - (extent.getWidth() / 2.0),
1067
                                                yCenter - (newHeight / 2.0), extent.getWidth(),
1068
                                                newHeight);
1069
                        } else {
1070
                                scale = escalaY;
1071
                                newWidth = imageSize.width / scale;
1072
                                adjustedExtent.setRect(xCenter - (newWidth / 2.0), yCenter
1073
                                                - (extent.getHeight() / 2.0), newWidth, extent
1074
                                                .getHeight());
1075
                        }
1076
                        escalado.setToScale(scale, -scale);
1077
                } else { // adjusted is same as extent
1078
                        scale = escalaX;
1079
                        adjustedExtent.setFrame(extent);
1080
                        escalado.setToScale(escalaX, -escalaY);
1081
                }
1082
                Envelope env = getAdjustedExtent();
1083
                if (env == null) {
1084
                        return;
1085
                }
1086
                translacion.setToTranslation(-env.getMinimum(0), -env.getMinimum(1)
1087
                                - getAdjustedExtent().getLength(1));
1088

    
1089
                AffineTransform offsetTrans = new AffineTransform();
1090
                offsetTrans.setToTranslation(offset.getX(), offset.getY());
1091

    
1092
                trans.setToIdentity();
1093
                trans.concatenate(offsetTrans);
1094
                trans.concatenate(escalado);
1095

    
1096
                trans.concatenate(translacion);
1097

    
1098
                // Calculamos las distancias de 1 pixel y 3 pixel con esa
1099
                // transformaci?n
1100
                // de coordenadas, de forma que est?n precalculadas para cuando las
1101
                // necesitemos
1102
                AffineTransform at;
1103

    
1104
                try {
1105
                        at = trans.createInverse();
1106

    
1107
                        Point2D pPixel = new Point2D.Float(1, 1);
1108
                        
1109
                        Point2D.Float pProv = new Point2D.Float();
1110
                        at.deltaTransform(pPixel, pProv);
1111

    
1112
                        dist1pixel = pProv.x;
1113
                        dist3pixel = 3 * pProv.x;
1114
                } catch (NoninvertibleTransformException e) {
1115
                        System.err.println("transformada afin = " + trans.toString());
1116
                        System.err.println("extent = " + extent.toString() + " imageSize= "
1117
                                        + imageSize.toString());
1118
                        throw new RuntimeException("Non invertible transform Exception", e);
1119
                }
1120
        }
1121

    
1122
        /**
1123
         * <p>
1124
         * Sets the offset.
1125
         * </p>
1126
         * <p>
1127
         * The offset is the position where start drawing the map.
1128
         * </p>
1129
         * 
1130
         * @param p
1131
         *            2D point that represents the offset in pixels
1132
         * 
1133
         * @see #getOffset()
1134
         */
1135
        public void setOffset(Point2D p) {
1136
                if (!offset.equals(p)) {
1137
                        this.updateDrawVersion();
1138
                        offset = p;
1139
                }
1140
        }
1141

    
1142
        /**
1143
         * <p>
1144
         * Gets the offset.
1145
         * </p>
1146
         * <p>
1147
         * The offset is the position where start drawing the map.
1148
         * </p>
1149
         * 
1150
         * @return 2D point that represents the offset in pixels
1151
         * 
1152
         * @see #setOffset(Point2D)
1153
         */
1154
        public Point2D getOffset() {
1155
                return offset;
1156
        }
1157

    
1158
        /**
1159
         * <p>
1160
         * Sets the background color.
1161
         * </p>
1162
         * 
1163
         * @param c
1164
         *            the new background color
1165
         * 
1166
         * @see #getBackColor()
1167
         */
1168
        public void setBackColor(Color c) {
1169
                if (!c.equals(this.backColor)) {
1170
                        this.updateDrawVersion();
1171
                        backColor = c;
1172
                        callColorChanged(backColor);
1173
                }
1174
        }
1175

    
1176
        /**
1177
         * <p>
1178
         * Gets the background color.
1179
         * </p>
1180
         * 
1181
         * @return the background color of the view
1182
         * 
1183
         * @see #setBackColor(Color)
1184
         */
1185
        public Color getBackColor() {
1186
                return backColor;
1187
        }
1188

    
1189
        /**
1190
         * <p>
1191
         * Returns the extent currently covered by the view adjusted (scaled) to the
1192
         * image size aspect.
1193
         * </p>
1194
         * 
1195
         * @return extent of the view adjusted to the image size aspect
1196
         * 
1197
         * @see #setAdjustable(boolean)
1198
         * @deprecated use {@link ViewPort#getAdjustedEnvelope()} instead
1199
         */
1200
        public Envelope getAdjustedExtent() {
1201
                return getAdjustedEnvelope();
1202
        }
1203

    
1204
        /**
1205
         * <p>
1206
         * Returns the envelope currently covered by the view adjusted (scaled) to
1207
         * the image size aspect.
1208
         * </p>
1209
         * 
1210
         * @return envelope of the view adjusted to the image size aspect
1211
         * 
1212
         * @see #setAdjustable(boolean)
1213
         */
1214
        public Envelope getAdjustedEnvelope() {
1215
                if (cliprect != null) {
1216
                        Rectangle2D r = adjustedExtent.createIntersection(cliprect);
1217
                        try {
1218
                                return geomManager.createEnvelope(r.getX(), r.getY(), r
1219
                                                .getMaxX(), r.getMaxY(), SUBTYPES.GEOM2D);
1220
                        } catch (CreateEnvelopeException e) {
1221
                                e.printStackTrace();
1222
                                logger.error("Error adjusting the extent", e);
1223
                        }
1224
                }
1225
                if (adjustedExtent != null) {
1226
                        try {
1227
                                return geomManager.createEnvelope(adjustedExtent.getX(),
1228
                                                adjustedExtent.getY(), adjustedExtent.getMaxX(),
1229
                                                adjustedExtent.getMaxY(), SUBTYPES.GEOM2D);
1230
                        } catch (CreateEnvelopeException e) {
1231
                                e.printStackTrace();
1232
                                logger.error("Error adjusting the extent", e);
1233
                        }
1234
                }
1235
                return null;
1236
        }
1237

    
1238
        /**
1239
         * <p>
1240
         * Returns the measurement unit of this view port used for measuring
1241
         * distances and displaying information.
1242
         * </p>
1243
         * 
1244
         * @return the measurement unit of this view used for measuring distances
1245
         *         and displaying information
1246
         * 
1247
         * @see #setDistanceUnits(int)
1248
         */
1249
        public int getDistanceUnits() {
1250
                return distanceUnits;
1251
        }
1252

    
1253
        /**
1254
         * <p>
1255
         * Returns the measurement unit of this view port used for measuring areas
1256
         * and displaying information.
1257
         * </p>
1258
         * 
1259
         * @return the measurement unit of this view used for measuring areas and
1260
         *         displaying information
1261
         * 
1262
         * @see #setDistanceUnits(int)
1263
         */
1264
        public int getDistanceArea() {
1265
                return distanceArea;
1266
        }
1267

    
1268
        /**
1269
         * <p>
1270
         * Sets the measurement unit of this view port used for measuring distances
1271
         * and displaying information.
1272
         * </p>
1273
         * 
1274
         * @param distanceUnits
1275
         *            the measurement unit of this view used for measuring distances
1276
         *            and displaying information
1277
         * 
1278
         * @see #getDistanceUnits()
1279
         */
1280
        public void setDistanceUnits(int distanceUnits) {
1281
                this.distanceUnits = distanceUnits;
1282
        }
1283

    
1284
        /**
1285
         * <p>
1286
         * Sets the measurement unit of this view port used for measuring areas and
1287
         * displaying information.
1288
         * </p>
1289
         * 
1290
         * @param distanceUnits
1291
         *            the measurement unit of this view used for measuring areas and
1292
         *            displaying information
1293
         * 
1294
         * @see #getDistanceUnits()
1295
         */
1296
        public void setDistanceArea(int distanceArea) {
1297
                this.distanceArea = distanceArea;
1298
        }
1299

    
1300
        /**
1301
         * <p>
1302
         * Gets the measurement unit used by this view port for the map.
1303
         * </p>
1304
         * 
1305
         * @return Returns the current map measure unit
1306
         * 
1307
         * @see #setMapUnits(int)
1308
         */
1309
        public int getMapUnits() {
1310
                return mapUnits;
1311
        }
1312

    
1313
        /**
1314
         * <p>
1315
         * Sets the measurement unit used by this view port for the map.
1316
         * </p>
1317
         * 
1318
         * @param mapUnits
1319
         *            the new map measure unit
1320
         * 
1321
         * @see #getMapUnits()
1322
         */
1323
        public void setMapUnits(int mapUnits) {
1324
                this.mapUnits = mapUnits;
1325
        }
1326

    
1327
        /**
1328
         * <p>
1329
         * Gets the width in <i>screen coordinates</i> of the rectangle where the
1330
         * image is displayed.
1331
         * </p>
1332
         * <p>
1333
         * Used by {@linkplain #calculateAffineTransform()} to calculate:<br>
1334
         * 
1335
         * <ul>
1336
         * <li>The new {@link #scale scale} .
1337
         * <li>The new {@link #adjustedExtent adjustableExtent} .
1338
         * <li>The new {@link #trans trans} .
1339
         * <li>The new real world coordinates equivalent to 1 pixel (
1340
         * {@link #dist1pixel dist1pixel}) .
1341
         * <li>The new real world coordinates equivalent to 3 pixels (
1342
         * {@link #dist3pixel dist3pixel}) .
1343
         * </ul>
1344
         * </p>
1345
         * 
1346
         * @see #getImageHeight()
1347
         * @see #getImageSize()
1348
         * @see #setImageSize(Dimension)
1349
         */
1350
        public int getImageWidth() {
1351
                return imageSize.width;
1352
        }
1353

    
1354
        /**
1355
         * <p>
1356
         * Gets the height in <i>screen coordinates</i> of the rectangle where the
1357
         * image is displayed.
1358
         * </p>
1359
         * <p>
1360
         * Used by {@linkplain #calculateAffineTransform()} to calculate:<br>
1361
         * 
1362
         * <ul>
1363
         * <li>The new {@link #scale scale} .
1364
         * <li>The new {@link #adjustedExtent adjustableExtent} .
1365
         * <li>The new {@link #trans trans} .
1366
         * <li>The new real world coordinates equivalent to 1 pixel (
1367
         * {@link #dist1pixel dist1pixel}) .
1368
         * <li>The new real world coordinates equivalent to 3 pixels (
1369
         * {@link #dist3pixel dist3pixel}) .
1370
         * </ul>
1371
         * </p>
1372
         * 
1373
         * @see #getImageWidth()
1374
         * @see #getImageSize()
1375
         * @see #setImageSize(Dimension)
1376
         */
1377
        public int getImageHeight() {
1378
                return imageSize.height;
1379
        }
1380

    
1381
        /**
1382
         * <p>
1383
         * Gets the distance in <i>world coordinates</i> equivalent to 1 pixel in
1384
         * the view with the current extent.
1385
         * </p>
1386
         * 
1387
         * @return the distance
1388
         * 
1389
         * @see #setDist1pixel(double)
1390
         */
1391
        public double getDist1pixel() {
1392
                return dist1pixel;
1393
        }
1394

    
1395
        /**
1396
         * <p>
1397
         * Sets the distance in <i>world coordinates</i> equivalent to 1 pixel in
1398
         * the view with the current extent.
1399
         * </p>
1400
         * 
1401
         * @param dist1pixel
1402
         *            the distance
1403
         * 
1404
         * @see #getDist1pixel()
1405
         */
1406
        public void setDist1pixel(double dist1pixel) {
1407
                if (dist1pixel == this.dist1pixel) {
1408
                        return;
1409
                }
1410
                this.updateDrawVersion();
1411
                this.dist1pixel = dist1pixel;
1412
        }
1413

    
1414
        /**
1415
         * <p>
1416
         * Gets the distance in <i>world coordinates</i> equivalent to 3 pixels in
1417
         * the view with the current extent.
1418
         * </p>
1419
         * 
1420
         * @return the distance
1421
         * 
1422
         * @see #setDist3pixel(double)
1423
         */
1424
        public double getDist3pixel() {
1425
                return dist3pixel;
1426
        }
1427

    
1428
        /**
1429
         * <p>
1430
         * Sets the distance in <i>world coordinates</i> equivalent to 3 pixels in
1431
         * the view with the current extent.
1432
         * </p>
1433
         * 
1434
         * @param dist3pixel
1435
         *            the distance
1436
         * 
1437
         * @see #getDist3pixel()
1438
         */
1439
        public void setDist3pixel(double dist3pixel) {
1440
                if (this.dist3pixel == dist3pixel) {
1441
                        return;
1442
                }
1443
                this.updateDrawVersion();
1444
                this.dist3pixel = dist3pixel;
1445
        }
1446

    
1447
        /**
1448
         * <p>
1449
         * Returns the last previous extents of this view port.
1450
         * </p>
1451
         * 
1452
         * @return the last previous extents of this view port
1453
         * 
1454
         * @see #setPreviousExtent()
1455
         * @deprecated use {@link ViewPort#getEnvelopes()}
1456
         */
1457
        public ExtentHistory getExtents() {
1458
                return getEnvelopes();
1459
        }
1460

    
1461
        /**
1462
         * <p>
1463
         * Returns the last previous extents of this view port.
1464
         * </p>
1465
         * 
1466
         * @return the last previous extents of this view port
1467
         * 
1468
         * @see #setPreviousExtent()
1469
         */
1470
        public ExtentHistory getEnvelopes() {
1471
                return extents;
1472
        }
1473

    
1474
        /**
1475
         * <p>
1476
         * Gets the projection used in this view port.
1477
         * </p>
1478
         * 
1479
         * @return projection used in this view port
1480
         * 
1481
         * @see #setProjection(IProjection)
1482
         */
1483
        public IProjection getProjection() {
1484
                return proj;
1485
        }
1486

    
1487
        /**
1488
         * <p>
1489
         * Sets the projection to this view port.
1490
         * </p>
1491
         * 
1492
         * @param proj
1493
         *            the new projection
1494
         * 
1495
         * @see #getProjection()
1496
         */
1497
        public void setProjection(IProjection proj) {
1498
                if (this.proj == null || !this.proj.getAbrev().equals(proj.getAbrev())) {
1499
                        this.updateDrawVersion();
1500
                        this.proj = proj;
1501
                        callProjectionChanged(proj);
1502
                }
1503
        }
1504

    
1505
        // -----------------------------------------------------------------------------------------------------------
1506
        // NOTA PARA DESARROLLADORES SOBRE EL M?TODO
1507
        // "public void setAffineTransform(AffineTransform at)"
1508
        // ==============================================================================================
1509
        // Only used for print, should be removed, redefining the {@link
1510
        // RasterAdapter RasterAdapter} interface,
1511
        // allowing it to receive a {@link ViewPortData ViewPortData} .
1512
        // -----------------------------------------------------------------------------------------------------------
1513

    
1514
        /**
1515
         * <p>
1516
         * Sets only the affine transform to this view port, without updating
1517
         * dependent attributes.
1518
         * </p>
1519
         * <p>
1520
         * <b><i>This method could be problematic!</i></b>
1521
         * </p>
1522
         * 
1523
         * @param at
1524
         *            the affine transform to set
1525
         * 
1526
         * @see #getAffineTransform()
1527
         * @see #calculateAffineTransform()
1528
         */
1529
        public void setAffineTransform(AffineTransform at) {
1530
                this.trans = at;
1531
        }
1532

    
1533
        /**
1534
         * <p>
1535
         * Returns an XML entity that represents this view port instance:<br>
1536
         * <ul>
1537
         * <li>Properties:
1538
         * <ul>
1539
         * <li><i>className</i>: name of this class.
1540
         * <li>If defined, the adjusted extent:
1541
         * <ul>
1542
         * <li><i>adjustedExtentX</i>: X coordinate of the adjusted extent.
1543
         * <li><i>adjustedExtentY</i>: Y coordinate of the adjusted extent.
1544
         * <li><i>adjustedExtentW</i>: width of the adjusted extent.
1545
         * <li><i>adjustedExtentH</i>: height of the adjusted extent.
1546
         * </ul>
1547
         * <li>If defined, the background color:
1548
         * <ul>
1549
         * <li><i>backColor</i>: background color.
1550
         * </ul>
1551
         * <li>If defined, the clip:
1552
         * <ul>
1553
         * <li><i>clipX</i>: X coordinate of the clip.
1554
         * <li><i>clipY</i>: Y coordinate of clip.
1555
         * <li><i>clipW</i>: width of the clip.
1556
         * <li><i>clipH</i>: height of the clip.
1557
         * </ul>
1558
         * <li><i>dist1pixel</i>: the distance in world coordinates equivalent to 1
1559
         * pixel in the view.
1560
         * <li><i>dist3pixel</i>: the distance in world coordinates equivalent to 3
1561
         * pixels in the view.
1562
         * <li><i>distanceUnits</i>: the distance measurement unit.
1563
         * <li>If defined, the extent:
1564
         * <ul>
1565
         * <li><i>extentX</i>: X coordinate of the extent.
1566
         * <li><i>extentY</i>: Y coordinate of the extent.
1567
         * <li><i>extentW</i>: width of the extent.
1568
         * <li><i>extentH</i>: height of the extent.
1569
         * </ul>
1570
         * <li><i>mapUnits</i>: the map measurement unit.
1571
         * <li><i>offsetX</i>: X coordinate of the offset.
1572
         * <li><i>offsetY</i>: Y coordinate of the offset.
1573
         * <li>If defined, the projection:
1574
         * <ul>
1575
         * <li>If its defined, the projection:
1576
         * <ul>
1577
         * <li><i>proj</i>: the projection.</li>
1578
         * </ul>
1579
         * </ul>
1580
         * <li><i>scale</i>: ratio between the size of <code>imageSize</code> and
1581
         * <code>extent</code>.
1582
         * </ul>
1583
         * <li>Child branches:
1584
         * <ul>
1585
         * <li>XML entity of the internal {@link ExtentHistory ExtentHistory} .
1586
         * </ul>
1587
         * </ul>
1588
         * 
1589
         * @return the XML entity
1590
         * 
1591
         * @see #createFromXML(XMLEntity)
1592
         */
1593
        public void saveToState(PersistentState state) throws PersistenceException {
1594

    
1595
                // XMLEntity xml = new XMLEntity();
1596
                // xml.putProperty("className", this.getClass().getName());
1597
                state.set("adjustedExtent", adjustedExtent);
1598
                
1599
                if (backColor != null) {
1600
                        state.set("backColor", color2String(backColor));
1601
                } else {
1602
                        throw new PersistenceException("Background color is null");
1603
                }
1604

    
1605
                if (cliprect != null) {
1606
                        state.set("clip", cliprect);
1607
                }
1608
                
1609
                state.set("dist1pixel", dist1pixel);
1610
                state.set("dist3pixel", dist3pixel);
1611
                state.set("distanceUnits", distanceUnits);
1612
                if (distanceArea != distanceUnits) {
1613
                        state.set("distanceArea", distanceArea);
1614
                }
1615
                
1616
                state.set("extent", extent);
1617
                state.set("extents", extents);
1618
                
1619
                state.set("mapUnits", mapUnits);
1620
                state.set("offsetX", offset.getX());
1621
                state.set("offsetY", offset.getY());
1622

    
1623
                state.set("proj", proj.getFullCode());
1624

    
1625
                state.set("imageWidth", imageSize.width);
1626
                state.set("imageHeight", imageSize.height);
1627
        }
1628
        
1629
        
1630
        
1631
        public static void registerPersistent() {
1632
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
1633
                DynStruct definition = manager.addDefinition(
1634
                                ViewPort.class,
1635
                                "ViewPort",
1636
                                "ViewPort Persistence definition",
1637
                                null, 
1638
                                null
1639
                );
1640
                definition.addDynFieldString("comment");
1641
                definition.addDynFieldInt("code").setMandatory(true);
1642
                definition.addDynFieldString("creationDate").setMandatory(true);
1643
                definition.addDynFieldString("name").setMandatory(true);
1644
                definition.addDynFieldString("owner");
1645
                definition.addDynFieldBoolean("locked");
1646
                
1647
                
1648
        definition.addDynFieldObject("adjustedExtent")
1649
                .setMandatory(true);
1650
        
1651
        definition.addDynFieldString("backColor")
1652
                .setMandatory(true);
1653
        
1654
        definition.addDynFieldObject("clip")
1655
                .setMandatory(false);
1656
        
1657
        definition.addDynFieldDouble("dist1pixel")
1658
                .setMandatory(true);
1659

    
1660
        definition.addDynFieldDouble("dist3pixel")
1661
                .setMandatory(true);
1662

    
1663
        definition.addDynFieldInt("distanceUnits")
1664
                .setMandatory(true);
1665
        
1666
        definition.addDynFieldInt("distanceArea")
1667
                .setMandatory(false);
1668
        
1669
        definition.addDynFieldObject("extent")
1670
                .setMandatory(true);
1671
        
1672
        definition.addDynFieldList("extents")
1673
                .setMandatory(true);
1674
        
1675
        definition.addDynFieldInt("mapUnits")
1676
                .setMandatory(true);
1677
        
1678
        definition.addDynFieldDouble("offsetX")
1679
                .setMandatory(true);
1680

    
1681
        definition.addDynFieldDouble("offsetY")
1682
                .setMandatory(true);
1683
        
1684
        definition.addDynFieldString("proj")
1685
                .setMandatory(true);
1686
        
1687
        definition.addDynFieldInt("imageWidth")
1688
                .setMandatory(true);
1689
        
1690
        definition.addDynFieldInt("imageHeight")
1691
                .setMandatory(true);
1692
        
1693
        }        
1694

    
1695
        public void loadFromState(PersistentState state) throws PersistenceException {
1696
                
1697
                // ViewPort vp = new ViewPort(null);
1698

    
1699
                adjustedExtent = (Rectangle2D) state.get("adjustedExtent");
1700
                
1701
                String str = state.getString("backColor");
1702
                setBackColor(string2Color(str));
1703

    
1704
                try {
1705
                        cliprect = (Rectangle2D) state.get("clip");
1706
                } catch (PersistenceException pex) {
1707
                        // not mandatory
1708
                        cliprect = null;
1709
                }
1710

    
1711
                setDist1pixel(state.getDouble("dist1pixel"));
1712
                setDist3pixel(state.getDouble("dist3pixel"));
1713
                setDistanceUnits(state.getInt("distanceUnits"));
1714

    
1715
                try {
1716
                        setDistanceArea(state.getInt("distanceArea"));
1717
                } catch (Exception pvnf) {
1718
                        // not mandatory , use dist units
1719
                        setDistanceArea(getDistanceUnits());
1720
                }        
1721
                
1722
                extents = (ExtentHistory) state.get("extents");
1723

    
1724
                extent = (Rectangle2D) state.get("extent");
1725

    
1726
                setMapUnits(state.getInt("mapUnits"));
1727

    
1728
                double x = state.getDouble("offsetX");
1729
                double y = state.getDouble("offsetY");
1730
                setOffset(new Point2D.Double(x, y));
1731

    
1732
                proj = CRSFactory.getCRS(state.getString("proj"));
1733
                
1734
                int w = state.getInt("imageWidth");
1735
                int h = state.getInt("imageHeight");
1736
                imageSize = new Dimension(w,h);
1737

    
1738
                refreshExtent();
1739
        }
1740

    
1741
        /**
1742
         * <p>
1743
         * Fast clone implementation: creates and returns a clone of this view port
1744
         * using XML entities.
1745
         * </p>
1746
         * <p>
1747
         * Isn't a <i>deepclone</i> to avoid unnecessary memory consumption.
1748
         * </p>
1749
         * 
1750
         * @return the new view port
1751
         * 
1752
         * @see #createFromXML(XMLEntity)
1753
         */
1754
        public ViewPort cloneViewPort() {
1755
            // TODO: make this class implement clonable.
1756
            //       see http://www.gvsig.org/web/projects/gvsig-desktop/docs/devel/org.gvsig.tools/2.1.0/org-gvsig-tools.lang
1757
                logger.warn("TODO: make this class implement clonable.");
1758
                return null; // createFromXML(getXMLEntity());
1759
        }
1760

    
1761
        /**
1762
         * <p>
1763
         * Returns a <code>String</code> representation of the main values of this
1764
         * view port: <code>{@linkplain #extent}</code>,
1765
         * <code>{@linkplain #adjustedExtent}</code>,
1766
         * <code>{@linkplain #imageSize}</code>, <code>{@linkplain #scale}</code>,
1767
         * and <code>{@linkplain #trans}</code>.
1768
         * </p>
1769
         * 
1770
         * @return a <code>string</code> representation of the main values of this
1771
         *         view port
1772
         */
1773
        public String toString() {
1774

    
1775
                String str;
1776
                str = "Datos del viewPort:\nExtent=" + extent + "\nadjustedExtent="
1777
                                + adjustedExtent + "\nimageSize=" + imageSize + "\nescale="
1778
                                + scale + "\ntrans=" + trans;
1779

    
1780
                return str;
1781
        }
1782

    
1783
        /**
1784
         * <p>
1785
         * Sets the position and size of the clipping rectangle.
1786
         * </p>
1787
         * 
1788
         * @param rectView
1789
         *            the clipping rectangle to set
1790
         */
1791
        public void setClipRect(Rectangle2D rectView) {
1792
                this.updateDrawVersion();
1793
                cliprect = rectView;
1794
        }
1795

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

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

    
1840
                double x1 = (-x * difw) - x + extent.getWidth() / 2;
1841
                double y1 = (-y * difw) - y + extent.getHeight() / 2;
1842
                double w1 = extent.getWidth() * difw;
1843
                double h1 = extent.getHeight() * difw;
1844
                extent.setRect(-x1, -y1, w1, h1);
1845
        }
1846

    
1847
        public long getDrawVersion() {
1848
                return this.drawVersion;
1849
        }
1850

    
1851
        protected void updateDrawVersion() {
1852
                this.drawVersion++;
1853
        }
1854
        
1855
        
1856
        
1857
        
1858
        
1859
    /**
1860
     * Obtiene la representaci?n de un color como String
1861
     *
1862
     * @param c Color
1863
     *
1864
     * @return String
1865
     */
1866
    public static String color2String(Color c) {
1867
            if (c == null) return null;
1868
        return c.getRed() + "," + c.getGreen() + "," + c.getBlue() + "," +
1869
        c.getAlpha();
1870
    }
1871

    
1872
    /**
1873
     * Obtiene el color de un string generado con color2String 
1874
     *
1875
     * @param stringColor string 
1876
     *
1877
     * @return Color
1878
     */
1879
    public static Color string2Color(String stringColor) {
1880
            if (stringColor == null || stringColor.equals("null")) return null;
1881
        String[] ints = new String[4];
1882
        
1883
        ints = CompatLocator.getStringUtils().split(stringColor, ",");
1884

    
1885
        int[] ret = new int[4];
1886

    
1887
        for (int i = 0; i < ret.length; i++) {
1888
            ret[i] = new Integer(ints[i]).intValue();
1889
        }
1890

    
1891
        return new Color(ret[0], ret[1], ret[2], ret[3]);
1892

    
1893
        /*
1894
           long color = new Long(stringColor).longValue();
1895
           long alpha = color / 16777216;
1896
           color = color % 16777216;
1897
        
1898
           long red = color / 65536;
1899
           color = color % 65536;
1900
           long green = color / 256;
1901
           color = color % 256;
1902
           long blue = color;
1903
           return new Color(red, green, blue, alpha);*/
1904
    }
1905

    
1906

    
1907

    
1908

    
1909
}